hs

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | LICENSE

win32_window.c (70222B)


      1 //========================================================================
      2 // GLFW 3.4 Win32 - www.glfw.org
      3 //------------------------------------------------------------------------
      4 // Copyright (c) 2002-2006 Marcus Geelnard
      5 // Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
      6 //
      7 // This software is provided 'as-is', without any express or implied
      8 // warranty. In no event will the authors be held liable for any damages
      9 // arising from the use of this software.
     10 //
     11 // Permission is granted to anyone to use this software for any purpose,
     12 // including commercial applications, and to alter it and redistribute it
     13 // freely, subject to the following restrictions:
     14 //
     15 // 1. The origin of this software must not be misrepresented; you must not
     16 //    claim that you wrote the original software. If you use this software
     17 //    in a product, an acknowledgment in the product documentation would
     18 //    be appreciated but is not required.
     19 //
     20 // 2. Altered source versions must be plainly marked as such, and must not
     21 //    be misrepresented as being the original software.
     22 //
     23 // 3. This notice may not be removed or altered from any source
     24 //    distribution.
     25 //
     26 //========================================================================
     27 // Please use C89 style variable declarations in this file because VS 2010
     28 //========================================================================
     29 
     30 #include "internal.h"
     31 
     32 #include <limits.h>
     33 #include <stdlib.h>
     34 #include <malloc.h>
     35 #include <string.h>
     36 #include <windowsx.h>
     37 #include <shellapi.h>
     38 
     39 // Returns the window style for the specified window
     40 //
     41 static DWORD getWindowStyle(const _GLFWwindow* window)
     42 {
     43     DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
     44 
     45     if (window->monitor)
     46         style |= WS_POPUP;
     47     else
     48     {
     49         style |= WS_SYSMENU | WS_MINIMIZEBOX;
     50 
     51         if (window->decorated)
     52         {
     53             style |= WS_CAPTION;
     54 
     55             if (window->resizable)
     56                 style |= WS_MAXIMIZEBOX | WS_THICKFRAME;
     57         }
     58         else
     59             style |= WS_POPUP;
     60     }
     61 
     62     return style;
     63 }
     64 
     65 // Returns the extended window style for the specified window
     66 //
     67 static DWORD getWindowExStyle(const _GLFWwindow* window)
     68 {
     69     DWORD style = WS_EX_APPWINDOW;
     70 
     71     if (window->monitor || window->floating)
     72         style |= WS_EX_TOPMOST;
     73 
     74     return style;
     75 }
     76 
     77 // Returns the image whose area most closely matches the desired one
     78 //
     79 static const GLFWimage* chooseImage(int count, const GLFWimage* images,
     80                                     int width, int height)
     81 {
     82     int i, leastDiff = INT_MAX;
     83     const GLFWimage* closest = NULL;
     84 
     85     for (i = 0;  i < count;  i++)
     86     {
     87         const int currDiff = abs(images[i].width * images[i].height -
     88                                  width * height);
     89         if (currDiff < leastDiff)
     90         {
     91             closest = images + i;
     92             leastDiff = currDiff;
     93         }
     94     }
     95 
     96     return closest;
     97 }
     98 
     99 // Creates an RGBA icon or cursor
    100 //
    101 static HICON createIcon(const GLFWimage* image,
    102                         int xhot, int yhot, GLFWbool icon)
    103 {
    104     int i;
    105     HDC dc;
    106     HICON handle;
    107     HBITMAP color, mask;
    108     BITMAPV5HEADER bi;
    109     ICONINFO ii;
    110     unsigned char* target = NULL;
    111     unsigned char* source = image->pixels;
    112 
    113     ZeroMemory(&bi, sizeof(bi));
    114     bi.bV5Size        = sizeof(bi);
    115     bi.bV5Width       = image->width;
    116     bi.bV5Height      = -image->height;
    117     bi.bV5Planes      = 1;
    118     bi.bV5BitCount    = 32;
    119     bi.bV5Compression = BI_BITFIELDS;
    120     bi.bV5RedMask     = 0x00ff0000;
    121     bi.bV5GreenMask   = 0x0000ff00;
    122     bi.bV5BlueMask    = 0x000000ff;
    123     bi.bV5AlphaMask   = 0xff000000;
    124 
    125     dc = GetDC(NULL);
    126     color = CreateDIBSection(dc,
    127                              (BITMAPINFO*) &bi,
    128                              DIB_RGB_COLORS,
    129                              (void**) &target,
    130                              NULL,
    131                              (DWORD) 0);
    132     ReleaseDC(NULL, dc);
    133 
    134     if (!color)
    135     {
    136         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
    137                              "Win32: Failed to create RGBA bitmap");
    138         return NULL;
    139     }
    140 
    141     mask = CreateBitmap(image->width, image->height, 1, 1, NULL);
    142     if (!mask)
    143     {
    144         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
    145                              "Win32: Failed to create mask bitmap");
    146         DeleteObject(color);
    147         return NULL;
    148     }
    149 
    150     for (i = 0;  i < image->width * image->height;  i++)
    151     {
    152         target[0] = source[2];
    153         target[1] = source[1];
    154         target[2] = source[0];
    155         target[3] = source[3];
    156         target += 4;
    157         source += 4;
    158     }
    159 
    160     ZeroMemory(&ii, sizeof(ii));
    161     ii.fIcon    = icon;
    162     ii.xHotspot = xhot;
    163     ii.yHotspot = yhot;
    164     ii.hbmMask  = mask;
    165     ii.hbmColor = color;
    166 
    167     handle = CreateIconIndirect(&ii);
    168 
    169     DeleteObject(color);
    170     DeleteObject(mask);
    171 
    172     if (!handle)
    173     {
    174         if (icon)
    175         {
    176             _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
    177                                  "Win32: Failed to create icon");
    178         }
    179         else
    180         {
    181             _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
    182                                  "Win32: Failed to create cursor");
    183         }
    184     }
    185 
    186     return handle;
    187 }
    188 
    189 // Translate content area size to full window size according to styles and DPI
    190 //
    191 static void getFullWindowSize(DWORD style, DWORD exStyle,
    192                               int contentWidth, int contentHeight,
    193                               int* fullWidth, int* fullHeight,
    194                               UINT dpi)
    195 {
    196     RECT rect = { 0, 0, contentWidth, contentHeight };
    197 
    198     if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
    199         AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi);
    200     else
    201         AdjustWindowRectEx(&rect, style, FALSE, exStyle);
    202 
    203     *fullWidth = rect.right - rect.left;
    204     *fullHeight = rect.bottom - rect.top;
    205 }
    206 
    207 // Enforce the content area aspect ratio based on which edge is being dragged
    208 //
    209 static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area)
    210 {
    211     int xoff, yoff;
    212     UINT dpi = USER_DEFAULT_SCREEN_DPI;
    213     const float ratio = (float) window->numer / (float) window->denom;
    214 
    215     if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
    216         dpi = GetDpiForWindow(window->win32.handle);
    217 
    218     getFullWindowSize(getWindowStyle(window), getWindowExStyle(window),
    219                       0, 0, &xoff, &yoff, dpi);
    220 
    221     if (edge == WMSZ_LEFT  || edge == WMSZ_BOTTOMLEFT ||
    222         edge == WMSZ_RIGHT || edge == WMSZ_BOTTOMRIGHT)
    223     {
    224         area->bottom = area->top + yoff +
    225             (int) ((area->right - area->left - xoff) / ratio);
    226     }
    227     else if (edge == WMSZ_TOPLEFT || edge == WMSZ_TOPRIGHT)
    228     {
    229         area->top = area->bottom - yoff -
    230             (int) ((area->right - area->left - xoff) / ratio);
    231     }
    232     else if (edge == WMSZ_TOP || edge == WMSZ_BOTTOM)
    233     {
    234         area->right = area->left + xoff +
    235             (int) ((area->bottom - area->top - yoff) * ratio);
    236     }
    237 }
    238 
    239 // Updates the cursor image according to its cursor mode
    240 //
    241 static void updateCursorImage(_GLFWwindow* window)
    242 {
    243     if (window->cursorMode == GLFW_CURSOR_NORMAL)
    244     {
    245         if (window->cursor)
    246             SetCursor(window->cursor->win32.handle);
    247         else
    248             SetCursor(LoadCursorW(NULL, (LPCWSTR)IDC_ARROW));
    249     }
    250     else
    251         SetCursor(NULL);
    252 }
    253 
    254 // Updates the cursor clip rect
    255 //
    256 static void updateClipRect(_GLFWwindow* window)
    257 {
    258     if (window)
    259     {
    260         RECT clipRect;
    261         GetClientRect(window->win32.handle, &clipRect);
    262         ClientToScreen(window->win32.handle, (POINT*) &clipRect.left);
    263         ClientToScreen(window->win32.handle, (POINT*) &clipRect.right);
    264         ClipCursor(&clipRect);
    265     }
    266     else
    267         ClipCursor(NULL);
    268 }
    269 
    270 // Enables WM_INPUT messages for the mouse for the specified window
    271 //
    272 static void enableRawMouseMotion(_GLFWwindow* window)
    273 {
    274     const RAWINPUTDEVICE rid = { 0x01, 0x02, 0, window->win32.handle };
    275 
    276     if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
    277     {
    278         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
    279                              "Win32: Failed to register raw input device");
    280     }
    281 }
    282 
    283 // Disables WM_INPUT messages for the mouse
    284 //
    285 static void disableRawMouseMotion(_GLFWwindow* window)
    286 {
    287     const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL };
    288 
    289     if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
    290     {
    291         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
    292                              "Win32: Failed to remove raw input device");
    293     }
    294 }
    295 
    296 // Apply disabled cursor mode to a focused window
    297 //
    298 static void disableCursor(_GLFWwindow* window)
    299 {
    300     _glfw.win32.disabledCursorWindow = window;
    301     _glfwPlatformGetCursorPos(window,
    302                               &_glfw.win32.restoreCursorPosX,
    303                               &_glfw.win32.restoreCursorPosY);
    304     updateCursorImage(window);
    305     _glfwCenterCursorInContentArea(window);
    306     updateClipRect(window);
    307 
    308     if (window->rawMouseMotion)
    309         enableRawMouseMotion(window);
    310 }
    311 
    312 // Exit disabled cursor mode for the specified window
    313 //
    314 static void enableCursor(_GLFWwindow* window)
    315 {
    316     if (window->rawMouseMotion)
    317         disableRawMouseMotion(window);
    318 
    319     _glfw.win32.disabledCursorWindow = NULL;
    320     updateClipRect(NULL);
    321     _glfwPlatformSetCursorPos(window,
    322                               _glfw.win32.restoreCursorPosX,
    323                               _glfw.win32.restoreCursorPosY);
    324     updateCursorImage(window);
    325 }
    326 
    327 // Returns whether the cursor is in the content area of the specified window
    328 //
    329 static GLFWbool cursorInContentArea(_GLFWwindow* window)
    330 {
    331     RECT area;
    332     POINT pos;
    333 
    334     if (!GetCursorPos(&pos))
    335         return GLFW_FALSE;
    336 
    337     if (WindowFromPoint(pos) != window->win32.handle)
    338         return GLFW_FALSE;
    339 
    340     GetClientRect(window->win32.handle, &area);
    341     ClientToScreen(window->win32.handle, (POINT*) &area.left);
    342     ClientToScreen(window->win32.handle, (POINT*) &area.right);
    343 
    344     return PtInRect(&area, pos);
    345 }
    346 
    347 // Update native window styles to match attributes
    348 //
    349 static void updateWindowStyles(const _GLFWwindow* window)
    350 {
    351     RECT rect;
    352     DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE);
    353     style &= ~(WS_OVERLAPPEDWINDOW | WS_POPUP);
    354     style |= getWindowStyle(window);
    355 
    356     GetClientRect(window->win32.handle, &rect);
    357 
    358     if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
    359     {
    360         AdjustWindowRectExForDpi(&rect, style, FALSE,
    361                                  getWindowExStyle(window),
    362                                  GetDpiForWindow(window->win32.handle));
    363     }
    364     else
    365         AdjustWindowRectEx(&rect, style, FALSE, getWindowExStyle(window));
    366 
    367     ClientToScreen(window->win32.handle, (POINT*) &rect.left);
    368     ClientToScreen(window->win32.handle, (POINT*) &rect.right);
    369     SetWindowLongW(window->win32.handle, GWL_STYLE, style);
    370     SetWindowPos(window->win32.handle, HWND_TOP,
    371                  rect.left, rect.top,
    372                  rect.right - rect.left, rect.bottom - rect.top,
    373                  SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER);
    374 }
    375 
    376 // Update window framebuffer transparency
    377 //
    378 static void updateFramebufferTransparency(const _GLFWwindow* window)
    379 {
    380     BOOL enabled;
    381 
    382     if (!IsWindowsVistaOrGreater())
    383         return;
    384 
    385     if (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled)
    386     {
    387         HRGN region = CreateRectRgn(0, 0, -1, -1);
    388         DWM_BLURBEHIND bb = {0};
    389         bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
    390         bb.hRgnBlur = region;
    391         bb.fEnable = TRUE;
    392 
    393         if (SUCCEEDED(DwmEnableBlurBehindWindow(window->win32.handle, &bb)))
    394         {
    395             // Decorated windows don't repaint the transparent background
    396             // leaving a trail behind animations
    397             // HACK: Making the window layered with a transparency color key
    398             //       seems to fix this.  Normally, when specifying
    399             //       a transparency color key to be used when composing the
    400             //       layered window, all pixels painted by the window in this
    401             //       color will be transparent.  That doesn't seem to be the
    402             //       case anymore, at least when used with blur behind window
    403             //       plus negative region.
    404             LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
    405             exStyle |= WS_EX_LAYERED;
    406             SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle);
    407 
    408             // Using a color key not equal to black to fix the trailing
    409             // issue.  When set to black, something is making the hit test
    410             // not resize with the window frame.
    411             SetLayeredWindowAttributes(window->win32.handle,
    412                                        RGB(255, 0, 255), 255, LWA_COLORKEY);
    413         }
    414 
    415         DeleteObject(region);
    416     }
    417     else
    418     {
    419         LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
    420         exStyle &= ~WS_EX_LAYERED;
    421         SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle);
    422         RedrawWindow(window->win32.handle, NULL, NULL,
    423                      RDW_ERASE | RDW_INVALIDATE | RDW_FRAME);
    424     }
    425 }
    426 
    427 // Retrieves and translates modifier keys
    428 //
    429 static int getKeyMods(void)
    430 {
    431     int mods = 0;
    432 
    433     if (GetKeyState(VK_SHIFT) & 0x8000)
    434         mods |= GLFW_MOD_SHIFT;
    435     if (GetKeyState(VK_CONTROL) & 0x8000)
    436         mods |= GLFW_MOD_CONTROL;
    437     if (GetKeyState(VK_MENU) & 0x8000)
    438         mods |= GLFW_MOD_ALT;
    439     if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000)
    440         mods |= GLFW_MOD_SUPER;
    441     if (GetKeyState(VK_CAPITAL) & 1)
    442         mods |= GLFW_MOD_CAPS_LOCK;
    443     if (GetKeyState(VK_NUMLOCK) & 1)
    444         mods |= GLFW_MOD_NUM_LOCK;
    445 
    446     return mods;
    447 }
    448 
    449 static void fitToMonitor(_GLFWwindow* window)
    450 {
    451     MONITORINFO mi = { sizeof(mi) };
    452     GetMonitorInfo(window->monitor->win32.handle, &mi);
    453     SetWindowPos(window->win32.handle, HWND_TOPMOST,
    454                  mi.rcMonitor.left,
    455                  mi.rcMonitor.top,
    456                  mi.rcMonitor.right - mi.rcMonitor.left,
    457                  mi.rcMonitor.bottom - mi.rcMonitor.top,
    458                  SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS);
    459 }
    460 
    461 // Make the specified window and its video mode active on its monitor
    462 //
    463 static void acquireMonitor(_GLFWwindow* window)
    464 {
    465     if (!_glfw.win32.acquiredMonitorCount)
    466     {
    467         SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
    468 
    469         // HACK: When mouse trails are enabled the cursor becomes invisible when
    470         //       the OpenGL ICD switches to page flipping
    471         if (IsWindowsXPOrGreater())
    472         {
    473             SystemParametersInfo(SPI_GETMOUSETRAILS, 0, &_glfw.win32.mouseTrailSize, 0);
    474             SystemParametersInfo(SPI_SETMOUSETRAILS, 0, 0, 0);
    475         }
    476     }
    477 
    478     if (!window->monitor->window)
    479         _glfw.win32.acquiredMonitorCount++;
    480 
    481     _glfwSetVideoModeWin32(window->monitor, &window->videoMode);
    482     _glfwInputMonitorWindow(window->monitor, window);
    483 }
    484 
    485 // Remove the window and restore the original video mode
    486 //
    487 static void releaseMonitor(_GLFWwindow* window)
    488 {
    489     if (window->monitor->window != window)
    490         return;
    491 
    492     _glfw.win32.acquiredMonitorCount--;
    493     if (!_glfw.win32.acquiredMonitorCount)
    494     {
    495         SetThreadExecutionState(ES_CONTINUOUS);
    496 
    497         // HACK: Restore mouse trail length saved in acquireMonitor
    498         if (IsWindowsXPOrGreater())
    499             SystemParametersInfo(SPI_SETMOUSETRAILS, _glfw.win32.mouseTrailSize, 0, 0);
    500     }
    501 
    502     _glfwInputMonitorWindow(window->monitor, NULL);
    503     _glfwRestoreVideoModeWin32(window->monitor);
    504 }
    505 
    506 // Window callback function (handles window messages)
    507 //
    508 static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
    509                                    WPARAM wParam, LPARAM lParam)
    510 {
    511     _GLFWwindow* window = (_GLFWwindow*)GetPropW(hWnd, L"GLFW");
    512     if (!window)
    513     {
    514         // This is the message handling for the hidden helper window
    515         // and for a regular window during its initial creation
    516 
    517         switch (uMsg)
    518         {
    519             case WM_NCCREATE:
    520             {
    521                 if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
    522                     EnableNonClientDpiScaling(hWnd);
    523 
    524                 break;
    525             }
    526 
    527             case WM_DISPLAYCHANGE:
    528                 _glfwPollMonitorsWin32();
    529                 break;
    530 
    531             case WM_DEVICECHANGE:
    532             {
    533                 if (wParam == DBT_DEVICEARRIVAL)
    534                 {
    535                     DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam;
    536                     if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
    537                         _glfwDetectJoystickConnectionWin32();
    538                 }
    539                 else if (wParam == DBT_DEVICEREMOVECOMPLETE)
    540                 {
    541                     DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam;
    542                     if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
    543                         _glfwDetectJoystickDisconnectionWin32();
    544                 }
    545 
    546                 break;
    547             }
    548         }
    549 
    550         return DefWindowProcW(hWnd, uMsg, wParam, lParam);
    551     }
    552 
    553     switch (uMsg)
    554     {
    555         case WM_MOUSEACTIVATE:
    556         {
    557             // HACK: Postpone cursor disabling when the window was activated by
    558             //       clicking a caption button
    559             if (HIWORD(lParam) == WM_LBUTTONDOWN)
    560             {
    561                 if (LOWORD(lParam) != HTCLIENT)
    562                     window->win32.frameAction = GLFW_TRUE;
    563             }
    564 
    565             break;
    566         }
    567 
    568         case WM_CAPTURECHANGED:
    569         {
    570             // HACK: Disable the cursor once the caption button action has been
    571             //       completed or cancelled
    572             if (lParam == 0 && window->win32.frameAction)
    573             {
    574                 if (window->cursorMode == GLFW_CURSOR_DISABLED)
    575                     disableCursor(window);
    576 
    577                 window->win32.frameAction = GLFW_FALSE;
    578             }
    579 
    580             break;
    581         }
    582 
    583         case WM_SETFOCUS:
    584         {
    585             _glfwInputWindowFocus(window, GLFW_TRUE);
    586 
    587             // HACK: Do not disable cursor while the user is interacting with
    588             //       a caption button
    589             if (window->win32.frameAction)
    590                 break;
    591 
    592             if (window->cursorMode == GLFW_CURSOR_DISABLED)
    593                 disableCursor(window);
    594 
    595             return 0;
    596         }
    597 
    598         case WM_KILLFOCUS:
    599         {
    600             if (window->cursorMode == GLFW_CURSOR_DISABLED)
    601                 enableCursor(window);
    602 
    603             if (window->monitor && window->autoIconify)
    604                 _glfwPlatformIconifyWindow(window);
    605 
    606             _glfwInputWindowFocus(window, GLFW_FALSE);
    607             return 0;
    608         }
    609 
    610         case WM_SYSCOMMAND:
    611         {
    612             switch (wParam & 0xfff0)
    613             {
    614                 case SC_SCREENSAVE:
    615                 case SC_MONITORPOWER:
    616                 {
    617                     if (window->monitor)
    618                     {
    619                         // We are running in full screen mode, so disallow
    620                         // screen saver and screen blanking
    621                         return 0;
    622                     }
    623                     else
    624                         break;
    625                 }
    626 
    627                 // User trying to access application menu using ALT?
    628                 case SC_KEYMENU:
    629                 {
    630                     if (!window->win32.keymenu)
    631                         return 0;
    632 
    633                     break;
    634                 }
    635             }
    636             break;
    637         }
    638 
    639         case WM_CLOSE:
    640         {
    641             _glfwInputWindowCloseRequest(window);
    642             return 0;
    643         }
    644 
    645         case WM_INPUTLANGCHANGE:
    646         {
    647             _glfwUpdateKeyNamesWin32();
    648             break;
    649         }
    650 
    651         case WM_CHAR:
    652         case WM_SYSCHAR:
    653         case WM_UNICHAR:
    654         {
    655             const GLFWbool plain = (uMsg != WM_SYSCHAR);
    656 
    657             if (uMsg == WM_UNICHAR && wParam == UNICODE_NOCHAR)
    658             {
    659                 // WM_UNICHAR is not sent by Windows, but is sent by some
    660                 // third-party input method engine
    661                 // Returning TRUE here announces support for this message
    662                 return TRUE;
    663             }
    664 
    665             _glfwInputChar(window, (unsigned int) wParam, getKeyMods(), plain);
    666 
    667             if (uMsg == WM_SYSCHAR && window->win32.keymenu)
    668                 break;
    669 
    670             return 0;
    671         }
    672 
    673         case WM_KEYDOWN:
    674         case WM_SYSKEYDOWN:
    675         case WM_KEYUP:
    676         case WM_SYSKEYUP:
    677         {
    678             int key, scancode;
    679             const int action = (HIWORD(lParam) & KF_UP) ? GLFW_RELEASE : GLFW_PRESS;
    680             const int mods = getKeyMods();
    681 
    682             scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff));
    683             if (!scancode)
    684             {
    685                 // NOTE: Some synthetic key messages have a scancode of zero
    686                 // HACK: Map the virtual key back to a usable scancode
    687                 scancode = MapVirtualKeyW((UINT) wParam, MAPVK_VK_TO_VSC);
    688             }
    689 
    690             key = _glfw.win32.keycodes[scancode];
    691 
    692             // The Ctrl keys require special handling
    693             if (wParam == VK_CONTROL)
    694             {
    695                 if (HIWORD(lParam) & KF_EXTENDED)
    696                 {
    697                     // Right side keys have the extended key bit set
    698                     key = GLFW_KEY_RIGHT_CONTROL;
    699                 }
    700                 else
    701                 {
    702                     // NOTE: Alt Gr sends Left Ctrl followed by Right Alt
    703                     // HACK: We only want one event for Alt Gr, so if we detect
    704                     //       this sequence we discard this Left Ctrl message now
    705                     //       and later report Right Alt normally
    706                     MSG next;
    707                     const DWORD time = GetMessageTime();
    708 
    709                     if (PeekMessageW(&next, NULL, 0, 0, PM_NOREMOVE))
    710                     {
    711                         if (next.message == WM_KEYDOWN ||
    712                             next.message == WM_SYSKEYDOWN ||
    713                             next.message == WM_KEYUP ||
    714                             next.message == WM_SYSKEYUP)
    715                         {
    716                             if (next.wParam == VK_MENU &&
    717                                 (HIWORD(next.lParam) & KF_EXTENDED) &&
    718                                 next.time == time)
    719                             {
    720                                 // Next message is Right Alt down so discard this
    721                                 break;
    722                             }
    723                         }
    724                     }
    725 
    726                     // This is a regular Left Ctrl message
    727                     key = GLFW_KEY_LEFT_CONTROL;
    728                 }
    729             }
    730             else if (wParam == VK_PROCESSKEY)
    731             {
    732                 // IME notifies that keys have been filtered by setting the
    733                 // virtual key-code to VK_PROCESSKEY
    734                 break;
    735             }
    736 
    737             if (action == GLFW_RELEASE && wParam == VK_SHIFT)
    738             {
    739                 // HACK: Release both Shift keys on Shift up event, as when both
    740                 //       are pressed the first release does not emit any event
    741                 // NOTE: The other half of this is in _glfwPlatformPollEvents
    742                 _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, action, mods);
    743                 _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, action, mods);
    744             }
    745             else if (wParam == VK_SNAPSHOT)
    746             {
    747                 // HACK: Key down is not reported for the Print Screen key
    748                 _glfwInputKey(window, key, scancode, GLFW_PRESS, mods);
    749                 _glfwInputKey(window, key, scancode, GLFW_RELEASE, mods);
    750             }
    751             else
    752                 _glfwInputKey(window, key, scancode, action, mods);
    753 
    754             break;
    755         }
    756 
    757         case WM_LBUTTONDOWN:
    758         case WM_RBUTTONDOWN:
    759         case WM_MBUTTONDOWN:
    760         case WM_XBUTTONDOWN:
    761         case WM_LBUTTONUP:
    762         case WM_RBUTTONUP:
    763         case WM_MBUTTONUP:
    764         case WM_XBUTTONUP:
    765         {
    766             int i, button, action;
    767 
    768             if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP)
    769                 button = GLFW_MOUSE_BUTTON_LEFT;
    770             else if (uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP)
    771                 button = GLFW_MOUSE_BUTTON_RIGHT;
    772             else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP)
    773                 button = GLFW_MOUSE_BUTTON_MIDDLE;
    774             else if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1)
    775                 button = GLFW_MOUSE_BUTTON_4;
    776             else
    777                 button = GLFW_MOUSE_BUTTON_5;
    778 
    779             if (uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN ||
    780                 uMsg == WM_MBUTTONDOWN || uMsg == WM_XBUTTONDOWN)
    781             {
    782                 action = GLFW_PRESS;
    783             }
    784             else
    785                 action = GLFW_RELEASE;
    786 
    787             for (i = 0;  i <= GLFW_MOUSE_BUTTON_LAST;  i++)
    788             {
    789                 if (window->mouseButtons[i] == GLFW_PRESS)
    790                     break;
    791             }
    792 
    793             if (i > GLFW_MOUSE_BUTTON_LAST)
    794                 SetCapture(hWnd);
    795 
    796             _glfwInputMouseClick(window, button, action, getKeyMods());
    797 
    798             for (i = 0;  i <= GLFW_MOUSE_BUTTON_LAST;  i++)
    799             {
    800                 if (window->mouseButtons[i] == GLFW_PRESS)
    801                     break;
    802             }
    803 
    804             if (i > GLFW_MOUSE_BUTTON_LAST)
    805                 ReleaseCapture();
    806 
    807             if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP)
    808                 return TRUE;
    809 
    810             return 0;
    811         }
    812 
    813         case WM_MOUSEMOVE:
    814         {
    815             const int x = GET_X_LPARAM(lParam);
    816             const int y = GET_Y_LPARAM(lParam);
    817 
    818             if (!window->win32.cursorTracked)
    819             {
    820                 TRACKMOUSEEVENT tme;
    821                 ZeroMemory(&tme, sizeof(tme));
    822                 tme.cbSize = sizeof(tme);
    823                 tme.dwFlags = TME_LEAVE;
    824                 tme.hwndTrack = window->win32.handle;
    825                 TrackMouseEvent(&tme);
    826 
    827                 window->win32.cursorTracked = GLFW_TRUE;
    828                 _glfwInputCursorEnter(window, GLFW_TRUE);
    829             }
    830 
    831             if (window->cursorMode == GLFW_CURSOR_DISABLED)
    832             {
    833                 const int dx = x - window->win32.lastCursorPosX;
    834                 const int dy = y - window->win32.lastCursorPosY;
    835 
    836                 if (_glfw.win32.disabledCursorWindow != window)
    837                     break;
    838                 if (window->rawMouseMotion)
    839                     break;
    840 
    841                 _glfwInputCursorPos(window,
    842                                     window->virtualCursorPosX + dx,
    843                                     window->virtualCursorPosY + dy);
    844             }
    845             else
    846                 _glfwInputCursorPos(window, x, y);
    847 
    848             window->win32.lastCursorPosX = x;
    849             window->win32.lastCursorPosY = y;
    850 
    851             return 0;
    852         }
    853 
    854         case WM_INPUT:
    855         {
    856             UINT size = 0;
    857             HRAWINPUT ri = (HRAWINPUT) lParam;
    858             RAWINPUT* data = NULL;
    859             int dx, dy;
    860 
    861             if (_glfw.win32.disabledCursorWindow != window)
    862                 break;
    863             if (!window->rawMouseMotion)
    864                 break;
    865 
    866             GetRawInputData(ri, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
    867             if (size > (UINT) _glfw.win32.rawInputSize)
    868             {
    869                 free(_glfw.win32.rawInput);
    870                 _glfw.win32.rawInput = (RAWINPUT*)calloc(size, 1);
    871                 _glfw.win32.rawInputSize = size;
    872             }
    873 
    874             size = _glfw.win32.rawInputSize;
    875             if (GetRawInputData(ri, RID_INPUT,
    876                                 _glfw.win32.rawInput, &size,
    877                                 sizeof(RAWINPUTHEADER)) == (UINT) -1)
    878             {
    879                 _glfwInputError(GLFW_PLATFORM_ERROR,
    880                                 "Win32: Failed to retrieve raw input data");
    881                 break;
    882             }
    883 
    884             data = _glfw.win32.rawInput;
    885             if (data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE)
    886             {
    887                 dx = data->data.mouse.lLastX - window->win32.lastCursorPosX;
    888                 dy = data->data.mouse.lLastY - window->win32.lastCursorPosY;
    889             }
    890             else
    891             {
    892                 dx = data->data.mouse.lLastX;
    893                 dy = data->data.mouse.lLastY;
    894             }
    895 
    896             _glfwInputCursorPos(window,
    897                                 window->virtualCursorPosX + dx,
    898                                 window->virtualCursorPosY + dy);
    899 
    900             window->win32.lastCursorPosX += dx;
    901             window->win32.lastCursorPosY += dy;
    902             break;
    903         }
    904 
    905         case WM_MOUSELEAVE:
    906         {
    907             window->win32.cursorTracked = GLFW_FALSE;
    908             _glfwInputCursorEnter(window, GLFW_FALSE);
    909             return 0;
    910         }
    911 
    912         case WM_MOUSEWHEEL:
    913         {
    914             _glfwInputScroll(window, 0.0, (SHORT) HIWORD(wParam) / (double) WHEEL_DELTA);
    915             return 0;
    916         }
    917 
    918         case WM_MOUSEHWHEEL:
    919         {
    920             // This message is only sent on Windows Vista and later
    921             // NOTE: The X-axis is inverted for consistency with macOS and X11
    922             _glfwInputScroll(window, -((SHORT) HIWORD(wParam) / (double) WHEEL_DELTA), 0.0);
    923             return 0;
    924         }
    925 
    926         case WM_ENTERSIZEMOVE:
    927         case WM_ENTERMENULOOP:
    928         {
    929             if (window->win32.frameAction)
    930                 break;
    931 
    932             // HACK: Enable the cursor while the user is moving or
    933             //       resizing the window or using the window menu
    934             if (window->cursorMode == GLFW_CURSOR_DISABLED)
    935                 enableCursor(window);
    936 
    937             break;
    938         }
    939 
    940         case WM_EXITSIZEMOVE:
    941         case WM_EXITMENULOOP:
    942         {
    943             if (window->win32.frameAction)
    944                 break;
    945 
    946             // HACK: Disable the cursor once the user is done moving or
    947             //       resizing the window or using the menu
    948             if (window->cursorMode == GLFW_CURSOR_DISABLED)
    949                 disableCursor(window);
    950 
    951             break;
    952         }
    953 
    954         case WM_SIZE:
    955         {
    956             const GLFWbool iconified = wParam == SIZE_MINIMIZED;
    957             const GLFWbool maximized = wParam == SIZE_MAXIMIZED ||
    958                                        (window->win32.maximized &&
    959                                         wParam != SIZE_RESTORED);
    960 
    961             if (_glfw.win32.disabledCursorWindow == window)
    962                 updateClipRect(window);
    963 
    964             if (window->win32.iconified != iconified)
    965                 _glfwInputWindowIconify(window, iconified);
    966 
    967             if (window->win32.maximized != maximized)
    968                 _glfwInputWindowMaximize(window, maximized);
    969 
    970             _glfwInputFramebufferSize(window, LOWORD(lParam), HIWORD(lParam));
    971             _glfwInputWindowSize(window, LOWORD(lParam), HIWORD(lParam));
    972 
    973             if (window->monitor && window->win32.iconified != iconified)
    974             {
    975                 if (iconified)
    976                     releaseMonitor(window);
    977                 else
    978                 {
    979                     acquireMonitor(window);
    980                     fitToMonitor(window);
    981                 }
    982             }
    983 
    984             window->win32.iconified = iconified;
    985             window->win32.maximized = maximized;
    986             return 0;
    987         }
    988 
    989         case WM_MOVE:
    990         {
    991             if (_glfw.win32.disabledCursorWindow == window)
    992                 updateClipRect(window);
    993 
    994             // NOTE: This cannot use LOWORD/HIWORD recommended by MSDN, as
    995             // those macros do not handle negative window positions correctly
    996             _glfwInputWindowPos(window,
    997                                 GET_X_LPARAM(lParam),
    998                                 GET_Y_LPARAM(lParam));
    999             return 0;
   1000         }
   1001 
   1002         case WM_SIZING:
   1003         {
   1004             if (window->numer == GLFW_DONT_CARE ||
   1005                 window->denom == GLFW_DONT_CARE)
   1006             {
   1007                 break;
   1008             }
   1009 
   1010             applyAspectRatio(window, (int) wParam, (RECT*) lParam);
   1011             return TRUE;
   1012         }
   1013 
   1014         case WM_GETMINMAXINFO:
   1015         {
   1016             int xoff, yoff;
   1017             UINT dpi = USER_DEFAULT_SCREEN_DPI;
   1018             MINMAXINFO* mmi = (MINMAXINFO*) lParam;
   1019 
   1020             if (window->monitor)
   1021                 break;
   1022 
   1023             if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
   1024                 dpi = GetDpiForWindow(window->win32.handle);
   1025 
   1026             getFullWindowSize(getWindowStyle(window), getWindowExStyle(window),
   1027                               0, 0, &xoff, &yoff, dpi);
   1028 
   1029             if (window->minwidth != GLFW_DONT_CARE &&
   1030                 window->minheight != GLFW_DONT_CARE)
   1031             {
   1032                 mmi->ptMinTrackSize.x = window->minwidth + xoff;
   1033                 mmi->ptMinTrackSize.y = window->minheight + yoff;
   1034             }
   1035 
   1036             if (window->maxwidth != GLFW_DONT_CARE &&
   1037                 window->maxheight != GLFW_DONT_CARE)
   1038             {
   1039                 mmi->ptMaxTrackSize.x = window->maxwidth + xoff;
   1040                 mmi->ptMaxTrackSize.y = window->maxheight + yoff;
   1041             }
   1042 
   1043             if (!window->decorated)
   1044             {
   1045                 MONITORINFO mi;
   1046                 const HMONITOR mh = MonitorFromWindow(window->win32.handle,
   1047                                                       MONITOR_DEFAULTTONEAREST);
   1048 
   1049                 ZeroMemory(&mi, sizeof(mi));
   1050                 mi.cbSize = sizeof(mi);
   1051                 GetMonitorInfo(mh, &mi);
   1052 
   1053                 mmi->ptMaxPosition.x = mi.rcWork.left - mi.rcMonitor.left;
   1054                 mmi->ptMaxPosition.y = mi.rcWork.top - mi.rcMonitor.top;
   1055                 mmi->ptMaxSize.x = mi.rcWork.right - mi.rcWork.left;
   1056                 mmi->ptMaxSize.y = mi.rcWork.bottom - mi.rcWork.top;
   1057             }
   1058 
   1059             return 0;
   1060         }
   1061 
   1062         case WM_PAINT:
   1063         {
   1064             _glfwInputWindowDamage(window);
   1065             break;
   1066         }
   1067 
   1068         case WM_ERASEBKGND:
   1069         {
   1070             return TRUE;
   1071         }
   1072 
   1073         case WM_NCACTIVATE:
   1074         case WM_NCPAINT:
   1075         {
   1076             // Prevent title bar from being drawn after restoring a minimized
   1077             // undecorated window
   1078             if (!window->decorated)
   1079                 return TRUE;
   1080 
   1081             break;
   1082         }
   1083 
   1084         case WM_DWMCOMPOSITIONCHANGED:
   1085         {
   1086             if (window->win32.transparent)
   1087                 updateFramebufferTransparency(window);
   1088             return 0;
   1089         }
   1090 
   1091         case WM_GETDPISCALEDSIZE:
   1092         {
   1093             if (window->win32.scaleToMonitor)
   1094                 break;
   1095 
   1096             // Adjust the window size to keep the content area size constant
   1097             if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32())
   1098             {
   1099                 RECT source = {0}, target = {0};
   1100                 SIZE* size = (SIZE*) lParam;
   1101 
   1102                 AdjustWindowRectExForDpi(&source, getWindowStyle(window),
   1103                                          FALSE, getWindowExStyle(window),
   1104                                          GetDpiForWindow(window->win32.handle));
   1105                 AdjustWindowRectExForDpi(&target, getWindowStyle(window),
   1106                                          FALSE, getWindowExStyle(window),
   1107                                          LOWORD(wParam));
   1108 
   1109                 size->cx += (target.right - target.left) -
   1110                             (source.right - source.left);
   1111                 size->cy += (target.bottom - target.top) -
   1112                             (source.bottom - source.top);
   1113                 return TRUE;
   1114             }
   1115 
   1116             break;
   1117         }
   1118 
   1119         case WM_DPICHANGED:
   1120         {
   1121             const float xscale = HIWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI;
   1122             const float yscale = LOWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI;
   1123 
   1124             // Only apply the suggested size if the OS is new enough to have
   1125             // sent a WM_GETDPISCALEDSIZE before this
   1126             if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32())
   1127             {
   1128                 RECT* suggested = (RECT*) lParam;
   1129                 SetWindowPos(window->win32.handle, HWND_TOP,
   1130                              suggested->left,
   1131                              suggested->top,
   1132                              suggested->right - suggested->left,
   1133                              suggested->bottom - suggested->top,
   1134                              SWP_NOACTIVATE | SWP_NOZORDER);
   1135             }
   1136 
   1137             _glfwInputWindowContentScale(window, xscale, yscale);
   1138             break;
   1139         }
   1140 
   1141         case WM_SETCURSOR:
   1142         {
   1143             if (LOWORD(lParam) == HTCLIENT)
   1144             {
   1145                 updateCursorImage(window);
   1146                 return TRUE;
   1147             }
   1148 
   1149             break;
   1150         }
   1151 
   1152         case WM_DROPFILES:
   1153         {
   1154             HDROP drop = (HDROP) wParam;
   1155             POINT pt;
   1156             int i;
   1157 
   1158             const int count = DragQueryFileW(drop, 0xffffffff, NULL, 0);
   1159             char** paths = (char**)calloc(count, sizeof(char*));
   1160 
   1161             // Move the mouse to the position of the drop
   1162             DragQueryPoint(drop, &pt);
   1163             _glfwInputCursorPos(window, pt.x, pt.y);
   1164 
   1165             for (i = 0;  i < count;  i++)
   1166             {
   1167                 const UINT length = DragQueryFileW(drop, i, NULL, 0);
   1168                 WCHAR* buffer = (WCHAR*)calloc((size_t) length + 1, sizeof(WCHAR));
   1169 
   1170                 DragQueryFileW(drop, i, buffer, length + 1);
   1171                 paths[i] = _glfwCreateUTF8FromWideStringWin32(buffer);
   1172 
   1173                 free(buffer);
   1174             }
   1175 
   1176             _glfwInputDrop(window, count, (const char**) paths);
   1177 
   1178             for (i = 0;  i < count;  i++)
   1179                 free(paths[i]);
   1180             free(paths);
   1181 
   1182             DragFinish(drop);
   1183             return 0;
   1184         }
   1185     }
   1186 
   1187     return DefWindowProcW(hWnd, uMsg, wParam, lParam);
   1188 }
   1189 
   1190 // Creates the GLFW window
   1191 //
   1192 static int createNativeWindow(_GLFWwindow* window,
   1193                               const _GLFWwndconfig* wndconfig,
   1194                               const _GLFWfbconfig* fbconfig)
   1195 {
   1196     int xpos, ypos, fullWidth, fullHeight;
   1197     WCHAR* wideTitle;
   1198     DWORD style = getWindowStyle(window);
   1199     DWORD exStyle = getWindowExStyle(window);
   1200 
   1201     if (window->monitor)
   1202     {
   1203         GLFWvidmode mode;
   1204 
   1205         // NOTE: This window placement is temporary and approximate, as the
   1206         //       correct position and size cannot be known until the monitor
   1207         //       video mode has been picked in _glfwSetVideoModeWin32
   1208         _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos);
   1209         _glfwPlatformGetVideoMode(window->monitor, &mode);
   1210         fullWidth  = mode.width;
   1211         fullHeight = mode.height;
   1212     }
   1213     else
   1214     {
   1215         xpos = CW_USEDEFAULT;
   1216         ypos = CW_USEDEFAULT;
   1217 
   1218         window->win32.maximized = wndconfig->maximized;
   1219         if (wndconfig->maximized)
   1220             style |= WS_MAXIMIZE;
   1221 
   1222         getFullWindowSize(style, exStyle,
   1223                           wndconfig->width, wndconfig->height,
   1224                           &fullWidth, &fullHeight,
   1225                           USER_DEFAULT_SCREEN_DPI);
   1226     }
   1227 
   1228     wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title);
   1229     if (!wideTitle)
   1230         return GLFW_FALSE;
   1231 
   1232     window->win32.handle = CreateWindowExW(exStyle,
   1233                                            _GLFW_WNDCLASSNAME,
   1234                                            wideTitle,
   1235                                            style,
   1236                                            xpos, ypos,
   1237                                            fullWidth, fullHeight,
   1238                                            NULL, // No parent window
   1239                                            NULL, // No window menu
   1240                                            GetModuleHandleW(NULL),
   1241                                            NULL);
   1242 
   1243     free(wideTitle);
   1244 
   1245     if (!window->win32.handle)
   1246     {
   1247         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
   1248                              "Win32: Failed to create window");
   1249         return GLFW_FALSE;
   1250     }
   1251 
   1252     SetPropW(window->win32.handle, L"GLFW", window);
   1253 
   1254     if (IsWindows7OrGreater())
   1255     {
   1256         ChangeWindowMessageFilterEx(window->win32.handle,
   1257                                     WM_DROPFILES, MSGFLT_ALLOW, NULL);
   1258         ChangeWindowMessageFilterEx(window->win32.handle,
   1259                                     WM_COPYDATA, MSGFLT_ALLOW, NULL);
   1260         ChangeWindowMessageFilterEx(window->win32.handle,
   1261                                     WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL);
   1262     }
   1263 
   1264     window->win32.scaleToMonitor = wndconfig->scaleToMonitor;
   1265     window->win32.keymenu = wndconfig->win32.keymenu;
   1266 
   1267     // Adjust window rect to account for DPI scaling of the window frame and
   1268     // (if enabled) DPI scaling of the content area
   1269     // This cannot be done until we know what monitor the window was placed on
   1270     if (!window->monitor)
   1271     {
   1272         RECT rect = { 0, 0, wndconfig->width, wndconfig->height };
   1273         WINDOWPLACEMENT wp = { sizeof(wp) };
   1274 
   1275         if (wndconfig->scaleToMonitor)
   1276         {
   1277             float xscale, yscale;
   1278             _glfwPlatformGetWindowContentScale(window, &xscale, &yscale);
   1279             rect.right = (int) (rect.right * xscale);
   1280             rect.bottom = (int) (rect.bottom * yscale);
   1281         }
   1282 
   1283         ClientToScreen(window->win32.handle, (POINT*) &rect.left);
   1284         ClientToScreen(window->win32.handle, (POINT*) &rect.right);
   1285 
   1286         if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
   1287         {
   1288             AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle,
   1289                                      GetDpiForWindow(window->win32.handle));
   1290         }
   1291         else
   1292             AdjustWindowRectEx(&rect, style, FALSE, exStyle);
   1293 
   1294         // Only update the restored window rect as the window may be maximized
   1295         GetWindowPlacement(window->win32.handle, &wp);
   1296         wp.rcNormalPosition = rect;
   1297         wp.showCmd = SW_HIDE;
   1298         SetWindowPlacement(window->win32.handle, &wp);
   1299     }
   1300 
   1301     DragAcceptFiles(window->win32.handle, TRUE);
   1302 
   1303     if (fbconfig->transparent)
   1304     {
   1305         updateFramebufferTransparency(window);
   1306         window->win32.transparent = GLFW_TRUE;
   1307     }
   1308 
   1309     return GLFW_TRUE;
   1310 }
   1311 
   1312 
   1313 //////////////////////////////////////////////////////////////////////////
   1314 //////                       GLFW internal API                      //////
   1315 //////////////////////////////////////////////////////////////////////////
   1316 
   1317 // Registers the GLFW window class
   1318 //
   1319 GLFWbool _glfwRegisterWindowClassWin32(void)
   1320 {
   1321     WNDCLASSEXW wc;
   1322 
   1323     ZeroMemory(&wc, sizeof(wc));
   1324     wc.cbSize        = sizeof(wc);
   1325     wc.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
   1326     wc.lpfnWndProc   = (WNDPROC) windowProc;
   1327     wc.hInstance     = GetModuleHandleW(NULL);
   1328     wc.hCursor       = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
   1329     wc.lpszClassName = _GLFW_WNDCLASSNAME;
   1330 
   1331     // Load user-provided icon if available
   1332     wc.hIcon = (HICON)LoadImageW(GetModuleHandleW(NULL),
   1333                           L"GLFW_ICON", IMAGE_ICON,
   1334                           0, 0, LR_DEFAULTSIZE | LR_SHARED);
   1335     if (!wc.hIcon)
   1336     {
   1337         // No user-provided icon found, load default icon
   1338         wc.hIcon = (HICON)LoadImageW(NULL,
   1339                               (LPCWSTR)IDI_APPLICATION, IMAGE_ICON,
   1340                               0, 0, LR_DEFAULTSIZE | LR_SHARED);
   1341     }
   1342 
   1343     if (!RegisterClassExW(&wc))
   1344     {
   1345         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
   1346                              "Win32: Failed to register window class");
   1347         return GLFW_FALSE;
   1348     }
   1349 
   1350     return GLFW_TRUE;
   1351 }
   1352 
   1353 // Unregisters the GLFW window class
   1354 //
   1355 void _glfwUnregisterWindowClassWin32(void)
   1356 {
   1357     UnregisterClassW(_GLFW_WNDCLASSNAME, GetModuleHandleW(NULL));
   1358 }
   1359 
   1360 
   1361 //////////////////////////////////////////////////////////////////////////
   1362 //////                       GLFW platform API                      //////
   1363 //////////////////////////////////////////////////////////////////////////
   1364 
   1365 int _glfwPlatformCreateWindow(_GLFWwindow* window,
   1366                               const _GLFWwndconfig* wndconfig,
   1367                               const _GLFWctxconfig* ctxconfig,
   1368                               const _GLFWfbconfig* fbconfig)
   1369 {
   1370     if (!createNativeWindow(window, wndconfig, fbconfig))
   1371         return GLFW_FALSE;
   1372 
   1373     if (ctxconfig->client != GLFW_NO_API)
   1374     {
   1375         if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
   1376         {
   1377             if (!_glfwInitWGL())
   1378                 return GLFW_FALSE;
   1379             if (!_glfwCreateContextWGL(window, ctxconfig, fbconfig))
   1380                 return GLFW_FALSE;
   1381         }
   1382         else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
   1383         {
   1384             if (!_glfwInitEGL())
   1385                 return GLFW_FALSE;
   1386             if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
   1387                 return GLFW_FALSE;
   1388         }
   1389         else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
   1390         {
   1391             if (!_glfwInitOSMesa())
   1392                 return GLFW_FALSE;
   1393             if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
   1394                 return GLFW_FALSE;
   1395         }
   1396     }
   1397 
   1398     if (window->monitor)
   1399     {
   1400         _glfwPlatformShowWindow(window);
   1401         _glfwPlatformFocusWindow(window);
   1402         acquireMonitor(window);
   1403         fitToMonitor(window);
   1404     }
   1405 
   1406     return GLFW_TRUE;
   1407 }
   1408 
   1409 void _glfwPlatformDestroyWindow(_GLFWwindow* window)
   1410 {
   1411     if (window->monitor)
   1412         releaseMonitor(window);
   1413 
   1414     if (window->context.destroy)
   1415         window->context.destroy(window);
   1416 
   1417     if (_glfw.win32.disabledCursorWindow == window)
   1418         _glfw.win32.disabledCursorWindow = NULL;
   1419 
   1420     if (window->win32.handle)
   1421     {
   1422         RemovePropW(window->win32.handle, L"GLFW");
   1423         DestroyWindow(window->win32.handle);
   1424         window->win32.handle = NULL;
   1425     }
   1426 
   1427     if (window->win32.bigIcon)
   1428         DestroyIcon(window->win32.bigIcon);
   1429 
   1430     if (window->win32.smallIcon)
   1431         DestroyIcon(window->win32.smallIcon);
   1432 }
   1433 
   1434 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
   1435 {
   1436     WCHAR* wideTitle = _glfwCreateWideStringFromUTF8Win32(title);
   1437     if (!wideTitle)
   1438         return;
   1439 
   1440     SetWindowTextW(window->win32.handle, wideTitle);
   1441     free(wideTitle);
   1442 }
   1443 
   1444 void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
   1445                                 int count, const GLFWimage* images)
   1446 {
   1447     HICON bigIcon = NULL, smallIcon = NULL;
   1448 
   1449     if (count)
   1450     {
   1451         const GLFWimage* bigImage = chooseImage(count, images,
   1452                                                 GetSystemMetrics(SM_CXICON),
   1453                                                 GetSystemMetrics(SM_CYICON));
   1454         const GLFWimage* smallImage = chooseImage(count, images,
   1455                                                   GetSystemMetrics(SM_CXSMICON),
   1456                                                   GetSystemMetrics(SM_CYSMICON));
   1457 
   1458         bigIcon = createIcon(bigImage, 0, 0, GLFW_TRUE);
   1459         smallIcon = createIcon(smallImage, 0, 0, GLFW_TRUE);
   1460     }
   1461     else
   1462     {
   1463         bigIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICON);
   1464         smallIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICONSM);
   1465     }
   1466 
   1467     SendMessage(window->win32.handle, WM_SETICON, ICON_BIG, (LPARAM) bigIcon);
   1468     SendMessage(window->win32.handle, WM_SETICON, ICON_SMALL, (LPARAM) smallIcon);
   1469 
   1470     if (window->win32.bigIcon)
   1471         DestroyIcon(window->win32.bigIcon);
   1472 
   1473     if (window->win32.smallIcon)
   1474         DestroyIcon(window->win32.smallIcon);
   1475 
   1476     if (count)
   1477     {
   1478         window->win32.bigIcon = bigIcon;
   1479         window->win32.smallIcon = smallIcon;
   1480     }
   1481 }
   1482 
   1483 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
   1484 {
   1485     POINT pos = { 0, 0 };
   1486     ClientToScreen(window->win32.handle, &pos);
   1487 
   1488     if (xpos)
   1489         *xpos = pos.x;
   1490     if (ypos)
   1491         *ypos = pos.y;
   1492 }
   1493 
   1494 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
   1495 {
   1496     RECT rect = { xpos, ypos, xpos, ypos };
   1497 
   1498     if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
   1499     {
   1500         AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
   1501                                  FALSE, getWindowExStyle(window),
   1502                                  GetDpiForWindow(window->win32.handle));
   1503     }
   1504     else
   1505     {
   1506         AdjustWindowRectEx(&rect, getWindowStyle(window),
   1507                            FALSE, getWindowExStyle(window));
   1508     }
   1509 
   1510     SetWindowPos(window->win32.handle, NULL, rect.left, rect.top, 0, 0,
   1511                  SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
   1512 }
   1513 
   1514 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
   1515 {
   1516     RECT area;
   1517     GetClientRect(window->win32.handle, &area);
   1518 
   1519     if (width)
   1520         *width = area.right;
   1521     if (height)
   1522         *height = area.bottom;
   1523 }
   1524 
   1525 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
   1526 {
   1527     if (window->monitor)
   1528     {
   1529         if (window->monitor->window == window)
   1530         {
   1531             acquireMonitor(window);
   1532             fitToMonitor(window);
   1533         }
   1534     }
   1535     else
   1536     {
   1537         RECT rect = { 0, 0, width, height };
   1538 
   1539         if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
   1540         {
   1541             AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
   1542                                      FALSE, getWindowExStyle(window),
   1543                                      GetDpiForWindow(window->win32.handle));
   1544         }
   1545         else
   1546         {
   1547             AdjustWindowRectEx(&rect, getWindowStyle(window),
   1548                                FALSE, getWindowExStyle(window));
   1549         }
   1550 
   1551         SetWindowPos(window->win32.handle, HWND_TOP,
   1552                      0, 0, rect.right - rect.left, rect.bottom - rect.top,
   1553                      SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER);
   1554     }
   1555 }
   1556 
   1557 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
   1558                                       int minwidth, int minheight,
   1559                                       int maxwidth, int maxheight)
   1560 {
   1561     RECT area;
   1562 
   1563     if ((minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) &&
   1564         (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE))
   1565     {
   1566         return;
   1567     }
   1568 
   1569     GetWindowRect(window->win32.handle, &area);
   1570     MoveWindow(window->win32.handle,
   1571                area.left, area.top,
   1572                area.right - area.left,
   1573                area.bottom - area.top, TRUE);
   1574 }
   1575 
   1576 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
   1577 {
   1578     RECT area;
   1579 
   1580     if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE)
   1581         return;
   1582 
   1583     GetWindowRect(window->win32.handle, &area);
   1584     applyAspectRatio(window, WMSZ_BOTTOMRIGHT, &area);
   1585     MoveWindow(window->win32.handle,
   1586                area.left, area.top,
   1587                area.right - area.left,
   1588                area.bottom - area.top, TRUE);
   1589 }
   1590 
   1591 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
   1592 {
   1593     _glfwPlatformGetWindowSize(window, width, height);
   1594 }
   1595 
   1596 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
   1597                                      int* left, int* top,
   1598                                      int* right, int* bottom)
   1599 {
   1600     RECT rect;
   1601     int width, height;
   1602 
   1603     _glfwPlatformGetWindowSize(window, &width, &height);
   1604     SetRect(&rect, 0, 0, width, height);
   1605 
   1606     if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
   1607     {
   1608         AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
   1609                                  FALSE, getWindowExStyle(window),
   1610                                  GetDpiForWindow(window->win32.handle));
   1611     }
   1612     else
   1613     {
   1614         AdjustWindowRectEx(&rect, getWindowStyle(window),
   1615                            FALSE, getWindowExStyle(window));
   1616     }
   1617 
   1618     if (left)
   1619         *left = -rect.left;
   1620     if (top)
   1621         *top = -rect.top;
   1622     if (right)
   1623         *right = rect.right - width;
   1624     if (bottom)
   1625         *bottom = rect.bottom - height;
   1626 }
   1627 
   1628 void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
   1629                                         float* xscale, float* yscale)
   1630 {
   1631     const HANDLE handle = MonitorFromWindow(window->win32.handle,
   1632                                             MONITOR_DEFAULTTONEAREST);
   1633     _glfwGetMonitorContentScaleWin32((HMONITOR)handle, xscale, yscale);
   1634 }
   1635 
   1636 void _glfwPlatformIconifyWindow(_GLFWwindow* window)
   1637 {
   1638     ShowWindow(window->win32.handle, SW_MINIMIZE);
   1639 }
   1640 
   1641 void _glfwPlatformRestoreWindow(_GLFWwindow* window)
   1642 {
   1643     ShowWindow(window->win32.handle, SW_RESTORE);
   1644 }
   1645 
   1646 void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
   1647 {
   1648     ShowWindow(window->win32.handle, SW_MAXIMIZE);
   1649 }
   1650 
   1651 void _glfwPlatformShowWindow(_GLFWwindow* window)
   1652 {
   1653     ShowWindow(window->win32.handle, SW_SHOWNA);
   1654 }
   1655 
   1656 void _glfwPlatformHideWindow(_GLFWwindow* window)
   1657 {
   1658     ShowWindow(window->win32.handle, SW_HIDE);
   1659 }
   1660 
   1661 void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
   1662 {
   1663     FlashWindow(window->win32.handle, TRUE);
   1664 }
   1665 
   1666 void _glfwPlatformFocusWindow(_GLFWwindow* window)
   1667 {
   1668     BringWindowToTop(window->win32.handle);
   1669     SetForegroundWindow(window->win32.handle);
   1670     SetFocus(window->win32.handle);
   1671 }
   1672 
   1673 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
   1674                                    _GLFWmonitor* monitor,
   1675                                    int xpos, int ypos,
   1676                                    int width, int height,
   1677                                    int refreshRate)
   1678 {
   1679     if (window->monitor == monitor)
   1680     {
   1681         if (monitor)
   1682         {
   1683             if (monitor->window == window)
   1684             {
   1685                 acquireMonitor(window);
   1686                 fitToMonitor(window);
   1687             }
   1688         }
   1689         else
   1690         {
   1691             RECT rect = { xpos, ypos, xpos + width, ypos + height };
   1692 
   1693             if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
   1694             {
   1695                 AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
   1696                                          FALSE, getWindowExStyle(window),
   1697                                          GetDpiForWindow(window->win32.handle));
   1698             }
   1699             else
   1700             {
   1701                 AdjustWindowRectEx(&rect, getWindowStyle(window),
   1702                                    FALSE, getWindowExStyle(window));
   1703             }
   1704 
   1705             SetWindowPos(window->win32.handle, HWND_TOP,
   1706                          rect.left, rect.top,
   1707                          rect.right - rect.left, rect.bottom - rect.top,
   1708                          SWP_NOCOPYBITS | SWP_NOACTIVATE | SWP_NOZORDER);
   1709         }
   1710 
   1711         return;
   1712     }
   1713 
   1714     if (window->monitor)
   1715         releaseMonitor(window);
   1716 
   1717     _glfwInputWindowMonitor(window, monitor);
   1718 
   1719     if (window->monitor)
   1720     {
   1721         MONITORINFO mi = { sizeof(mi) };
   1722         UINT flags = SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOCOPYBITS;
   1723 
   1724         if (window->decorated)
   1725         {
   1726             DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE);
   1727             style &= ~WS_OVERLAPPEDWINDOW;
   1728             style |= getWindowStyle(window);
   1729             SetWindowLongW(window->win32.handle, GWL_STYLE, style);
   1730             flags |= SWP_FRAMECHANGED;
   1731         }
   1732 
   1733         acquireMonitor(window);
   1734 
   1735         GetMonitorInfo(window->monitor->win32.handle, &mi);
   1736         SetWindowPos(window->win32.handle, HWND_TOPMOST,
   1737                      mi.rcMonitor.left,
   1738                      mi.rcMonitor.top,
   1739                      mi.rcMonitor.right - mi.rcMonitor.left,
   1740                      mi.rcMonitor.bottom - mi.rcMonitor.top,
   1741                      flags);
   1742     }
   1743     else
   1744     {
   1745         HWND after;
   1746         RECT rect = { xpos, ypos, xpos + width, ypos + height };
   1747         DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE);
   1748         UINT flags = SWP_NOACTIVATE | SWP_NOCOPYBITS;
   1749 
   1750         if (window->decorated)
   1751         {
   1752             style &= ~WS_POPUP;
   1753             style |= getWindowStyle(window);
   1754             SetWindowLongW(window->win32.handle, GWL_STYLE, style);
   1755 
   1756             flags |= SWP_FRAMECHANGED;
   1757         }
   1758 
   1759         if (window->floating)
   1760             after = HWND_TOPMOST;
   1761         else
   1762             after = HWND_NOTOPMOST;
   1763 
   1764         if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
   1765         {
   1766             AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
   1767                                      FALSE, getWindowExStyle(window),
   1768                                      GetDpiForWindow(window->win32.handle));
   1769         }
   1770         else
   1771         {
   1772             AdjustWindowRectEx(&rect, getWindowStyle(window),
   1773                                FALSE, getWindowExStyle(window));
   1774         }
   1775 
   1776         SetWindowPos(window->win32.handle, after,
   1777                      rect.left, rect.top,
   1778                      rect.right - rect.left, rect.bottom - rect.top,
   1779                      flags);
   1780     }
   1781 }
   1782 
   1783 int _glfwPlatformWindowFocused(_GLFWwindow* window)
   1784 {
   1785     return window->win32.handle == GetActiveWindow();
   1786 }
   1787 
   1788 int _glfwPlatformWindowIconified(_GLFWwindow* window)
   1789 {
   1790     return IsIconic(window->win32.handle);
   1791 }
   1792 
   1793 int _glfwPlatformWindowVisible(_GLFWwindow* window)
   1794 {
   1795     return IsWindowVisible(window->win32.handle);
   1796 }
   1797 
   1798 int _glfwPlatformWindowMaximized(_GLFWwindow* window)
   1799 {
   1800     return IsZoomed(window->win32.handle);
   1801 }
   1802 
   1803 int _glfwPlatformWindowHovered(_GLFWwindow* window)
   1804 {
   1805     return cursorInContentArea(window);
   1806 }
   1807 
   1808 int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
   1809 {
   1810     BOOL enabled;
   1811 
   1812     if (!window->win32.transparent)
   1813         return GLFW_FALSE;
   1814 
   1815     if (!IsWindowsVistaOrGreater())
   1816         return GLFW_FALSE;
   1817 
   1818     return SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled;
   1819 }
   1820 
   1821 void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
   1822 {
   1823     updateWindowStyles(window);
   1824 }
   1825 
   1826 void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
   1827 {
   1828     updateWindowStyles(window);
   1829 }
   1830 
   1831 void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
   1832 {
   1833     const HWND after = enabled ? HWND_TOPMOST : HWND_NOTOPMOST;
   1834     SetWindowPos(window->win32.handle, after, 0, 0, 0, 0,
   1835                  SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
   1836 }
   1837 
   1838 float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
   1839 {
   1840     BYTE alpha;
   1841     DWORD flags;
   1842 
   1843     if ((GetWindowLongW(window->win32.handle, GWL_EXSTYLE) & WS_EX_LAYERED) &&
   1844         GetLayeredWindowAttributes(window->win32.handle, NULL, &alpha, &flags))
   1845     {
   1846         if (flags & LWA_ALPHA)
   1847             return alpha / 255.f;
   1848     }
   1849 
   1850     return 1.f;
   1851 }
   1852 
   1853 void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
   1854 {
   1855     if (opacity < 1.f)
   1856     {
   1857         const BYTE alpha = (BYTE) (255 * opacity);
   1858         DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
   1859         style |= WS_EX_LAYERED;
   1860         SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style);
   1861         SetLayeredWindowAttributes(window->win32.handle, 0, alpha, LWA_ALPHA);
   1862     }
   1863     else
   1864     {
   1865         DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
   1866         style &= ~WS_EX_LAYERED;
   1867         SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style);
   1868     }
   1869 }
   1870 
   1871 void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled)
   1872 {
   1873     if (_glfw.win32.disabledCursorWindow != window)
   1874         return;
   1875 
   1876     if (enabled)
   1877         enableRawMouseMotion(window);
   1878     else
   1879         disableRawMouseMotion(window);
   1880 }
   1881 
   1882 GLFWbool _glfwPlatformRawMouseMotionSupported(void)
   1883 {
   1884     return GLFW_TRUE;
   1885 }
   1886 
   1887 void _glfwPlatformPollEvents(void)
   1888 {
   1889     MSG msg;
   1890     HWND handle;
   1891     _GLFWwindow* window;
   1892 
   1893     while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
   1894     {
   1895         if (msg.message == WM_QUIT)
   1896         {
   1897             // NOTE: While GLFW does not itself post WM_QUIT, other processes
   1898             //       may post it to this one, for example Task Manager
   1899             // HACK: Treat WM_QUIT as a close on all windows
   1900 
   1901             window = _glfw.windowListHead;
   1902             while (window)
   1903             {
   1904                 _glfwInputWindowCloseRequest(window);
   1905                 window = window->next;
   1906             }
   1907         }
   1908         else
   1909         {
   1910             TranslateMessage(&msg);
   1911             DispatchMessageW(&msg);
   1912         }
   1913     }
   1914 
   1915     // HACK: Release modifier keys that the system did not emit KEYUP for
   1916     // NOTE: Shift keys on Windows tend to "stick" when both are pressed as
   1917     //       no key up message is generated by the first key release
   1918     // NOTE: Windows key is not reported as released by the Win+V hotkey
   1919     //       Other Win hotkeys are handled implicitly by _glfwInputWindowFocus
   1920     //       because they change the input focus
   1921     // NOTE: The other half of this is in the WM_*KEY* handler in windowProc
   1922     handle = GetActiveWindow();
   1923     if (handle)
   1924     {
   1925         window = (_GLFWwindow*)GetPropW(handle, L"GLFW");
   1926         if (window)
   1927         {
   1928             int i;
   1929             const int keys[4][2] =
   1930             {
   1931                 { VK_LSHIFT, GLFW_KEY_LEFT_SHIFT },
   1932                 { VK_RSHIFT, GLFW_KEY_RIGHT_SHIFT },
   1933                 { VK_LWIN, GLFW_KEY_LEFT_SUPER },
   1934                 { VK_RWIN, GLFW_KEY_RIGHT_SUPER }
   1935             };
   1936 
   1937             for (i = 0;  i < 4;  i++)
   1938             {
   1939                 const int vk = keys[i][0];
   1940                 const int key = keys[i][1];
   1941                 const int scancode = _glfw.win32.scancodes[key];
   1942 
   1943                 if ((GetKeyState(vk) & 0x8000))
   1944                     continue;
   1945                 if (window->keys[key] != GLFW_PRESS)
   1946                     continue;
   1947 
   1948                 _glfwInputKey(window, key, scancode, GLFW_RELEASE, getKeyMods());
   1949             }
   1950         }
   1951     }
   1952 
   1953     window = _glfw.win32.disabledCursorWindow;
   1954     if (window)
   1955     {
   1956         int width, height;
   1957         _glfwPlatformGetWindowSize(window, &width, &height);
   1958 
   1959         // NOTE: Re-center the cursor only if it has moved since the last call,
   1960         //       to avoid breaking glfwWaitEvents with WM_MOUSEMOVE
   1961         if (window->win32.lastCursorPosX != width / 2 ||
   1962             window->win32.lastCursorPosY != height / 2)
   1963         {
   1964             _glfwPlatformSetCursorPos(window, width / 2, height / 2);
   1965         }
   1966     }
   1967 }
   1968 
   1969 void _glfwPlatformWaitEvents(void)
   1970 {
   1971     WaitMessage();
   1972 
   1973     _glfwPlatformPollEvents();
   1974 }
   1975 
   1976 void _glfwPlatformWaitEventsTimeout(double timeout)
   1977 {
   1978     MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD) (timeout * 1e3), QS_ALLEVENTS);
   1979 
   1980     _glfwPlatformPollEvents();
   1981 }
   1982 
   1983 void _glfwPlatformPostEmptyEvent(void)
   1984 {
   1985     PostMessage(_glfw.win32.helperWindowHandle, WM_NULL, 0, 0);
   1986 }
   1987 
   1988 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
   1989 {
   1990     POINT pos;
   1991 
   1992     if (GetCursorPos(&pos))
   1993     {
   1994         ScreenToClient(window->win32.handle, &pos);
   1995 
   1996         if (xpos)
   1997             *xpos = pos.x;
   1998         if (ypos)
   1999             *ypos = pos.y;
   2000     }
   2001 }
   2002 
   2003 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos)
   2004 {
   2005     POINT pos = { (int) xpos, (int) ypos };
   2006 
   2007     // Store the new position so it can be recognized later
   2008     window->win32.lastCursorPosX = pos.x;
   2009     window->win32.lastCursorPosY = pos.y;
   2010 
   2011     ClientToScreen(window->win32.handle, &pos);
   2012     SetCursorPos(pos.x, pos.y);
   2013 }
   2014 
   2015 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
   2016 {
   2017     if (mode == GLFW_CURSOR_DISABLED)
   2018     {
   2019         if (_glfwPlatformWindowFocused(window))
   2020             disableCursor(window);
   2021     }
   2022     else if (_glfw.win32.disabledCursorWindow == window)
   2023         enableCursor(window);
   2024     else if (cursorInContentArea(window))
   2025         updateCursorImage(window);
   2026 }
   2027 
   2028 const char* _glfwPlatformGetScancodeName(int scancode)
   2029 {
   2030     if (scancode < 0 || scancode > (KF_EXTENDED | 0xff) ||
   2031         _glfw.win32.keycodes[scancode] == GLFW_KEY_UNKNOWN)
   2032     {
   2033         _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode");
   2034         return NULL;
   2035     }
   2036 
   2037     return _glfw.win32.keynames[_glfw.win32.keycodes[scancode]];
   2038 }
   2039 
   2040 int _glfwPlatformGetKeyScancode(int key)
   2041 {
   2042     return _glfw.win32.scancodes[key];
   2043 }
   2044 
   2045 int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
   2046                               const GLFWimage* image,
   2047                               int xhot, int yhot)
   2048 {
   2049     cursor->win32.handle = (HCURSOR) createIcon(image, xhot, yhot, GLFW_FALSE);
   2050     if (!cursor->win32.handle)
   2051         return GLFW_FALSE;
   2052 
   2053     return GLFW_TRUE;
   2054 }
   2055 
   2056 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
   2057 {
   2058     int id = 0;
   2059 
   2060     if (shape == GLFW_ARROW_CURSOR)
   2061         id = OCR_NORMAL;
   2062     else if (shape == GLFW_IBEAM_CURSOR)
   2063         id = OCR_IBEAM;
   2064     else if (shape == GLFW_CROSSHAIR_CURSOR)
   2065         id = OCR_CROSS;
   2066     else if (shape == GLFW_POINTING_HAND_CURSOR)
   2067         id = OCR_HAND;
   2068     else if (shape == GLFW_RESIZE_EW_CURSOR)
   2069         id = OCR_SIZEWE;
   2070     else if (shape == GLFW_RESIZE_NS_CURSOR)
   2071         id = OCR_SIZENS;
   2072     else if (shape == GLFW_RESIZE_NWSE_CURSOR)
   2073         id = OCR_SIZENWSE;
   2074     else if (shape == GLFW_RESIZE_NESW_CURSOR)
   2075         id = OCR_SIZENESW;
   2076     else if (shape == GLFW_RESIZE_ALL_CURSOR)
   2077         id = OCR_SIZEALL;
   2078     else if (shape == GLFW_NOT_ALLOWED_CURSOR)
   2079         id = OCR_NO;
   2080     else
   2081     {
   2082         _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Unknown standard cursor");
   2083         return GLFW_FALSE;
   2084     }
   2085 
   2086     cursor->win32.handle = (HCURSOR)LoadImageW(NULL,
   2087                                       MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0,
   2088                                       LR_DEFAULTSIZE | LR_SHARED);
   2089     if (!cursor->win32.handle)
   2090     {
   2091         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
   2092                              "Win32: Failed to create standard cursor");
   2093         return GLFW_FALSE;
   2094     }
   2095 
   2096     return GLFW_TRUE;
   2097 }
   2098 
   2099 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
   2100 {
   2101     if (cursor->win32.handle)
   2102         DestroyIcon((HICON) cursor->win32.handle);
   2103 }
   2104 
   2105 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
   2106 {
   2107     if (cursorInContentArea(window))
   2108         updateCursorImage(window);
   2109 }
   2110 
   2111 void _glfwPlatformSetClipboardString(const char* string)
   2112 {
   2113     int characterCount;
   2114     HANDLE object;
   2115     WCHAR* buffer;
   2116 
   2117     characterCount = MultiByteToWideChar(CP_UTF8, 0, string, -1, NULL, 0);
   2118     if (!characterCount)
   2119         return;
   2120 
   2121     object = GlobalAlloc(GMEM_MOVEABLE, characterCount * sizeof(WCHAR));
   2122     if (!object)
   2123     {
   2124         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
   2125                              "Win32: Failed to allocate global handle for clipboard");
   2126         return;
   2127     }
   2128 
   2129     buffer = (WCHAR*)GlobalLock(object);
   2130     if (!buffer)
   2131     {
   2132         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
   2133                              "Win32: Failed to lock global handle");
   2134         GlobalFree(object);
   2135         return;
   2136     }
   2137 
   2138     MultiByteToWideChar(CP_UTF8, 0, string, -1, buffer, characterCount);
   2139     GlobalUnlock(object);
   2140 
   2141     if (!OpenClipboard(_glfw.win32.helperWindowHandle))
   2142     {
   2143         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
   2144                              "Win32: Failed to open clipboard");
   2145         GlobalFree(object);
   2146         return;
   2147     }
   2148 
   2149     EmptyClipboard();
   2150     SetClipboardData(CF_UNICODETEXT, object);
   2151     CloseClipboard();
   2152 }
   2153 
   2154 const char* _glfwPlatformGetClipboardString(void)
   2155 {
   2156     HANDLE object;
   2157     WCHAR* buffer;
   2158 
   2159     if (!OpenClipboard(_glfw.win32.helperWindowHandle))
   2160     {
   2161         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
   2162                              "Win32: Failed to open clipboard");
   2163         return NULL;
   2164     }
   2165 
   2166     object = GetClipboardData(CF_UNICODETEXT);
   2167     if (!object)
   2168     {
   2169         _glfwInputErrorWin32(GLFW_FORMAT_UNAVAILABLE,
   2170                              "Win32: Failed to convert clipboard to string");
   2171         CloseClipboard();
   2172         return NULL;
   2173     }
   2174 
   2175     buffer = (WCHAR*)GlobalLock(object);
   2176     if (!buffer)
   2177     {
   2178         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
   2179                              "Win32: Failed to lock global handle");
   2180         CloseClipboard();
   2181         return NULL;
   2182     }
   2183 
   2184     free(_glfw.win32.clipboardString);
   2185     _glfw.win32.clipboardString = _glfwCreateUTF8FromWideStringWin32(buffer);
   2186 
   2187     GlobalUnlock(object);
   2188     CloseClipboard();
   2189 
   2190     return _glfw.win32.clipboardString;
   2191 }
   2192 
   2193 void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
   2194 {
   2195     if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_win32_surface)
   2196         return;
   2197 
   2198     extensions[0] = (char*)"VK_KHR_surface";
   2199     extensions[1] = (char*)"VK_KHR_win32_surface";
   2200 }
   2201 
   2202 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
   2203                                                       VkPhysicalDevice device,
   2204                                                       uint32_t queuefamily)
   2205 {
   2206     PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR
   2207         vkGetPhysicalDeviceWin32PresentationSupportKHR =
   2208         (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)
   2209         vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR");
   2210     if (!vkGetPhysicalDeviceWin32PresentationSupportKHR)
   2211     {
   2212         _glfwInputError(GLFW_API_UNAVAILABLE,
   2213                         "Win32: Vulkan instance missing VK_KHR_win32_surface extension");
   2214         return GLFW_FALSE;
   2215     }
   2216 
   2217     return vkGetPhysicalDeviceWin32PresentationSupportKHR(device, queuefamily);
   2218 }
   2219 
   2220 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
   2221                                           _GLFWwindow* window,
   2222                                           const VkAllocationCallbacks* allocator,
   2223                                           VkSurfaceKHR* surface)
   2224 {
   2225     VkResult err;
   2226     VkWin32SurfaceCreateInfoKHR sci;
   2227     PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR;
   2228 
   2229     vkCreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR)
   2230         vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR");
   2231     if (!vkCreateWin32SurfaceKHR)
   2232     {
   2233         _glfwInputError(GLFW_API_UNAVAILABLE,
   2234                         "Win32: Vulkan instance missing VK_KHR_win32_surface extension");
   2235         return VK_ERROR_EXTENSION_NOT_PRESENT;
   2236     }
   2237 
   2238     memset(&sci, 0, sizeof(sci));
   2239     sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
   2240     sci.hinstance = GetModuleHandle(NULL);
   2241     sci.hwnd = window->win32.handle;
   2242 
   2243     err = vkCreateWin32SurfaceKHR(instance, &sci, allocator, surface);
   2244     if (err)
   2245     {
   2246         _glfwInputError(GLFW_PLATFORM_ERROR,
   2247                         "Win32: Failed to create Vulkan surface: %s",
   2248                         _glfwGetVulkanResultString(err));
   2249     }
   2250 
   2251     return err;
   2252 }
   2253 
   2254 
   2255 //////////////////////////////////////////////////////////////////////////
   2256 //////                        GLFW native API                       //////
   2257 //////////////////////////////////////////////////////////////////////////
   2258 
   2259 GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle)
   2260 {
   2261     _GLFWwindow* window = (_GLFWwindow*) handle;
   2262     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
   2263     return window->win32.handle;
   2264 }
   2265