1 //========================================================================
2 // GLFW 3.5 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
28 #include "internal.h"
29
30 #if defined(_GLFW_WIN32)
31
32 #include <limits.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <assert.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, int xhot, int yhot, GLFWbool icon)
102 {
103 int i;
104 HDC dc;
105 HICON handle;
106 HBITMAP color, mask;
107 BITMAPV5HEADER bi;
108 ICONINFO ii;
109 unsigned char* target = NULL;
110 unsigned char* source = image->pixels;
111
112 ZeroMemory(&bi, sizeof(bi));
113 bi.bV5Size = sizeof(bi);
114 bi.bV5Width = image->width;
115 bi.bV5Height = -image->height;
116 bi.bV5Planes = 1;
117 bi.bV5BitCount = 32;
118 bi.bV5Compression = BI_BITFIELDS;
119 bi.bV5RedMask = 0x00ff0000;
120 bi.bV5GreenMask = 0x0000ff00;
121 bi.bV5BlueMask = 0x000000ff;
122 bi.bV5AlphaMask = 0xff000000;
123
124 dc = GetDC(NULL);
125 color = CreateDIBSection(dc,
126 (BITMAPINFO*) &bi,
127 DIB_RGB_COLORS,
128 (void**) &target,
129 NULL,
130 (DWORD) 0);
131 ReleaseDC(NULL, dc);
132
133 if (!color)
134 {
135 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
136 "Win32: Failed to create RGBA bitmap");
137 return NULL;
138 }
139
140 mask = CreateBitmap(image->width, image->height, 1, 1, NULL);
141 if (!mask)
142 {
143 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
144 "Win32: Failed to create mask bitmap");
145 DeleteObject(color);
146 return NULL;
147 }
148
149 for (i = 0; i < image->width * image->height; i++)
150 {
151 target[0] = source[2];
152 target[1] = source[1];
153 target[2] = source[0];
154 target[3] = source[3];
155 target += 4;
156 source += 4;
157 }
158
159 ZeroMemory(&ii, sizeof(ii));
160 ii.fIcon = icon;
161 ii.xHotspot = xhot;
162 ii.yHotspot = yhot;
163 ii.hbmMask = mask;
164 ii.hbmColor = color;
165
166 handle = CreateIconIndirect(&ii);
167
168 DeleteObject(color);
169 DeleteObject(mask);
170
171 if (!handle)
172 {
173 if (icon)
174 {
175 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
176 "Win32: Failed to create icon");
177 }
178 else
179 {
180 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
181 "Win32: Failed to create cursor");
182 }
183 }
184
185 return handle;
186 }
187
188 // Enforce the content area aspect ratio based on which edge is being dragged
189 //
applyAspectRatio(_GLFWwindow* window, int edge, RECT* area)190 static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area)
191 {
192 RECT frame = {0};
193 const float ratio = (float) window->numer / (float) window->denom;
194 const DWORD style = getWindowStyle(window);
195 const DWORD exStyle = getWindowExStyle(window);
196
197 if (_glfwIsWindows10Version1607OrGreaterWin32())
198 {
199 AdjustWindowRectExForDpi(&frame, style, FALSE, exStyle,
200 GetDpiForWindow(window->win32.handle));
201 }
202 else
203 AdjustWindowRectEx(&frame, style, FALSE, exStyle);
204
205 if (edge == WMSZ_LEFT || edge == WMSZ_BOTTOMLEFT ||
206 edge == WMSZ_RIGHT || edge == WMSZ_BOTTOMRIGHT)
207 {
208 area->bottom = area->top + (frame.bottom - frame.top) +
209 (int) (((area->right - area->left) - (frame.right - frame.left)) / ratio);
210 }
211 else if (edge == WMSZ_TOPLEFT || edge == WMSZ_TOPRIGHT)
212 {
213 area->top = area->bottom - (frame.bottom - frame.top) -
214 (int) (((area->right - area->left) - (frame.right - frame.left)) / ratio);
215 }
216 else if (edge == WMSZ_TOP || edge == WMSZ_BOTTOM)
217 {
218 area->right = area->left + (frame.right - frame.left) +
219 (int) (((area->bottom - area->top) - (frame.bottom - frame.top)) * ratio);
220 }
221 }
222
223 // Updates the cursor image according to its cursor mode
224 //
updateCursorImage(_GLFWwindow* window)225 static void updateCursorImage(_GLFWwindow* window)
226 {
227 if (window->cursorMode == GLFW_CURSOR_NORMAL ||
228 window->cursorMode == GLFW_CURSOR_CAPTURED)
229 {
230 if (window->cursor)
231 SetCursor(window->cursor->win32.handle);
232 else
233 SetCursor(LoadCursorW(NULL, IDC_ARROW));
234 }
235 else
236 {
237 // NOTE: Via Remote Desktop, setting the cursor to NULL does not hide it.
238 // HACK: When running locally, it is set to NULL, but when connected via Remote
239 // Desktop, this is a transparent cursor.
240 SetCursor(_glfw.win32.blankCursor);
241 }
242 }
243
244 // Sets the cursor clip rect to the window content area
245 //
captureCursor(_GLFWwindow* window)246 static void captureCursor(_GLFWwindow* window)
247 {
248 RECT clipRect;
249 GetClientRect(window->win32.handle, &clipRect);
250 ClientToScreen(window->win32.handle, (POINT*) &clipRect.left);
251 ClientToScreen(window->win32.handle, (POINT*) &clipRect.right);
252 ClipCursor(&clipRect);
253 _glfw.win32.capturedCursorWindow = window;
254 }
255
256 // Disabled clip cursor
257 //
releaseCursor(void)258 static void releaseCursor(void)
259 {
260 ClipCursor(NULL);
261 _glfw.win32.capturedCursorWindow = NULL;
262 }
263
264 // Enables WM_INPUT messages for the mouse for the specified window
265 //
enableRawMouseMotion(_GLFWwindow* window)266 static void enableRawMouseMotion(_GLFWwindow* window)
267 {
268 const RAWINPUTDEVICE rid = { 0x01, 0x02, 0, window->win32.handle };
269
270 if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
271 {
272 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
273 "Win32: Failed to register raw input device");
274 }
275 }
276
277 // Disables WM_INPUT messages for the mouse
278 //
disableRawMouseMotion(_GLFWwindow* window)279 static void disableRawMouseMotion(_GLFWwindow* window)
280 {
281 const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL };
282
283 if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
284 {
285 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
286 "Win32: Failed to remove raw input device");
287 }
288 }
289
290 // Apply disabled cursor mode to a focused window
291 //
disableCursor(_GLFWwindow* window)292 static void disableCursor(_GLFWwindow* window)
293 {
294 _glfw.win32.disabledCursorWindow = window;
295 _glfwGetCursorPosWin32(window,
296 &_glfw.win32.restoreCursorPosX,
297 &_glfw.win32.restoreCursorPosY);
298 updateCursorImage(window);
299 _glfwCenterCursorInContentArea(window);
300 captureCursor(window);
301
302 if (window->rawMouseMotion)
303 enableRawMouseMotion(window);
304 }
305
306 // Exit disabled cursor mode for the specified window
307 //
enableCursor(_GLFWwindow* window)308 static void enableCursor(_GLFWwindow* window)
309 {
310 if (window->rawMouseMotion)
311 disableRawMouseMotion(window);
312
313 _glfw.win32.disabledCursorWindow = NULL;
314 releaseCursor();
315 _glfwSetCursorPosWin32(window,
316 _glfw.win32.restoreCursorPosX,
317 _glfw.win32.restoreCursorPosY);
318 updateCursorImage(window);
319 }
320
321 // Returns whether the cursor is in the content area of the specified window
322 //
cursorInContentArea(_GLFWwindow* window)323 static GLFWbool cursorInContentArea(_GLFWwindow* window)
324 {
325 RECT area;
326 POINT pos;
327
328 if (!GetCursorPos(&pos))
329 return GLFW_FALSE;
330
331 if (WindowFromPoint(pos) != window->win32.handle)
332 return GLFW_FALSE;
333
334 GetClientRect(window->win32.handle, &area);
335 ClientToScreen(window->win32.handle, (POINT*) &area.left);
336 ClientToScreen(window->win32.handle, (POINT*) &area.right);
337
338 return PtInRect(&area, pos);
339 }
340
341 // Update native window styles to match attributes
342 //
updateWindowStyles(const _GLFWwindow* window)343 static void updateWindowStyles(const _GLFWwindow* window)
344 {
345 RECT rect;
346 DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE);
347 style &= ~(WS_OVERLAPPEDWINDOW | WS_POPUP);
348 style |= getWindowStyle(window);
349
350 GetClientRect(window->win32.handle, &rect);
351
352 if (_glfwIsWindows10Version1607OrGreaterWin32())
353 {
354 AdjustWindowRectExForDpi(&rect, style, FALSE,
355 getWindowExStyle(window),
356 GetDpiForWindow(window->win32.handle));
357 }
358 else
359 AdjustWindowRectEx(&rect, style, FALSE, getWindowExStyle(window));
360
361 ClientToScreen(window->win32.handle, (POINT*) &rect.left);
362 ClientToScreen(window->win32.handle, (POINT*) &rect.right);
363 SetWindowLongW(window->win32.handle, GWL_STYLE, style);
364 SetWindowPos(window->win32.handle, HWND_TOP,
365 rect.left, rect.top,
366 rect.right - rect.left, rect.bottom - rect.top,
367 SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER);
368 }
369
370 // Update window framebuffer transparency
371 //
updateFramebufferTransparency(const _GLFWwindow* window)372 static void updateFramebufferTransparency(const _GLFWwindow* window)
373 {
374 BOOL composition, opaque;
375 DWORD color;
376
377 if (!IsWindowsVistaOrGreater())
378 return;
379
380 if (FAILED(DwmIsCompositionEnabled(&composition)) || !composition)
381 return;
382
383 if (IsWindows8OrGreater() ||
384 (SUCCEEDED(DwmGetColorizationColor(&color, &opaque)) && !opaque))
385 {
386 HRGN region = CreateRectRgn(0, 0, -1, -1);
387 DWM_BLURBEHIND bb = {0};
388 bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
389 bb.hRgnBlur = region;
390 bb.fEnable = TRUE;
391
392 DwmEnableBlurBehindWindow(window->win32.handle, &bb);
393 DeleteObject(region);
394 }
395 else
396 {
397 // HACK: Disable framebuffer transparency on Windows 7 when the
398 // colorization color is opaque, because otherwise the window
399 // contents is blended additively with the previous frame instead
400 // of replacing it
401 DWM_BLURBEHIND bb = {0};
402 bb.dwFlags = DWM_BB_ENABLE;
403 DwmEnableBlurBehindWindow(window->win32.handle, &bb);
404 }
405 }
406
407 // Retrieves and translates modifier keys
408 //
getKeyMods(void)409 static int getKeyMods(void)
410 {
411 int mods = 0;
412
413 if (GetKeyState(VK_SHIFT) & 0x8000)
414 mods |= GLFW_MOD_SHIFT;
415 if (GetKeyState(VK_CONTROL) & 0x8000)
416 mods |= GLFW_MOD_CONTROL;
417 if (GetKeyState(VK_MENU) & 0x8000)
418 mods |= GLFW_MOD_ALT;
419 if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000)
420 mods |= GLFW_MOD_SUPER;
421 if (GetKeyState(VK_CAPITAL) & 1)
422 mods |= GLFW_MOD_CAPS_LOCK;
423 if (GetKeyState(VK_NUMLOCK) & 1)
424 mods |= GLFW_MOD_NUM_LOCK;
425
426 return mods;
427 }
428
fitToMonitor(_GLFWwindow* window)429 static void fitToMonitor(_GLFWwindow* window)
430 {
431 MONITORINFO mi = { sizeof(mi) };
432 GetMonitorInfoW(window->monitor->win32.handle, &mi);
433 SetWindowPos(window->win32.handle, HWND_TOPMOST,
434 mi.rcMonitor.left,
435 mi.rcMonitor.top,
436 mi.rcMonitor.right - mi.rcMonitor.left,
437 mi.rcMonitor.bottom - mi.rcMonitor.top,
438 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS);
439 }
440
441 // Make the specified window and its video mode active on its monitor
442 //
acquireMonitor(_GLFWwindow* window)443 static void acquireMonitor(_GLFWwindow* window)
444 {
445 if (!_glfw.win32.acquiredMonitorCount)
446 {
447 SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
448
449 // HACK: When mouse trails are enabled the cursor becomes invisible when
450 // the OpenGL ICD switches to page flipping
451 SystemParametersInfoW(SPI_GETMOUSETRAILS, 0, &_glfw.win32.mouseTrailSize, 0);
452 SystemParametersInfoW(SPI_SETMOUSETRAILS, 0, 0, 0);
453 }
454
455 if (!window->monitor->window)
456 _glfw.win32.acquiredMonitorCount++;
457
458 _glfwSetVideoModeWin32(window->monitor, &window->videoMode);
459 _glfwInputMonitorWindow(window->monitor, window);
460 }
461
462 // Remove the window and restore the original video mode
463 //
releaseMonitor(_GLFWwindow* window)464 static void releaseMonitor(_GLFWwindow* window)
465 {
466 if (window->monitor->window != window)
467 return;
468
469 _glfw.win32.acquiredMonitorCount--;
470 if (!_glfw.win32.acquiredMonitorCount)
471 {
472 SetThreadExecutionState(ES_CONTINUOUS);
473
474 // HACK: Restore mouse trail length saved in acquireMonitor
475 SystemParametersInfoW(SPI_SETMOUSETRAILS, _glfw.win32.mouseTrailSize, 0, 0);
476 }
477
478 _glfwInputMonitorWindow(window->monitor, NULL);
479 _glfwRestoreVideoModeWin32(window->monitor);
480 }
481
482 // Manually maximize the window, for when SW_MAXIMIZE cannot be used
483 //
maximizeWindowManually(_GLFWwindow* window)484 static void maximizeWindowManually(_GLFWwindow* window)
485 {
486 RECT rect;
487 DWORD style;
488 MONITORINFO mi = { sizeof(mi) };
489
490 GetMonitorInfoW(MonitorFromWindow(window->win32.handle,
491 MONITOR_DEFAULTTONEAREST), &mi);
492
493 rect = mi.rcWork;
494
495 if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE)
496 {
497 rect.right = _glfw_min(rect.right, rect.left + window->maxwidth);
498 rect.bottom = _glfw_min(rect.bottom, rect.top + window->maxheight);
499 }
500
501 style = GetWindowLongW(window->win32.handle, GWL_STYLE);
502 style |= WS_MAXIMIZE;
503 SetWindowLongW(window->win32.handle, GWL_STYLE, style);
504
505 if (window->decorated)
506 {
507 const DWORD exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
508
509 if (_glfwIsWindows10Version1607OrGreaterWin32())
510 {
511 const UINT dpi = GetDpiForWindow(window->win32.handle);
512 AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi);
513 OffsetRect(&rect, 0, GetSystemMetricsForDpi(SM_CYCAPTION, dpi));
514 }
515 else
516 {
517 AdjustWindowRectEx(&rect, style, FALSE, exStyle);
518 OffsetRect(&rect, 0, GetSystemMetrics(SM_CYCAPTION));
519 }
520
521 rect.bottom = _glfw_min(rect.bottom, mi.rcWork.bottom);
522 }
523
524 SetWindowPos(window->win32.handle, HWND_TOP,
525 rect.left,
526 rect.top,
527 rect.right - rect.left,
528 rect.bottom - rect.top,
529 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED);
530 }
531
532 // Window procedure for user-created windows
533 //
windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)534 static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
535 {
536 _GLFWwindow* window = GetPropW(hWnd, L"GLFW");
537 if (!window)
538 {
539 if (uMsg == WM_NCCREATE)
540 {
541 if (_glfwIsWindows10Version1607OrGreaterWin32())
542 {
543 const CREATESTRUCTW* cs = (const CREATESTRUCTW*) lParam;
544 const _GLFWwndconfig* wndconfig = cs->lpCreateParams;
545
546 // On per-monitor DPI aware V1 systems, only enable
547 // non-client scaling for windows that scale the client area
548 // We need WM_GETDPISCALEDSIZE from V2 to keep the client
549 // area static when the non-client area is scaled
550 if (wndconfig && wndconfig->scaleToMonitor)
551 EnableNonClientDpiScaling(hWnd);
552 }
553 }
554
555 return DefWindowProcW(hWnd, uMsg, wParam, lParam);
556 }
557
558 switch (uMsg)
559 {
560 case WM_MOUSEACTIVATE:
561 {
562 // HACK: Postpone cursor disabling when the window was activated by
563 // clicking a caption button
564 if (HIWORD(lParam) == WM_LBUTTONDOWN)
565 {
566 if (LOWORD(lParam) != HTCLIENT)
567 window->win32.frameAction = GLFW_TRUE;
568 }
569
570 break;
571 }
572
573 case WM_CAPTURECHANGED:
574 {
575 // HACK: Disable the cursor once the caption button action has been
576 // completed or cancelled
577 if (lParam == 0 && window->win32.frameAction)
578 {
579 if (window->cursorMode == GLFW_CURSOR_DISABLED)
580 disableCursor(window);
581 else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
582 captureCursor(window);
583
584 window->win32.frameAction = GLFW_FALSE;
585 }
586
587 break;
588 }
589
590 case WM_SETFOCUS:
591 {
592 _glfwInputWindowFocus(window, GLFW_TRUE);
593
594 // HACK: Do not disable cursor while the user is interacting with
595 // a caption button
596 if (window->win32.frameAction)
597 break;
598
599 if (window->cursorMode == GLFW_CURSOR_DISABLED)
600 disableCursor(window);
601 else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
602 captureCursor(window);
603
604 return 0;
605 }
606
607 case WM_KILLFOCUS:
608 {
609 if (window->cursorMode == GLFW_CURSOR_DISABLED)
610 enableCursor(window);
611 else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
612 releaseCursor();
613
614 if (window->monitor && window->autoIconify)
615 _glfwIconifyWindowWin32(window);
616
617 _glfwInputWindowFocus(window, GLFW_FALSE);
618 return 0;
619 }
620
621 case WM_SYSCOMMAND:
622 {
623 switch (wParam & 0xfff0)
624 {
625 case SC_SCREENSAVE:
626 case SC_MONITORPOWER:
627 {
628 if (window->monitor)
629 {
630 // We are running in full screen mode, so disallow
631 // screen saver and screen blanking
632 return 0;
633 }
634 else
635 break;
636 }
637
638 // User trying to access application menu using ALT?
639 case SC_KEYMENU:
640 {
641 if (!window->win32.keymenu)
642 return 0;
643
644 break;
645 }
646 }
647 break;
648 }
649
650 case WM_CLOSE:
651 {
652 _glfwInputWindowCloseRequest(window);
653 return 0;
654 }
655
656 case WM_INPUTLANGCHANGE:
657 {
658 _glfwUpdateKeyNamesWin32();
659 break;
660 }
661
662 case WM_CHAR:
663 case WM_SYSCHAR:
664 {
665 if (wParam >= 0xd800 && wParam <= 0xdbff)
666 window->win32.highSurrogate = (WCHAR) wParam;
667 else
668 {
669 uint32_t codepoint = 0;
670
671 if (wParam >= 0xdc00 && wParam <= 0xdfff)
672 {
673 if (window->win32.highSurrogate)
674 {
675 codepoint += (window->win32.highSurrogate - 0xd800) << 10;
676 codepoint += (WCHAR) wParam - 0xdc00;
677 codepoint += 0x10000;
678 }
679 }
680 else
681 codepoint = (WCHAR) wParam;
682
683 window->win32.highSurrogate = 0;
684 _glfwInputChar(window, codepoint, getKeyMods(), uMsg != WM_SYSCHAR);
685 }
686
687 if (uMsg == WM_SYSCHAR && window->win32.keymenu)
688 break;
689
690 return 0;
691 }
692
693 case WM_UNICHAR:
694 {
695 if (wParam == UNICODE_NOCHAR)
696 {
697 // WM_UNICHAR is not sent by Windows, but is sent by some
698 // third-party input method engine
699 // Returning TRUE here announces support for this message
700 return TRUE;
701 }
702
703 _glfwInputChar(window, (uint32_t) wParam, getKeyMods(), GLFW_TRUE);
704 return 0;
705 }
706
707 case WM_KEYDOWN:
708 case WM_SYSKEYDOWN:
709 case WM_KEYUP:
710 case WM_SYSKEYUP:
711 {
712 int key, scancode;
713 const int action = (HIWORD(lParam) & KF_UP) ? GLFW_RELEASE : GLFW_PRESS;
714 const int mods = getKeyMods();
715
716 scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff));
717 if (!scancode)
718 {
719 // NOTE: Some synthetic key messages have a scancode of zero
720 // HACK: Map the virtual key back to a usable scancode
721 scancode = MapVirtualKeyW((UINT) wParam, MAPVK_VK_TO_VSC);
722 }
723
724 // HACK: Alt+PrtSc has a different scancode than just PrtSc
725 if (scancode == 0x54)
726 scancode = 0x137;
727
728 // HACK: Ctrl+Pause has a different scancode than just Pause
729 if (scancode == 0x146)
730 scancode = 0x45;
731
732 // HACK: CJK IME sets the extended bit for right Shift
733 if (scancode == 0x136)
734 scancode = 0x36;
735
736 key = _glfw.win32.keycodes[scancode];
737
738 // The Ctrl keys require special handling
739 if (wParam == VK_CONTROL)
740 {
741 if (HIWORD(lParam) & KF_EXTENDED)
742 {
743 // Right side keys have the extended key bit set
744 key = GLFW_KEY_RIGHT_CONTROL;
745 }
746 else
747 {
748 // NOTE: Alt Gr sends Left Ctrl followed by Right Alt
749 // HACK: We only want one event for Alt Gr, so if we detect
750 // this sequence we discard this Left Ctrl message now
751 // and later report Right Alt normally
752 MSG next;
753 const DWORD time = GetMessageTime();
754
755 if (PeekMessageW(&next, NULL, 0, 0, PM_NOREMOVE))
756 {
757 if (next.message == WM_KEYDOWN ||
758 next.message == WM_SYSKEYDOWN ||
759 next.message == WM_KEYUP ||
760 next.message == WM_SYSKEYUP)
761 {
762 if (next.wParam == VK_MENU &&
763 (HIWORD(next.lParam) & KF_EXTENDED) &&
764 next.time == time)
765 {
766 // Next message is Right Alt down so discard this
767 break;
768 }
769 }
770 }
771
772 // This is a regular Left Ctrl message
773 key = GLFW_KEY_LEFT_CONTROL;
774 }
775 }
776 else if (wParam == VK_PROCESSKEY)
777 {
778 // IME notifies that keys have been filtered by setting the
779 // virtual key-code to VK_PROCESSKEY
780 break;
781 }
782
783 if (action == GLFW_RELEASE && wParam == VK_SHIFT)
784 {
785 // HACK: Release both Shift keys on Shift up event, as when both
786 // are pressed the first release does not emit any event
787 // NOTE: The other half of this is in _glfwPollEventsWin32
788 _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, action, mods);
789 _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, action, mods);
790 }
791 else if (wParam == VK_SNAPSHOT)
792 {
793 // HACK: Key down is not reported for the Print Screen key
794 _glfwInputKey(window, key, scancode, GLFW_PRESS, mods);
795 _glfwInputKey(window, key, scancode, GLFW_RELEASE, mods);
796 }
797 else
798 _glfwInputKey(window, key, scancode, action, mods);
799
800 break;
801 }
802
803 case WM_LBUTTONDOWN:
804 case WM_RBUTTONDOWN:
805 case WM_MBUTTONDOWN:
806 case WM_XBUTTONDOWN:
807 case WM_LBUTTONUP:
808 case WM_RBUTTONUP:
809 case WM_MBUTTONUP:
810 case WM_XBUTTONUP:
811 {
812 int i, button, action;
813
814 if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP)
815 button = GLFW_MOUSE_BUTTON_LEFT;
816 else if (uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP)
817 button = GLFW_MOUSE_BUTTON_RIGHT;
818 else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP)
819 button = GLFW_MOUSE_BUTTON_MIDDLE;
820 else if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1)
821 button = GLFW_MOUSE_BUTTON_4;
822 else
823 button = GLFW_MOUSE_BUTTON_5;
824
825 if (uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN ||
826 uMsg == WM_MBUTTONDOWN || uMsg == WM_XBUTTONDOWN)
827 {
828 action = GLFW_PRESS;
829 }
830 else
831 action = GLFW_RELEASE;
832
833 for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++)
834 {
835 if (window->mouseButtons[i] == GLFW_PRESS)
836 break;
837 }
838
839 if (i > GLFW_MOUSE_BUTTON_LAST)
840 SetCapture(hWnd);
841
842 _glfwInputMouseClick(window, button, action, getKeyMods());
843
844 for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++)
845 {
846 if (window->mouseButtons[i] == GLFW_PRESS)
847 break;
848 }
849
850 if (i > GLFW_MOUSE_BUTTON_LAST)
851 ReleaseCapture();
852
853 if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP)
854 return TRUE;
855
856 return 0;
857 }
858
859 case WM_MOUSEMOVE:
860 {
861 const int x = GET_X_LPARAM(lParam);
862 const int y = GET_Y_LPARAM(lParam);
863
864 if (!window->win32.cursorTracked)
865 {
866 TRACKMOUSEEVENT tme;
867 ZeroMemory(&tme, sizeof(tme));
868 tme.cbSize = sizeof(tme);
869 tme.dwFlags = TME_LEAVE;
870 tme.hwndTrack = window->win32.handle;
871 TrackMouseEvent(&tme);
872
873 window->win32.cursorTracked = GLFW_TRUE;
874 _glfwInputCursorEnter(window, GLFW_TRUE);
875 }
876
877 if (window->cursorMode == GLFW_CURSOR_DISABLED)
878 {
879 const int dx = x - window->win32.lastCursorPosX;
880 const int dy = y - window->win32.lastCursorPosY;
881
882 if (_glfw.win32.disabledCursorWindow != window)
883 break;
884 if (window->rawMouseMotion)
885 break;
886
887 _glfwInputCursorPos(window,
888 window->virtualCursorPosX + dx,
889 window->virtualCursorPosY + dy);
890 }
891 else
892 _glfwInputCursorPos(window, x, y);
893
894 window->win32.lastCursorPosX = x;
895 window->win32.lastCursorPosY = y;
896
897 return 0;
898 }
899
900 case WM_INPUT:
901 {
902 UINT size = 0;
903 HRAWINPUT ri = (HRAWINPUT) lParam;
904 RAWINPUT* data = NULL;
905 int dx, dy;
906
907 if (_glfw.win32.disabledCursorWindow != window)
908 break;
909 if (!window->rawMouseMotion)
910 break;
911
912 GetRawInputData(ri, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
913 if (size > (UINT) _glfw.win32.rawInputSize)
914 {
915 _glfw_free(_glfw.win32.rawInput);
916 _glfw.win32.rawInput = _glfw_calloc(size, 1);
917 _glfw.win32.rawInputSize = size;
918 }
919
920 size = _glfw.win32.rawInputSize;
921 if (GetRawInputData(ri, RID_INPUT,
922 _glfw.win32.rawInput, &size,
923 sizeof(RAWINPUTHEADER)) == (UINT) -1)
924 {
925 _glfwInputError(GLFW_PLATFORM_ERROR,
926 "Win32: Failed to retrieve raw input data");
927 break;
928 }
929
930 data = _glfw.win32.rawInput;
931 if (data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE)
932 {
933 POINT pos = {0};
934 int width, height;
935
936 if (data->data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP)
937 {
938 pos.x += GetSystemMetrics(SM_XVIRTUALSCREEN);
939 pos.y += GetSystemMetrics(SM_YVIRTUALSCREEN);
940 width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
941 height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
942 }
943 else
944 {
945 width = GetSystemMetrics(SM_CXSCREEN);
946 height = GetSystemMetrics(SM_CYSCREEN);
947 }
948
949 pos.x += (int) ((data->data.mouse.lLastX / 65535.f) * width);
950 pos.y += (int) ((data->data.mouse.lLastY / 65535.f) * height);
951 ScreenToClient(window->win32.handle, &pos);
952
953 dx = pos.x - window->win32.lastCursorPosX;
954 dy = pos.y - window->win32.lastCursorPosY;
955 }
956 else
957 {
958 dx = data->data.mouse.lLastX;
959 dy = data->data.mouse.lLastY;
960 }
961
962 _glfwInputCursorPos(window,
963 window->virtualCursorPosX + dx,
964 window->virtualCursorPosY + dy);
965
966 window->win32.lastCursorPosX += dx;
967 window->win32.lastCursorPosY += dy;
968 break;
969 }
970
971 case WM_MOUSELEAVE:
972 {
973 window->win32.cursorTracked = GLFW_FALSE;
974 _glfwInputCursorEnter(window, GLFW_FALSE);
975 return 0;
976 }
977
978 case WM_MOUSEWHEEL:
979 {
980 _glfwInputScroll(window, 0.0, (SHORT) HIWORD(wParam) / (double) WHEEL_DELTA);
981 return 0;
982 }
983
984 case WM_MOUSEHWHEEL:
985 {
986 // This message is only sent on Windows Vista and later
987 // NOTE: The X-axis is inverted for consistency with macOS and X11
988 _glfwInputScroll(window, -((SHORT) HIWORD(wParam) / (double) WHEEL_DELTA), 0.0);
989 return 0;
990 }
991
992 case WM_ENTERSIZEMOVE:
993 case WM_ENTERMENULOOP:
994 {
995 if (window->win32.frameAction)
996 break;
997
998 // HACK: Enable the cursor while the user is moving or
999 // resizing the window or using the window menu
1000 if (window->cursorMode == GLFW_CURSOR_DISABLED)
1001 enableCursor(window);
1002 else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
1003 releaseCursor();
1004
1005 break;
1006 }
1007
1008 case WM_EXITSIZEMOVE:
1009 case WM_EXITMENULOOP:
1010 {
1011 if (window->win32.frameAction)
1012 break;
1013
1014 // HACK: Disable the cursor once the user is done moving or
1015 // resizing the window or using the menu
1016 if (window->cursorMode == GLFW_CURSOR_DISABLED)
1017 disableCursor(window);
1018 else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
1019 captureCursor(window);
1020
1021 break;
1022 }
1023
1024 case WM_SIZE:
1025 {
1026 const int width = LOWORD(lParam);
1027 const int height = HIWORD(lParam);
1028 const GLFWbool iconified = wParam == SIZE_MINIMIZED;
1029 const GLFWbool maximized = wParam == SIZE_MAXIMIZED ||
1030 (window->win32.maximized &&
1031 wParam != SIZE_RESTORED);
1032
1033 if (_glfw.win32.capturedCursorWindow == window)
1034 captureCursor(window);
1035
1036 if (window->win32.iconified != iconified)
1037 _glfwInputWindowIconify(window, iconified);
1038
1039 if (window->win32.maximized != maximized)
1040 _glfwInputWindowMaximize(window, maximized);
1041
1042 if (width != window->win32.width || height != window->win32.height)
1043 {
1044 window->win32.width = width;
1045 window->win32.height = height;
1046
1047 _glfwInputFramebufferSize(window, width, height);
1048 _glfwInputWindowSize(window, width, height);
1049 }
1050
1051 if (window->monitor && window->win32.iconified != iconified)
1052 {
1053 if (iconified)
1054 releaseMonitor(window);
1055 else
1056 {
1057 acquireMonitor(window);
1058 fitToMonitor(window);
1059 }
1060 }
1061
1062 window->win32.iconified = iconified;
1063 window->win32.maximized = maximized;
1064 return 0;
1065 }
1066
1067 case WM_MOVE:
1068 {
1069 if (_glfw.win32.capturedCursorWindow == window)
1070 captureCursor(window);
1071
1072 // NOTE: This cannot use LOWORD/HIWORD recommended by MSDN, as
1073 // those macros do not handle negative window positions correctly
1074 _glfwInputWindowPos(window,
1075 GET_X_LPARAM(lParam),
1076 GET_Y_LPARAM(lParam));
1077 return 0;
1078 }
1079
1080 case WM_SIZING:
1081 {
1082 if (window->numer == GLFW_DONT_CARE ||
1083 window->denom == GLFW_DONT_CARE)
1084 {
1085 break;
1086 }
1087
1088 applyAspectRatio(window, (int) wParam, (RECT*) lParam);
1089 return TRUE;
1090 }
1091
1092 case WM_GETMINMAXINFO:
1093 {
1094 RECT frame = {0};
1095 MINMAXINFO* mmi = (MINMAXINFO*) lParam;
1096 const DWORD style = getWindowStyle(window);
1097 const DWORD exStyle = getWindowExStyle(window);
1098
1099 if (window->monitor)
1100 break;
1101
1102 if (_glfwIsWindows10Version1607OrGreaterWin32())
1103 {
1104 AdjustWindowRectExForDpi(&frame, style, FALSE, exStyle,
1105 GetDpiForWindow(window->win32.handle));
1106 }
1107 else
1108 AdjustWindowRectEx(&frame, style, FALSE, exStyle);
1109
1110 if (window->minwidth != GLFW_DONT_CARE &&
1111 window->minheight != GLFW_DONT_CARE)
1112 {
1113 mmi->ptMinTrackSize.x = window->minwidth + frame.right - frame.left;
1114 mmi->ptMinTrackSize.y = window->minheight + frame.bottom - frame.top;
1115 }
1116
1117 if (window->maxwidth != GLFW_DONT_CARE &&
1118 window->maxheight != GLFW_DONT_CARE)
1119 {
1120 mmi->ptMaxTrackSize.x = window->maxwidth + frame.right - frame.left;
1121 mmi->ptMaxTrackSize.y = window->maxheight + frame.bottom - frame.top;
1122 }
1123
1124 if (!window->decorated)
1125 {
1126 MONITORINFO mi;
1127 const HMONITOR mh = MonitorFromWindow(window->win32.handle,
1128 MONITOR_DEFAULTTONEAREST);
1129
1130 ZeroMemory(&mi, sizeof(mi));
1131 mi.cbSize = sizeof(mi);
1132 GetMonitorInfoW(mh, &mi);
1133
1134 mmi->ptMaxPosition.x = mi.rcWork.left - mi.rcMonitor.left;
1135 mmi->ptMaxPosition.y = mi.rcWork.top - mi.rcMonitor.top;
1136 mmi->ptMaxSize.x = mi.rcWork.right - mi.rcWork.left;
1137 mmi->ptMaxSize.y = mi.rcWork.bottom - mi.rcWork.top;
1138 }
1139
1140 return 0;
1141 }
1142
1143 case WM_PAINT:
1144 {
1145 _glfwInputWindowDamage(window);
1146 break;
1147 }
1148
1149 case WM_ERASEBKGND:
1150 {
1151 return TRUE;
1152 }
1153
1154 case WM_NCACTIVATE:
1155 case WM_NCPAINT:
1156 {
1157 // Prevent title bar from being drawn after restoring a minimized
1158 // undecorated window
1159 if (!window->decorated)
1160 return TRUE;
1161
1162 break;
1163 }
1164
1165 case WM_DWMCOMPOSITIONCHANGED:
1166 case WM_DWMCOLORIZATIONCOLORCHANGED:
1167 {
1168 if (window->win32.transparent)
1169 updateFramebufferTransparency(window);
1170 return 0;
1171 }
1172
1173 case WM_GETDPISCALEDSIZE:
1174 {
1175 if (window->win32.scaleToMonitor)
1176 break;
1177
1178 // Adjust the window size to keep the content area size constant
1179 if (_glfwIsWindows10Version1703OrGreaterWin32())
1180 {
1181 RECT source = {0}, target = {0};
1182 SIZE* size = (SIZE*) lParam;
1183
1184 AdjustWindowRectExForDpi(&source, getWindowStyle(window),
1185 FALSE, getWindowExStyle(window),
1186 GetDpiForWindow(window->win32.handle));
1187 AdjustWindowRectExForDpi(&target, getWindowStyle(window),
1188 FALSE, getWindowExStyle(window),
1189 LOWORD(wParam));
1190
1191 size->cx += (target.right - target.left) -
1192 (source.right - source.left);
1193 size->cy += (target.bottom - target.top) -
1194 (source.bottom - source.top);
1195 return TRUE;
1196 }
1197
1198 break;
1199 }
1200
1201 case WM_DPICHANGED:
1202 {
1203 const float xscale = HIWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI;
1204 const float yscale = LOWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI;
1205
1206 // Resize windowed mode windows that either permit rescaling or that
1207 // need it to compensate for non-client area scaling
1208 if (!window->monitor &&
1209 (window->win32.scaleToMonitor ||
1210 _glfwIsWindows10Version1703OrGreaterWin32()))
1211 {
1212 RECT* suggested = (RECT*) lParam;
1213 SetWindowPos(window->win32.handle, HWND_TOP,
1214 suggested->left,
1215 suggested->top,
1216 suggested->right - suggested->left,
1217 suggested->bottom - suggested->top,
1218 SWP_NOACTIVATE | SWP_NOZORDER);
1219 }
1220
1221 _glfwInputWindowContentScale(window, xscale, yscale);
1222 break;
1223 }
1224
1225 case WM_SETCURSOR:
1226 {
1227 if (LOWORD(lParam) == HTCLIENT)
1228 {
1229 updateCursorImage(window);
1230 return TRUE;
1231 }
1232
1233 break;
1234 }
1235
1236 case WM_DROPFILES:
1237 {
1238 HDROP drop = (HDROP) wParam;
1239 POINT pt;
1240 int i;
1241
1242 const int count = DragQueryFileW(drop, 0xffffffff, NULL, 0);
1243 char** paths = _glfw_calloc(count, sizeof(char*));
1244
1245 // Move the mouse to the position of the drop
1246 DragQueryPoint(drop, &pt);
1247 _glfwInputCursorPos(window, pt.x, pt.y);
1248
1249 for (i = 0; i < count; i++)
1250 {
1251 const UINT length = DragQueryFileW(drop, i, NULL, 0);
1252 WCHAR* buffer = _glfw_calloc((size_t) length + 1, sizeof(WCHAR));
1253
1254 DragQueryFileW(drop, i, buffer, length + 1);
1255 paths[i] = _glfwCreateUTF8FromWideStringWin32(buffer);
1256
1257 _glfw_free(buffer);
1258 }
1259
1260 _glfwInputDrop(window, count, (const char**) paths);
1261
1262 for (i = 0; i < count; i++)
1263 _glfw_free(paths[i]);
1264 _glfw_free(paths);
1265
1266 DragFinish(drop);
1267 return 0;
1268 }
1269 }
1270
1271 return DefWindowProcW(hWnd, uMsg, wParam, lParam);
1272 }
1273
1274 // Creates the GLFW window
1275 //
createNativeWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWfbconfig* fbconfig)1276 static int createNativeWindow(_GLFWwindow* window,
1277 const _GLFWwndconfig* wndconfig,
1278 const _GLFWfbconfig* fbconfig)
1279 {
1280 int frameX, frameY, frameWidth, frameHeight;
1281 WCHAR* wideTitle;
1282 DWORD style = getWindowStyle(window);
1283 DWORD exStyle = getWindowExStyle(window);
1284
1285 if (!_glfw.win32.mainWindowClass)
1286 {
1287 WNDCLASSEXW wc = { sizeof(wc) };
1288 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
1289 wc.lpfnWndProc = windowProc;
1290 wc.hInstance = _glfw.win32.instance;
1291 wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
1292 #if defined(_GLFW_WNDCLASSNAME)
1293 wc.lpszClassName = _GLFW_WNDCLASSNAME;
1294 #else
1295 wc.lpszClassName = L"GLFW30";
1296 #endif
1297 // Load user-provided icon if available
1298 wc.hIcon = LoadImageW(GetModuleHandleW(NULL),
1299 L"GLFW_ICON", IMAGE_ICON,
1300 0, 0, LR_DEFAULTSIZE | LR_SHARED);
1301 if (!wc.hIcon)
1302 {
1303 // No user-provided icon found, load default icon
1304 wc.hIcon = LoadImageW(NULL,
1305 IDI_APPLICATION, IMAGE_ICON,
1306 0, 0, LR_DEFAULTSIZE | LR_SHARED);
1307 }
1308
1309 _glfw.win32.mainWindowClass = RegisterClassExW(&wc);
1310 if (!_glfw.win32.mainWindowClass)
1311 {
1312 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
1313 "Win32: Failed to register window class");
1314 return GLFW_FALSE;
1315 }
1316 }
1317
1318 if (GetSystemMetrics(SM_REMOTESESSION))
1319 {
1320 // NOTE: On Remote Desktop, setting the cursor to NULL does not hide it
1321 // HACK: Create a transparent cursor and always set that instead of NULL
1322 // When not on Remote Desktop, this handle is NULL and normal hiding is used
1323 if (!_glfw.win32.blankCursor)
1324 {
1325 const int cursorWidth = GetSystemMetrics(SM_CXCURSOR);
1326 const int cursorHeight = GetSystemMetrics(SM_CYCURSOR);
1327
1328 unsigned char* cursorPixels = _glfw_calloc(cursorWidth * cursorHeight, 4);
1329 if (!cursorPixels)
1330 return GLFW_FALSE;
1331
1332 // NOTE: Windows checks whether the image is fully transparent and if so
1333 // just ignores the alpha channel and makes the whole cursor opaque
1334 // HACK: Make one pixel slightly less transparent
1335 cursorPixels[3] = 1;
1336
1337 const GLFWimage cursorImage = { cursorWidth, cursorHeight, cursorPixels };
1338 _glfw.win32.blankCursor = createIcon(&cursorImage, 0, 0, FALSE);
1339 _glfw_free(cursorPixels);
1340
1341 if (!_glfw.win32.blankCursor)
1342 return GLFW_FALSE;
1343 }
1344 }
1345
1346 if (window->monitor)
1347 {
1348 MONITORINFO mi = { sizeof(mi) };
1349 GetMonitorInfoW(window->monitor->win32.handle, &mi);
1350
1351 // NOTE: This window placement is temporary and approximate, as the
1352 // correct position and size cannot be known until the monitor
1353 // video mode has been picked in _glfwSetVideoModeWin32
1354 frameX = mi.rcMonitor.left;
1355 frameY = mi.rcMonitor.top;
1356 frameWidth = mi.rcMonitor.right - mi.rcMonitor.left;
1357 frameHeight = mi.rcMonitor.bottom - mi.rcMonitor.top;
1358 }
1359 else
1360 {
1361 RECT rect = { 0, 0, wndconfig->width, wndconfig->height };
1362
1363 window->win32.maximized = wndconfig->maximized;
1364 if (wndconfig->maximized)
1365 style |= WS_MAXIMIZE;
1366
1367 AdjustWindowRectEx(&rect, style, FALSE, exStyle);
1368
1369 if (wndconfig->xpos == GLFW_ANY_POSITION && wndconfig->ypos == GLFW_ANY_POSITION)
1370 {
1371 frameX = CW_USEDEFAULT;
1372 frameY = CW_USEDEFAULT;
1373 }
1374 else
1375 {
1376 frameX = wndconfig->xpos + rect.left;
1377 frameY = wndconfig->ypos + rect.top;
1378 }
1379
1380 frameWidth = rect.right - rect.left;
1381 frameHeight = rect.bottom - rect.top;
1382 }
1383
1384 wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title);
1385 if (!wideTitle)
1386 return GLFW_FALSE;
1387
1388 window->win32.handle = CreateWindowExW(exStyle,
1389 MAKEINTATOM(_glfw.win32.mainWindowClass),
1390 wideTitle,
1391 style,
1392 frameX, frameY,
1393 frameWidth, frameHeight,
1394 NULL, // No parent window
1395 NULL, // No window menu
1396 _glfw.win32.instance,
1397 (LPVOID) wndconfig);
1398
1399 _glfw_free(wideTitle);
1400
1401 if (!window->win32.handle)
1402 {
1403 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
1404 "Win32: Failed to create window");
1405 return GLFW_FALSE;
1406 }
1407
1408 SetPropW(window->win32.handle, L"GLFW", window);
1409
1410 if (IsWindows7OrGreater())
1411 {
1412 ChangeWindowMessageFilterEx(window->win32.handle,
1413 WM_DROPFILES, MSGFLT_ALLOW, NULL);
1414 ChangeWindowMessageFilterEx(window->win32.handle,
1415 WM_COPYDATA, MSGFLT_ALLOW, NULL);
1416 ChangeWindowMessageFilterEx(window->win32.handle,
1417 WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL);
1418 }
1419
1420 window->win32.scaleToMonitor = wndconfig->scaleToMonitor;
1421 window->win32.keymenu = wndconfig->win32.keymenu;
1422 window->win32.showDefault = wndconfig->win32.showDefault;
1423
1424 if (!window->monitor)
1425 {
1426 RECT rect = { 0, 0, wndconfig->width, wndconfig->height };
1427 WINDOWPLACEMENT wp = { sizeof(wp) };
1428 const HMONITOR mh = MonitorFromWindow(window->win32.handle,
1429 MONITOR_DEFAULTTONEAREST);
1430
1431 // Adjust window rect to account for DPI scaling of the window frame and
1432 // (if enabled) DPI scaling of the content area
1433 // This cannot be done until we know what monitor the window was placed on
1434 // Only update the restored window rect as the window may be maximized
1435
1436 if (wndconfig->scaleToMonitor)
1437 {
1438 float xscale, yscale;
1439 _glfwGetHMONITORContentScaleWin32(mh, &xscale, &yscale);
1440
1441 if (xscale > 0.f && yscale > 0.f)
1442 {
1443 rect.right = (int) (rect.right * xscale);
1444 rect.bottom = (int) (rect.bottom * yscale);
1445 }
1446 }
1447
1448 if (_glfwIsWindows10Version1607OrGreaterWin32())
1449 {
1450 AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle,
1451 GetDpiForWindow(window->win32.handle));
1452 }
1453 else
1454 AdjustWindowRectEx(&rect, style, FALSE, exStyle);
1455
1456 GetWindowPlacement(window->win32.handle, &wp);
1457 OffsetRect(&rect,
1458 wp.rcNormalPosition.left - rect.left,
1459 wp.rcNormalPosition.top - rect.top);
1460
1461 wp.rcNormalPosition = rect;
1462 wp.showCmd = SW_HIDE;
1463 SetWindowPlacement(window->win32.handle, &wp);
1464
1465 // Adjust rect of maximized undecorated window, because by default Windows will
1466 // make such a window cover the whole monitor instead of its workarea
1467
1468 if (wndconfig->maximized && !wndconfig->decorated)
1469 {
1470 MONITORINFO mi = { sizeof(mi) };
1471 GetMonitorInfoW(mh, &mi);
1472
1473 SetWindowPos(window->win32.handle, HWND_TOP,
1474 mi.rcWork.left,
1475 mi.rcWork.top,
1476 mi.rcWork.right - mi.rcWork.left,
1477 mi.rcWork.bottom - mi.rcWork.top,
1478 SWP_NOACTIVATE | SWP_NOZORDER);
1479 }
1480 }
1481
1482 DragAcceptFiles(window->win32.handle, TRUE);
1483
1484 if (fbconfig->transparent)
1485 {
1486 updateFramebufferTransparency(window);
1487 window->win32.transparent = GLFW_TRUE;
1488 }
1489
1490 _glfwGetWindowSizeWin32(window, &window->win32.width, &window->win32.height);
1491
1492 return GLFW_TRUE;
1493 }
1494
_glfwCreateWindowWin32(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig)1495 GLFWbool _glfwCreateWindowWin32(_GLFWwindow* window,
1496 const _GLFWwndconfig* wndconfig,
1497 const _GLFWctxconfig* ctxconfig,
1498 const _GLFWfbconfig* fbconfig)
1499 {
1500 if (!createNativeWindow(window, wndconfig, fbconfig))
1501 return GLFW_FALSE;
1502
1503 if (ctxconfig->client != GLFW_NO_API)
1504 {
1505 if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
1506 {
1507 if (!_glfwInitWGL())
1508 return GLFW_FALSE;
1509 if (!_glfwCreateContextWGL(window, ctxconfig, fbconfig))
1510 return GLFW_FALSE;
1511 }
1512 else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
1513 {
1514 if (!_glfwInitEGL())
1515 return GLFW_FALSE;
1516 if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
1517 return GLFW_FALSE;
1518 }
1519 else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
1520 {
1521 if (!_glfwInitOSMesa())
1522 return GLFW_FALSE;
1523 if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
1524 return GLFW_FALSE;
1525 }
1526
1527 if (!_glfwRefreshContextAttribs(window, ctxconfig))
1528 return GLFW_FALSE;
1529 }
1530
1531 if (wndconfig->mousePassthrough)
1532 _glfwSetWindowMousePassthroughWin32(window, GLFW_TRUE);
1533
1534 if (window->monitor)
1535 {
1536 _glfwShowWindowWin32(window);
1537 _glfwFocusWindowWin32(window);
1538 acquireMonitor(window);
1539 fitToMonitor(window);
1540
1541 if (wndconfig->centerCursor)
1542 _glfwCenterCursorInContentArea(window);
1543 }
1544 else
1545 {
1546 if (wndconfig->visible)
1547 {
1548 _glfwShowWindowWin32(window);
1549 if (wndconfig->focused)
1550 _glfwFocusWindowWin32(window);
1551 }
1552 }
1553
1554 return GLFW_TRUE;
1555 }
1556
_glfwDestroyWindowWin32(_GLFWwindow* window)1557 void _glfwDestroyWindowWin32(_GLFWwindow* window)
1558 {
1559 if (window->monitor)
1560 releaseMonitor(window);
1561
1562 if (window->context.destroy)
1563 window->context.destroy(window);
1564
1565 if (_glfw.win32.disabledCursorWindow == window)
1566 enableCursor(window);
1567
1568 if (_glfw.win32.capturedCursorWindow == window)
1569 releaseCursor();
1570
1571 if (window->win32.handle)
1572 {
1573 RemovePropW(window->win32.handle, L"GLFW");
1574 DestroyWindow(window->win32.handle);
1575 window->win32.handle = NULL;
1576 }
1577
1578 if (window->win32.bigIcon)
1579 DestroyIcon(window->win32.bigIcon);
1580
1581 if (window->win32.smallIcon)
1582 DestroyIcon(window->win32.smallIcon);
1583 }
1584
_glfwSetWindowTitleWin32(_GLFWwindow* window, const char* title)1585 void _glfwSetWindowTitleWin32(_GLFWwindow* window, const char* title)
1586 {
1587 WCHAR* wideTitle = _glfwCreateWideStringFromUTF8Win32(title);
1588 if (!wideTitle)
1589 return;
1590
1591 SetWindowTextW(window->win32.handle, wideTitle);
1592 _glfw_free(wideTitle);
1593 }
1594
_glfwSetWindowIconWin32(_GLFWwindow* window, int count, const GLFWimage* images)1595 void _glfwSetWindowIconWin32(_GLFWwindow* window, int count, const GLFWimage* images)
1596 {
1597 HICON bigIcon = NULL, smallIcon = NULL;
1598
1599 if (count)
1600 {
1601 const GLFWimage* bigImage = chooseImage(count, images,
1602 GetSystemMetrics(SM_CXICON),
1603 GetSystemMetrics(SM_CYICON));
1604 const GLFWimage* smallImage = chooseImage(count, images,
1605 GetSystemMetrics(SM_CXSMICON),
1606 GetSystemMetrics(SM_CYSMICON));
1607
1608 bigIcon = createIcon(bigImage, 0, 0, GLFW_TRUE);
1609 smallIcon = createIcon(smallImage, 0, 0, GLFW_TRUE);
1610 }
1611 else
1612 {
1613 bigIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICON);
1614 smallIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICONSM);
1615 }
1616
1617 SendMessageW(window->win32.handle, WM_SETICON, ICON_BIG, (LPARAM) bigIcon);
1618 SendMessageW(window->win32.handle, WM_SETICON, ICON_SMALL, (LPARAM) smallIcon);
1619
1620 if (window->win32.bigIcon)
1621 DestroyIcon(window->win32.bigIcon);
1622
1623 if (window->win32.smallIcon)
1624 DestroyIcon(window->win32.smallIcon);
1625
1626 if (count)
1627 {
1628 window->win32.bigIcon = bigIcon;
1629 window->win32.smallIcon = smallIcon;
1630 }
1631 }
1632
_glfwGetWindowPosWin32(_GLFWwindow* window, int* xpos, int* ypos)1633 void _glfwGetWindowPosWin32(_GLFWwindow* window, int* xpos, int* ypos)
1634 {
1635 POINT pos = { 0, 0 };
1636 ClientToScreen(window->win32.handle, &pos);
1637
1638 if (xpos)
1639 *xpos = pos.x;
1640 if (ypos)
1641 *ypos = pos.y;
1642 }
1643
_glfwSetWindowPosWin32(_GLFWwindow* window, int xpos, int ypos)1644 void _glfwSetWindowPosWin32(_GLFWwindow* window, int xpos, int ypos)
1645 {
1646 RECT rect = { xpos, ypos, xpos, ypos };
1647
1648 if (_glfwIsWindows10Version1607OrGreaterWin32())
1649 {
1650 AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
1651 FALSE, getWindowExStyle(window),
1652 GetDpiForWindow(window->win32.handle));
1653 }
1654 else
1655 {
1656 AdjustWindowRectEx(&rect, getWindowStyle(window),
1657 FALSE, getWindowExStyle(window));
1658 }
1659
1660 SetWindowPos(window->win32.handle, NULL, rect.left, rect.top, 0, 0,
1661 SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
1662 }
1663
_glfwGetWindowSizeWin32(_GLFWwindow* window, int* width, int* height)1664 void _glfwGetWindowSizeWin32(_GLFWwindow* window, int* width, int* height)
1665 {
1666 RECT area;
1667 GetClientRect(window->win32.handle, &area);
1668
1669 if (width)
1670 *width = area.right;
1671 if (height)
1672 *height = area.bottom;
1673 }
1674
_glfwSetWindowSizeWin32(_GLFWwindow* window, int width, int height)1675 void _glfwSetWindowSizeWin32(_GLFWwindow* window, int width, int height)
1676 {
1677 if (window->monitor)
1678 {
1679 if (window->monitor->window == window)
1680 {
1681 acquireMonitor(window);
1682 fitToMonitor(window);
1683 }
1684 }
1685 else
1686 {
1687 RECT rect = { 0, 0, width, height };
1688
1689 if (_glfwIsWindows10Version1607OrGreaterWin32())
1690 {
1691 AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
1692 FALSE, getWindowExStyle(window),
1693 GetDpiForWindow(window->win32.handle));
1694 }
1695 else
1696 {
1697 AdjustWindowRectEx(&rect, getWindowStyle(window),
1698 FALSE, getWindowExStyle(window));
1699 }
1700
1701 SetWindowPos(window->win32.handle, HWND_TOP,
1702 0, 0, rect.right - rect.left, rect.bottom - rect.top,
1703 SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER);
1704 }
1705 }
1706
_glfwSetWindowSizeLimitsWin32(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight)1707 void _glfwSetWindowSizeLimitsWin32(_GLFWwindow* window,
1708 int minwidth, int minheight,
1709 int maxwidth, int maxheight)
1710 {
1711 RECT area;
1712
1713 if ((minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) &&
1714 (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE))
1715 {
1716 return;
1717 }
1718
1719 GetWindowRect(window->win32.handle, &area);
1720 MoveWindow(window->win32.handle,
1721 area.left, area.top,
1722 area.right - area.left,
1723 area.bottom - area.top, TRUE);
1724 }
1725
_glfwSetWindowAspectRatioWin32(_GLFWwindow* window, int numer, int denom)1726 void _glfwSetWindowAspectRatioWin32(_GLFWwindow* window, int numer, int denom)
1727 {
1728 RECT area;
1729
1730 if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE)
1731 return;
1732
1733 GetWindowRect(window->win32.handle, &area);
1734 applyAspectRatio(window, WMSZ_BOTTOMRIGHT, &area);
1735 MoveWindow(window->win32.handle,
1736 area.left, area.top,
1737 area.right - area.left,
1738 area.bottom - area.top, TRUE);
1739 }
1740
_glfwGetFramebufferSizeWin32(_GLFWwindow* window, int* width, int* height)1741 void _glfwGetFramebufferSizeWin32(_GLFWwindow* window, int* width, int* height)
1742 {
1743 _glfwGetWindowSizeWin32(window, width, height);
1744 }
1745
_glfwGetWindowFrameSizeWin32(_GLFWwindow* window, int* left, int* top, int* right, int* bottom)1746 void _glfwGetWindowFrameSizeWin32(_GLFWwindow* window,
1747 int* left, int* top,
1748 int* right, int* bottom)
1749 {
1750 RECT rect;
1751 int width, height;
1752
1753 _glfwGetWindowSizeWin32(window, &width, &height);
1754 SetRect(&rect, 0, 0, width, height);
1755
1756 if (_glfwIsWindows10Version1607OrGreaterWin32())
1757 {
1758 AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
1759 FALSE, getWindowExStyle(window),
1760 GetDpiForWindow(window->win32.handle));
1761 }
1762 else
1763 {
1764 AdjustWindowRectEx(&rect, getWindowStyle(window),
1765 FALSE, getWindowExStyle(window));
1766 }
1767
1768 if (left)
1769 *left = -rect.left;
1770 if (top)
1771 *top = -rect.top;
1772 if (right)
1773 *right = rect.right - width;
1774 if (bottom)
1775 *bottom = rect.bottom - height;
1776 }
1777
_glfwGetWindowContentScaleWin32(_GLFWwindow* window, float* xscale, float* yscale)1778 void _glfwGetWindowContentScaleWin32(_GLFWwindow* window, float* xscale, float* yscale)
1779 {
1780 const HANDLE handle = MonitorFromWindow(window->win32.handle,
1781 MONITOR_DEFAULTTONEAREST);
1782 _glfwGetHMONITORContentScaleWin32(handle, xscale, yscale);
1783 }
1784
_glfwIconifyWindowWin32(_GLFWwindow* window)1785 void _glfwIconifyWindowWin32(_GLFWwindow* window)
1786 {
1787 ShowWindow(window->win32.handle, SW_MINIMIZE);
1788 }
1789
_glfwRestoreWindowWin32(_GLFWwindow* window)1790 void _glfwRestoreWindowWin32(_GLFWwindow* window)
1791 {
1792 ShowWindow(window->win32.handle, SW_RESTORE);
1793 }
1794
_glfwMaximizeWindowWin32(_GLFWwindow* window)1795 void _glfwMaximizeWindowWin32(_GLFWwindow* window)
1796 {
1797 if (IsWindowVisible(window->win32.handle))
1798 ShowWindow(window->win32.handle, SW_MAXIMIZE);
1799 else
1800 maximizeWindowManually(window);
1801 }
1802
_glfwShowWindowWin32(_GLFWwindow* window)1803 void _glfwShowWindowWin32(_GLFWwindow* window)
1804 {
1805 int showCommand = SW_SHOWNA;
1806
1807 if (window->win32.showDefault)
1808 {
1809 // NOTE: GLFW windows currently do not seem to match the Windows 10 definition of
1810 // a main window, so even SW_SHOWDEFAULT does nothing
1811 // This definition is undocumented and can change (source: Raymond Chen)
1812 // HACK: Apply the STARTUPINFO show command manually if available
1813 STARTUPINFOW si = { sizeof(si) };
1814 GetStartupInfoW(&si);
1815 if (si.dwFlags & STARTF_USESHOWWINDOW)
1816 showCommand = si.wShowWindow;
1817
1818 window->win32.showDefault = GLFW_FALSE;
1819 }
1820
1821 ShowWindow(window->win32.handle, showCommand);
1822 }
1823
_glfwHideWindowWin32(_GLFWwindow* window)1824 void _glfwHideWindowWin32(_GLFWwindow* window)
1825 {
1826 ShowWindow(window->win32.handle, SW_HIDE);
1827 }
1828
_glfwRequestWindowAttentionWin32(_GLFWwindow* window)1829 void _glfwRequestWindowAttentionWin32(_GLFWwindow* window)
1830 {
1831 FlashWindow(window->win32.handle, TRUE);
1832 }
1833
_glfwFocusWindowWin32(_GLFWwindow* window)1834 void _glfwFocusWindowWin32(_GLFWwindow* window)
1835 {
1836 BringWindowToTop(window->win32.handle);
1837 SetForegroundWindow(window->win32.handle);
1838 SetFocus(window->win32.handle);
1839 }
1840
_glfwSetWindowMonitorWin32(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate)1841 void _glfwSetWindowMonitorWin32(_GLFWwindow* window,
1842 _GLFWmonitor* monitor,
1843 int xpos, int ypos,
1844 int width, int height,
1845 int refreshRate)
1846 {
1847 if (window->monitor == monitor)
1848 {
1849 if (monitor)
1850 {
1851 if (monitor->window == window)
1852 {
1853 acquireMonitor(window);
1854 fitToMonitor(window);
1855 }
1856 }
1857 else
1858 {
1859 RECT rect = { xpos, ypos, xpos + width, ypos + height };
1860
1861 if (_glfwIsWindows10Version1607OrGreaterWin32())
1862 {
1863 AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
1864 FALSE, getWindowExStyle(window),
1865 GetDpiForWindow(window->win32.handle));
1866 }
1867 else
1868 {
1869 AdjustWindowRectEx(&rect, getWindowStyle(window),
1870 FALSE, getWindowExStyle(window));
1871 }
1872
1873 SetWindowPos(window->win32.handle, HWND_TOP,
1874 rect.left, rect.top,
1875 rect.right - rect.left, rect.bottom - rect.top,
1876 SWP_NOCOPYBITS | SWP_NOACTIVATE | SWP_NOZORDER);
1877 }
1878
1879 return;
1880 }
1881
1882 if (window->monitor)
1883 releaseMonitor(window);
1884
1885 _glfwInputWindowMonitor(window, monitor);
1886
1887 if (window->monitor)
1888 {
1889 MONITORINFO mi = { sizeof(mi) };
1890 UINT flags = SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOCOPYBITS;
1891
1892 if (window->decorated)
1893 {
1894 DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE);
1895 style &= ~WS_OVERLAPPEDWINDOW;
1896 style |= getWindowStyle(window);
1897 SetWindowLongW(window->win32.handle, GWL_STYLE, style);
1898 flags |= SWP_FRAMECHANGED;
1899 }
1900
1901 acquireMonitor(window);
1902
1903 GetMonitorInfoW(window->monitor->win32.handle, &mi);
1904 SetWindowPos(window->win32.handle, HWND_TOPMOST,
1905 mi.rcMonitor.left,
1906 mi.rcMonitor.top,
1907 mi.rcMonitor.right - mi.rcMonitor.left,
1908 mi.rcMonitor.bottom - mi.rcMonitor.top,
1909 flags);
1910 }
1911 else
1912 {
1913 HWND after;
1914 RECT rect = { xpos, ypos, xpos + width, ypos + height };
1915 DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE);
1916 UINT flags = SWP_NOACTIVATE | SWP_NOCOPYBITS;
1917
1918 if (window->decorated)
1919 {
1920 style &= ~WS_POPUP;
1921 style |= getWindowStyle(window);
1922 SetWindowLongW(window->win32.handle, GWL_STYLE, style);
1923
1924 flags |= SWP_FRAMECHANGED;
1925 }
1926
1927 if (window->floating)
1928 after = HWND_TOPMOST;
1929 else
1930 after = HWND_NOTOPMOST;
1931
1932 if (_glfwIsWindows10Version1607OrGreaterWin32())
1933 {
1934 AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
1935 FALSE, getWindowExStyle(window),
1936 GetDpiForWindow(window->win32.handle));
1937 }
1938 else
1939 {
1940 AdjustWindowRectEx(&rect, getWindowStyle(window),
1941 FALSE, getWindowExStyle(window));
1942 }
1943
1944 SetWindowPos(window->win32.handle, after,
1945 rect.left, rect.top,
1946 rect.right - rect.left, rect.bottom - rect.top,
1947 flags);
1948 }
1949 }
1950
_glfwWindowFocusedWin32(_GLFWwindow* window)1951 GLFWbool _glfwWindowFocusedWin32(_GLFWwindow* window)
1952 {
1953 return window->win32.handle == GetActiveWindow();
1954 }
1955
_glfwWindowIconifiedWin32(_GLFWwindow* window)1956 GLFWbool _glfwWindowIconifiedWin32(_GLFWwindow* window)
1957 {
1958 return IsIconic(window->win32.handle);
1959 }
1960
_glfwWindowVisibleWin32(_GLFWwindow* window)1961 GLFWbool _glfwWindowVisibleWin32(_GLFWwindow* window)
1962 {
1963 return IsWindowVisible(window->win32.handle);
1964 }
1965
_glfwWindowMaximizedWin32(_GLFWwindow* window)1966 GLFWbool _glfwWindowMaximizedWin32(_GLFWwindow* window)
1967 {
1968 return IsZoomed(window->win32.handle);
1969 }
1970
_glfwWindowHoveredWin32(_GLFWwindow* window)1971 GLFWbool _glfwWindowHoveredWin32(_GLFWwindow* window)
1972 {
1973 return cursorInContentArea(window);
1974 }
1975
_glfwFramebufferTransparentWin32(_GLFWwindow* window)1976 GLFWbool _glfwFramebufferTransparentWin32(_GLFWwindow* window)
1977 {
1978 BOOL composition, opaque;
1979 DWORD color;
1980
1981 if (!window->win32.transparent)
1982 return GLFW_FALSE;
1983
1984 if (!IsWindowsVistaOrGreater())
1985 return GLFW_FALSE;
1986
1987 if (FAILED(DwmIsCompositionEnabled(&composition)) || !composition)
1988 return GLFW_FALSE;
1989
1990 if (!IsWindows8OrGreater())
1991 {
1992 // HACK: Disable framebuffer transparency on Windows 7 when the
1993 // colorization color is opaque, because otherwise the window
1994 // contents is blended additively with the previous frame instead
1995 // of replacing it
1996 if (FAILED(DwmGetColorizationColor(&color, &opaque)) || opaque)
1997 return GLFW_FALSE;
1998 }
1999
2000 return GLFW_TRUE;
2001 }
2002
_glfwSetWindowResizableWin32(_GLFWwindow* window, GLFWbool enabled)2003 void _glfwSetWindowResizableWin32(_GLFWwindow* window, GLFWbool enabled)
2004 {
2005 updateWindowStyles(window);
2006 }
2007
_glfwSetWindowDecoratedWin32(_GLFWwindow* window, GLFWbool enabled)2008 void _glfwSetWindowDecoratedWin32(_GLFWwindow* window, GLFWbool enabled)
2009 {
2010 updateWindowStyles(window);
2011 }
2012
_glfwSetWindowFloatingWin32(_GLFWwindow* window, GLFWbool enabled)2013 void _glfwSetWindowFloatingWin32(_GLFWwindow* window, GLFWbool enabled)
2014 {
2015 const HWND after = enabled ? HWND_TOPMOST : HWND_NOTOPMOST;
2016 SetWindowPos(window->win32.handle, after, 0, 0, 0, 0,
2017 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
2018 }
2019
_glfwSetWindowMousePassthroughWin32(_GLFWwindow* window, GLFWbool enabled)2020 void _glfwSetWindowMousePassthroughWin32(_GLFWwindow* window, GLFWbool enabled)
2021 {
2022 COLORREF key = 0;
2023 BYTE alpha = 0;
2024 DWORD flags = 0;
2025 DWORD exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
2026
2027 if (exStyle & WS_EX_LAYERED)
2028 GetLayeredWindowAttributes(window->win32.handle, &key, &alpha, &flags);
2029
2030 if (enabled)
2031 exStyle |= (WS_EX_TRANSPARENT | WS_EX_LAYERED);
2032 else
2033 {
2034 exStyle &= ~WS_EX_TRANSPARENT;
2035 // NOTE: Window opacity also needs the layered window style so do not
2036 // remove it if the window is alpha blended
2037 if (exStyle & WS_EX_LAYERED)
2038 {
2039 if (!(flags & LWA_ALPHA))
2040 exStyle &= ~WS_EX_LAYERED;
2041 }
2042 }
2043
2044 SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle);
2045
2046 if (enabled)
2047 SetLayeredWindowAttributes(window->win32.handle, key, alpha, flags);
2048 }
2049
_glfwGetWindowOpacityWin32(_GLFWwindow* window)2050 float _glfwGetWindowOpacityWin32(_GLFWwindow* window)
2051 {
2052 BYTE alpha;
2053 DWORD flags;
2054
2055 if ((GetWindowLongW(window->win32.handle, GWL_EXSTYLE) & WS_EX_LAYERED) &&
2056 GetLayeredWindowAttributes(window->win32.handle, NULL, &alpha, &flags))
2057 {
2058 if (flags & LWA_ALPHA)
2059 return alpha / 255.f;
2060 }
2061
2062 return 1.f;
2063 }
2064
_glfwSetWindowOpacityWin32(_GLFWwindow* window, float opacity)2065 void _glfwSetWindowOpacityWin32(_GLFWwindow* window, float opacity)
2066 {
2067 LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
2068 if (opacity < 1.f || (exStyle & WS_EX_TRANSPARENT))
2069 {
2070 const BYTE alpha = (BYTE) (255 * opacity);
2071 exStyle |= WS_EX_LAYERED;
2072 SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle);
2073 SetLayeredWindowAttributes(window->win32.handle, 0, alpha, LWA_ALPHA);
2074 }
2075 else if (exStyle & WS_EX_TRANSPARENT)
2076 {
2077 SetLayeredWindowAttributes(window->win32.handle, 0, 0, 0);
2078 }
2079 else
2080 {
2081 exStyle &= ~WS_EX_LAYERED;
2082 SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle);
2083 }
2084 }
2085
_glfwSetRawMouseMotionWin32(_GLFWwindow *window, GLFWbool enabled)2086 void _glfwSetRawMouseMotionWin32(_GLFWwindow *window, GLFWbool enabled)
2087 {
2088 if (_glfw.win32.disabledCursorWindow != window)
2089 return;
2090
2091 if (enabled)
2092 enableRawMouseMotion(window);
2093 else
2094 disableRawMouseMotion(window);
2095 }
2096
_glfwRawMouseMotionSupportedWin32(void)2097 GLFWbool _glfwRawMouseMotionSupportedWin32(void)
2098 {
2099 return GLFW_TRUE;
2100 }
2101
_glfwPollEventsWin32(void)2102 void _glfwPollEventsWin32(void)
2103 {
2104 MSG msg;
2105 HWND handle;
2106 _GLFWwindow* window;
2107
2108 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
2109 {
2110 if (msg.message == WM_QUIT)
2111 {
2112 // NOTE: While GLFW does not itself post WM_QUIT, other processes
2113 // may post it to this one, for example Task Manager
2114 // HACK: Treat WM_QUIT as a close on all windows
2115
2116 window = _glfw.windowListHead;
2117 while (window)
2118 {
2119 _glfwInputWindowCloseRequest(window);
2120 window = window->next;
2121 }
2122 }
2123 else
2124 {
2125 TranslateMessage(&msg);
2126 DispatchMessageW(&msg);
2127 }
2128 }
2129
2130 // HACK: Release modifier keys that the system did not emit KEYUP for
2131 // NOTE: Shift keys on Windows tend to "stick" when both are pressed as
2132 // no key up message is generated by the first key release
2133 // NOTE: Windows key is not reported as released by the Win+V hotkey
2134 // Other Win hotkeys are handled implicitly by _glfwInputWindowFocus
2135 // because they change the input focus
2136 // NOTE: The other half of this is in the WM_*KEY* handler in windowProc
2137 handle = GetActiveWindow();
2138 if (handle)
2139 {
2140 window = GetPropW(handle, L"GLFW");
2141 if (window)
2142 {
2143 int i;
2144 const int keys[4][2] =
2145 {
2146 { VK_LSHIFT, GLFW_KEY_LEFT_SHIFT },
2147 { VK_RSHIFT, GLFW_KEY_RIGHT_SHIFT },
2148 { VK_LWIN, GLFW_KEY_LEFT_SUPER },
2149 { VK_RWIN, GLFW_KEY_RIGHT_SUPER }
2150 };
2151
2152 for (i = 0; i < 4; i++)
2153 {
2154 const int vk = keys[i][0];
2155 const int key = keys[i][1];
2156 const int scancode = _glfw.win32.scancodes[key];
2157
2158 if ((GetKeyState(vk) & 0x8000))
2159 continue;
2160 if (window->keys[key] != GLFW_PRESS)
2161 continue;
2162
2163 _glfwInputKey(window, key, scancode, GLFW_RELEASE, getKeyMods());
2164 }
2165 }
2166 }
2167
2168 window = _glfw.win32.disabledCursorWindow;
2169 if (window)
2170 {
2171 int width, height;
2172 _glfwGetWindowSizeWin32(window, &width, &height);
2173
2174 // NOTE: Re-center the cursor only if it has moved since the last call,
2175 // to avoid breaking glfwWaitEvents with WM_MOUSEMOVE
2176 // The re-center is required in order to prevent the mouse cursor stopping at the edges of the screen.
2177 if (window->win32.lastCursorPosX != width / 2 ||
2178 window->win32.lastCursorPosY != height / 2)
2179 {
2180 _glfwSetCursorPosWin32(window, width / 2, height / 2);
2181 }
2182 }
2183 }
2184
_glfwWaitEventsWin32(void)2185 void _glfwWaitEventsWin32(void)
2186 {
2187 WaitMessage();
2188
2189 _glfwPollEventsWin32();
2190 }
2191
_glfwWaitEventsTimeoutWin32(double timeout)2192 void _glfwWaitEventsTimeoutWin32(double timeout)
2193 {
2194 MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD) (timeout * 1e3), QS_ALLINPUT);
2195
2196 _glfwPollEventsWin32();
2197 }
2198
_glfwPostEmptyEventWin32(void)2199 void _glfwPostEmptyEventWin32(void)
2200 {
2201 PostMessageW(_glfw.win32.helperWindowHandle, WM_NULL, 0, 0);
2202 }
2203
_glfwGetCursorPosWin32(_GLFWwindow* window, double* xpos, double* ypos)2204 void _glfwGetCursorPosWin32(_GLFWwindow* window, double* xpos, double* ypos)
2205 {
2206 POINT pos;
2207
2208 if (GetCursorPos(&pos))
2209 {
2210 ScreenToClient(window->win32.handle, &pos);
2211
2212 if (xpos)
2213 *xpos = pos.x;
2214 if (ypos)
2215 *ypos = pos.y;
2216 }
2217 }
2218
_glfwSetCursorPosWin32(_GLFWwindow* window, double xpos, double ypos)2219 void _glfwSetCursorPosWin32(_GLFWwindow* window, double xpos, double ypos)
2220 {
2221 POINT pos = { (int) xpos, (int) ypos };
2222
2223 // Store the new position so it can be recognized later
2224 window->win32.lastCursorPosX = pos.x;
2225 window->win32.lastCursorPosY = pos.y;
2226
2227 ClientToScreen(window->win32.handle, &pos);
2228 SetCursorPos(pos.x, pos.y);
2229 }
2230
_glfwSetCursorModeWin32(_GLFWwindow* window, int mode)2231 void _glfwSetCursorModeWin32(_GLFWwindow* window, int mode)
2232 {
2233 if (_glfwWindowFocusedWin32(window))
2234 {
2235 if (mode == GLFW_CURSOR_DISABLED)
2236 {
2237 _glfwGetCursorPosWin32(window,
2238 &_glfw.win32.restoreCursorPosX,
2239 &_glfw.win32.restoreCursorPosY);
2240 _glfwCenterCursorInContentArea(window);
2241 if (window->rawMouseMotion)
2242 enableRawMouseMotion(window);
2243 }
2244 else if (_glfw.win32.disabledCursorWindow == window)
2245 {
2246 if (window->rawMouseMotion)
2247 disableRawMouseMotion(window);
2248 }
2249
2250 if (mode == GLFW_CURSOR_DISABLED || mode == GLFW_CURSOR_CAPTURED)
2251 captureCursor(window);
2252 else
2253 releaseCursor();
2254
2255 if (mode == GLFW_CURSOR_DISABLED)
2256 _glfw.win32.disabledCursorWindow = window;
2257 else if (_glfw.win32.disabledCursorWindow == window)
2258 {
2259 _glfw.win32.disabledCursorWindow = NULL;
2260 _glfwSetCursorPosWin32(window,
2261 _glfw.win32.restoreCursorPosX,
2262 _glfw.win32.restoreCursorPosY);
2263 }
2264 }
2265
2266 if (cursorInContentArea(window))
2267 updateCursorImage(window);
2268 }
2269
_glfwGetScancodeNameWin32(int scancode)2270 const char* _glfwGetScancodeNameWin32(int scancode)
2271 {
2272 if (scancode < 0 || scancode > (KF_EXTENDED | 0xff))
2273 {
2274 _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode %i", scancode);
2275 return NULL;
2276 }
2277
2278 const int key = _glfw.win32.keycodes[scancode];
2279 if (key == GLFW_KEY_UNKNOWN)
2280 return NULL;
2281
2282 return _glfw.win32.keynames[key];
2283 }
2284
_glfwGetKeyScancodeWin32(int key)2285 int _glfwGetKeyScancodeWin32(int key)
2286 {
2287 return _glfw.win32.scancodes[key];
2288 }
2289
_glfwCreateCursorWin32(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot)2290 GLFWbool _glfwCreateCursorWin32(_GLFWcursor* cursor,
2291 const GLFWimage* image,
2292 int xhot, int yhot)
2293 {
2294 cursor->win32.handle = (HCURSOR) createIcon(image, xhot, yhot, GLFW_FALSE);
2295 if (!cursor->win32.handle)
2296 return GLFW_FALSE;
2297
2298 return GLFW_TRUE;
2299 }
2300
_glfwCreateStandardCursorWin32(_GLFWcursor* cursor, int shape)2301 GLFWbool _glfwCreateStandardCursorWin32(_GLFWcursor* cursor, int shape)
2302 {
2303 int id = 0;
2304
2305 switch (shape)
2306 {
2307 case GLFW_ARROW_CURSOR:
2308 id = OCR_NORMAL;
2309 break;
2310 case GLFW_IBEAM_CURSOR:
2311 id = OCR_IBEAM;
2312 break;
2313 case GLFW_CROSSHAIR_CURSOR:
2314 id = OCR_CROSS;
2315 break;
2316 case GLFW_POINTING_HAND_CURSOR:
2317 id = OCR_HAND;
2318 break;
2319 case GLFW_RESIZE_EW_CURSOR:
2320 id = OCR_SIZEWE;
2321 break;
2322 case GLFW_RESIZE_NS_CURSOR:
2323 id = OCR_SIZENS;
2324 break;
2325 case GLFW_RESIZE_NWSE_CURSOR:
2326 id = OCR_SIZENWSE;
2327 break;
2328 case GLFW_RESIZE_NESW_CURSOR:
2329 id = OCR_SIZENESW;
2330 break;
2331 case GLFW_RESIZE_ALL_CURSOR:
2332 id = OCR_SIZEALL;
2333 break;
2334 case GLFW_NOT_ALLOWED_CURSOR:
2335 id = OCR_NO;
2336 break;
2337 default:
2338 _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Unknown standard cursor");
2339 return GLFW_FALSE;
2340 }
2341
2342 cursor->win32.handle = LoadImageW(NULL,
2343 MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0,
2344 LR_DEFAULTSIZE | LR_SHARED);
2345 if (!cursor->win32.handle)
2346 {
2347 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
2348 "Win32: Failed to create standard cursor");
2349 return GLFW_FALSE;
2350 }
2351
2352 return GLFW_TRUE;
2353 }
2354
_glfwDestroyCursorWin32(_GLFWcursor* cursor)2355 void _glfwDestroyCursorWin32(_GLFWcursor* cursor)
2356 {
2357 if (cursor->win32.handle)
2358 DestroyIcon((HICON) cursor->win32.handle);
2359 }
2360
_glfwSetCursorWin32(_GLFWwindow* window, _GLFWcursor* cursor)2361 void _glfwSetCursorWin32(_GLFWwindow* window, _GLFWcursor* cursor)
2362 {
2363 if (cursorInContentArea(window))
2364 updateCursorImage(window);
2365 }
2366
_glfwSetClipboardStringWin32(const char* string)2367 void _glfwSetClipboardStringWin32(const char* string)
2368 {
2369 int characterCount, tries = 0;
2370 HANDLE object;
2371 WCHAR* buffer;
2372
2373 characterCount = MultiByteToWideChar(CP_UTF8, 0, string, -1, NULL, 0);
2374 if (!characterCount)
2375 return;
2376
2377 object = GlobalAlloc(GMEM_MOVEABLE, characterCount * sizeof(WCHAR));
2378 if (!object)
2379 {
2380 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
2381 "Win32: Failed to allocate global handle for clipboard");
2382 return;
2383 }
2384
2385 buffer = GlobalLock(object);
2386 if (!buffer)
2387 {
2388 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
2389 "Win32: Failed to lock global handle");
2390 GlobalFree(object);
2391 return;
2392 }
2393
2394 MultiByteToWideChar(CP_UTF8, 0, string, -1, buffer, characterCount);
2395 GlobalUnlock(object);
2396
2397 // NOTE: Retry clipboard opening a few times as some other application may have it
2398 // open and also the Windows Clipboard History reads it after each update
2399 while (!OpenClipboard(_glfw.win32.helperWindowHandle))
2400 {
2401 Sleep(1);
2402 tries++;
2403
2404 if (tries == 3)
2405 {
2406 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
2407 "Win32: Failed to open clipboard");
2408 GlobalFree(object);
2409 return;
2410 }
2411 }
2412
2413 EmptyClipboard();
2414 SetClipboardData(CF_UNICODETEXT, object);
2415 CloseClipboard();
2416 }
2417
_glfwGetClipboardStringWin32(void)2418 const char* _glfwGetClipboardStringWin32(void)
2419 {
2420 HANDLE object;
2421 WCHAR* buffer;
2422 int tries = 0;
2423
2424 // NOTE: Retry clipboard opening a few times as some other application may have it
2425 // open and also the Windows Clipboard History reads it after each update
2426 while (!OpenClipboard(_glfw.win32.helperWindowHandle))
2427 {
2428 Sleep(1);
2429 tries++;
2430
2431 if (tries == 3)
2432 {
2433 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
2434 "Win32: Failed to open clipboard");
2435 return NULL;
2436 }
2437 }
2438
2439 object = GetClipboardData(CF_UNICODETEXT);
2440 if (!object)
2441 {
2442 _glfwInputErrorWin32(GLFW_FORMAT_UNAVAILABLE,
2443 "Win32: Failed to convert clipboard to string");
2444 CloseClipboard();
2445 return NULL;
2446 }
2447
2448 buffer = GlobalLock(object);
2449 if (!buffer)
2450 {
2451 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
2452 "Win32: Failed to lock global handle");
2453 CloseClipboard();
2454 return NULL;
2455 }
2456
2457 _glfw_free(_glfw.win32.clipboardString);
2458 _glfw.win32.clipboardString = _glfwCreateUTF8FromWideStringWin32(buffer);
2459
2460 GlobalUnlock(object);
2461 CloseClipboard();
2462
2463 return _glfw.win32.clipboardString;
2464 }
2465
_glfwGetEGLPlatformWin32(EGLint** attribs)2466 EGLenum _glfwGetEGLPlatformWin32(EGLint** attribs)
2467 {
2468 if (_glfw.egl.ANGLE_platform_angle)
2469 {
2470 int type = 0;
2471
2472 if (_glfw.egl.ANGLE_platform_angle_opengl)
2473 {
2474 if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_OPENGL)
2475 type = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE;
2476 else if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_OPENGLES)
2477 type = EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE;
2478 }
2479
2480 if (_glfw.egl.ANGLE_platform_angle_d3d)
2481 {
2482 if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_D3D9)
2483 type = EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE;
2484 else if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_D3D11)
2485 type = EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE;
2486 }
2487
2488 if (_glfw.egl.ANGLE_platform_angle_vulkan)
2489 {
2490 if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_VULKAN)
2491 type = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
2492 }
2493
2494 if (type)
2495 {
2496 *attribs = _glfw_calloc(3, sizeof(EGLint));
2497 (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE;
2498 (*attribs)[1] = type;
2499 (*attribs)[2] = EGL_NONE;
2500 return EGL_PLATFORM_ANGLE_ANGLE;
2501 }
2502 }
2503
2504 return 0;
2505 }
2506
_glfwGetEGLNativeDisplayWin32(void)2507 EGLNativeDisplayType _glfwGetEGLNativeDisplayWin32(void)
2508 {
2509 return GetDC(_glfw.win32.helperWindowHandle);
2510 }
2511
_glfwGetEGLNativeWindowWin32(_GLFWwindow* window)2512 EGLNativeWindowType _glfwGetEGLNativeWindowWin32(_GLFWwindow* window)
2513 {
2514 return window->win32.handle;
2515 }
2516
_glfwGetRequiredInstanceExtensionsWin32(char** extensions)2517 void _glfwGetRequiredInstanceExtensionsWin32(char** extensions)
2518 {
2519 if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_win32_surface)
2520 return;
2521
2522 extensions[0] = "VK_KHR_surface";
2523 extensions[1] = "VK_KHR_win32_surface";
2524 }
2525
_glfwGetPhysicalDevicePresentationSupportWin32(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily)2526 GLFWbool _glfwGetPhysicalDevicePresentationSupportWin32(VkInstance instance,
2527 VkPhysicalDevice device,
2528 uint32_t queuefamily)
2529 {
2530 PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR
2531 vkGetPhysicalDeviceWin32PresentationSupportKHR =
2532 (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)
2533 vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR");
2534 if (!vkGetPhysicalDeviceWin32PresentationSupportKHR)
2535 {
2536 _glfwInputError(GLFW_API_UNAVAILABLE,
2537 "Win32: Vulkan instance missing VK_KHR_win32_surface extension");
2538 return GLFW_FALSE;
2539 }
2540
2541 return vkGetPhysicalDeviceWin32PresentationSupportKHR(device, queuefamily);
2542 }
2543
_glfwCreateWindowSurfaceWin32(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface)2544 VkResult _glfwCreateWindowSurfaceWin32(VkInstance instance,
2545 _GLFWwindow* window,
2546 const VkAllocationCallbacks* allocator,
2547 VkSurfaceKHR* surface)
2548 {
2549 VkResult err;
2550 VkWin32SurfaceCreateInfoKHR sci;
2551 PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR;
2552
2553 vkCreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR)
2554 vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR");
2555 if (!vkCreateWin32SurfaceKHR)
2556 {
2557 _glfwInputError(GLFW_API_UNAVAILABLE,
2558 "Win32: Vulkan instance missing VK_KHR_win32_surface extension");
2559 return VK_ERROR_EXTENSION_NOT_PRESENT;
2560 }
2561
2562 memset(&sci, 0, sizeof(sci));
2563 sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
2564 sci.hinstance = _glfw.win32.instance;
2565 sci.hwnd = window->win32.handle;
2566
2567 err = vkCreateWin32SurfaceKHR(instance, &sci, allocator, surface);
2568 if (err)
2569 {
2570 _glfwInputError(GLFW_PLATFORM_ERROR,
2571 "Win32: Failed to create Vulkan surface: %s",
2572 _glfwGetVulkanResultString(err));
2573 }
2574
2575 return err;
2576 }
2577
glfwGetWin32Window(GLFWwindow* handle)2578 GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle)
2579 {
2580 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
2581
2582 if (_glfw.platform.platformID != GLFW_PLATFORM_WIN32)
2583 {
2584 _glfwInputError(GLFW_PLATFORM_UNAVAILABLE,
2585 "Win32: Platform not initialized");
2586 return NULL;
2587 }
2588
2589 _GLFWwindow* window = (_GLFWwindow*) handle;
2590 assert(window != NULL);
2591
2592 return window->win32.handle;
2593 }
2594
2595 #endif // _GLFW_WIN32
2596
2597