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