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