win32_window.c (70222B)
1 //======================================================================== 2 // GLFW 3.4 Win32 - www.glfw.org 3 //------------------------------------------------------------------------ 4 // Copyright (c) 2002-2006 Marcus Geelnard 5 // Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org> 6 // 7 // This software is provided 'as-is', without any express or implied 8 // warranty. In no event will the authors be held liable for any damages 9 // arising from the use of this software. 10 // 11 // Permission is granted to anyone to use this software for any purpose, 12 // including commercial applications, and to alter it and redistribute it 13 // freely, subject to the following restrictions: 14 // 15 // 1. The origin of this software must not be misrepresented; you must not 16 // claim that you wrote the original software. If you use this software 17 // in a product, an acknowledgment in the product documentation would 18 // be appreciated but is not required. 19 // 20 // 2. Altered source versions must be plainly marked as such, and must not 21 // be misrepresented as being the original software. 22 // 23 // 3. This notice may not be removed or altered from any source 24 // distribution. 25 // 26 //======================================================================== 27 // Please use C89 style variable declarations in this file because VS 2010 28 //======================================================================== 29 30 #include "internal.h" 31 32 #include <limits.h> 33 #include <stdlib.h> 34 #include <malloc.h> 35 #include <string.h> 36 #include <windowsx.h> 37 #include <shellapi.h> 38 39 // Returns the window style for the specified window 40 // 41 static DWORD getWindowStyle(const _GLFWwindow* window) 42 { 43 DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; 44 45 if (window->monitor) 46 style |= WS_POPUP; 47 else 48 { 49 style |= WS_SYSMENU | WS_MINIMIZEBOX; 50 51 if (window->decorated) 52 { 53 style |= WS_CAPTION; 54 55 if (window->resizable) 56 style |= WS_MAXIMIZEBOX | WS_THICKFRAME; 57 } 58 else 59 style |= WS_POPUP; 60 } 61 62 return style; 63 } 64 65 // Returns the extended window style for the specified window 66 // 67 static DWORD getWindowExStyle(const _GLFWwindow* window) 68 { 69 DWORD style = WS_EX_APPWINDOW; 70 71 if (window->monitor || window->floating) 72 style |= WS_EX_TOPMOST; 73 74 return style; 75 } 76 77 // Returns the image whose area most closely matches the desired one 78 // 79 static const GLFWimage* chooseImage(int count, const GLFWimage* images, 80 int width, int height) 81 { 82 int i, leastDiff = INT_MAX; 83 const GLFWimage* closest = NULL; 84 85 for (i = 0; i < count; i++) 86 { 87 const int currDiff = abs(images[i].width * images[i].height - 88 width * height); 89 if (currDiff < leastDiff) 90 { 91 closest = images + i; 92 leastDiff = currDiff; 93 } 94 } 95 96 return closest; 97 } 98 99 // Creates an RGBA icon or cursor 100 // 101 static HICON createIcon(const GLFWimage* image, 102 int xhot, int yhot, GLFWbool icon) 103 { 104 int i; 105 HDC dc; 106 HICON handle; 107 HBITMAP color, mask; 108 BITMAPV5HEADER bi; 109 ICONINFO ii; 110 unsigned char* target = NULL; 111 unsigned char* source = image->pixels; 112 113 ZeroMemory(&bi, sizeof(bi)); 114 bi.bV5Size = sizeof(bi); 115 bi.bV5Width = image->width; 116 bi.bV5Height = -image->height; 117 bi.bV5Planes = 1; 118 bi.bV5BitCount = 32; 119 bi.bV5Compression = BI_BITFIELDS; 120 bi.bV5RedMask = 0x00ff0000; 121 bi.bV5GreenMask = 0x0000ff00; 122 bi.bV5BlueMask = 0x000000ff; 123 bi.bV5AlphaMask = 0xff000000; 124 125 dc = GetDC(NULL); 126 color = CreateDIBSection(dc, 127 (BITMAPINFO*) &bi, 128 DIB_RGB_COLORS, 129 (void**) &target, 130 NULL, 131 (DWORD) 0); 132 ReleaseDC(NULL, dc); 133 134 if (!color) 135 { 136 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, 137 "Win32: Failed to create RGBA bitmap"); 138 return NULL; 139 } 140 141 mask = CreateBitmap(image->width, image->height, 1, 1, NULL); 142 if (!mask) 143 { 144 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, 145 "Win32: Failed to create mask bitmap"); 146 DeleteObject(color); 147 return NULL; 148 } 149 150 for (i = 0; i < image->width * image->height; i++) 151 { 152 target[0] = source[2]; 153 target[1] = source[1]; 154 target[2] = source[0]; 155 target[3] = source[3]; 156 target += 4; 157 source += 4; 158 } 159 160 ZeroMemory(&ii, sizeof(ii)); 161 ii.fIcon = icon; 162 ii.xHotspot = xhot; 163 ii.yHotspot = yhot; 164 ii.hbmMask = mask; 165 ii.hbmColor = color; 166 167 handle = CreateIconIndirect(&ii); 168 169 DeleteObject(color); 170 DeleteObject(mask); 171 172 if (!handle) 173 { 174 if (icon) 175 { 176 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, 177 "Win32: Failed to create icon"); 178 } 179 else 180 { 181 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, 182 "Win32: Failed to create cursor"); 183 } 184 } 185 186 return handle; 187 } 188 189 // Translate content area size to full window size according to styles and DPI 190 // 191 static void getFullWindowSize(DWORD style, DWORD exStyle, 192 int contentWidth, int contentHeight, 193 int* fullWidth, int* fullHeight, 194 UINT dpi) 195 { 196 RECT rect = { 0, 0, contentWidth, contentHeight }; 197 198 if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) 199 AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi); 200 else 201 AdjustWindowRectEx(&rect, style, FALSE, exStyle); 202 203 *fullWidth = rect.right - rect.left; 204 *fullHeight = rect.bottom - rect.top; 205 } 206 207 // Enforce the content area aspect ratio based on which edge is being dragged 208 // 209 static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area) 210 { 211 int xoff, yoff; 212 UINT dpi = USER_DEFAULT_SCREEN_DPI; 213 const float ratio = (float) window->numer / (float) window->denom; 214 215 if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) 216 dpi = GetDpiForWindow(window->win32.handle); 217 218 getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), 219 0, 0, &xoff, &yoff, dpi); 220 221 if (edge == WMSZ_LEFT || edge == WMSZ_BOTTOMLEFT || 222 edge == WMSZ_RIGHT || edge == WMSZ_BOTTOMRIGHT) 223 { 224 area->bottom = area->top + yoff + 225 (int) ((area->right - area->left - xoff) / ratio); 226 } 227 else if (edge == WMSZ_TOPLEFT || edge == WMSZ_TOPRIGHT) 228 { 229 area->top = area->bottom - yoff - 230 (int) ((area->right - area->left - xoff) / ratio); 231 } 232 else if (edge == WMSZ_TOP || edge == WMSZ_BOTTOM) 233 { 234 area->right = area->left + xoff + 235 (int) ((area->bottom - area->top - yoff) * ratio); 236 } 237 } 238 239 // Updates the cursor image according to its cursor mode 240 // 241 static void updateCursorImage(_GLFWwindow* window) 242 { 243 if (window->cursorMode == GLFW_CURSOR_NORMAL) 244 { 245 if (window->cursor) 246 SetCursor(window->cursor->win32.handle); 247 else 248 SetCursor(LoadCursorW(NULL, (LPCWSTR)IDC_ARROW)); 249 } 250 else 251 SetCursor(NULL); 252 } 253 254 // Updates the cursor clip rect 255 // 256 static void updateClipRect(_GLFWwindow* window) 257 { 258 if (window) 259 { 260 RECT clipRect; 261 GetClientRect(window->win32.handle, &clipRect); 262 ClientToScreen(window->win32.handle, (POINT*) &clipRect.left); 263 ClientToScreen(window->win32.handle, (POINT*) &clipRect.right); 264 ClipCursor(&clipRect); 265 } 266 else 267 ClipCursor(NULL); 268 } 269 270 // Enables WM_INPUT messages for the mouse for the specified window 271 // 272 static void enableRawMouseMotion(_GLFWwindow* window) 273 { 274 const RAWINPUTDEVICE rid = { 0x01, 0x02, 0, window->win32.handle }; 275 276 if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) 277 { 278 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, 279 "Win32: Failed to register raw input device"); 280 } 281 } 282 283 // Disables WM_INPUT messages for the mouse 284 // 285 static void disableRawMouseMotion(_GLFWwindow* window) 286 { 287 const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL }; 288 289 if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) 290 { 291 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, 292 "Win32: Failed to remove raw input device"); 293 } 294 } 295 296 // Apply disabled cursor mode to a focused window 297 // 298 static void disableCursor(_GLFWwindow* window) 299 { 300 _glfw.win32.disabledCursorWindow = window; 301 _glfwPlatformGetCursorPos(window, 302 &_glfw.win32.restoreCursorPosX, 303 &_glfw.win32.restoreCursorPosY); 304 updateCursorImage(window); 305 _glfwCenterCursorInContentArea(window); 306 updateClipRect(window); 307 308 if (window->rawMouseMotion) 309 enableRawMouseMotion(window); 310 } 311 312 // Exit disabled cursor mode for the specified window 313 // 314 static void enableCursor(_GLFWwindow* window) 315 { 316 if (window->rawMouseMotion) 317 disableRawMouseMotion(window); 318 319 _glfw.win32.disabledCursorWindow = NULL; 320 updateClipRect(NULL); 321 _glfwPlatformSetCursorPos(window, 322 _glfw.win32.restoreCursorPosX, 323 _glfw.win32.restoreCursorPosY); 324 updateCursorImage(window); 325 } 326 327 // Returns whether the cursor is in the content area of the specified window 328 // 329 static GLFWbool cursorInContentArea(_GLFWwindow* window) 330 { 331 RECT area; 332 POINT pos; 333 334 if (!GetCursorPos(&pos)) 335 return GLFW_FALSE; 336 337 if (WindowFromPoint(pos) != window->win32.handle) 338 return GLFW_FALSE; 339 340 GetClientRect(window->win32.handle, &area); 341 ClientToScreen(window->win32.handle, (POINT*) &area.left); 342 ClientToScreen(window->win32.handle, (POINT*) &area.right); 343 344 return PtInRect(&area, pos); 345 } 346 347 // Update native window styles to match attributes 348 // 349 static void updateWindowStyles(const _GLFWwindow* window) 350 { 351 RECT rect; 352 DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); 353 style &= ~(WS_OVERLAPPEDWINDOW | WS_POPUP); 354 style |= getWindowStyle(window); 355 356 GetClientRect(window->win32.handle, &rect); 357 358 if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) 359 { 360 AdjustWindowRectExForDpi(&rect, style, FALSE, 361 getWindowExStyle(window), 362 GetDpiForWindow(window->win32.handle)); 363 } 364 else 365 AdjustWindowRectEx(&rect, style, FALSE, getWindowExStyle(window)); 366 367 ClientToScreen(window->win32.handle, (POINT*) &rect.left); 368 ClientToScreen(window->win32.handle, (POINT*) &rect.right); 369 SetWindowLongW(window->win32.handle, GWL_STYLE, style); 370 SetWindowPos(window->win32.handle, HWND_TOP, 371 rect.left, rect.top, 372 rect.right - rect.left, rect.bottom - rect.top, 373 SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER); 374 } 375 376 // Update window framebuffer transparency 377 // 378 static void updateFramebufferTransparency(const _GLFWwindow* window) 379 { 380 BOOL enabled; 381 382 if (!IsWindowsVistaOrGreater()) 383 return; 384 385 if (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled) 386 { 387 HRGN region = CreateRectRgn(0, 0, -1, -1); 388 DWM_BLURBEHIND bb = {0}; 389 bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; 390 bb.hRgnBlur = region; 391 bb.fEnable = TRUE; 392 393 if (SUCCEEDED(DwmEnableBlurBehindWindow(window->win32.handle, &bb))) 394 { 395 // Decorated windows don't repaint the transparent background 396 // leaving a trail behind animations 397 // HACK: Making the window layered with a transparency color key 398 // seems to fix this. Normally, when specifying 399 // a transparency color key to be used when composing the 400 // layered window, all pixels painted by the window in this 401 // color will be transparent. That doesn't seem to be the 402 // case anymore, at least when used with blur behind window 403 // plus negative region. 404 LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); 405 exStyle |= WS_EX_LAYERED; 406 SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle); 407 408 // Using a color key not equal to black to fix the trailing 409 // issue. When set to black, something is making the hit test 410 // not resize with the window frame. 411 SetLayeredWindowAttributes(window->win32.handle, 412 RGB(255, 0, 255), 255, LWA_COLORKEY); 413 } 414 415 DeleteObject(region); 416 } 417 else 418 { 419 LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); 420 exStyle &= ~WS_EX_LAYERED; 421 SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle); 422 RedrawWindow(window->win32.handle, NULL, NULL, 423 RDW_ERASE | RDW_INVALIDATE | RDW_FRAME); 424 } 425 } 426 427 // Retrieves and translates modifier keys 428 // 429 static int getKeyMods(void) 430 { 431 int mods = 0; 432 433 if (GetKeyState(VK_SHIFT) & 0x8000) 434 mods |= GLFW_MOD_SHIFT; 435 if (GetKeyState(VK_CONTROL) & 0x8000) 436 mods |= GLFW_MOD_CONTROL; 437 if (GetKeyState(VK_MENU) & 0x8000) 438 mods |= GLFW_MOD_ALT; 439 if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000) 440 mods |= GLFW_MOD_SUPER; 441 if (GetKeyState(VK_CAPITAL) & 1) 442 mods |= GLFW_MOD_CAPS_LOCK; 443 if (GetKeyState(VK_NUMLOCK) & 1) 444 mods |= GLFW_MOD_NUM_LOCK; 445 446 return mods; 447 } 448 449 static void fitToMonitor(_GLFWwindow* window) 450 { 451 MONITORINFO mi = { sizeof(mi) }; 452 GetMonitorInfo(window->monitor->win32.handle, &mi); 453 SetWindowPos(window->win32.handle, HWND_TOPMOST, 454 mi.rcMonitor.left, 455 mi.rcMonitor.top, 456 mi.rcMonitor.right - mi.rcMonitor.left, 457 mi.rcMonitor.bottom - mi.rcMonitor.top, 458 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS); 459 } 460 461 // Make the specified window and its video mode active on its monitor 462 // 463 static void acquireMonitor(_GLFWwindow* window) 464 { 465 if (!_glfw.win32.acquiredMonitorCount) 466 { 467 SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED); 468 469 // HACK: When mouse trails are enabled the cursor becomes invisible when 470 // the OpenGL ICD switches to page flipping 471 if (IsWindowsXPOrGreater()) 472 { 473 SystemParametersInfo(SPI_GETMOUSETRAILS, 0, &_glfw.win32.mouseTrailSize, 0); 474 SystemParametersInfo(SPI_SETMOUSETRAILS, 0, 0, 0); 475 } 476 } 477 478 if (!window->monitor->window) 479 _glfw.win32.acquiredMonitorCount++; 480 481 _glfwSetVideoModeWin32(window->monitor, &window->videoMode); 482 _glfwInputMonitorWindow(window->monitor, window); 483 } 484 485 // Remove the window and restore the original video mode 486 // 487 static void releaseMonitor(_GLFWwindow* window) 488 { 489 if (window->monitor->window != window) 490 return; 491 492 _glfw.win32.acquiredMonitorCount--; 493 if (!_glfw.win32.acquiredMonitorCount) 494 { 495 SetThreadExecutionState(ES_CONTINUOUS); 496 497 // HACK: Restore mouse trail length saved in acquireMonitor 498 if (IsWindowsXPOrGreater()) 499 SystemParametersInfo(SPI_SETMOUSETRAILS, _glfw.win32.mouseTrailSize, 0, 0); 500 } 501 502 _glfwInputMonitorWindow(window->monitor, NULL); 503 _glfwRestoreVideoModeWin32(window->monitor); 504 } 505 506 // Window callback function (handles window messages) 507 // 508 static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, 509 WPARAM wParam, LPARAM lParam) 510 { 511 _GLFWwindow* window = (_GLFWwindow*)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 { 630 if (!window->win32.keymenu) 631 return 0; 632 633 break; 634 } 635 } 636 break; 637 } 638 639 case WM_CLOSE: 640 { 641 _glfwInputWindowCloseRequest(window); 642 return 0; 643 } 644 645 case WM_INPUTLANGCHANGE: 646 { 647 _glfwUpdateKeyNamesWin32(); 648 break; 649 } 650 651 case WM_CHAR: 652 case WM_SYSCHAR: 653 case WM_UNICHAR: 654 { 655 const GLFWbool plain = (uMsg != WM_SYSCHAR); 656 657 if (uMsg == WM_UNICHAR && wParam == UNICODE_NOCHAR) 658 { 659 // WM_UNICHAR is not sent by Windows, but is sent by some 660 // third-party input method engine 661 // Returning TRUE here announces support for this message 662 return TRUE; 663 } 664 665 _glfwInputChar(window, (unsigned int) wParam, getKeyMods(), plain); 666 667 if (uMsg == WM_SYSCHAR && window->win32.keymenu) 668 break; 669 670 return 0; 671 } 672 673 case WM_KEYDOWN: 674 case WM_SYSKEYDOWN: 675 case WM_KEYUP: 676 case WM_SYSKEYUP: 677 { 678 int key, scancode; 679 const int action = (HIWORD(lParam) & KF_UP) ? GLFW_RELEASE : GLFW_PRESS; 680 const int mods = getKeyMods(); 681 682 scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff)); 683 if (!scancode) 684 { 685 // NOTE: Some synthetic key messages have a scancode of zero 686 // HACK: Map the virtual key back to a usable scancode 687 scancode = MapVirtualKeyW((UINT) wParam, MAPVK_VK_TO_VSC); 688 } 689 690 key = _glfw.win32.keycodes[scancode]; 691 692 // The Ctrl keys require special handling 693 if (wParam == VK_CONTROL) 694 { 695 if (HIWORD(lParam) & KF_EXTENDED) 696 { 697 // Right side keys have the extended key bit set 698 key = GLFW_KEY_RIGHT_CONTROL; 699 } 700 else 701 { 702 // NOTE: Alt Gr sends Left Ctrl followed by Right Alt 703 // HACK: We only want one event for Alt Gr, so if we detect 704 // this sequence we discard this Left Ctrl message now 705 // and later report Right Alt normally 706 MSG next; 707 const DWORD time = GetMessageTime(); 708 709 if (PeekMessageW(&next, NULL, 0, 0, PM_NOREMOVE)) 710 { 711 if (next.message == WM_KEYDOWN || 712 next.message == WM_SYSKEYDOWN || 713 next.message == WM_KEYUP || 714 next.message == WM_SYSKEYUP) 715 { 716 if (next.wParam == VK_MENU && 717 (HIWORD(next.lParam) & KF_EXTENDED) && 718 next.time == time) 719 { 720 // Next message is Right Alt down so discard this 721 break; 722 } 723 } 724 } 725 726 // This is a regular Left Ctrl message 727 key = GLFW_KEY_LEFT_CONTROL; 728 } 729 } 730 else if (wParam == VK_PROCESSKEY) 731 { 732 // IME notifies that keys have been filtered by setting the 733 // virtual key-code to VK_PROCESSKEY 734 break; 735 } 736 737 if (action == GLFW_RELEASE && wParam == VK_SHIFT) 738 { 739 // HACK: Release both Shift keys on Shift up event, as when both 740 // are pressed the first release does not emit any event 741 // NOTE: The other half of this is in _glfwPlatformPollEvents 742 _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, action, mods); 743 _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, action, mods); 744 } 745 else if (wParam == VK_SNAPSHOT) 746 { 747 // HACK: Key down is not reported for the Print Screen key 748 _glfwInputKey(window, key, scancode, GLFW_PRESS, mods); 749 _glfwInputKey(window, key, scancode, GLFW_RELEASE, mods); 750 } 751 else 752 _glfwInputKey(window, key, scancode, action, mods); 753 754 break; 755 } 756 757 case WM_LBUTTONDOWN: 758 case WM_RBUTTONDOWN: 759 case WM_MBUTTONDOWN: 760 case WM_XBUTTONDOWN: 761 case WM_LBUTTONUP: 762 case WM_RBUTTONUP: 763 case WM_MBUTTONUP: 764 case WM_XBUTTONUP: 765 { 766 int i, button, action; 767 768 if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP) 769 button = GLFW_MOUSE_BUTTON_LEFT; 770 else if (uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP) 771 button = GLFW_MOUSE_BUTTON_RIGHT; 772 else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP) 773 button = GLFW_MOUSE_BUTTON_MIDDLE; 774 else if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) 775 button = GLFW_MOUSE_BUTTON_4; 776 else 777 button = GLFW_MOUSE_BUTTON_5; 778 779 if (uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN || 780 uMsg == WM_MBUTTONDOWN || uMsg == WM_XBUTTONDOWN) 781 { 782 action = GLFW_PRESS; 783 } 784 else 785 action = GLFW_RELEASE; 786 787 for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) 788 { 789 if (window->mouseButtons[i] == GLFW_PRESS) 790 break; 791 } 792 793 if (i > GLFW_MOUSE_BUTTON_LAST) 794 SetCapture(hWnd); 795 796 _glfwInputMouseClick(window, button, action, getKeyMods()); 797 798 for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) 799 { 800 if (window->mouseButtons[i] == GLFW_PRESS) 801 break; 802 } 803 804 if (i > GLFW_MOUSE_BUTTON_LAST) 805 ReleaseCapture(); 806 807 if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP) 808 return TRUE; 809 810 return 0; 811 } 812 813 case WM_MOUSEMOVE: 814 { 815 const int x = GET_X_LPARAM(lParam); 816 const int y = GET_Y_LPARAM(lParam); 817 818 if (!window->win32.cursorTracked) 819 { 820 TRACKMOUSEEVENT tme; 821 ZeroMemory(&tme, sizeof(tme)); 822 tme.cbSize = sizeof(tme); 823 tme.dwFlags = TME_LEAVE; 824 tme.hwndTrack = window->win32.handle; 825 TrackMouseEvent(&tme); 826 827 window->win32.cursorTracked = GLFW_TRUE; 828 _glfwInputCursorEnter(window, GLFW_TRUE); 829 } 830 831 if (window->cursorMode == GLFW_CURSOR_DISABLED) 832 { 833 const int dx = x - window->win32.lastCursorPosX; 834 const int dy = y - window->win32.lastCursorPosY; 835 836 if (_glfw.win32.disabledCursorWindow != window) 837 break; 838 if (window->rawMouseMotion) 839 break; 840 841 _glfwInputCursorPos(window, 842 window->virtualCursorPosX + dx, 843 window->virtualCursorPosY + dy); 844 } 845 else 846 _glfwInputCursorPos(window, x, y); 847 848 window->win32.lastCursorPosX = x; 849 window->win32.lastCursorPosY = y; 850 851 return 0; 852 } 853 854 case WM_INPUT: 855 { 856 UINT size = 0; 857 HRAWINPUT ri = (HRAWINPUT) lParam; 858 RAWINPUT* data = NULL; 859 int dx, dy; 860 861 if (_glfw.win32.disabledCursorWindow != window) 862 break; 863 if (!window->rawMouseMotion) 864 break; 865 866 GetRawInputData(ri, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)); 867 if (size > (UINT) _glfw.win32.rawInputSize) 868 { 869 free(_glfw.win32.rawInput); 870 _glfw.win32.rawInput = (RAWINPUT*)calloc(size, 1); 871 _glfw.win32.rawInputSize = size; 872 } 873 874 size = _glfw.win32.rawInputSize; 875 if (GetRawInputData(ri, RID_INPUT, 876 _glfw.win32.rawInput, &size, 877 sizeof(RAWINPUTHEADER)) == (UINT) -1) 878 { 879 _glfwInputError(GLFW_PLATFORM_ERROR, 880 "Win32: Failed to retrieve raw input data"); 881 break; 882 } 883 884 data = _glfw.win32.rawInput; 885 if (data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) 886 { 887 dx = data->data.mouse.lLastX - window->win32.lastCursorPosX; 888 dy = data->data.mouse.lLastY - window->win32.lastCursorPosY; 889 } 890 else 891 { 892 dx = data->data.mouse.lLastX; 893 dy = data->data.mouse.lLastY; 894 } 895 896 _glfwInputCursorPos(window, 897 window->virtualCursorPosX + dx, 898 window->virtualCursorPosY + dy); 899 900 window->win32.lastCursorPosX += dx; 901 window->win32.lastCursorPosY += dy; 902 break; 903 } 904 905 case WM_MOUSELEAVE: 906 { 907 window->win32.cursorTracked = GLFW_FALSE; 908 _glfwInputCursorEnter(window, GLFW_FALSE); 909 return 0; 910 } 911 912 case WM_MOUSEWHEEL: 913 { 914 _glfwInputScroll(window, 0.0, (SHORT) HIWORD(wParam) / (double) WHEEL_DELTA); 915 return 0; 916 } 917 918 case WM_MOUSEHWHEEL: 919 { 920 // This message is only sent on Windows Vista and later 921 // NOTE: The X-axis is inverted for consistency with macOS and X11 922 _glfwInputScroll(window, -((SHORT) HIWORD(wParam) / (double) WHEEL_DELTA), 0.0); 923 return 0; 924 } 925 926 case WM_ENTERSIZEMOVE: 927 case WM_ENTERMENULOOP: 928 { 929 if (window->win32.frameAction) 930 break; 931 932 // HACK: Enable the cursor while the user is moving or 933 // resizing the window or using the window menu 934 if (window->cursorMode == GLFW_CURSOR_DISABLED) 935 enableCursor(window); 936 937 break; 938 } 939 940 case WM_EXITSIZEMOVE: 941 case WM_EXITMENULOOP: 942 { 943 if (window->win32.frameAction) 944 break; 945 946 // HACK: Disable the cursor once the user is done moving or 947 // resizing the window or using the menu 948 if (window->cursorMode == GLFW_CURSOR_DISABLED) 949 disableCursor(window); 950 951 break; 952 } 953 954 case WM_SIZE: 955 { 956 const GLFWbool iconified = wParam == SIZE_MINIMIZED; 957 const GLFWbool maximized = wParam == SIZE_MAXIMIZED || 958 (window->win32.maximized && 959 wParam != SIZE_RESTORED); 960 961 if (_glfw.win32.disabledCursorWindow == window) 962 updateClipRect(window); 963 964 if (window->win32.iconified != iconified) 965 _glfwInputWindowIconify(window, iconified); 966 967 if (window->win32.maximized != maximized) 968 _glfwInputWindowMaximize(window, maximized); 969 970 _glfwInputFramebufferSize(window, LOWORD(lParam), HIWORD(lParam)); 971 _glfwInputWindowSize(window, LOWORD(lParam), HIWORD(lParam)); 972 973 if (window->monitor && window->win32.iconified != iconified) 974 { 975 if (iconified) 976 releaseMonitor(window); 977 else 978 { 979 acquireMonitor(window); 980 fitToMonitor(window); 981 } 982 } 983 984 window->win32.iconified = iconified; 985 window->win32.maximized = maximized; 986 return 0; 987 } 988 989 case WM_MOVE: 990 { 991 if (_glfw.win32.disabledCursorWindow == window) 992 updateClipRect(window); 993 994 // NOTE: This cannot use LOWORD/HIWORD recommended by MSDN, as 995 // those macros do not handle negative window positions correctly 996 _glfwInputWindowPos(window, 997 GET_X_LPARAM(lParam), 998 GET_Y_LPARAM(lParam)); 999 return 0; 1000 } 1001 1002 case WM_SIZING: 1003 { 1004 if (window->numer == GLFW_DONT_CARE || 1005 window->denom == GLFW_DONT_CARE) 1006 { 1007 break; 1008 } 1009 1010 applyAspectRatio(window, (int) wParam, (RECT*) lParam); 1011 return TRUE; 1012 } 1013 1014 case WM_GETMINMAXINFO: 1015 { 1016 int xoff, yoff; 1017 UINT dpi = USER_DEFAULT_SCREEN_DPI; 1018 MINMAXINFO* mmi = (MINMAXINFO*) lParam; 1019 1020 if (window->monitor) 1021 break; 1022 1023 if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) 1024 dpi = GetDpiForWindow(window->win32.handle); 1025 1026 getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), 1027 0, 0, &xoff, &yoff, dpi); 1028 1029 if (window->minwidth != GLFW_DONT_CARE && 1030 window->minheight != GLFW_DONT_CARE) 1031 { 1032 mmi->ptMinTrackSize.x = window->minwidth + xoff; 1033 mmi->ptMinTrackSize.y = window->minheight + yoff; 1034 } 1035 1036 if (window->maxwidth != GLFW_DONT_CARE && 1037 window->maxheight != GLFW_DONT_CARE) 1038 { 1039 mmi->ptMaxTrackSize.x = window->maxwidth + xoff; 1040 mmi->ptMaxTrackSize.y = window->maxheight + yoff; 1041 } 1042 1043 if (!window->decorated) 1044 { 1045 MONITORINFO mi; 1046 const HMONITOR mh = MonitorFromWindow(window->win32.handle, 1047 MONITOR_DEFAULTTONEAREST); 1048 1049 ZeroMemory(&mi, sizeof(mi)); 1050 mi.cbSize = sizeof(mi); 1051 GetMonitorInfo(mh, &mi); 1052 1053 mmi->ptMaxPosition.x = mi.rcWork.left - mi.rcMonitor.left; 1054 mmi->ptMaxPosition.y = mi.rcWork.top - mi.rcMonitor.top; 1055 mmi->ptMaxSize.x = mi.rcWork.right - mi.rcWork.left; 1056 mmi->ptMaxSize.y = mi.rcWork.bottom - mi.rcWork.top; 1057 } 1058 1059 return 0; 1060 } 1061 1062 case WM_PAINT: 1063 { 1064 _glfwInputWindowDamage(window); 1065 break; 1066 } 1067 1068 case WM_ERASEBKGND: 1069 { 1070 return TRUE; 1071 } 1072 1073 case WM_NCACTIVATE: 1074 case WM_NCPAINT: 1075 { 1076 // Prevent title bar from being drawn after restoring a minimized 1077 // undecorated window 1078 if (!window->decorated) 1079 return TRUE; 1080 1081 break; 1082 } 1083 1084 case WM_DWMCOMPOSITIONCHANGED: 1085 { 1086 if (window->win32.transparent) 1087 updateFramebufferTransparency(window); 1088 return 0; 1089 } 1090 1091 case WM_GETDPISCALEDSIZE: 1092 { 1093 if (window->win32.scaleToMonitor) 1094 break; 1095 1096 // Adjust the window size to keep the content area size constant 1097 if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) 1098 { 1099 RECT source = {0}, target = {0}; 1100 SIZE* size = (SIZE*) lParam; 1101 1102 AdjustWindowRectExForDpi(&source, getWindowStyle(window), 1103 FALSE, getWindowExStyle(window), 1104 GetDpiForWindow(window->win32.handle)); 1105 AdjustWindowRectExForDpi(&target, getWindowStyle(window), 1106 FALSE, getWindowExStyle(window), 1107 LOWORD(wParam)); 1108 1109 size->cx += (target.right - target.left) - 1110 (source.right - source.left); 1111 size->cy += (target.bottom - target.top) - 1112 (source.bottom - source.top); 1113 return TRUE; 1114 } 1115 1116 break; 1117 } 1118 1119 case WM_DPICHANGED: 1120 { 1121 const float xscale = HIWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI; 1122 const float yscale = LOWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI; 1123 1124 // Only apply the suggested size if the OS is new enough to have 1125 // sent a WM_GETDPISCALEDSIZE before this 1126 if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) 1127 { 1128 RECT* suggested = (RECT*) lParam; 1129 SetWindowPos(window->win32.handle, HWND_TOP, 1130 suggested->left, 1131 suggested->top, 1132 suggested->right - suggested->left, 1133 suggested->bottom - suggested->top, 1134 SWP_NOACTIVATE | SWP_NOZORDER); 1135 } 1136 1137 _glfwInputWindowContentScale(window, xscale, yscale); 1138 break; 1139 } 1140 1141 case WM_SETCURSOR: 1142 { 1143 if (LOWORD(lParam) == HTCLIENT) 1144 { 1145 updateCursorImage(window); 1146 return TRUE; 1147 } 1148 1149 break; 1150 } 1151 1152 case WM_DROPFILES: 1153 { 1154 HDROP drop = (HDROP) wParam; 1155 POINT pt; 1156 int i; 1157 1158 const int count = DragQueryFileW(drop, 0xffffffff, NULL, 0); 1159 char** paths = (char**)calloc(count, sizeof(char*)); 1160 1161 // Move the mouse to the position of the drop 1162 DragQueryPoint(drop, &pt); 1163 _glfwInputCursorPos(window, pt.x, pt.y); 1164 1165 for (i = 0; i < count; i++) 1166 { 1167 const UINT length = DragQueryFileW(drop, i, NULL, 0); 1168 WCHAR* buffer = (WCHAR*)calloc((size_t) length + 1, sizeof(WCHAR)); 1169 1170 DragQueryFileW(drop, i, buffer, length + 1); 1171 paths[i] = _glfwCreateUTF8FromWideStringWin32(buffer); 1172 1173 free(buffer); 1174 } 1175 1176 _glfwInputDrop(window, count, (const char**) paths); 1177 1178 for (i = 0; i < count; i++) 1179 free(paths[i]); 1180 free(paths); 1181 1182 DragFinish(drop); 1183 return 0; 1184 } 1185 } 1186 1187 return DefWindowProcW(hWnd, uMsg, wParam, lParam); 1188 } 1189 1190 // Creates the GLFW window 1191 // 1192 static int createNativeWindow(_GLFWwindow* window, 1193 const _GLFWwndconfig* wndconfig, 1194 const _GLFWfbconfig* fbconfig) 1195 { 1196 int xpos, ypos, fullWidth, fullHeight; 1197 WCHAR* wideTitle; 1198 DWORD style = getWindowStyle(window); 1199 DWORD exStyle = getWindowExStyle(window); 1200 1201 if (window->monitor) 1202 { 1203 GLFWvidmode mode; 1204 1205 // NOTE: This window placement is temporary and approximate, as the 1206 // correct position and size cannot be known until the monitor 1207 // video mode has been picked in _glfwSetVideoModeWin32 1208 _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); 1209 _glfwPlatformGetVideoMode(window->monitor, &mode); 1210 fullWidth = mode.width; 1211 fullHeight = mode.height; 1212 } 1213 else 1214 { 1215 xpos = CW_USEDEFAULT; 1216 ypos = CW_USEDEFAULT; 1217 1218 window->win32.maximized = wndconfig->maximized; 1219 if (wndconfig->maximized) 1220 style |= WS_MAXIMIZE; 1221 1222 getFullWindowSize(style, exStyle, 1223 wndconfig->width, wndconfig->height, 1224 &fullWidth, &fullHeight, 1225 USER_DEFAULT_SCREEN_DPI); 1226 } 1227 1228 wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title); 1229 if (!wideTitle) 1230 return GLFW_FALSE; 1231 1232 window->win32.handle = CreateWindowExW(exStyle, 1233 _GLFW_WNDCLASSNAME, 1234 wideTitle, 1235 style, 1236 xpos, ypos, 1237 fullWidth, fullHeight, 1238 NULL, // No parent window 1239 NULL, // No window menu 1240 GetModuleHandleW(NULL), 1241 NULL); 1242 1243 free(wideTitle); 1244 1245 if (!window->win32.handle) 1246 { 1247 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, 1248 "Win32: Failed to create window"); 1249 return GLFW_FALSE; 1250 } 1251 1252 SetPropW(window->win32.handle, L"GLFW", window); 1253 1254 if (IsWindows7OrGreater()) 1255 { 1256 ChangeWindowMessageFilterEx(window->win32.handle, 1257 WM_DROPFILES, MSGFLT_ALLOW, NULL); 1258 ChangeWindowMessageFilterEx(window->win32.handle, 1259 WM_COPYDATA, MSGFLT_ALLOW, NULL); 1260 ChangeWindowMessageFilterEx(window->win32.handle, 1261 WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL); 1262 } 1263 1264 window->win32.scaleToMonitor = wndconfig->scaleToMonitor; 1265 window->win32.keymenu = wndconfig->win32.keymenu; 1266 1267 // Adjust window rect to account for DPI scaling of the window frame and 1268 // (if enabled) DPI scaling of the content area 1269 // This cannot be done until we know what monitor the window was placed on 1270 if (!window->monitor) 1271 { 1272 RECT rect = { 0, 0, wndconfig->width, wndconfig->height }; 1273 WINDOWPLACEMENT wp = { sizeof(wp) }; 1274 1275 if (wndconfig->scaleToMonitor) 1276 { 1277 float xscale, yscale; 1278 _glfwPlatformGetWindowContentScale(window, &xscale, &yscale); 1279 rect.right = (int) (rect.right * xscale); 1280 rect.bottom = (int) (rect.bottom * yscale); 1281 } 1282 1283 ClientToScreen(window->win32.handle, (POINT*) &rect.left); 1284 ClientToScreen(window->win32.handle, (POINT*) &rect.right); 1285 1286 if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) 1287 { 1288 AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, 1289 GetDpiForWindow(window->win32.handle)); 1290 } 1291 else 1292 AdjustWindowRectEx(&rect, style, FALSE, exStyle); 1293 1294 // Only update the restored window rect as the window may be maximized 1295 GetWindowPlacement(window->win32.handle, &wp); 1296 wp.rcNormalPosition = rect; 1297 wp.showCmd = SW_HIDE; 1298 SetWindowPlacement(window->win32.handle, &wp); 1299 } 1300 1301 DragAcceptFiles(window->win32.handle, TRUE); 1302 1303 if (fbconfig->transparent) 1304 { 1305 updateFramebufferTransparency(window); 1306 window->win32.transparent = GLFW_TRUE; 1307 } 1308 1309 return GLFW_TRUE; 1310 } 1311 1312 1313 ////////////////////////////////////////////////////////////////////////// 1314 ////// GLFW internal API ////// 1315 ////////////////////////////////////////////////////////////////////////// 1316 1317 // Registers the GLFW window class 1318 // 1319 GLFWbool _glfwRegisterWindowClassWin32(void) 1320 { 1321 WNDCLASSEXW wc; 1322 1323 ZeroMemory(&wc, sizeof(wc)); 1324 wc.cbSize = sizeof(wc); 1325 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 1326 wc.lpfnWndProc = (WNDPROC) windowProc; 1327 wc.hInstance = GetModuleHandleW(NULL); 1328 wc.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); 1329 wc.lpszClassName = _GLFW_WNDCLASSNAME; 1330 1331 // Load user-provided icon if available 1332 wc.hIcon = (HICON)LoadImageW(GetModuleHandleW(NULL), 1333 L"GLFW_ICON", IMAGE_ICON, 1334 0, 0, LR_DEFAULTSIZE | LR_SHARED); 1335 if (!wc.hIcon) 1336 { 1337 // No user-provided icon found, load default icon 1338 wc.hIcon = (HICON)LoadImageW(NULL, 1339 (LPCWSTR)IDI_APPLICATION, IMAGE_ICON, 1340 0, 0, LR_DEFAULTSIZE | LR_SHARED); 1341 } 1342 1343 if (!RegisterClassExW(&wc)) 1344 { 1345 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, 1346 "Win32: Failed to register window class"); 1347 return GLFW_FALSE; 1348 } 1349 1350 return GLFW_TRUE; 1351 } 1352 1353 // Unregisters the GLFW window class 1354 // 1355 void _glfwUnregisterWindowClassWin32(void) 1356 { 1357 UnregisterClassW(_GLFW_WNDCLASSNAME, GetModuleHandleW(NULL)); 1358 } 1359 1360 1361 ////////////////////////////////////////////////////////////////////////// 1362 ////// GLFW platform API ////// 1363 ////////////////////////////////////////////////////////////////////////// 1364 1365 int _glfwPlatformCreateWindow(_GLFWwindow* window, 1366 const _GLFWwndconfig* wndconfig, 1367 const _GLFWctxconfig* ctxconfig, 1368 const _GLFWfbconfig* fbconfig) 1369 { 1370 if (!createNativeWindow(window, wndconfig, fbconfig)) 1371 return GLFW_FALSE; 1372 1373 if (ctxconfig->client != GLFW_NO_API) 1374 { 1375 if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) 1376 { 1377 if (!_glfwInitWGL()) 1378 return GLFW_FALSE; 1379 if (!_glfwCreateContextWGL(window, ctxconfig, fbconfig)) 1380 return GLFW_FALSE; 1381 } 1382 else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) 1383 { 1384 if (!_glfwInitEGL()) 1385 return GLFW_FALSE; 1386 if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) 1387 return GLFW_FALSE; 1388 } 1389 else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) 1390 { 1391 if (!_glfwInitOSMesa()) 1392 return GLFW_FALSE; 1393 if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) 1394 return GLFW_FALSE; 1395 } 1396 } 1397 1398 if (window->monitor) 1399 { 1400 _glfwPlatformShowWindow(window); 1401 _glfwPlatformFocusWindow(window); 1402 acquireMonitor(window); 1403 fitToMonitor(window); 1404 } 1405 1406 return GLFW_TRUE; 1407 } 1408 1409 void _glfwPlatformDestroyWindow(_GLFWwindow* window) 1410 { 1411 if (window->monitor) 1412 releaseMonitor(window); 1413 1414 if (window->context.destroy) 1415 window->context.destroy(window); 1416 1417 if (_glfw.win32.disabledCursorWindow == window) 1418 _glfw.win32.disabledCursorWindow = NULL; 1419 1420 if (window->win32.handle) 1421 { 1422 RemovePropW(window->win32.handle, L"GLFW"); 1423 DestroyWindow(window->win32.handle); 1424 window->win32.handle = NULL; 1425 } 1426 1427 if (window->win32.bigIcon) 1428 DestroyIcon(window->win32.bigIcon); 1429 1430 if (window->win32.smallIcon) 1431 DestroyIcon(window->win32.smallIcon); 1432 } 1433 1434 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) 1435 { 1436 WCHAR* wideTitle = _glfwCreateWideStringFromUTF8Win32(title); 1437 if (!wideTitle) 1438 return; 1439 1440 SetWindowTextW(window->win32.handle, wideTitle); 1441 free(wideTitle); 1442 } 1443 1444 void _glfwPlatformSetWindowIcon(_GLFWwindow* window, 1445 int count, const GLFWimage* images) 1446 { 1447 HICON bigIcon = NULL, smallIcon = NULL; 1448 1449 if (count) 1450 { 1451 const GLFWimage* bigImage = chooseImage(count, images, 1452 GetSystemMetrics(SM_CXICON), 1453 GetSystemMetrics(SM_CYICON)); 1454 const GLFWimage* smallImage = chooseImage(count, images, 1455 GetSystemMetrics(SM_CXSMICON), 1456 GetSystemMetrics(SM_CYSMICON)); 1457 1458 bigIcon = createIcon(bigImage, 0, 0, GLFW_TRUE); 1459 smallIcon = createIcon(smallImage, 0, 0, GLFW_TRUE); 1460 } 1461 else 1462 { 1463 bigIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICON); 1464 smallIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICONSM); 1465 } 1466 1467 SendMessage(window->win32.handle, WM_SETICON, ICON_BIG, (LPARAM) bigIcon); 1468 SendMessage(window->win32.handle, WM_SETICON, ICON_SMALL, (LPARAM) smallIcon); 1469 1470 if (window->win32.bigIcon) 1471 DestroyIcon(window->win32.bigIcon); 1472 1473 if (window->win32.smallIcon) 1474 DestroyIcon(window->win32.smallIcon); 1475 1476 if (count) 1477 { 1478 window->win32.bigIcon = bigIcon; 1479 window->win32.smallIcon = smallIcon; 1480 } 1481 } 1482 1483 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) 1484 { 1485 POINT pos = { 0, 0 }; 1486 ClientToScreen(window->win32.handle, &pos); 1487 1488 if (xpos) 1489 *xpos = pos.x; 1490 if (ypos) 1491 *ypos = pos.y; 1492 } 1493 1494 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) 1495 { 1496 RECT rect = { xpos, ypos, xpos, ypos }; 1497 1498 if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) 1499 { 1500 AdjustWindowRectExForDpi(&rect, getWindowStyle(window), 1501 FALSE, getWindowExStyle(window), 1502 GetDpiForWindow(window->win32.handle)); 1503 } 1504 else 1505 { 1506 AdjustWindowRectEx(&rect, getWindowStyle(window), 1507 FALSE, getWindowExStyle(window)); 1508 } 1509 1510 SetWindowPos(window->win32.handle, NULL, rect.left, rect.top, 0, 0, 1511 SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE); 1512 } 1513 1514 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) 1515 { 1516 RECT area; 1517 GetClientRect(window->win32.handle, &area); 1518 1519 if (width) 1520 *width = area.right; 1521 if (height) 1522 *height = area.bottom; 1523 } 1524 1525 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) 1526 { 1527 if (window->monitor) 1528 { 1529 if (window->monitor->window == window) 1530 { 1531 acquireMonitor(window); 1532 fitToMonitor(window); 1533 } 1534 } 1535 else 1536 { 1537 RECT rect = { 0, 0, width, height }; 1538 1539 if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) 1540 { 1541 AdjustWindowRectExForDpi(&rect, getWindowStyle(window), 1542 FALSE, getWindowExStyle(window), 1543 GetDpiForWindow(window->win32.handle)); 1544 } 1545 else 1546 { 1547 AdjustWindowRectEx(&rect, getWindowStyle(window), 1548 FALSE, getWindowExStyle(window)); 1549 } 1550 1551 SetWindowPos(window->win32.handle, HWND_TOP, 1552 0, 0, rect.right - rect.left, rect.bottom - rect.top, 1553 SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER); 1554 } 1555 } 1556 1557 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, 1558 int minwidth, int minheight, 1559 int maxwidth, int maxheight) 1560 { 1561 RECT area; 1562 1563 if ((minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) && 1564 (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)) 1565 { 1566 return; 1567 } 1568 1569 GetWindowRect(window->win32.handle, &area); 1570 MoveWindow(window->win32.handle, 1571 area.left, area.top, 1572 area.right - area.left, 1573 area.bottom - area.top, TRUE); 1574 } 1575 1576 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) 1577 { 1578 RECT area; 1579 1580 if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE) 1581 return; 1582 1583 GetWindowRect(window->win32.handle, &area); 1584 applyAspectRatio(window, WMSZ_BOTTOMRIGHT, &area); 1585 MoveWindow(window->win32.handle, 1586 area.left, area.top, 1587 area.right - area.left, 1588 area.bottom - area.top, TRUE); 1589 } 1590 1591 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) 1592 { 1593 _glfwPlatformGetWindowSize(window, width, height); 1594 } 1595 1596 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, 1597 int* left, int* top, 1598 int* right, int* bottom) 1599 { 1600 RECT rect; 1601 int width, height; 1602 1603 _glfwPlatformGetWindowSize(window, &width, &height); 1604 SetRect(&rect, 0, 0, width, height); 1605 1606 if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) 1607 { 1608 AdjustWindowRectExForDpi(&rect, getWindowStyle(window), 1609 FALSE, getWindowExStyle(window), 1610 GetDpiForWindow(window->win32.handle)); 1611 } 1612 else 1613 { 1614 AdjustWindowRectEx(&rect, getWindowStyle(window), 1615 FALSE, getWindowExStyle(window)); 1616 } 1617 1618 if (left) 1619 *left = -rect.left; 1620 if (top) 1621 *top = -rect.top; 1622 if (right) 1623 *right = rect.right - width; 1624 if (bottom) 1625 *bottom = rect.bottom - height; 1626 } 1627 1628 void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, 1629 float* xscale, float* yscale) 1630 { 1631 const HANDLE handle = MonitorFromWindow(window->win32.handle, 1632 MONITOR_DEFAULTTONEAREST); 1633 _glfwGetMonitorContentScaleWin32((HMONITOR)handle, xscale, yscale); 1634 } 1635 1636 void _glfwPlatformIconifyWindow(_GLFWwindow* window) 1637 { 1638 ShowWindow(window->win32.handle, SW_MINIMIZE); 1639 } 1640 1641 void _glfwPlatformRestoreWindow(_GLFWwindow* window) 1642 { 1643 ShowWindow(window->win32.handle, SW_RESTORE); 1644 } 1645 1646 void _glfwPlatformMaximizeWindow(_GLFWwindow* window) 1647 { 1648 ShowWindow(window->win32.handle, SW_MAXIMIZE); 1649 } 1650 1651 void _glfwPlatformShowWindow(_GLFWwindow* window) 1652 { 1653 ShowWindow(window->win32.handle, SW_SHOWNA); 1654 } 1655 1656 void _glfwPlatformHideWindow(_GLFWwindow* window) 1657 { 1658 ShowWindow(window->win32.handle, SW_HIDE); 1659 } 1660 1661 void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) 1662 { 1663 FlashWindow(window->win32.handle, TRUE); 1664 } 1665 1666 void _glfwPlatformFocusWindow(_GLFWwindow* window) 1667 { 1668 BringWindowToTop(window->win32.handle); 1669 SetForegroundWindow(window->win32.handle); 1670 SetFocus(window->win32.handle); 1671 } 1672 1673 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, 1674 _GLFWmonitor* monitor, 1675 int xpos, int ypos, 1676 int width, int height, 1677 int refreshRate) 1678 { 1679 if (window->monitor == monitor) 1680 { 1681 if (monitor) 1682 { 1683 if (monitor->window == window) 1684 { 1685 acquireMonitor(window); 1686 fitToMonitor(window); 1687 } 1688 } 1689 else 1690 { 1691 RECT rect = { xpos, ypos, xpos + width, ypos + height }; 1692 1693 if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) 1694 { 1695 AdjustWindowRectExForDpi(&rect, getWindowStyle(window), 1696 FALSE, getWindowExStyle(window), 1697 GetDpiForWindow(window->win32.handle)); 1698 } 1699 else 1700 { 1701 AdjustWindowRectEx(&rect, getWindowStyle(window), 1702 FALSE, getWindowExStyle(window)); 1703 } 1704 1705 SetWindowPos(window->win32.handle, HWND_TOP, 1706 rect.left, rect.top, 1707 rect.right - rect.left, rect.bottom - rect.top, 1708 SWP_NOCOPYBITS | SWP_NOACTIVATE | SWP_NOZORDER); 1709 } 1710 1711 return; 1712 } 1713 1714 if (window->monitor) 1715 releaseMonitor(window); 1716 1717 _glfwInputWindowMonitor(window, monitor); 1718 1719 if (window->monitor) 1720 { 1721 MONITORINFO mi = { sizeof(mi) }; 1722 UINT flags = SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOCOPYBITS; 1723 1724 if (window->decorated) 1725 { 1726 DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); 1727 style &= ~WS_OVERLAPPEDWINDOW; 1728 style |= getWindowStyle(window); 1729 SetWindowLongW(window->win32.handle, GWL_STYLE, style); 1730 flags |= SWP_FRAMECHANGED; 1731 } 1732 1733 acquireMonitor(window); 1734 1735 GetMonitorInfo(window->monitor->win32.handle, &mi); 1736 SetWindowPos(window->win32.handle, HWND_TOPMOST, 1737 mi.rcMonitor.left, 1738 mi.rcMonitor.top, 1739 mi.rcMonitor.right - mi.rcMonitor.left, 1740 mi.rcMonitor.bottom - mi.rcMonitor.top, 1741 flags); 1742 } 1743 else 1744 { 1745 HWND after; 1746 RECT rect = { xpos, ypos, xpos + width, ypos + height }; 1747 DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); 1748 UINT flags = SWP_NOACTIVATE | SWP_NOCOPYBITS; 1749 1750 if (window->decorated) 1751 { 1752 style &= ~WS_POPUP; 1753 style |= getWindowStyle(window); 1754 SetWindowLongW(window->win32.handle, GWL_STYLE, style); 1755 1756 flags |= SWP_FRAMECHANGED; 1757 } 1758 1759 if (window->floating) 1760 after = HWND_TOPMOST; 1761 else 1762 after = HWND_NOTOPMOST; 1763 1764 if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) 1765 { 1766 AdjustWindowRectExForDpi(&rect, getWindowStyle(window), 1767 FALSE, getWindowExStyle(window), 1768 GetDpiForWindow(window->win32.handle)); 1769 } 1770 else 1771 { 1772 AdjustWindowRectEx(&rect, getWindowStyle(window), 1773 FALSE, getWindowExStyle(window)); 1774 } 1775 1776 SetWindowPos(window->win32.handle, after, 1777 rect.left, rect.top, 1778 rect.right - rect.left, rect.bottom - rect.top, 1779 flags); 1780 } 1781 } 1782 1783 int _glfwPlatformWindowFocused(_GLFWwindow* window) 1784 { 1785 return window->win32.handle == GetActiveWindow(); 1786 } 1787 1788 int _glfwPlatformWindowIconified(_GLFWwindow* window) 1789 { 1790 return IsIconic(window->win32.handle); 1791 } 1792 1793 int _glfwPlatformWindowVisible(_GLFWwindow* window) 1794 { 1795 return IsWindowVisible(window->win32.handle); 1796 } 1797 1798 int _glfwPlatformWindowMaximized(_GLFWwindow* window) 1799 { 1800 return IsZoomed(window->win32.handle); 1801 } 1802 1803 int _glfwPlatformWindowHovered(_GLFWwindow* window) 1804 { 1805 return cursorInContentArea(window); 1806 } 1807 1808 int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) 1809 { 1810 BOOL enabled; 1811 1812 if (!window->win32.transparent) 1813 return GLFW_FALSE; 1814 1815 if (!IsWindowsVistaOrGreater()) 1816 return GLFW_FALSE; 1817 1818 return SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled; 1819 } 1820 1821 void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) 1822 { 1823 updateWindowStyles(window); 1824 } 1825 1826 void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) 1827 { 1828 updateWindowStyles(window); 1829 } 1830 1831 void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) 1832 { 1833 const HWND after = enabled ? HWND_TOPMOST : HWND_NOTOPMOST; 1834 SetWindowPos(window->win32.handle, after, 0, 0, 0, 0, 1835 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); 1836 } 1837 1838 float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) 1839 { 1840 BYTE alpha; 1841 DWORD flags; 1842 1843 if ((GetWindowLongW(window->win32.handle, GWL_EXSTYLE) & WS_EX_LAYERED) && 1844 GetLayeredWindowAttributes(window->win32.handle, NULL, &alpha, &flags)) 1845 { 1846 if (flags & LWA_ALPHA) 1847 return alpha / 255.f; 1848 } 1849 1850 return 1.f; 1851 } 1852 1853 void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) 1854 { 1855 if (opacity < 1.f) 1856 { 1857 const BYTE alpha = (BYTE) (255 * opacity); 1858 DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); 1859 style |= WS_EX_LAYERED; 1860 SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style); 1861 SetLayeredWindowAttributes(window->win32.handle, 0, alpha, LWA_ALPHA); 1862 } 1863 else 1864 { 1865 DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); 1866 style &= ~WS_EX_LAYERED; 1867 SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style); 1868 } 1869 } 1870 1871 void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) 1872 { 1873 if (_glfw.win32.disabledCursorWindow != window) 1874 return; 1875 1876 if (enabled) 1877 enableRawMouseMotion(window); 1878 else 1879 disableRawMouseMotion(window); 1880 } 1881 1882 GLFWbool _glfwPlatformRawMouseMotionSupported(void) 1883 { 1884 return GLFW_TRUE; 1885 } 1886 1887 void _glfwPlatformPollEvents(void) 1888 { 1889 MSG msg; 1890 HWND handle; 1891 _GLFWwindow* window; 1892 1893 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) 1894 { 1895 if (msg.message == WM_QUIT) 1896 { 1897 // NOTE: While GLFW does not itself post WM_QUIT, other processes 1898 // may post it to this one, for example Task Manager 1899 // HACK: Treat WM_QUIT as a close on all windows 1900 1901 window = _glfw.windowListHead; 1902 while (window) 1903 { 1904 _glfwInputWindowCloseRequest(window); 1905 window = window->next; 1906 } 1907 } 1908 else 1909 { 1910 TranslateMessage(&msg); 1911 DispatchMessageW(&msg); 1912 } 1913 } 1914 1915 // HACK: Release modifier keys that the system did not emit KEYUP for 1916 // NOTE: Shift keys on Windows tend to "stick" when both are pressed as 1917 // no key up message is generated by the first key release 1918 // NOTE: Windows key is not reported as released by the Win+V hotkey 1919 // Other Win hotkeys are handled implicitly by _glfwInputWindowFocus 1920 // because they change the input focus 1921 // NOTE: The other half of this is in the WM_*KEY* handler in windowProc 1922 handle = GetActiveWindow(); 1923 if (handle) 1924 { 1925 window = (_GLFWwindow*)GetPropW(handle, L"GLFW"); 1926 if (window) 1927 { 1928 int i; 1929 const int keys[4][2] = 1930 { 1931 { VK_LSHIFT, GLFW_KEY_LEFT_SHIFT }, 1932 { VK_RSHIFT, GLFW_KEY_RIGHT_SHIFT }, 1933 { VK_LWIN, GLFW_KEY_LEFT_SUPER }, 1934 { VK_RWIN, GLFW_KEY_RIGHT_SUPER } 1935 }; 1936 1937 for (i = 0; i < 4; i++) 1938 { 1939 const int vk = keys[i][0]; 1940 const int key = keys[i][1]; 1941 const int scancode = _glfw.win32.scancodes[key]; 1942 1943 if ((GetKeyState(vk) & 0x8000)) 1944 continue; 1945 if (window->keys[key] != GLFW_PRESS) 1946 continue; 1947 1948 _glfwInputKey(window, key, scancode, GLFW_RELEASE, getKeyMods()); 1949 } 1950 } 1951 } 1952 1953 window = _glfw.win32.disabledCursorWindow; 1954 if (window) 1955 { 1956 int width, height; 1957 _glfwPlatformGetWindowSize(window, &width, &height); 1958 1959 // NOTE: Re-center the cursor only if it has moved since the last call, 1960 // to avoid breaking glfwWaitEvents with WM_MOUSEMOVE 1961 if (window->win32.lastCursorPosX != width / 2 || 1962 window->win32.lastCursorPosY != height / 2) 1963 { 1964 _glfwPlatformSetCursorPos(window, width / 2, height / 2); 1965 } 1966 } 1967 } 1968 1969 void _glfwPlatformWaitEvents(void) 1970 { 1971 WaitMessage(); 1972 1973 _glfwPlatformPollEvents(); 1974 } 1975 1976 void _glfwPlatformWaitEventsTimeout(double timeout) 1977 { 1978 MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD) (timeout * 1e3), QS_ALLEVENTS); 1979 1980 _glfwPlatformPollEvents(); 1981 } 1982 1983 void _glfwPlatformPostEmptyEvent(void) 1984 { 1985 PostMessage(_glfw.win32.helperWindowHandle, WM_NULL, 0, 0); 1986 } 1987 1988 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) 1989 { 1990 POINT pos; 1991 1992 if (GetCursorPos(&pos)) 1993 { 1994 ScreenToClient(window->win32.handle, &pos); 1995 1996 if (xpos) 1997 *xpos = pos.x; 1998 if (ypos) 1999 *ypos = pos.y; 2000 } 2001 } 2002 2003 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos) 2004 { 2005 POINT pos = { (int) xpos, (int) ypos }; 2006 2007 // Store the new position so it can be recognized later 2008 window->win32.lastCursorPosX = pos.x; 2009 window->win32.lastCursorPosY = pos.y; 2010 2011 ClientToScreen(window->win32.handle, &pos); 2012 SetCursorPos(pos.x, pos.y); 2013 } 2014 2015 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) 2016 { 2017 if (mode == GLFW_CURSOR_DISABLED) 2018 { 2019 if (_glfwPlatformWindowFocused(window)) 2020 disableCursor(window); 2021 } 2022 else if (_glfw.win32.disabledCursorWindow == window) 2023 enableCursor(window); 2024 else if (cursorInContentArea(window)) 2025 updateCursorImage(window); 2026 } 2027 2028 const char* _glfwPlatformGetScancodeName(int scancode) 2029 { 2030 if (scancode < 0 || scancode > (KF_EXTENDED | 0xff) || 2031 _glfw.win32.keycodes[scancode] == GLFW_KEY_UNKNOWN) 2032 { 2033 _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode"); 2034 return NULL; 2035 } 2036 2037 return _glfw.win32.keynames[_glfw.win32.keycodes[scancode]]; 2038 } 2039 2040 int _glfwPlatformGetKeyScancode(int key) 2041 { 2042 return _glfw.win32.scancodes[key]; 2043 } 2044 2045 int _glfwPlatformCreateCursor(_GLFWcursor* cursor, 2046 const GLFWimage* image, 2047 int xhot, int yhot) 2048 { 2049 cursor->win32.handle = (HCURSOR) createIcon(image, xhot, yhot, GLFW_FALSE); 2050 if (!cursor->win32.handle) 2051 return GLFW_FALSE; 2052 2053 return GLFW_TRUE; 2054 } 2055 2056 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) 2057 { 2058 int id = 0; 2059 2060 if (shape == GLFW_ARROW_CURSOR) 2061 id = OCR_NORMAL; 2062 else if (shape == GLFW_IBEAM_CURSOR) 2063 id = OCR_IBEAM; 2064 else if (shape == GLFW_CROSSHAIR_CURSOR) 2065 id = OCR_CROSS; 2066 else if (shape == GLFW_POINTING_HAND_CURSOR) 2067 id = OCR_HAND; 2068 else if (shape == GLFW_RESIZE_EW_CURSOR) 2069 id = OCR_SIZEWE; 2070 else if (shape == GLFW_RESIZE_NS_CURSOR) 2071 id = OCR_SIZENS; 2072 else if (shape == GLFW_RESIZE_NWSE_CURSOR) 2073 id = OCR_SIZENWSE; 2074 else if (shape == GLFW_RESIZE_NESW_CURSOR) 2075 id = OCR_SIZENESW; 2076 else if (shape == GLFW_RESIZE_ALL_CURSOR) 2077 id = OCR_SIZEALL; 2078 else if (shape == GLFW_NOT_ALLOWED_CURSOR) 2079 id = OCR_NO; 2080 else 2081 { 2082 _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Unknown standard cursor"); 2083 return GLFW_FALSE; 2084 } 2085 2086 cursor->win32.handle = (HCURSOR)LoadImageW(NULL, 2087 MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0, 2088 LR_DEFAULTSIZE | LR_SHARED); 2089 if (!cursor->win32.handle) 2090 { 2091 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, 2092 "Win32: Failed to create standard cursor"); 2093 return GLFW_FALSE; 2094 } 2095 2096 return GLFW_TRUE; 2097 } 2098 2099 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) 2100 { 2101 if (cursor->win32.handle) 2102 DestroyIcon((HICON) cursor->win32.handle); 2103 } 2104 2105 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) 2106 { 2107 if (cursorInContentArea(window)) 2108 updateCursorImage(window); 2109 } 2110 2111 void _glfwPlatformSetClipboardString(const char* string) 2112 { 2113 int characterCount; 2114 HANDLE object; 2115 WCHAR* buffer; 2116 2117 characterCount = MultiByteToWideChar(CP_UTF8, 0, string, -1, NULL, 0); 2118 if (!characterCount) 2119 return; 2120 2121 object = GlobalAlloc(GMEM_MOVEABLE, characterCount * sizeof(WCHAR)); 2122 if (!object) 2123 { 2124 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, 2125 "Win32: Failed to allocate global handle for clipboard"); 2126 return; 2127 } 2128 2129 buffer = (WCHAR*)GlobalLock(object); 2130 if (!buffer) 2131 { 2132 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, 2133 "Win32: Failed to lock global handle"); 2134 GlobalFree(object); 2135 return; 2136 } 2137 2138 MultiByteToWideChar(CP_UTF8, 0, string, -1, buffer, characterCount); 2139 GlobalUnlock(object); 2140 2141 if (!OpenClipboard(_glfw.win32.helperWindowHandle)) 2142 { 2143 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, 2144 "Win32: Failed to open clipboard"); 2145 GlobalFree(object); 2146 return; 2147 } 2148 2149 EmptyClipboard(); 2150 SetClipboardData(CF_UNICODETEXT, object); 2151 CloseClipboard(); 2152 } 2153 2154 const char* _glfwPlatformGetClipboardString(void) 2155 { 2156 HANDLE object; 2157 WCHAR* buffer; 2158 2159 if (!OpenClipboard(_glfw.win32.helperWindowHandle)) 2160 { 2161 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, 2162 "Win32: Failed to open clipboard"); 2163 return NULL; 2164 } 2165 2166 object = GetClipboardData(CF_UNICODETEXT); 2167 if (!object) 2168 { 2169 _glfwInputErrorWin32(GLFW_FORMAT_UNAVAILABLE, 2170 "Win32: Failed to convert clipboard to string"); 2171 CloseClipboard(); 2172 return NULL; 2173 } 2174 2175 buffer = (WCHAR*)GlobalLock(object); 2176 if (!buffer) 2177 { 2178 _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, 2179 "Win32: Failed to lock global handle"); 2180 CloseClipboard(); 2181 return NULL; 2182 } 2183 2184 free(_glfw.win32.clipboardString); 2185 _glfw.win32.clipboardString = _glfwCreateUTF8FromWideStringWin32(buffer); 2186 2187 GlobalUnlock(object); 2188 CloseClipboard(); 2189 2190 return _glfw.win32.clipboardString; 2191 } 2192 2193 void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) 2194 { 2195 if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_win32_surface) 2196 return; 2197 2198 extensions[0] = (char*)"VK_KHR_surface"; 2199 extensions[1] = (char*)"VK_KHR_win32_surface"; 2200 } 2201 2202 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, 2203 VkPhysicalDevice device, 2204 uint32_t queuefamily) 2205 { 2206 PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR 2207 vkGetPhysicalDeviceWin32PresentationSupportKHR = 2208 (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR) 2209 vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR"); 2210 if (!vkGetPhysicalDeviceWin32PresentationSupportKHR) 2211 { 2212 _glfwInputError(GLFW_API_UNAVAILABLE, 2213 "Win32: Vulkan instance missing VK_KHR_win32_surface extension"); 2214 return GLFW_FALSE; 2215 } 2216 2217 return vkGetPhysicalDeviceWin32PresentationSupportKHR(device, queuefamily); 2218 } 2219 2220 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, 2221 _GLFWwindow* window, 2222 const VkAllocationCallbacks* allocator, 2223 VkSurfaceKHR* surface) 2224 { 2225 VkResult err; 2226 VkWin32SurfaceCreateInfoKHR sci; 2227 PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR; 2228 2229 vkCreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR) 2230 vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR"); 2231 if (!vkCreateWin32SurfaceKHR) 2232 { 2233 _glfwInputError(GLFW_API_UNAVAILABLE, 2234 "Win32: Vulkan instance missing VK_KHR_win32_surface extension"); 2235 return VK_ERROR_EXTENSION_NOT_PRESENT; 2236 } 2237 2238 memset(&sci, 0, sizeof(sci)); 2239 sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; 2240 sci.hinstance = GetModuleHandle(NULL); 2241 sci.hwnd = window->win32.handle; 2242 2243 err = vkCreateWin32SurfaceKHR(instance, &sci, allocator, surface); 2244 if (err) 2245 { 2246 _glfwInputError(GLFW_PLATFORM_ERROR, 2247 "Win32: Failed to create Vulkan surface: %s", 2248 _glfwGetVulkanResultString(err)); 2249 } 2250 2251 return err; 2252 } 2253 2254 2255 ////////////////////////////////////////////////////////////////////////// 2256 ////// GLFW native API ////// 2257 ////////////////////////////////////////////////////////////////////////// 2258 2259 GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle) 2260 { 2261 _GLFWwindow* window = (_GLFWwindow*) handle; 2262 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 2263 return window->win32.handle; 2264 } 2265