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