1 //========================================================================
2 // GLFW 3.2 Win32 - www.glfw.org
3 //------------------------------------------------------------------------
4 // Copyright (c) 2002-2006 Marcus Geelnard
5 // Copyright (c) 2006-2016 Camilla Berglund <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 #include <limits.h>
31 #include <stdlib.h>
32 #include <malloc.h>
33 #include <string.h>
34 #include <windowsx.h>
35 #include <shellapi.h>
36
37 #define _GLFW_KEY_INVALID -2
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 if (window->decorated)
50 {
51 style |= WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
52
53 if (window->resizable)
54 style |= WS_MAXIMIZEBOX | WS_THICKFRAME;
55 }
56 else
57 style |= WS_POPUP;
58 }
59
60 return style;
61 }
62
63 // Returns the extended window style for the specified window
64 //
getWindowExStyle(const _GLFWwindow * window)65 static DWORD getWindowExStyle(const _GLFWwindow* window)
66 {
67 DWORD style = WS_EX_APPWINDOW;
68
69 if (window->monitor || window->floating)
70 style |= WS_EX_TOPMOST;
71
72 return style;
73 }
74
75 // Returns the image whose area most closely matches the desired one
76 //
chooseImage(int count,const GLFWimage * images,int width,int height)77 static const GLFWimage* chooseImage(int count, const GLFWimage* images,
78 int width, int height)
79 {
80 int i, leastDiff = INT_MAX;
81 const GLFWimage* closest = NULL;
82
83 for (i = 0; i < count; i++)
84 {
85 const int currDiff = abs(images[i].width * images[i].height -
86 width * height);
87 if (currDiff < leastDiff)
88 {
89 closest = images + i;
90 leastDiff = currDiff;
91 }
92 }
93
94 return closest;
95 }
96
97 // Creates an RGBA icon or cursor
98 //
createIcon(const GLFWimage * image,int xhot,int yhot,GLFWbool icon)99 static HICON createIcon(const GLFWimage* image,
100 int xhot, int yhot, GLFWbool icon)
101 {
102 int i;
103 HDC dc;
104 HICON handle;
105 HBITMAP color, mask;
106 BITMAPV5HEADER bi;
107 ICONINFO ii;
108 unsigned char* target = NULL;
109 unsigned char* source = image->pixels;
110
111 ZeroMemory(&bi, sizeof(bi));
112 bi.bV5Size = sizeof(BITMAPV5HEADER);
113 bi.bV5Width = image->width;
114 bi.bV5Height = -image->height;
115 bi.bV5Planes = 1;
116 bi.bV5BitCount = 32;
117 bi.bV5Compression = BI_BITFIELDS;
118 bi.bV5RedMask = 0x00ff0000;
119 bi.bV5GreenMask = 0x0000ff00;
120 bi.bV5BlueMask = 0x000000ff;
121 bi.bV5AlphaMask = 0xff000000;
122
123 dc = GetDC(NULL);
124 color = CreateDIBSection(dc,
125 (BITMAPINFO*) &bi,
126 DIB_RGB_COLORS,
127 (void**) &target,
128 NULL,
129 (DWORD) 0);
130 ReleaseDC(NULL, dc);
131
132 if (!color)
133 {
134 _glfwInputError(GLFW_PLATFORM_ERROR,
135 "Win32: Failed to create RGBA bitmap");
136 return NULL;
137 }
138
139 mask = CreateBitmap(image->width, image->height, 1, 1, NULL);
140 if (!mask)
141 {
142 _glfwInputError(GLFW_PLATFORM_ERROR,
143 "Win32: Failed to create mask bitmap");
144 DeleteObject(color);
145 return NULL;
146 }
147
148 for (i = 0; i < image->width * image->height; i++)
149 {
150 target[0] = source[2];
151 target[1] = source[1];
152 target[2] = source[0];
153 target[3] = source[3];
154 target += 4;
155 source += 4;
156 }
157
158 ZeroMemory(&ii, sizeof(ii));
159 ii.fIcon = icon;
160 ii.xHotspot = xhot;
161 ii.yHotspot = yhot;
162 ii.hbmMask = mask;
163 ii.hbmColor = color;
164
165 handle = CreateIconIndirect(&ii);
166
167 DeleteObject(color);
168 DeleteObject(mask);
169
170 if (!handle)
171 {
172 if (icon)
173 _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create icon");
174 else
175 _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create cursor");
176 }
177
178 return handle;
179 }
180
181 // Translate client window size to full window size according to styles
182 //
getFullWindowSize(DWORD style,DWORD exStyle,int clientWidth,int clientHeight,int * fullWidth,int * fullHeight)183 static void getFullWindowSize(DWORD style, DWORD exStyle,
184 int clientWidth, int clientHeight,
185 int* fullWidth, int* fullHeight)
186 {
187 RECT rect = { 0, 0, clientWidth, clientHeight };
188 AdjustWindowRectEx(&rect, style, FALSE, exStyle);
189 *fullWidth = rect.right - rect.left;
190 *fullHeight = rect.bottom - rect.top;
191 }
192
193 // Enforce the client rect aspect ratio based on which edge is being dragged
194 //
applyAspectRatio(_GLFWwindow * window,int edge,RECT * area)195 static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area)
196 {
197 int xoff, yoff;
198 const float ratio = (float) window->numer / (float) window->denom;
199
200 getFullWindowSize(getWindowStyle(window), getWindowExStyle(window),
201 0, 0, &xoff, &yoff);
202
203 if (edge == WMSZ_LEFT || edge == WMSZ_BOTTOMLEFT ||
204 edge == WMSZ_RIGHT || edge == WMSZ_BOTTOMRIGHT)
205 {
206 area->bottom = area->top + yoff +
207 (int) ((area->right - area->left - xoff) / ratio);
208 }
209 else if (edge == WMSZ_TOPLEFT || edge == WMSZ_TOPRIGHT)
210 {
211 area->top = area->bottom - yoff -
212 (int) ((area->right - area->left - xoff) / ratio);
213 }
214 else if (edge == WMSZ_TOP || edge == WMSZ_BOTTOM)
215 {
216 area->right = area->left + xoff +
217 (int) ((area->bottom - area->top - yoff) * ratio);
218 }
219 }
220
221 // Centers the cursor over the window client area
222 //
centerCursor(_GLFWwindow * window)223 static void centerCursor(_GLFWwindow* window)
224 {
225 int width, height;
226 _glfwPlatformGetWindowSize(window, &width, &height);
227 _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0);
228 }
229
230 // Returns whether the cursor is in the client area of the specified window
231 //
cursorInClientArea(_GLFWwindow * window)232 static GLFWbool cursorInClientArea(_GLFWwindow* window)
233 {
234 RECT area;
235 POINT pos;
236
237 if (!GetCursorPos(&pos))
238 return GLFW_FALSE;
239
240 if (WindowFromPoint(pos) != window->win32.handle)
241 return GLFW_FALSE;
242
243 GetClientRect(window->win32.handle, &area);
244 ClientToScreen(window->win32.handle, (POINT*) &area.left);
245 ClientToScreen(window->win32.handle, (POINT*) &area.right);
246
247 return PtInRect(&area, pos);
248 }
249
250 // Updates the cursor image according to its cursor mode
251 //
updateCursorImage(_GLFWwindow * window)252 static void updateCursorImage(_GLFWwindow* window)
253 {
254 if (window->cursorMode == GLFW_CURSOR_NORMAL)
255 {
256 if (window->cursor)
257 SetCursor(window->cursor->win32.handle);
258 else
259 SetCursor(LoadCursorW(NULL, IDC_ARROW));
260 }
261 else
262 SetCursor(NULL);
263 }
264
265 // Updates the cursor clip rect
266 //
updateClipRect(_GLFWwindow * window)267 static void updateClipRect(_GLFWwindow* window)
268 {
269 if (window)
270 {
271 RECT clipRect;
272 GetClientRect(window->win32.handle, &clipRect);
273 ClientToScreen(window->win32.handle, (POINT*) &clipRect.left);
274 ClientToScreen(window->win32.handle, (POINT*) &clipRect.right);
275 ClipCursor(&clipRect);
276 }
277 else
278 ClipCursor(NULL);
279 }
280
281 // Translates a GLFW standard cursor to a resource ID
282 //
translateCursorShape(int shape)283 static LPWSTR translateCursorShape(int shape)
284 {
285 switch (shape)
286 {
287 case GLFW_ARROW_CURSOR:
288 return IDC_ARROW;
289 case GLFW_IBEAM_CURSOR:
290 return IDC_IBEAM;
291 case GLFW_CROSSHAIR_CURSOR:
292 return IDC_CROSS;
293 case GLFW_HAND_CURSOR:
294 return IDC_HAND;
295 case GLFW_HRESIZE_CURSOR:
296 return IDC_SIZEWE;
297 case GLFW_VRESIZE_CURSOR:
298 return IDC_SIZENS;
299 }
300
301 return NULL;
302 }
303
304 // Retrieves and translates modifier keys
305 //
getKeyMods(void)306 static int getKeyMods(void)
307 {
308 int mods = 0;
309
310 if (GetKeyState(VK_SHIFT) & (1 << 31))
311 mods |= GLFW_MOD_SHIFT;
312 if (GetKeyState(VK_CONTROL) & (1 << 31))
313 mods |= GLFW_MOD_CONTROL;
314 if (GetKeyState(VK_MENU) & (1 << 31))
315 mods |= GLFW_MOD_ALT;
316 if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & (1 << 31))
317 mods |= GLFW_MOD_SUPER;
318
319 return mods;
320 }
321
322 // Retrieves and translates modifier keys
323 //
getAsyncKeyMods(void)324 static int getAsyncKeyMods(void)
325 {
326 int mods = 0;
327
328 if (GetAsyncKeyState(VK_SHIFT) & (1 << 31))
329 mods |= GLFW_MOD_SHIFT;
330 if (GetAsyncKeyState(VK_CONTROL) & (1 << 31))
331 mods |= GLFW_MOD_CONTROL;
332 if (GetAsyncKeyState(VK_MENU) & (1 << 31))
333 mods |= GLFW_MOD_ALT;
334 if ((GetAsyncKeyState(VK_LWIN) | GetAsyncKeyState(VK_RWIN)) & (1 << 31))
335 mods |= GLFW_MOD_SUPER;
336
337 return mods;
338 }
339
340 // Translates a Windows key to the corresponding GLFW key
341 //
translateKey(WPARAM wParam,LPARAM lParam)342 static int translateKey(WPARAM wParam, LPARAM lParam)
343 {
344 if (wParam == VK_CONTROL)
345 {
346 // The CTRL keys require special handling
347
348 MSG next;
349 DWORD time;
350
351 // Is this an extended key (i.e. right key)?
352 if (lParam & 0x01000000)
353 return GLFW_KEY_RIGHT_CONTROL;
354
355 // Here is a trick: "Alt Gr" sends LCTRL, then RALT. We only
356 // want the RALT message, so we try to see if the next message
357 // is a RALT message. In that case, this is a false LCTRL!
358 time = GetMessageTime();
359
360 if (PeekMessageW(&next, NULL, 0, 0, PM_NOREMOVE))
361 {
362 if (next.message == WM_KEYDOWN ||
363 next.message == WM_SYSKEYDOWN ||
364 next.message == WM_KEYUP ||
365 next.message == WM_SYSKEYUP)
366 {
367 if (next.wParam == VK_MENU &&
368 (next.lParam & 0x01000000) &&
369 next.time == time)
370 {
371 // Next message is a RALT down message, which
372 // means that this is not a proper LCTRL message
373 return _GLFW_KEY_INVALID;
374 }
375 }
376 }
377
378 return GLFW_KEY_LEFT_CONTROL;
379 }
380
381 if (wParam == VK_PROCESSKEY)
382 {
383 // IME notifies that keys have been filtered by setting the virtual
384 // key-code to VK_PROCESSKEY
385 return _GLFW_KEY_INVALID;
386 }
387
388 return _glfw.win32.publicKeys[HIWORD(lParam) & 0x1FF];
389 }
390
391 // Make the specified window and its video mode active on its monitor
392 //
acquireMonitor(_GLFWwindow * window)393 static GLFWbool acquireMonitor(_GLFWwindow* window)
394 {
395 GLFWvidmode mode;
396 GLFWbool status;
397 int xpos, ypos;
398
399 status = _glfwSetVideoModeWin32(window->monitor, &window->videoMode);
400
401 _glfwPlatformGetVideoMode(window->monitor, &mode);
402 _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos);
403
404 SetWindowPos(window->win32.handle, HWND_TOPMOST,
405 xpos, ypos, mode.width, mode.height,
406 SWP_NOACTIVATE | SWP_NOCOPYBITS);
407
408 _glfwInputMonitorWindowChange(window->monitor, window);
409 return status;
410 }
411
412 // Remove the window and restore the original video mode
413 //
releaseMonitor(_GLFWwindow * window)414 static void releaseMonitor(_GLFWwindow* window)
415 {
416 if (window->monitor->window != window)
417 return;
418
419 _glfwInputMonitorWindowChange(window->monitor, NULL);
420 _glfwRestoreVideoModeWin32(window->monitor);
421 }
422
423 // Window callback function (handles window messages)
424 //
windowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)425 static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
426 WPARAM wParam, LPARAM lParam)
427 {
428 _GLFWwindow* window = GetPropW(hWnd, L"GLFW");
429 if (!window)
430 {
431 // This is the message handling for the hidden helper window
432
433 switch (uMsg)
434 {
435 case WM_DEVICECHANGE:
436 {
437 if (wParam == DBT_DEVNODES_CHANGED)
438 {
439 _glfwInputMonitorChange();
440 return TRUE;
441 }
442 else if (wParam == DBT_DEVICEARRIVAL)
443 {
444 DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam;
445 if (dbh)
446 {
447 if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
448 _glfwDetectJoystickConnectionWin32();
449 }
450 }
451 else if (wParam == DBT_DEVICEREMOVECOMPLETE)
452 {
453 DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam;
454 if (dbh)
455 {
456 if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
457 _glfwDetectJoystickDisconnectionWin32();
458 }
459 }
460
461 break;
462 }
463 }
464
465 return DefWindowProcW(hWnd, uMsg, wParam, lParam);
466 }
467
468 switch (uMsg)
469 {
470 case WM_SETFOCUS:
471 {
472 _glfwInputWindowFocus(window, GLFW_TRUE);
473
474 if (window->cursorMode == GLFW_CURSOR_DISABLED)
475 _glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED);
476
477 return 0;
478 }
479
480 case WM_KILLFOCUS:
481 {
482 if (window->cursorMode == GLFW_CURSOR_DISABLED)
483 _glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL);
484
485 if (window->monitor && window->autoIconify)
486 _glfwPlatformIconifyWindow(window);
487
488 _glfwInputWindowFocus(window, GLFW_FALSE);
489 return 0;
490 }
491
492 case WM_SYSCOMMAND:
493 {
494 switch (wParam & 0xfff0)
495 {
496 case SC_SCREENSAVE:
497 case SC_MONITORPOWER:
498 {
499 if (window->monitor)
500 {
501 // We are running in full screen mode, so disallow
502 // screen saver and screen blanking
503 return 0;
504 }
505 else
506 break;
507 }
508
509 // User trying to access application menu using ALT?
510 case SC_KEYMENU:
511 return 0;
512 }
513 break;
514 }
515
516 case WM_CLOSE:
517 {
518 _glfwInputWindowCloseRequest(window);
519 return 0;
520 }
521
522 case WM_CHAR:
523 case WM_SYSCHAR:
524 case WM_UNICHAR:
525 {
526 const GLFWbool plain = (uMsg != WM_SYSCHAR);
527
528 if (uMsg == WM_UNICHAR && wParam == UNICODE_NOCHAR)
529 {
530 // WM_UNICHAR is not sent by Windows, but is sent by some
531 // third-party input method engine
532 // Returning TRUE here announces support for this message
533 return TRUE;
534 }
535
536 _glfwInputChar(window, (unsigned int) wParam, getKeyMods(), plain);
537 return 0;
538 }
539
540 case WM_KEYDOWN:
541 case WM_SYSKEYDOWN:
542 case WM_KEYUP:
543 case WM_SYSKEYUP:
544 {
545 const int key = translateKey(wParam, lParam);
546 const int scancode = (lParam >> 16) & 0x1ff;
547 const int action = ((lParam >> 31) & 1) ? GLFW_RELEASE : GLFW_PRESS;
548 const int mods = getKeyMods();
549
550 if (key == _GLFW_KEY_INVALID)
551 break;
552
553 if (action == GLFW_RELEASE && wParam == VK_SHIFT)
554 {
555 // Release both Shift keys on Shift up event, as only one event
556 // is sent even if both keys are released
557 _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, action, mods);
558 _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, action, mods);
559 }
560 else if (wParam == VK_SNAPSHOT)
561 {
562 // Key down is not reported for the Print Screen key
563 _glfwInputKey(window, key, scancode, GLFW_PRESS, mods);
564 _glfwInputKey(window, key, scancode, GLFW_RELEASE, mods);
565 }
566 else
567 _glfwInputKey(window, key, scancode, action, mods);
568
569 break;
570 }
571
572 case WM_LBUTTONDOWN:
573 case WM_RBUTTONDOWN:
574 case WM_MBUTTONDOWN:
575 case WM_XBUTTONDOWN:
576 case WM_LBUTTONUP:
577 case WM_RBUTTONUP:
578 case WM_MBUTTONUP:
579 case WM_XBUTTONUP:
580 {
581 int button, action;
582
583 if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP)
584 button = GLFW_MOUSE_BUTTON_LEFT;
585 else if (uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP)
586 button = GLFW_MOUSE_BUTTON_RIGHT;
587 else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP)
588 button = GLFW_MOUSE_BUTTON_MIDDLE;
589 else if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1)
590 button = GLFW_MOUSE_BUTTON_4;
591 else
592 button = GLFW_MOUSE_BUTTON_5;
593
594 if (uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN ||
595 uMsg == WM_MBUTTONDOWN || uMsg == WM_XBUTTONDOWN)
596 {
597 action = GLFW_PRESS;
598 SetCapture(hWnd);
599 }
600 else
601 {
602 action = GLFW_RELEASE;
603 ReleaseCapture();
604 }
605
606 _glfwInputMouseClick(window, button, action, getKeyMods());
607
608 if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP)
609 return TRUE;
610
611 return 0;
612 }
613
614 case WM_MOUSEMOVE:
615 {
616 const int x = GET_X_LPARAM(lParam);
617 const int y = GET_Y_LPARAM(lParam);
618
619 if (window->cursorMode == GLFW_CURSOR_DISABLED)
620 {
621 const int dx = x - window->win32.lastCursorPosX;
622 const int dy = y - window->win32.lastCursorPosY;
623
624 if (_glfw.win32.disabledCursorWindow != window)
625 break;
626
627 _glfwInputCursorPos(window,
628 window->virtualCursorPosX + dx,
629 window->virtualCursorPosY + dy);
630 }
631 else
632 _glfwInputCursorPos(window, x, y);
633
634 window->win32.lastCursorPosX = x;
635 window->win32.lastCursorPosY = y;
636
637 if (!window->win32.cursorTracked)
638 {
639 TRACKMOUSEEVENT tme;
640 ZeroMemory(&tme, sizeof(tme));
641 tme.cbSize = sizeof(tme);
642 tme.dwFlags = TME_LEAVE;
643 tme.hwndTrack = window->win32.handle;
644 TrackMouseEvent(&tme);
645
646 window->win32.cursorTracked = GLFW_TRUE;
647 _glfwInputCursorEnter(window, GLFW_TRUE);
648 }
649
650 return 0;
651 }
652
653 case WM_MOUSELEAVE:
654 {
655 window->win32.cursorTracked = GLFW_FALSE;
656 _glfwInputCursorEnter(window, GLFW_FALSE);
657 return 0;
658 }
659
660 case WM_MOUSEWHEEL:
661 {
662 _glfwInputScroll(window, 0.0, (SHORT) HIWORD(wParam) / (double) WHEEL_DELTA);
663 return 0;
664 }
665
666 case WM_MOUSEHWHEEL:
667 {
668 // This message is only sent on Windows Vista and later
669 // NOTE: The X-axis is inverted for consistency with OS X and X11.
670 _glfwInputScroll(window, -((SHORT) HIWORD(wParam) / (double) WHEEL_DELTA), 0.0);
671 return 0;
672 }
673
674 case WM_ENTERSIZEMOVE:
675 case WM_ENTERMENULOOP:
676 {
677 if (window->cursorMode == GLFW_CURSOR_DISABLED)
678 _glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL);
679
680 break;
681 }
682
683 case WM_EXITSIZEMOVE:
684 case WM_EXITMENULOOP:
685 {
686 if (window->cursorMode == GLFW_CURSOR_DISABLED)
687 _glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED);
688
689 break;
690 }
691
692 case WM_SIZE:
693 {
694 const GLFWbool iconified =
695 !window->win32.iconified && wParam == SIZE_MINIMIZED;
696 const GLFWbool restored =
697 window->win32.iconified &&
698 (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED);
699
700 if (_glfw.win32.disabledCursorWindow == window)
701 updateClipRect(window);
702
703 if (iconified)
704 _glfwInputWindowIconify(window, GLFW_TRUE);
705 else if (restored)
706 _glfwInputWindowIconify(window, GLFW_FALSE);
707
708 _glfwInputFramebufferSize(window, LOWORD(lParam), HIWORD(lParam));
709 _glfwInputWindowSize(window, LOWORD(lParam), HIWORD(lParam));
710
711 if (iconified)
712 {
713 window->win32.iconified = GLFW_TRUE;
714 if (window->monitor)
715 releaseMonitor(window);
716 }
717 else if (restored)
718 {
719 window->win32.iconified = GLFW_FALSE;
720 if (window->monitor)
721 acquireMonitor(window);
722 }
723
724 return 0;
725 }
726
727 case WM_MOVE:
728 {
729 if (_glfw.win32.disabledCursorWindow == window)
730 updateClipRect(window);
731
732 // NOTE: This cannot use LOWORD/HIWORD recommended by MSDN, as
733 // those macros do not handle negative window positions correctly
734 _glfwInputWindowPos(window,
735 GET_X_LPARAM(lParam),
736 GET_Y_LPARAM(lParam));
737 return 0;
738 }
739
740 case WM_SIZING:
741 {
742 if (window->numer == GLFW_DONT_CARE ||
743 window->denom == GLFW_DONT_CARE)
744 {
745 break;
746 }
747
748 applyAspectRatio(window, (int) wParam, (RECT*) lParam);
749 return TRUE;
750 }
751
752 case WM_GETMINMAXINFO:
753 {
754 int xoff, yoff;
755 MINMAXINFO* mmi = (MINMAXINFO*) lParam;
756
757 if (window->monitor)
758 break;
759
760 getFullWindowSize(getWindowStyle(window), getWindowExStyle(window),
761 0, 0, &xoff, &yoff);
762
763 if (window->minwidth != GLFW_DONT_CARE &&
764 window->minheight != GLFW_DONT_CARE)
765 {
766 mmi->ptMinTrackSize.x = window->minwidth + xoff;
767 mmi->ptMinTrackSize.y = window->minheight + yoff;
768 }
769
770 if (window->maxwidth != GLFW_DONT_CARE &&
771 window->maxheight != GLFW_DONT_CARE)
772 {
773 mmi->ptMaxTrackSize.x = window->maxwidth + xoff;
774 mmi->ptMaxTrackSize.y = window->maxheight + yoff;
775 }
776
777 return 0;
778 }
779
780 case WM_PAINT:
781 {
782 _glfwInputWindowDamage(window);
783 break;
784 }
785
786 case WM_ERASEBKGND:
787 {
788 return TRUE;
789 }
790
791 case WM_SETCURSOR:
792 {
793 if (LOWORD(lParam) == HTCLIENT)
794 {
795 updateCursorImage(window);
796 return TRUE;
797 }
798
799 break;
800 }
801
802 case WM_DPICHANGED:
803 {
804 RECT* rect = (RECT*) lParam;
805 SetWindowPos(window->win32.handle,
806 HWND_TOP,
807 rect->left,
808 rect->top,
809 rect->right - rect->left,
810 rect->bottom - rect->top,
811 SWP_NOACTIVATE | SWP_NOZORDER);
812 break;
813 }
814
815 case WM_DROPFILES:
816 {
817 HDROP drop = (HDROP) wParam;
818 POINT pt;
819 int i;
820
821 const int count = DragQueryFileW(drop, 0xffffffff, NULL, 0);
822 char** paths = calloc(count, sizeof(char*));
823
824 // Move the mouse to the position of the drop
825 DragQueryPoint(drop, &pt);
826 _glfwInputCursorPos(window, pt.x, pt.y);
827
828 for (i = 0; i < count; i++)
829 {
830 const UINT length = DragQueryFileW(drop, i, NULL, 0);
831 WCHAR* buffer = calloc(length + 1, sizeof(WCHAR));
832
833 DragQueryFileW(drop, i, buffer, length + 1);
834 paths[i] = _glfwCreateUTF8FromWideStringWin32(buffer);
835
836 free(buffer);
837 }
838
839 _glfwInputDrop(window, count, (const char**) paths);
840
841 for (i = 0; i < count; i++)
842 free(paths[i]);
843 free(paths);
844
845 DragFinish(drop);
846 return 0;
847 }
848 }
849
850 return DefWindowProcW(hWnd, uMsg, wParam, lParam);
851 }
852
853 // Creates the GLFW window
854 //
createNativeWindow(_GLFWwindow * window,const _GLFWwndconfig * wndconfig)855 static int createNativeWindow(_GLFWwindow* window,
856 const _GLFWwndconfig* wndconfig)
857 {
858 int xpos, ypos, fullWidth, fullHeight;
859 WCHAR* wideTitle;
860 DWORD style = getWindowStyle(window);
861 DWORD exStyle = getWindowExStyle(window);
862
863 if (window->monitor)
864 {
865 GLFWvidmode mode;
866
867 // NOTE: This window placement is temporary and approximate, as the
868 // correct position and size cannot be known until the monitor
869 // video mode has been set
870 _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos);
871 _glfwPlatformGetVideoMode(window->monitor, &mode);
872 fullWidth = mode.width;
873 fullHeight = mode.height;
874 }
875 else
876 {
877 xpos = CW_USEDEFAULT;
878 ypos = CW_USEDEFAULT;
879
880 if (wndconfig->maximized)
881 style |= WS_MAXIMIZE;
882
883 getFullWindowSize(style, exStyle,
884 wndconfig->width, wndconfig->height,
885 &fullWidth, &fullHeight);
886 }
887
888 wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title);
889 if (!wideTitle)
890 {
891 _glfwInputError(GLFW_PLATFORM_ERROR,
892 "Win32: Failed to convert window title to UTF-16");
893 return GLFW_FALSE;
894 }
895
896 window->win32.handle = CreateWindowExW(exStyle,
897 _GLFW_WNDCLASSNAME,
898 wideTitle,
899 style,
900 xpos, ypos,
901 fullWidth, fullHeight,
902 NULL, // No parent window
903 NULL, // No window menu
904 GetModuleHandleW(NULL),
905 NULL);
906
907 free(wideTitle);
908
909 if (!window->win32.handle)
910 {
911 _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create window");
912 return GLFW_FALSE;
913 }
914
915 SetPropW(window->win32.handle, L"GLFW", window);
916
917 if (_glfw_ChangeWindowMessageFilterEx)
918 {
919 _glfw_ChangeWindowMessageFilterEx(window->win32.handle,
920 WM_DROPFILES, MSGFLT_ALLOW, NULL);
921 _glfw_ChangeWindowMessageFilterEx(window->win32.handle,
922 WM_COPYDATA, MSGFLT_ALLOW, NULL);
923 _glfw_ChangeWindowMessageFilterEx(window->win32.handle,
924 WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL);
925 }
926
927 DragAcceptFiles(window->win32.handle, TRUE);
928
929 return GLFW_TRUE;
930 }
931
932
933 //////////////////////////////////////////////////////////////////////////
934 ////// GLFW internal API //////
935 //////////////////////////////////////////////////////////////////////////
936
937 // Registers the GLFW window class
938 //
_glfwRegisterWindowClassWin32(void)939 GLFWbool _glfwRegisterWindowClassWin32(void)
940 {
941 WNDCLASSEXW wc;
942
943 ZeroMemory(&wc, sizeof(wc));
944 wc.cbSize = sizeof(wc);
945 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
946 wc.lpfnWndProc = (WNDPROC) windowProc;
947 wc.hInstance = GetModuleHandleW(NULL);
948 wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
949 wc.lpszClassName = _GLFW_WNDCLASSNAME;
950
951 // Load user-provided icon if available
952 wc.hIcon = LoadImageW(GetModuleHandleW(NULL),
953 L"GLFW_ICON", IMAGE_ICON,
954 0, 0, LR_DEFAULTSIZE | LR_SHARED);
955 if (!wc.hIcon)
956 {
957 // No user-provided icon found, load default icon
958 wc.hIcon = LoadImageW(NULL,
959 IDI_APPLICATION, IMAGE_ICON,
960 0, 0, LR_DEFAULTSIZE | LR_SHARED);
961 }
962
963 if (!RegisterClassExW(&wc))
964 {
965 _glfwInputError(GLFW_PLATFORM_ERROR,
966 "Win32: Failed to register window class");
967 return GLFW_FALSE;
968 }
969
970 return GLFW_TRUE;
971 }
972
973 // Unregisters the GLFW window class
974 //
_glfwUnregisterWindowClassWin32(void)975 void _glfwUnregisterWindowClassWin32(void)
976 {
977 UnregisterClassW(_GLFW_WNDCLASSNAME, GetModuleHandleW(NULL));
978 }
979
980
981 //////////////////////////////////////////////////////////////////////////
982 ////// GLFW platform API //////
983 //////////////////////////////////////////////////////////////////////////
984
_glfwPlatformCreateWindow(_GLFWwindow * window,const _GLFWwndconfig * wndconfig,const _GLFWctxconfig * ctxconfig,const _GLFWfbconfig * fbconfig)985 int _glfwPlatformCreateWindow(_GLFWwindow* window,
986 const _GLFWwndconfig* wndconfig,
987 const _GLFWctxconfig* ctxconfig,
988 const _GLFWfbconfig* fbconfig)
989 {
990 if (!createNativeWindow(window, wndconfig))
991 return GLFW_FALSE;
992
993 if (ctxconfig->client != GLFW_NO_API)
994 {
995 if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
996 {
997 if (!_glfwInitWGL())
998 return GLFW_FALSE;
999 if (!_glfwCreateContextWGL(window, ctxconfig, fbconfig))
1000 return GLFW_FALSE;
1001 }
1002 else
1003 {
1004 if (!_glfwInitEGL())
1005 return GLFW_FALSE;
1006 if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
1007 return GLFW_FALSE;
1008 }
1009 }
1010
1011 if (window->monitor)
1012 {
1013 _glfwPlatformShowWindow(window);
1014 _glfwPlatformFocusWindow(window);
1015 if (!acquireMonitor(window))
1016 return GLFW_FALSE;
1017
1018 centerCursor(window);
1019 }
1020
1021 return GLFW_TRUE;
1022 }
1023
_glfwPlatformDestroyWindow(_GLFWwindow * window)1024 void _glfwPlatformDestroyWindow(_GLFWwindow* window)
1025 {
1026 if (window->monitor)
1027 releaseMonitor(window);
1028
1029 if (window->context.destroy)
1030 window->context.destroy(window);
1031
1032 if (_glfw.win32.disabledCursorWindow == window)
1033 _glfw.win32.disabledCursorWindow = NULL;
1034
1035 if (window->win32.handle)
1036 {
1037 RemovePropW(window->win32.handle, L"GLFW");
1038 DestroyWindow(window->win32.handle);
1039 window->win32.handle = NULL;
1040 }
1041
1042 if (window->win32.bigIcon)
1043 DestroyIcon(window->win32.bigIcon);
1044
1045 if (window->win32.smallIcon)
1046 DestroyIcon(window->win32.smallIcon);
1047 }
1048
_glfwPlatformSetWindowTitle(_GLFWwindow * window,const char * title)1049 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
1050 {
1051 WCHAR* wideTitle = _glfwCreateWideStringFromUTF8Win32(title);
1052 if (!wideTitle)
1053 {
1054 _glfwInputError(GLFW_PLATFORM_ERROR,
1055 "Win32: Failed to convert window title to UTF-16");
1056 return;
1057 }
1058
1059 SetWindowTextW(window->win32.handle, wideTitle);
1060 free(wideTitle);
1061 }
1062
_glfwPlatformSetWindowIcon(_GLFWwindow * window,int count,const GLFWimage * images)1063 void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
1064 int count, const GLFWimage* images)
1065 {
1066 HICON bigIcon = NULL, smallIcon = NULL;
1067
1068 if (count)
1069 {
1070 const GLFWimage* bigImage = chooseImage(count, images,
1071 GetSystemMetrics(SM_CXICON),
1072 GetSystemMetrics(SM_CYICON));
1073 const GLFWimage* smallImage = chooseImage(count, images,
1074 GetSystemMetrics(SM_CXSMICON),
1075 GetSystemMetrics(SM_CYSMICON));
1076
1077 bigIcon = createIcon(bigImage, 0, 0, GLFW_TRUE);
1078 smallIcon = createIcon(smallImage, 0, 0, GLFW_TRUE);
1079 }
1080 else
1081 {
1082 bigIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICON);
1083 smallIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICONSM);
1084 }
1085
1086 SendMessage(window->win32.handle, WM_SETICON, ICON_BIG, (LPARAM) bigIcon);
1087 SendMessage(window->win32.handle, WM_SETICON, ICON_SMALL, (LPARAM) smallIcon);
1088
1089 if (window->win32.bigIcon)
1090 DestroyIcon(window->win32.bigIcon);
1091
1092 if (window->win32.smallIcon)
1093 DestroyIcon(window->win32.smallIcon);
1094
1095 if (count)
1096 {
1097 window->win32.bigIcon = bigIcon;
1098 window->win32.smallIcon = smallIcon;
1099 }
1100 }
1101
_glfwPlatformGetWindowPos(_GLFWwindow * window,int * xpos,int * ypos)1102 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
1103 {
1104 POINT pos = { 0, 0 };
1105 ClientToScreen(window->win32.handle, &pos);
1106
1107 if (xpos)
1108 *xpos = pos.x;
1109 if (ypos)
1110 *ypos = pos.y;
1111 }
1112
_glfwPlatformSetWindowPos(_GLFWwindow * window,int xpos,int ypos)1113 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
1114 {
1115 RECT rect = { xpos, ypos, xpos, ypos };
1116 AdjustWindowRectEx(&rect, getWindowStyle(window),
1117 FALSE, getWindowExStyle(window));
1118 SetWindowPos(window->win32.handle, NULL, rect.left, rect.top, 0, 0,
1119 SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
1120 }
1121
_glfwPlatformGetWindowSize(_GLFWwindow * window,int * width,int * height)1122 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
1123 {
1124 RECT area;
1125 GetClientRect(window->win32.handle, &area);
1126
1127 if (width)
1128 *width = area.right;
1129 if (height)
1130 *height = area.bottom;
1131 }
1132
_glfwPlatformSetWindowSize(_GLFWwindow * window,int width,int height)1133 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
1134 {
1135 if (window->monitor)
1136 {
1137 if (window->monitor->window == window)
1138 acquireMonitor(window);
1139 }
1140 else
1141 {
1142 RECT rect = { 0, 0, width, height };
1143 AdjustWindowRectEx(&rect, getWindowStyle(window),
1144 FALSE, getWindowExStyle(window));
1145 SetWindowPos(window->win32.handle, HWND_TOP,
1146 0, 0, rect.right - rect.left, rect.bottom - rect.top,
1147 SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER);
1148 }
1149 }
1150
_glfwPlatformSetWindowSizeLimits(_GLFWwindow * window,int minwidth,int minheight,int maxwidth,int maxheight)1151 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
1152 int minwidth, int minheight,
1153 int maxwidth, int maxheight)
1154 {
1155 RECT area;
1156
1157 if ((minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) &&
1158 (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE))
1159 {
1160 return;
1161 }
1162
1163 GetWindowRect(window->win32.handle, &area);
1164 MoveWindow(window->win32.handle,
1165 area.left, area.top,
1166 area.right - area.left,
1167 area.bottom - area.top, TRUE);
1168 }
1169
_glfwPlatformSetWindowAspectRatio(_GLFWwindow * window,int numer,int denom)1170 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
1171 {
1172 RECT area;
1173
1174 if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE)
1175 return;
1176
1177 GetWindowRect(window->win32.handle, &area);
1178 applyAspectRatio(window, WMSZ_BOTTOMRIGHT, &area);
1179 MoveWindow(window->win32.handle,
1180 area.left, area.top,
1181 area.right - area.left,
1182 area.bottom - area.top, TRUE);
1183 }
1184
_glfwPlatformGetFramebufferSize(_GLFWwindow * window,int * width,int * height)1185 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
1186 {
1187 _glfwPlatformGetWindowSize(window, width, height);
1188 }
1189
_glfwPlatformGetWindowFrameSize(_GLFWwindow * window,int * left,int * top,int * right,int * bottom)1190 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
1191 int* left, int* top,
1192 int* right, int* bottom)
1193 {
1194 RECT rect;
1195 int width, height;
1196
1197 _glfwPlatformGetWindowSize(window, &width, &height);
1198 SetRect(&rect, 0, 0, width, height);
1199 AdjustWindowRectEx(&rect, getWindowStyle(window),
1200 FALSE, getWindowExStyle(window));
1201
1202 if (left)
1203 *left = -rect.left;
1204 if (top)
1205 *top = -rect.top;
1206 if (right)
1207 *right = rect.right - width;
1208 if (bottom)
1209 *bottom = rect.bottom - height;
1210 }
1211
_glfwPlatformIconifyWindow(_GLFWwindow * window)1212 void _glfwPlatformIconifyWindow(_GLFWwindow* window)
1213 {
1214 ShowWindow(window->win32.handle, SW_MINIMIZE);
1215 }
1216
_glfwPlatformRestoreWindow(_GLFWwindow * window)1217 void _glfwPlatformRestoreWindow(_GLFWwindow* window)
1218 {
1219 ShowWindow(window->win32.handle, SW_RESTORE);
1220 }
1221
_glfwPlatformMaximizeWindow(_GLFWwindow * window)1222 void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
1223 {
1224 ShowWindow(window->win32.handle, SW_MAXIMIZE);
1225 }
1226
_glfwPlatformShowWindow(_GLFWwindow * window)1227 void _glfwPlatformShowWindow(_GLFWwindow* window)
1228 {
1229 ShowWindow(window->win32.handle, SW_SHOW);
1230 }
1231
_glfwPlatformHideWindow(_GLFWwindow * window)1232 void _glfwPlatformHideWindow(_GLFWwindow* window)
1233 {
1234 ShowWindow(window->win32.handle, SW_HIDE);
1235 }
1236
_glfwPlatformFocusWindow(_GLFWwindow * window)1237 void _glfwPlatformFocusWindow(_GLFWwindow* window)
1238 {
1239 BringWindowToTop(window->win32.handle);
1240 SetForegroundWindow(window->win32.handle);
1241 SetFocus(window->win32.handle);
1242 }
1243
_glfwPlatformSetWindowMonitor(_GLFWwindow * window,_GLFWmonitor * monitor,int xpos,int ypos,int width,int height,int refreshRate)1244 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
1245 _GLFWmonitor* monitor,
1246 int xpos, int ypos,
1247 int width, int height,
1248 int refreshRate)
1249 {
1250 if (window->monitor == monitor)
1251 {
1252 if (monitor)
1253 {
1254 if (monitor->window == window)
1255 acquireMonitor(window);
1256 }
1257 else
1258 {
1259 RECT rect = { xpos, ypos, xpos + width, ypos + height };
1260 AdjustWindowRectEx(&rect, getWindowStyle(window),
1261 FALSE, getWindowExStyle(window));
1262 SetWindowPos(window->win32.handle, HWND_TOP,
1263 rect.left, rect.top,
1264 rect.right - rect.left, rect.bottom - rect.top,
1265 SWP_NOCOPYBITS | SWP_NOACTIVATE | SWP_NOZORDER);
1266 }
1267
1268 return;
1269 }
1270
1271 if (window->monitor)
1272 releaseMonitor(window);
1273
1274 _glfwInputWindowMonitorChange(window, monitor);
1275
1276 if (monitor)
1277 {
1278 GLFWvidmode mode;
1279 DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE);
1280 UINT flags = SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOCOPYBITS;
1281
1282 if (window->decorated)
1283 {
1284 style &= ~WS_OVERLAPPEDWINDOW;
1285 style |= getWindowStyle(window);
1286 SetWindowLongW(window->win32.handle, GWL_STYLE, style);
1287
1288 flags |= SWP_FRAMECHANGED;
1289 }
1290
1291 _glfwPlatformGetVideoMode(monitor, &mode);
1292 _glfwPlatformGetMonitorPos(monitor, &xpos, &ypos);
1293
1294 SetWindowPos(window->win32.handle, HWND_TOPMOST,
1295 xpos, ypos, mode.width, mode.height,
1296 flags);
1297
1298 acquireMonitor(window);
1299 }
1300 else
1301 {
1302 HWND after;
1303 RECT rect = { xpos, ypos, xpos + width, ypos + height };
1304 DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE);
1305 UINT flags = SWP_NOACTIVATE | SWP_NOCOPYBITS;
1306
1307 if (window->decorated)
1308 {
1309 style &= ~WS_POPUP;
1310 style |= getWindowStyle(window);
1311 SetWindowLongW(window->win32.handle, GWL_STYLE, style);
1312
1313 flags |= SWP_FRAMECHANGED;
1314 }
1315
1316 if (window->floating)
1317 after = HWND_TOPMOST;
1318 else
1319 after = HWND_NOTOPMOST;
1320
1321 AdjustWindowRectEx(&rect, getWindowStyle(window),
1322 FALSE, getWindowExStyle(window));
1323 SetWindowPos(window->win32.handle, after,
1324 rect.left, rect.top,
1325 rect.right - rect.left, rect.bottom - rect.top,
1326 flags);
1327 }
1328 }
1329
_glfwPlatformWindowFocused(_GLFWwindow * window)1330 int _glfwPlatformWindowFocused(_GLFWwindow* window)
1331 {
1332 return window->win32.handle == GetActiveWindow();
1333 }
1334
_glfwPlatformWindowIconified(_GLFWwindow * window)1335 int _glfwPlatformWindowIconified(_GLFWwindow* window)
1336 {
1337 return IsIconic(window->win32.handle);
1338 }
1339
_glfwPlatformWindowVisible(_GLFWwindow * window)1340 int _glfwPlatformWindowVisible(_GLFWwindow* window)
1341 {
1342 return IsWindowVisible(window->win32.handle);
1343 }
1344
_glfwPlatformWindowMaximized(_GLFWwindow * window)1345 int _glfwPlatformWindowMaximized(_GLFWwindow* window)
1346 {
1347 return IsZoomed(window->win32.handle);
1348 }
1349
_glfwPlatformPollEvents(void)1350 void _glfwPlatformPollEvents(void)
1351 {
1352 MSG msg;
1353 HWND handle;
1354 _GLFWwindow* window;
1355
1356 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
1357 {
1358 if (msg.message == WM_QUIT)
1359 {
1360 // Treat WM_QUIT as a close on all windows
1361 // While GLFW does not itself post WM_QUIT, other processes may post
1362 // it to this one, for example Task Manager
1363
1364 window = _glfw.windowListHead;
1365 while (window)
1366 {
1367 _glfwInputWindowCloseRequest(window);
1368 window = window->next;
1369 }
1370 }
1371 else
1372 {
1373 TranslateMessage(&msg);
1374 DispatchMessageW(&msg);
1375 }
1376 }
1377
1378 handle = GetActiveWindow();
1379 if (handle)
1380 {
1381 // LSHIFT/RSHIFT fixup (keys tend to "stick" without this fix)
1382 // This is the only async event handling in GLFW, but it solves some
1383 // nasty problems
1384 window = GetPropW(handle, L"GLFW");
1385 if (window)
1386 {
1387 const int mods = getAsyncKeyMods();
1388
1389 // Get current state of left and right shift keys
1390 const int lshiftDown = (GetAsyncKeyState(VK_LSHIFT) >> 15) & 1;
1391 const int rshiftDown = (GetAsyncKeyState(VK_RSHIFT) >> 15) & 1;
1392
1393 // See if this differs from our belief of what has happened
1394 // (we only have to check for lost key up events)
1395 if (!lshiftDown && window->keys[GLFW_KEY_LEFT_SHIFT] == 1)
1396 _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, 0, GLFW_RELEASE, mods);
1397
1398 if (!rshiftDown && window->keys[GLFW_KEY_RIGHT_SHIFT] == 1)
1399 _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, 0, GLFW_RELEASE, mods);
1400 }
1401 }
1402
1403 window = _glfw.win32.disabledCursorWindow;
1404 if (window)
1405 {
1406 int width, height;
1407 _glfwPlatformGetWindowSize(window, &width, &height);
1408
1409 // NOTE: Re-center the cursor only if it has moved since the last call,
1410 // to avoid breaking glfwWaitEvents with WM_MOUSEMOVE
1411 if (window->win32.lastCursorPosX != width / 2 ||
1412 window->win32.lastCursorPosY != height / 2)
1413 {
1414 _glfwPlatformSetCursorPos(window, width / 2, height / 2);
1415 }
1416 }
1417 }
1418
_glfwPlatformWaitEvents(void)1419 void _glfwPlatformWaitEvents(void)
1420 {
1421 WaitMessage();
1422
1423 _glfwPlatformPollEvents();
1424 }
1425
_glfwPlatformWaitEventsTimeout(double timeout)1426 void _glfwPlatformWaitEventsTimeout(double timeout)
1427 {
1428 MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD) (timeout * 1e3), QS_ALLEVENTS);
1429
1430 _glfwPlatformPollEvents();
1431 }
1432
_glfwPlatformPostEmptyEvent(void)1433 void _glfwPlatformPostEmptyEvent(void)
1434 {
1435 _GLFWwindow* window = _glfw.windowListHead;
1436 PostMessage(window->win32.handle, WM_NULL, 0, 0);
1437 }
1438
_glfwPlatformGetCursorPos(_GLFWwindow * window,double * xpos,double * ypos)1439 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
1440 {
1441 POINT pos;
1442
1443 if (GetCursorPos(&pos))
1444 {
1445 ScreenToClient(window->win32.handle, &pos);
1446
1447 if (xpos)
1448 *xpos = pos.x;
1449 if (ypos)
1450 *ypos = pos.y;
1451 }
1452 }
1453
_glfwPlatformSetCursorPos(_GLFWwindow * window,double xpos,double ypos)1454 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos)
1455 {
1456 POINT pos = { (int) xpos, (int) ypos };
1457
1458 // Store the new position so it can be recognized later
1459 window->win32.lastCursorPosX = pos.x;
1460 window->win32.lastCursorPosY = pos.y;
1461
1462 ClientToScreen(window->win32.handle, &pos);
1463 SetCursorPos(pos.x, pos.y);
1464 }
1465
_glfwPlatformSetCursorMode(_GLFWwindow * window,int mode)1466 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
1467 {
1468 if (mode == GLFW_CURSOR_DISABLED)
1469 {
1470 _glfw.win32.disabledCursorWindow = window;
1471 _glfwPlatformGetCursorPos(window,
1472 &_glfw.win32.restoreCursorPosX,
1473 &_glfw.win32.restoreCursorPosY);
1474 centerCursor(window);
1475 updateClipRect(window);
1476 }
1477 else if (_glfw.win32.disabledCursorWindow == window)
1478 {
1479 _glfw.win32.disabledCursorWindow = NULL;
1480 updateClipRect(NULL);
1481 _glfwPlatformSetCursorPos(window,
1482 _glfw.win32.restoreCursorPosX,
1483 _glfw.win32.restoreCursorPosY);
1484 }
1485
1486 if (cursorInClientArea(window))
1487 updateCursorImage(window);
1488 }
1489
_glfwPlatformGetKeyName(int key,int scancode)1490 const char* _glfwPlatformGetKeyName(int key, int scancode)
1491 {
1492 WCHAR name[16];
1493
1494 if (key != GLFW_KEY_UNKNOWN)
1495 scancode = _glfw.win32.nativeKeys[key];
1496
1497 if (!_glfwIsPrintable(_glfw.win32.publicKeys[scancode]))
1498 return NULL;
1499
1500 if (!GetKeyNameTextW(scancode << 16, name, sizeof(name) / sizeof(WCHAR)))
1501 return NULL;
1502
1503 if (!WideCharToMultiByte(CP_UTF8, 0, name, -1,
1504 _glfw.win32.keyName,
1505 sizeof(_glfw.win32.keyName),
1506 NULL, NULL))
1507 {
1508 return NULL;
1509 }
1510
1511 return _glfw.win32.keyName;
1512 }
1513
_glfwPlatformCreateCursor(_GLFWcursor * cursor,const GLFWimage * image,int xhot,int yhot)1514 int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
1515 const GLFWimage* image,
1516 int xhot, int yhot)
1517 {
1518 cursor->win32.handle = (HCURSOR) createIcon(image, xhot, yhot, GLFW_FALSE);
1519 if (!cursor->win32.handle)
1520 return GLFW_FALSE;
1521
1522 return GLFW_TRUE;
1523 }
1524
_glfwPlatformCreateStandardCursor(_GLFWcursor * cursor,int shape)1525 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
1526 {
1527 cursor->win32.handle =
1528 CopyCursor(LoadCursorW(NULL, translateCursorShape(shape)));
1529 if (!cursor->win32.handle)
1530 {
1531 _glfwInputError(GLFW_PLATFORM_ERROR,
1532 "Win32: Failed to create standard cursor");
1533 return GLFW_FALSE;
1534 }
1535
1536 return GLFW_TRUE;
1537 }
1538
_glfwPlatformDestroyCursor(_GLFWcursor * cursor)1539 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
1540 {
1541 if (cursor->win32.handle)
1542 DestroyIcon((HICON) cursor->win32.handle);
1543 }
1544
_glfwPlatformSetCursor(_GLFWwindow * window,_GLFWcursor * cursor)1545 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
1546 {
1547 if (cursorInClientArea(window))
1548 updateCursorImage(window);
1549 }
1550
_glfwPlatformSetClipboardString(_GLFWwindow * window,const char * string)1551 void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string)
1552 {
1553 int characterCount;
1554 HANDLE object;
1555 WCHAR* buffer;
1556
1557 characterCount = MultiByteToWideChar(CP_UTF8, 0, string, -1, NULL, 0);
1558 if (!characterCount)
1559 {
1560 _glfwInputError(GLFW_PLATFORM_ERROR,
1561 "Win32: Failed to convert clipboard string to UTF-16");
1562 return;
1563 }
1564
1565 object = GlobalAlloc(GMEM_MOVEABLE, characterCount * sizeof(WCHAR));
1566 if (!object)
1567 {
1568 _glfwInputError(GLFW_PLATFORM_ERROR,
1569 "Win32: Failed to allocate global handle for clipboard");
1570 return;
1571 }
1572
1573 buffer = GlobalLock(object);
1574 if (!buffer)
1575 {
1576 GlobalFree(object);
1577
1578 _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to lock global handle");
1579 return;
1580 }
1581
1582 MultiByteToWideChar(CP_UTF8, 0, string, -1, buffer, characterCount);
1583 GlobalUnlock(object);
1584
1585 if (!OpenClipboard(_glfw.win32.helperWindowHandle))
1586 {
1587 GlobalFree(object);
1588
1589 _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to open clipboard");
1590 return;
1591 }
1592
1593 EmptyClipboard();
1594 SetClipboardData(CF_UNICODETEXT, object);
1595 CloseClipboard();
1596 }
1597
_glfwPlatformGetClipboardString(_GLFWwindow * window)1598 const char* _glfwPlatformGetClipboardString(_GLFWwindow* window)
1599 {
1600 HANDLE object;
1601 WCHAR* buffer;
1602
1603 if (!OpenClipboard(_glfw.win32.helperWindowHandle))
1604 {
1605 _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to open clipboard");
1606 return NULL;
1607 }
1608
1609 object = GetClipboardData(CF_UNICODETEXT);
1610 if (!object)
1611 {
1612 CloseClipboard();
1613
1614 _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
1615 "Win32: Failed to convert clipboard to string");
1616 return NULL;
1617 }
1618
1619 buffer = GlobalLock(object);
1620 if (!buffer)
1621 {
1622 CloseClipboard();
1623
1624 _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to lock global handle");
1625 return NULL;
1626 }
1627
1628 free(_glfw.win32.clipboardString);
1629 _glfw.win32.clipboardString =
1630 _glfwCreateUTF8FromWideStringWin32(buffer);
1631
1632 GlobalUnlock(object);
1633 CloseClipboard();
1634
1635 if (!_glfw.win32.clipboardString)
1636 {
1637 _glfwInputError(GLFW_PLATFORM_ERROR,
1638 "Win32: Failed to convert wide string to UTF-8");
1639 return NULL;
1640 }
1641
1642 return _glfw.win32.clipboardString;
1643 }
1644
_glfwPlatformGetRequiredInstanceExtensions(uint32_t * count)1645 char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count)
1646 {
1647 char** extensions;
1648
1649 *count = 0;
1650
1651 if (!_glfw.vk.KHR_win32_surface)
1652 return NULL;
1653
1654 extensions = calloc(2, sizeof(char*));
1655 extensions[0] = strdup("VK_KHR_surface");
1656 extensions[1] = strdup("VK_KHR_win32_surface");
1657
1658 *count = 2;
1659 return extensions;
1660 }
1661
_glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,VkPhysicalDevice device,uint32_t queuefamily)1662 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
1663 VkPhysicalDevice device,
1664 uint32_t queuefamily)
1665 {
1666 PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR =
1667 (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)
1668 vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR");
1669 if (!vkGetPhysicalDeviceWin32PresentationSupportKHR)
1670 {
1671 _glfwInputError(GLFW_API_UNAVAILABLE,
1672 "Win32: Vulkan instance missing VK_KHR_win32_surface extension");
1673 return GLFW_FALSE;
1674 }
1675
1676 return vkGetPhysicalDeviceWin32PresentationSupportKHR(device, queuefamily);
1677 }
1678
_glfwPlatformCreateWindowSurface(VkInstance instance,_GLFWwindow * window,const VkAllocationCallbacks * allocator,VkSurfaceKHR * surface)1679 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
1680 _GLFWwindow* window,
1681 const VkAllocationCallbacks* allocator,
1682 VkSurfaceKHR* surface)
1683 {
1684 VkResult err;
1685 VkWin32SurfaceCreateInfoKHR sci;
1686 PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR;
1687
1688 vkCreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR)
1689 vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR");
1690 if (!vkCreateWin32SurfaceKHR)
1691 {
1692 _glfwInputError(GLFW_API_UNAVAILABLE,
1693 "Win32: Vulkan instance missing VK_KHR_win32_surface extension");
1694 return VK_ERROR_EXTENSION_NOT_PRESENT;
1695 }
1696
1697 memset(&sci, 0, sizeof(sci));
1698 sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
1699 sci.hinstance = GetModuleHandle(NULL);
1700 sci.hwnd = window->win32.handle;
1701
1702 err = vkCreateWin32SurfaceKHR(instance, &sci, allocator, surface);
1703 if (err)
1704 {
1705 _glfwInputError(GLFW_PLATFORM_ERROR,
1706 "Win32: Failed to create Vulkan surface: %s",
1707 _glfwGetVulkanResultString(err));
1708 }
1709
1710 return err;
1711 }
1712
1713
1714 //////////////////////////////////////////////////////////////////////////
1715 ////// GLFW native API //////
1716 //////////////////////////////////////////////////////////////////////////
1717
glfwGetWin32Window(GLFWwindow * handle)1718 GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle)
1719 {
1720 _GLFWwindow* window = (_GLFWwindow*) handle;
1721 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
1722 return window->win32.handle;
1723 }
1724
1725