zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

win32_window.c (70829B)


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