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