twitchapon-anim

Basic Twitchapon Receiver/Visuals
git clone git://bsandro.tech/twitchapon-anim
Log | Files | Refs | README | LICENSE

win32_window.c (69428B)


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