SDL_windowsevents.c (51232B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20 */ 21 #include "../../SDL_internal.h" 22 23 #if SDL_VIDEO_DRIVER_WINDOWS 24 25 #include "SDL_windowsvideo.h" 26 #include "SDL_windowsshape.h" 27 #include "SDL_system.h" 28 #include "SDL_syswm.h" 29 #include "SDL_timer.h" 30 #include "SDL_vkeys.h" 31 #include "SDL_hints.h" 32 #include "../../events/SDL_events_c.h" 33 #include "../../events/SDL_touch_c.h" 34 #include "../../events/scancodes_windows.h" 35 #include "SDL_hints.h" 36 37 /* Dropfile support */ 38 #include <shellapi.h> 39 40 /* For GET_X_LPARAM, GET_Y_LPARAM. */ 41 #include <windowsx.h> 42 43 /* #define WMMSG_DEBUG */ 44 #ifdef WMMSG_DEBUG 45 #include <stdio.h> 46 #include "wmmsg.h" 47 #endif 48 49 /* Masks for processing the windows KEYDOWN and KEYUP messages */ 50 #define REPEATED_KEYMASK (1<<30) 51 #define EXTENDED_KEYMASK (1<<24) 52 53 #define VK_ENTER 10 /* Keypad Enter ... no VKEY defined? */ 54 #ifndef VK_OEM_NEC_EQUAL 55 #define VK_OEM_NEC_EQUAL 0x92 56 #endif 57 58 /* Make sure XBUTTON stuff is defined that isn't in older Platform SDKs... */ 59 #ifndef WM_XBUTTONDOWN 60 #define WM_XBUTTONDOWN 0x020B 61 #endif 62 #ifndef WM_XBUTTONUP 63 #define WM_XBUTTONUP 0x020C 64 #endif 65 #ifndef GET_XBUTTON_WPARAM 66 #define GET_XBUTTON_WPARAM(w) (HIWORD(w)) 67 #endif 68 #ifndef WM_INPUT 69 #define WM_INPUT 0x00ff 70 #endif 71 #ifndef WM_TOUCH 72 #define WM_TOUCH 0x0240 73 #endif 74 #ifndef WM_MOUSEHWHEEL 75 #define WM_MOUSEHWHEEL 0x020E 76 #endif 77 #ifndef WM_POINTERUPDATE 78 #define WM_POINTERUPDATE 0x0245 79 #endif 80 #ifndef WM_UNICHAR 81 #define WM_UNICHAR 0x0109 82 #endif 83 84 static SDL_Scancode 85 VKeytoScancodeFallback(WPARAM vkey) 86 { 87 switch (vkey) { 88 case VK_LEFT: return SDL_SCANCODE_LEFT; 89 case VK_UP: return SDL_SCANCODE_UP; 90 case VK_RIGHT: return SDL_SCANCODE_RIGHT; 91 case VK_DOWN: return SDL_SCANCODE_DOWN; 92 93 default: return SDL_SCANCODE_UNKNOWN; 94 } 95 } 96 97 static SDL_Scancode 98 VKeytoScancode(WPARAM vkey) 99 { 100 switch (vkey) { 101 case VK_MODECHANGE: return SDL_SCANCODE_MODE; 102 case VK_SELECT: return SDL_SCANCODE_SELECT; 103 case VK_EXECUTE: return SDL_SCANCODE_EXECUTE; 104 case VK_HELP: return SDL_SCANCODE_HELP; 105 case VK_PAUSE: return SDL_SCANCODE_PAUSE; 106 case VK_NUMLOCK: return SDL_SCANCODE_NUMLOCKCLEAR; 107 108 case VK_F13: return SDL_SCANCODE_F13; 109 case VK_F14: return SDL_SCANCODE_F14; 110 case VK_F15: return SDL_SCANCODE_F15; 111 case VK_F16: return SDL_SCANCODE_F16; 112 case VK_F17: return SDL_SCANCODE_F17; 113 case VK_F18: return SDL_SCANCODE_F18; 114 case VK_F19: return SDL_SCANCODE_F19; 115 case VK_F20: return SDL_SCANCODE_F20; 116 case VK_F21: return SDL_SCANCODE_F21; 117 case VK_F22: return SDL_SCANCODE_F22; 118 case VK_F23: return SDL_SCANCODE_F23; 119 case VK_F24: return SDL_SCANCODE_F24; 120 121 case VK_OEM_NEC_EQUAL: return SDL_SCANCODE_KP_EQUALS; 122 case VK_BROWSER_BACK: return SDL_SCANCODE_AC_BACK; 123 case VK_BROWSER_FORWARD: return SDL_SCANCODE_AC_FORWARD; 124 case VK_BROWSER_REFRESH: return SDL_SCANCODE_AC_REFRESH; 125 case VK_BROWSER_STOP: return SDL_SCANCODE_AC_STOP; 126 case VK_BROWSER_SEARCH: return SDL_SCANCODE_AC_SEARCH; 127 case VK_BROWSER_FAVORITES: return SDL_SCANCODE_AC_BOOKMARKS; 128 case VK_BROWSER_HOME: return SDL_SCANCODE_AC_HOME; 129 case VK_VOLUME_MUTE: return SDL_SCANCODE_AUDIOMUTE; 130 case VK_VOLUME_DOWN: return SDL_SCANCODE_VOLUMEDOWN; 131 case VK_VOLUME_UP: return SDL_SCANCODE_VOLUMEUP; 132 133 case VK_MEDIA_NEXT_TRACK: return SDL_SCANCODE_AUDIONEXT; 134 case VK_MEDIA_PREV_TRACK: return SDL_SCANCODE_AUDIOPREV; 135 case VK_MEDIA_STOP: return SDL_SCANCODE_AUDIOSTOP; 136 case VK_MEDIA_PLAY_PAUSE: return SDL_SCANCODE_AUDIOPLAY; 137 case VK_LAUNCH_MAIL: return SDL_SCANCODE_MAIL; 138 case VK_LAUNCH_MEDIA_SELECT: return SDL_SCANCODE_MEDIASELECT; 139 140 case VK_OEM_102: return SDL_SCANCODE_NONUSBACKSLASH; 141 142 case VK_ATTN: return SDL_SCANCODE_SYSREQ; 143 case VK_CRSEL: return SDL_SCANCODE_CRSEL; 144 case VK_EXSEL: return SDL_SCANCODE_EXSEL; 145 case VK_OEM_CLEAR: return SDL_SCANCODE_CLEAR; 146 147 case VK_LAUNCH_APP1: return SDL_SCANCODE_APP1; 148 case VK_LAUNCH_APP2: return SDL_SCANCODE_APP2; 149 150 default: return SDL_SCANCODE_UNKNOWN; 151 } 152 } 153 154 static SDL_Scancode 155 WindowsScanCodeToSDLScanCode(LPARAM lParam, WPARAM wParam) 156 { 157 SDL_Scancode code; 158 int nScanCode = (lParam >> 16) & 0xFF; 159 SDL_bool bIsExtended = (lParam & (1 << 24)) != 0; 160 161 code = VKeytoScancode(wParam); 162 163 if (code == SDL_SCANCODE_UNKNOWN && nScanCode <= 127) { 164 code = windows_scancode_table[nScanCode]; 165 166 if (bIsExtended) { 167 switch (code) { 168 case SDL_SCANCODE_RETURN: 169 code = SDL_SCANCODE_KP_ENTER; 170 break; 171 case SDL_SCANCODE_LALT: 172 code = SDL_SCANCODE_RALT; 173 break; 174 case SDL_SCANCODE_LCTRL: 175 code = SDL_SCANCODE_RCTRL; 176 break; 177 case SDL_SCANCODE_SLASH: 178 code = SDL_SCANCODE_KP_DIVIDE; 179 break; 180 case SDL_SCANCODE_CAPSLOCK: 181 code = SDL_SCANCODE_KP_PLUS; 182 break; 183 default: 184 break; 185 } 186 } else { 187 switch (code) { 188 case SDL_SCANCODE_HOME: 189 code = SDL_SCANCODE_KP_7; 190 break; 191 case SDL_SCANCODE_UP: 192 code = SDL_SCANCODE_KP_8; 193 break; 194 case SDL_SCANCODE_PAGEUP: 195 code = SDL_SCANCODE_KP_9; 196 break; 197 case SDL_SCANCODE_LEFT: 198 code = SDL_SCANCODE_KP_4; 199 break; 200 case SDL_SCANCODE_RIGHT: 201 code = SDL_SCANCODE_KP_6; 202 break; 203 case SDL_SCANCODE_END: 204 code = SDL_SCANCODE_KP_1; 205 break; 206 case SDL_SCANCODE_DOWN: 207 code = SDL_SCANCODE_KP_2; 208 break; 209 case SDL_SCANCODE_PAGEDOWN: 210 code = SDL_SCANCODE_KP_3; 211 break; 212 case SDL_SCANCODE_INSERT: 213 code = SDL_SCANCODE_KP_0; 214 break; 215 case SDL_SCANCODE_DELETE: 216 code = SDL_SCANCODE_KP_PERIOD; 217 break; 218 case SDL_SCANCODE_PRINTSCREEN: 219 code = SDL_SCANCODE_KP_MULTIPLY; 220 break; 221 default: 222 break; 223 } 224 } 225 } 226 227 /* The on-screen keyboard can generate VK_LEFT and VK_RIGHT events without a scancode 228 * value set, however we cannot simply map these in VKeytoScancode() or we will be 229 * incorrectly handling the arrow keys on the number pad when NumLock is disabled 230 * (which also generate VK_LEFT, VK_RIGHT, etc in that scenario). Instead, we'll only 231 * map them if none of the above special number pad mappings applied. */ 232 if (code == SDL_SCANCODE_UNKNOWN) { 233 code = VKeytoScancodeFallback(wParam); 234 } 235 236 return code; 237 } 238 239 static SDL_bool 240 WIN_ShouldIgnoreFocusClick() 241 { 242 return !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, SDL_FALSE); 243 } 244 245 static void 246 WIN_CheckWParamMouseButton(SDL_bool bwParamMousePressed, Uint32 mouseFlags, SDL_bool bSwapButtons, SDL_WindowData *data, Uint8 button, SDL_MouseID mouseID) 247 { 248 if (bSwapButtons) { 249 if (button == SDL_BUTTON_LEFT) { 250 button = SDL_BUTTON_RIGHT; 251 } 252 else if (button == SDL_BUTTON_RIGHT) { 253 button = SDL_BUTTON_LEFT; 254 } 255 } 256 257 if (data->focus_click_pending & SDL_BUTTON(button)) { 258 /* Ignore the button click for activation */ 259 if (!bwParamMousePressed) { 260 data->focus_click_pending &= ~SDL_BUTTON(button); 261 WIN_UpdateClipCursor(data->window); 262 } 263 if (WIN_ShouldIgnoreFocusClick()) { 264 return; 265 } 266 } 267 268 if (bwParamMousePressed && !(mouseFlags & SDL_BUTTON(button))) { 269 SDL_SendMouseButton(data->window, mouseID, SDL_PRESSED, button); 270 } else if (!bwParamMousePressed && (mouseFlags & SDL_BUTTON(button))) { 271 SDL_SendMouseButton(data->window, mouseID, SDL_RELEASED, button); 272 } 273 } 274 275 /* 276 * Some windows systems fail to send a WM_LBUTTONDOWN sometimes, but each mouse move contains the current button state also 277 * so this function reconciles our view of the world with the current buttons reported by windows 278 */ 279 static void 280 WIN_CheckWParamMouseButtons(WPARAM wParam, SDL_WindowData *data, SDL_MouseID mouseID) 281 { 282 if (wParam != data->mouse_button_flags) { 283 Uint32 mouseFlags = SDL_GetMouseState(NULL, NULL); 284 285 /* WM_LBUTTONDOWN and friends handle button swapping for us. No need to check SM_SWAPBUTTON here. */ 286 WIN_CheckWParamMouseButton((wParam & MK_LBUTTON), mouseFlags, SDL_FALSE, data, SDL_BUTTON_LEFT, mouseID); 287 WIN_CheckWParamMouseButton((wParam & MK_MBUTTON), mouseFlags, SDL_FALSE, data, SDL_BUTTON_MIDDLE, mouseID); 288 WIN_CheckWParamMouseButton((wParam & MK_RBUTTON), mouseFlags, SDL_FALSE, data, SDL_BUTTON_RIGHT, mouseID); 289 WIN_CheckWParamMouseButton((wParam & MK_XBUTTON1), mouseFlags, SDL_FALSE, data, SDL_BUTTON_X1, mouseID); 290 WIN_CheckWParamMouseButton((wParam & MK_XBUTTON2), mouseFlags, SDL_FALSE, data, SDL_BUTTON_X2, mouseID); 291 292 data->mouse_button_flags = wParam; 293 } 294 } 295 296 static void 297 WIN_CheckRawMouseButtons(ULONG rawButtons, SDL_WindowData *data) 298 { 299 if (rawButtons != data->mouse_button_flags) { 300 Uint32 mouseFlags = SDL_GetMouseState(NULL, NULL); 301 SDL_bool swapButtons = GetSystemMetrics(SM_SWAPBUTTON) != 0; 302 if ((rawButtons & RI_MOUSE_BUTTON_1_DOWN)) 303 WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_1_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_LEFT, 0); 304 if ((rawButtons & RI_MOUSE_BUTTON_1_UP)) 305 WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_1_UP), mouseFlags, swapButtons, data, SDL_BUTTON_LEFT, 0); 306 if ((rawButtons & RI_MOUSE_BUTTON_2_DOWN)) 307 WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_2_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_RIGHT, 0); 308 if ((rawButtons & RI_MOUSE_BUTTON_2_UP)) 309 WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_2_UP), mouseFlags, swapButtons, data, SDL_BUTTON_RIGHT, 0); 310 if ((rawButtons & RI_MOUSE_BUTTON_3_DOWN)) 311 WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_3_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_MIDDLE, 0); 312 if ((rawButtons & RI_MOUSE_BUTTON_3_UP)) 313 WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_3_UP), mouseFlags, swapButtons, data, SDL_BUTTON_MIDDLE, 0); 314 if ((rawButtons & RI_MOUSE_BUTTON_4_DOWN)) 315 WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_4_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_X1, 0); 316 if ((rawButtons & RI_MOUSE_BUTTON_4_UP)) 317 WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_4_UP), mouseFlags, swapButtons, data, SDL_BUTTON_X1, 0); 318 if ((rawButtons & RI_MOUSE_BUTTON_5_DOWN)) 319 WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_5_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_X2, 0); 320 if ((rawButtons & RI_MOUSE_BUTTON_5_UP)) 321 WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_5_UP), mouseFlags, swapButtons, data, SDL_BUTTON_X2, 0); 322 data->mouse_button_flags = rawButtons; 323 } 324 } 325 326 static void 327 WIN_CheckAsyncMouseRelease(SDL_WindowData *data) 328 { 329 Uint32 mouseFlags; 330 SHORT keyState; 331 SDL_bool swapButtons; 332 333 /* mouse buttons may have changed state here, we need to resync them, 334 but we will get a WM_MOUSEMOVE right away which will fix things up if in non raw mode also 335 */ 336 mouseFlags = SDL_GetMouseState(NULL, NULL); 337 swapButtons = GetSystemMetrics(SM_SWAPBUTTON) != 0; 338 339 keyState = GetAsyncKeyState(VK_LBUTTON); 340 if (!(keyState & 0x8000)) { 341 WIN_CheckWParamMouseButton(SDL_FALSE, mouseFlags, swapButtons, data, SDL_BUTTON_LEFT, 0); 342 } 343 keyState = GetAsyncKeyState(VK_RBUTTON); 344 if (!(keyState & 0x8000)) { 345 WIN_CheckWParamMouseButton(SDL_FALSE, mouseFlags, swapButtons, data, SDL_BUTTON_RIGHT, 0); 346 } 347 keyState = GetAsyncKeyState(VK_MBUTTON); 348 if (!(keyState & 0x8000)) { 349 WIN_CheckWParamMouseButton(SDL_FALSE, mouseFlags, swapButtons, data, SDL_BUTTON_MIDDLE, 0); 350 } 351 keyState = GetAsyncKeyState(VK_XBUTTON1); 352 if (!(keyState & 0x8000)) { 353 WIN_CheckWParamMouseButton(SDL_FALSE, mouseFlags, swapButtons, data, SDL_BUTTON_X1, 0); 354 } 355 keyState = GetAsyncKeyState(VK_XBUTTON2); 356 if (!(keyState & 0x8000)) { 357 WIN_CheckWParamMouseButton(SDL_FALSE, mouseFlags, swapButtons, data, SDL_BUTTON_X2, 0); 358 } 359 data->mouse_button_flags = 0; 360 } 361 362 static BOOL 363 WIN_ConvertUTF32toUTF8(UINT32 codepoint, char * text) 364 { 365 if (codepoint <= 0x7F) { 366 text[0] = (char) codepoint; 367 text[1] = '\0'; 368 } else if (codepoint <= 0x7FF) { 369 text[0] = 0xC0 | (char) ((codepoint >> 6) & 0x1F); 370 text[1] = 0x80 | (char) (codepoint & 0x3F); 371 text[2] = '\0'; 372 } else if (codepoint <= 0xFFFF) { 373 text[0] = 0xE0 | (char) ((codepoint >> 12) & 0x0F); 374 text[1] = 0x80 | (char) ((codepoint >> 6) & 0x3F); 375 text[2] = 0x80 | (char) (codepoint & 0x3F); 376 text[3] = '\0'; 377 } else if (codepoint <= 0x10FFFF) { 378 text[0] = 0xF0 | (char) ((codepoint >> 18) & 0x0F); 379 text[1] = 0x80 | (char) ((codepoint >> 12) & 0x3F); 380 text[2] = 0x80 | (char) ((codepoint >> 6) & 0x3F); 381 text[3] = 0x80 | (char) (codepoint & 0x3F); 382 text[4] = '\0'; 383 } else { 384 return SDL_FALSE; 385 } 386 return SDL_TRUE; 387 } 388 389 static SDL_bool 390 ShouldGenerateWindowCloseOnAltF4(void) 391 { 392 return !SDL_GetHintBoolean(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, SDL_FALSE); 393 } 394 395 /* Win10 "Fall Creators Update" introduced the bug that SetCursorPos() (as used by SDL_WarpMouseInWindow()) 396 doesn't reliably generate WM_MOUSEMOVE events anymore (see #3931) which breaks relative mouse mode via warping. 397 This is used to implement a workaround.. */ 398 static SDL_bool isWin10FCUorNewer = SDL_FALSE; 399 400 /* We want to generate mouse events from mouse and pen, and touch events from touchscreens */ 401 #define MI_WP_SIGNATURE 0xFF515700 402 #define MI_WP_SIGNATURE_MASK 0xFFFFFF00 403 #define IsTouchEvent(dw) ((dw) & MI_WP_SIGNATURE_MASK) == MI_WP_SIGNATURE 404 405 typedef enum 406 { 407 SDL_MOUSE_EVENT_SOURCE_UNKNOWN, 408 SDL_MOUSE_EVENT_SOURCE_MOUSE, 409 SDL_MOUSE_EVENT_SOURCE_TOUCH, 410 SDL_MOUSE_EVENT_SOURCE_PEN, 411 } SDL_MOUSE_EVENT_SOURCE; 412 413 static SDL_MOUSE_EVENT_SOURCE GetMouseMessageSource() 414 { 415 LPARAM extrainfo = GetMessageExtraInfo(); 416 /* Mouse data (ignoring synthetic mouse events generated for touchscreens) */ 417 /* Versions below Vista will set the low 7 bits to the Mouse ID and don't use bit 7: 418 Check bits 8-32 for the signature (which will indicate a Tablet PC Pen or Touch Device). 419 Only check bit 7 when Vista and up(Cleared=Pen, Set=Touch(which we need to filter out)), 420 when the signature is set. The Mouse ID will be zero for an actual mouse. */ 421 if (IsTouchEvent(extrainfo)) { 422 if (extrainfo & 0x80) { 423 return SDL_MOUSE_EVENT_SOURCE_TOUCH; 424 } else { 425 return SDL_MOUSE_EVENT_SOURCE_PEN; 426 } 427 } 428 return SDL_MOUSE_EVENT_SOURCE_MOUSE; 429 } 430 431 LRESULT CALLBACK 432 WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 433 { 434 SDL_WindowData *data; 435 LRESULT returnCode = -1; 436 437 /* Send a SDL_SYSWMEVENT if the application wants them */ 438 if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) { 439 SDL_SysWMmsg wmmsg; 440 441 SDL_VERSION(&wmmsg.version); 442 wmmsg.subsystem = SDL_SYSWM_WINDOWS; 443 wmmsg.msg.win.hwnd = hwnd; 444 wmmsg.msg.win.msg = msg; 445 wmmsg.msg.win.wParam = wParam; 446 wmmsg.msg.win.lParam = lParam; 447 SDL_SendSysWMEvent(&wmmsg); 448 } 449 450 /* Get the window data for the window */ 451 data = (SDL_WindowData *) GetProp(hwnd, TEXT("SDL_WindowData")); 452 if (!data) { 453 return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam); 454 } 455 456 #ifdef WMMSG_DEBUG 457 { 458 char message[1024]; 459 if (msg > MAX_WMMSG) { 460 SDL_snprintf(message, sizeof(message), "Received windows message: %p UNKNOWN (%d) -- 0x%X, 0x%X\n", hwnd, msg, wParam, lParam); 461 } else { 462 SDL_snprintf(message, sizeof(message), "Received windows message: %p %s -- 0x%X, 0x%X\n", hwnd, wmtab[msg], wParam, lParam); 463 } 464 OutputDebugStringA(message); 465 } 466 #endif /* WMMSG_DEBUG */ 467 468 if (IME_HandleMessage(hwnd, msg, wParam, &lParam, data->videodata)) 469 return 0; 470 471 switch (msg) { 472 473 case WM_SHOWWINDOW: 474 { 475 if (wParam) { 476 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0); 477 } else { 478 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0); 479 } 480 } 481 break; 482 483 case WM_NCACTIVATE: 484 { 485 /* Don't immediately clip the cursor in case we're clicking minimize/maximize buttons */ 486 data->skip_update_clipcursor = SDL_TRUE; 487 } 488 break; 489 490 case WM_ACTIVATE: 491 { 492 POINT cursorPos; 493 BOOL minimized; 494 495 minimized = HIWORD(wParam); 496 if (!minimized && (LOWORD(wParam) != WA_INACTIVE)) { 497 /* Don't mark the window as shown if it's activated before being shown */ 498 if (!IsWindowVisible(hwnd)) { 499 break; 500 } 501 if (LOWORD(wParam) == WA_CLICKACTIVE) { 502 SDL_bool swapButtons = GetSystemMetrics(SM_SWAPBUTTON) != 0; 503 if (GetAsyncKeyState(VK_LBUTTON)) { 504 data->focus_click_pending |= !swapButtons ? SDL_BUTTON_LMASK : SDL_BUTTON_RMASK; 505 } 506 if (GetAsyncKeyState(VK_RBUTTON)) { 507 data->focus_click_pending |= !swapButtons ? SDL_BUTTON_RMASK : SDL_BUTTON_LMASK; 508 } 509 if (GetAsyncKeyState(VK_MBUTTON)) { 510 data->focus_click_pending |= SDL_BUTTON_MMASK; 511 } 512 if (GetAsyncKeyState(VK_XBUTTON1)) { 513 data->focus_click_pending |= SDL_BUTTON_X1MASK; 514 } 515 if (GetAsyncKeyState(VK_XBUTTON2)) { 516 data->focus_click_pending |= SDL_BUTTON_X2MASK; 517 } 518 } 519 520 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0); 521 if (SDL_GetKeyboardFocus() != data->window) { 522 SDL_SetKeyboardFocus(data->window); 523 } 524 525 GetCursorPos(&cursorPos); 526 ScreenToClient(hwnd, &cursorPos); 527 SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y); 528 529 WIN_CheckAsyncMouseRelease(data); 530 WIN_UpdateClipCursor(data->window); 531 532 /* 533 * FIXME: Update keyboard state 534 */ 535 WIN_CheckClipboardUpdate(data->videodata); 536 537 SDL_ToggleModState(KMOD_CAPS, (GetKeyState(VK_CAPITAL) & 0x0001) != 0); 538 SDL_ToggleModState(KMOD_NUM, (GetKeyState(VK_NUMLOCK) & 0x0001) != 0); 539 } else { 540 RECT rect; 541 542 data->in_window_deactivation = SDL_TRUE; 543 544 if (SDL_GetKeyboardFocus() == data->window) { 545 SDL_SetKeyboardFocus(NULL); 546 WIN_ResetDeadKeys(); 547 } 548 549 if (GetClipCursor(&rect) && SDL_memcmp(&rect, &data->cursor_clipped_rect, sizeof(rect)) == 0) { 550 ClipCursor(NULL); 551 SDL_zero(data->cursor_clipped_rect); 552 } 553 554 data->in_window_deactivation = SDL_FALSE; 555 } 556 } 557 returnCode = 0; 558 break; 559 560 case WM_POINTERUPDATE: 561 { 562 data->last_pointer_update = lParam; 563 break; 564 } 565 566 case WM_MOUSEMOVE: 567 { 568 SDL_Mouse *mouse = SDL_GetMouse(); 569 if (!mouse->relative_mode || mouse->relative_mode_warp) { 570 /* Only generate mouse events for real mouse */ 571 if (GetMouseMessageSource() != SDL_MOUSE_EVENT_SOURCE_TOUCH && 572 lParam != data->last_pointer_update) { 573 SDL_SendMouseMotion(data->window, 0, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 574 if (isWin10FCUorNewer && mouse->relative_mode_warp) { 575 /* To work around #3931, Win10 bug introduced in Fall Creators Update, where 576 SetCursorPos() (SDL_WarpMouseInWindow()) doesn't reliably generate mouse events anymore, 577 after each windows mouse event generate a fake event for the middle of the window 578 if relative_mode_warp is used */ 579 int center_x = 0, center_y = 0; 580 SDL_GetWindowSize(data->window, ¢er_x, ¢er_y); 581 center_x /= 2; 582 center_y /= 2; 583 SDL_SendMouseMotion(data->window, 0, 0, center_x, center_y); 584 } 585 } 586 } else { 587 /* We still need to update focus */ 588 SDL_SetMouseFocus(data->window); 589 } 590 } 591 /* don't break here, fall through to check the wParam like the button presses */ 592 case WM_LBUTTONUP: 593 case WM_RBUTTONUP: 594 case WM_MBUTTONUP: 595 case WM_XBUTTONUP: 596 case WM_LBUTTONDOWN: 597 case WM_LBUTTONDBLCLK: 598 case WM_RBUTTONDOWN: 599 case WM_RBUTTONDBLCLK: 600 case WM_MBUTTONDOWN: 601 case WM_MBUTTONDBLCLK: 602 case WM_XBUTTONDOWN: 603 case WM_XBUTTONDBLCLK: 604 { 605 SDL_Mouse *mouse = SDL_GetMouse(); 606 if (!mouse->relative_mode || mouse->relative_mode_warp) { 607 if (GetMouseMessageSource() != SDL_MOUSE_EVENT_SOURCE_TOUCH && 608 lParam != data->last_pointer_update) { 609 WIN_CheckWParamMouseButtons(wParam, data, 0); 610 } 611 } 612 } 613 break; 614 615 case WM_INPUT: 616 { 617 SDL_Mouse *mouse = SDL_GetMouse(); 618 HRAWINPUT hRawInput = (HRAWINPUT)lParam; 619 RAWINPUT inp; 620 UINT size = sizeof(inp); 621 const SDL_bool isRelative = mouse->relative_mode || mouse->relative_mode_warp; 622 const SDL_bool isCapture = ((data->window->flags & SDL_WINDOW_MOUSE_CAPTURE) != 0); 623 624 if (!isRelative || mouse->focus != data->window) { 625 if (!isCapture) { 626 break; 627 } 628 } 629 630 GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER)); 631 632 /* Mouse data (ignoring synthetic mouse events generated for touchscreens) */ 633 if (inp.header.dwType == RIM_TYPEMOUSE) { 634 if (GetMouseMessageSource() == SDL_MOUSE_EVENT_SOURCE_TOUCH || 635 (GetMessageExtraInfo() & 0x82) == 0x82) { 636 break; 637 } 638 if (isRelative) { 639 RAWMOUSE* rawmouse = &inp.data.mouse; 640 641 if ((rawmouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE) { 642 SDL_SendMouseMotion(data->window, 0, 1, (int)rawmouse->lLastX, (int)rawmouse->lLastY); 643 } else if (rawmouse->lLastX || rawmouse->lLastY) { 644 /* synthesize relative moves from the abs position */ 645 static SDL_Point lastMousePoint; 646 SDL_bool virtual_desktop = (rawmouse->usFlags & MOUSE_VIRTUAL_DESKTOP) ? SDL_TRUE : SDL_FALSE; 647 int w = GetSystemMetrics(virtual_desktop ? SM_CXVIRTUALSCREEN : SM_CXSCREEN); 648 int h = GetSystemMetrics(virtual_desktop ? SM_CYVIRTUALSCREEN : SM_CYSCREEN); 649 int x = (int)(((float)rawmouse->lLastX / 65535.0f) * w); 650 int y = (int)(((float)rawmouse->lLastY / 65535.0f) * h); 651 652 if (lastMousePoint.x == 0 && lastMousePoint.y == 0) { 653 lastMousePoint.x = x; 654 lastMousePoint.y = y; 655 } 656 657 SDL_SendMouseMotion(data->window, 0, 1, (int)(x-lastMousePoint.x), (int)(y-lastMousePoint.y)); 658 659 lastMousePoint.x = x; 660 lastMousePoint.y = y; 661 } 662 WIN_CheckRawMouseButtons(rawmouse->usButtonFlags, data); 663 } else if (isCapture) { 664 /* we check for where Windows thinks the system cursor lives in this case, so we don't really lose mouse accel, etc. */ 665 POINT pt; 666 RECT hwndRect; 667 HWND currentHnd; 668 669 GetCursorPos(&pt); 670 currentHnd = WindowFromPoint(pt); 671 ScreenToClient(hwnd, &pt); 672 GetClientRect(hwnd, &hwndRect); 673 674 /* if in the window, WM_MOUSEMOVE, etc, will cover it. */ 675 if(currentHnd != hwnd || pt.x < 0 || pt.y < 0 || pt.x > hwndRect.right || pt.y > hwndRect.right) { 676 SDL_bool swapButtons = GetSystemMetrics(SM_SWAPBUTTON) != 0; 677 678 SDL_SendMouseMotion(data->window, 0, 0, (int)pt.x, (int)pt.y); 679 SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_LBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, !swapButtons ? SDL_BUTTON_LEFT : SDL_BUTTON_RIGHT); 680 SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_RBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, !swapButtons ? SDL_BUTTON_RIGHT : SDL_BUTTON_LEFT); 681 SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_MIDDLE); 682 SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X1); 683 SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X2); 684 } 685 } else { 686 SDL_assert(0 && "Shouldn't happen"); 687 } 688 } 689 } 690 break; 691 692 case WM_MOUSEWHEEL: 693 case WM_MOUSEHWHEEL: 694 { 695 short amount = GET_WHEEL_DELTA_WPARAM(wParam); 696 float fAmount = (float) amount / WHEEL_DELTA; 697 if (msg == WM_MOUSEWHEEL) 698 SDL_SendMouseWheel(data->window, 0, 0.0f, fAmount, SDL_MOUSEWHEEL_NORMAL); 699 else 700 SDL_SendMouseWheel(data->window, 0, fAmount, 0.0f, SDL_MOUSEWHEEL_NORMAL); 701 } 702 break; 703 704 #ifdef WM_MOUSELEAVE 705 case WM_MOUSELEAVE: 706 if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode && !(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) { 707 if (!IsIconic(hwnd)) { 708 SDL_Mouse *mouse; 709 POINT cursorPos; 710 GetCursorPos(&cursorPos); 711 ScreenToClient(hwnd, &cursorPos); 712 mouse = SDL_GetMouse(); 713 if (!mouse->was_touch_mouse_events) { /* we're not a touch handler causing a mouse leave? */ 714 SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y); 715 } else { /* touch handling? */ 716 mouse->was_touch_mouse_events = SDL_FALSE; /* not anymore */ 717 if (mouse->touch_mouse_events) { /* convert touch to mouse events */ 718 SDL_SendMouseMotion(data->window, SDL_TOUCH_MOUSEID, 0, cursorPos.x, cursorPos.y); 719 } else { /* normal handling */ 720 SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y); 721 } 722 } 723 } 724 SDL_SetMouseFocus(NULL); 725 } 726 returnCode = 0; 727 break; 728 #endif /* WM_MOUSELEAVE */ 729 730 case WM_KEYDOWN: 731 case WM_SYSKEYDOWN: 732 { 733 SDL_Scancode code = WindowsScanCodeToSDLScanCode(lParam, wParam); 734 const Uint8 *keyboardState = SDL_GetKeyboardState(NULL); 735 736 /* Detect relevant keyboard shortcuts */ 737 if (keyboardState[SDL_SCANCODE_LALT] == SDL_PRESSED || keyboardState[SDL_SCANCODE_RALT] == SDL_PRESSED) { 738 /* ALT+F4: Close window */ 739 if (code == SDL_SCANCODE_F4 && ShouldGenerateWindowCloseOnAltF4()) { 740 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0); 741 } 742 } 743 744 if (code != SDL_SCANCODE_UNKNOWN) { 745 SDL_SendKeyboardKey(SDL_PRESSED, code); 746 } 747 } 748 749 returnCode = 0; 750 break; 751 752 case WM_SYSKEYUP: 753 case WM_KEYUP: 754 { 755 SDL_Scancode code = WindowsScanCodeToSDLScanCode(lParam, wParam); 756 const Uint8 *keyboardState = SDL_GetKeyboardState(NULL); 757 758 if (code != SDL_SCANCODE_UNKNOWN) { 759 if (code == SDL_SCANCODE_PRINTSCREEN && 760 keyboardState[code] == SDL_RELEASED) { 761 SDL_SendKeyboardKey(SDL_PRESSED, code); 762 } 763 SDL_SendKeyboardKey(SDL_RELEASED, code); 764 } 765 } 766 returnCode = 0; 767 break; 768 769 case WM_UNICHAR: 770 if (wParam == UNICODE_NOCHAR) { 771 returnCode = 1; 772 break; 773 } 774 /* otherwise fall through to below */ 775 case WM_CHAR: 776 { 777 char text[5]; 778 if (WIN_ConvertUTF32toUTF8((UINT32)wParam, text)) { 779 SDL_SendKeyboardText(text); 780 } 781 } 782 returnCode = 0; 783 break; 784 785 #ifdef WM_INPUTLANGCHANGE 786 case WM_INPUTLANGCHANGE: 787 { 788 WIN_UpdateKeymap(); 789 SDL_SendKeymapChangedEvent(); 790 } 791 returnCode = 1; 792 break; 793 #endif /* WM_INPUTLANGCHANGE */ 794 795 case WM_NCLBUTTONDOWN: 796 { 797 data->in_title_click = SDL_TRUE; 798 } 799 break; 800 801 case WM_CAPTURECHANGED: 802 { 803 data->in_title_click = SDL_FALSE; 804 805 /* The mouse may have been released during a modal loop */ 806 WIN_CheckAsyncMouseRelease(data); 807 } 808 break; 809 810 #ifdef WM_GETMINMAXINFO 811 case WM_GETMINMAXINFO: 812 { 813 MINMAXINFO *info; 814 RECT size; 815 int x, y; 816 int w, h; 817 int min_w, min_h; 818 int max_w, max_h; 819 BOOL constrain_max_size; 820 821 if (SDL_IsShapedWindow(data->window)) { 822 Win32_ResizeWindowShape(data->window); 823 } 824 825 /* If this is an expected size change, allow it */ 826 if (data->expected_resize) { 827 break; 828 } 829 830 /* Get the current position of our window */ 831 GetWindowRect(hwnd, &size); 832 x = size.left; 833 y = size.top; 834 835 /* Calculate current size of our window */ 836 SDL_GetWindowSize(data->window, &w, &h); 837 SDL_GetWindowMinimumSize(data->window, &min_w, &min_h); 838 SDL_GetWindowMaximumSize(data->window, &max_w, &max_h); 839 840 /* Store in min_w and min_h difference between current size and minimal 841 size so we don't need to call AdjustWindowRectEx twice */ 842 min_w -= w; 843 min_h -= h; 844 if (max_w && max_h) { 845 max_w -= w; 846 max_h -= h; 847 constrain_max_size = TRUE; 848 } else { 849 constrain_max_size = FALSE; 850 } 851 852 if (!(SDL_GetWindowFlags(data->window) & SDL_WINDOW_BORDERLESS)) { 853 LONG style = GetWindowLong(hwnd, GWL_STYLE); 854 /* DJM - according to the docs for GetMenu(), the 855 return value is undefined if hwnd is a child window. 856 Apparently it's too difficult for MS to check 857 inside their function, so I have to do it here. 858 */ 859 BOOL menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); 860 size.top = 0; 861 size.left = 0; 862 size.bottom = h; 863 size.right = w; 864 865 AdjustWindowRectEx(&size, style, menu, 0); 866 w = size.right - size.left; 867 h = size.bottom - size.top; 868 } 869 870 /* Fix our size to the current size */ 871 info = (MINMAXINFO *) lParam; 872 if (SDL_GetWindowFlags(data->window) & SDL_WINDOW_RESIZABLE) { 873 info->ptMinTrackSize.x = w + min_w; 874 info->ptMinTrackSize.y = h + min_h; 875 if (constrain_max_size) { 876 info->ptMaxTrackSize.x = w + max_w; 877 info->ptMaxTrackSize.y = h + max_h; 878 } 879 } else { 880 info->ptMaxSize.x = w; 881 info->ptMaxSize.y = h; 882 info->ptMaxPosition.x = x; 883 info->ptMaxPosition.y = y; 884 info->ptMinTrackSize.x = w; 885 info->ptMinTrackSize.y = h; 886 info->ptMaxTrackSize.x = w; 887 info->ptMaxTrackSize.y = h; 888 } 889 } 890 returnCode = 0; 891 break; 892 #endif /* WM_GETMINMAXINFO */ 893 894 case WM_WINDOWPOSCHANGING: 895 896 if (data->expected_resize) { 897 returnCode = 0; 898 } 899 break; 900 901 case WM_WINDOWPOSCHANGED: 902 { 903 RECT rect; 904 int x, y; 905 int w, h; 906 907 if (data->initializing || data->in_border_change) { 908 break; 909 } 910 911 if (!GetClientRect(hwnd, &rect) || IsRectEmpty(&rect)) { 912 break; 913 } 914 ClientToScreen(hwnd, (LPPOINT) & rect); 915 ClientToScreen(hwnd, (LPPOINT) & rect + 1); 916 917 WIN_UpdateClipCursor(data->window); 918 919 x = rect.left; 920 y = rect.top; 921 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED, x, y); 922 923 w = rect.right - rect.left; 924 h = rect.bottom - rect.top; 925 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED, w, h); 926 927 /* Forces a WM_PAINT event */ 928 InvalidateRect(hwnd, NULL, FALSE); 929 } 930 break; 931 932 case WM_SIZE: 933 { 934 switch (wParam) { 935 case SIZE_MAXIMIZED: 936 SDL_SendWindowEvent(data->window, 937 SDL_WINDOWEVENT_RESTORED, 0, 0); 938 SDL_SendWindowEvent(data->window, 939 SDL_WINDOWEVENT_MAXIMIZED, 0, 0); 940 break; 941 case SIZE_MINIMIZED: 942 SDL_SendWindowEvent(data->window, 943 SDL_WINDOWEVENT_MINIMIZED, 0, 0); 944 break; 945 default: 946 SDL_SendWindowEvent(data->window, 947 SDL_WINDOWEVENT_RESTORED, 0, 0); 948 break; 949 } 950 } 951 break; 952 953 case WM_SETCURSOR: 954 { 955 Uint16 hittest; 956 957 hittest = LOWORD(lParam); 958 if (hittest == HTCLIENT) { 959 SetCursor(SDL_cursor); 960 returnCode = TRUE; 961 } else if (!g_WindowFrameUsableWhileCursorHidden && !SDL_cursor) { 962 SetCursor(NULL); 963 returnCode = TRUE; 964 } 965 } 966 break; 967 968 /* We were occluded, refresh our display */ 969 case WM_PAINT: 970 { 971 RECT rect; 972 if (GetUpdateRect(hwnd, &rect, FALSE)) { 973 ValidateRect(hwnd, NULL); 974 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0); 975 } 976 } 977 returnCode = 0; 978 break; 979 980 /* We'll do our own drawing, prevent flicker */ 981 case WM_ERASEBKGND: 982 { 983 } 984 return (1); 985 986 case WM_SYSCOMMAND: 987 { 988 if ((wParam & 0xFFF0) == SC_KEYMENU) { 989 return (0); 990 } 991 992 #if defined(SC_SCREENSAVE) || defined(SC_MONITORPOWER) 993 /* Don't start the screensaver or blank the monitor in fullscreen apps */ 994 if ((wParam & 0xFFF0) == SC_SCREENSAVE || 995 (wParam & 0xFFF0) == SC_MONITORPOWER) { 996 if (SDL_GetVideoDevice()->suspend_screensaver) { 997 return (0); 998 } 999 } 1000 #endif /* System has screensaver support */ 1001 } 1002 break; 1003 1004 case WM_CLOSE: 1005 { 1006 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0); 1007 } 1008 returnCode = 0; 1009 break; 1010 1011 case WM_TOUCH: 1012 if (data->videodata->GetTouchInputInfo && data->videodata->CloseTouchInputHandle) { 1013 UINT i, num_inputs = LOWORD(wParam); 1014 SDL_bool isstack; 1015 PTOUCHINPUT inputs = SDL_small_alloc(TOUCHINPUT, num_inputs, &isstack); 1016 if (data->videodata->GetTouchInputInfo((HTOUCHINPUT)lParam, num_inputs, inputs, sizeof(TOUCHINPUT))) { 1017 RECT rect; 1018 float x, y; 1019 1020 if (!GetClientRect(hwnd, &rect) || 1021 (rect.right == rect.left && rect.bottom == rect.top)) { 1022 if (inputs) { 1023 SDL_small_free(inputs, isstack); 1024 } 1025 break; 1026 } 1027 ClientToScreen(hwnd, (LPPOINT) & rect); 1028 ClientToScreen(hwnd, (LPPOINT) & rect + 1); 1029 rect.top *= 100; 1030 rect.left *= 100; 1031 rect.bottom *= 100; 1032 rect.right *= 100; 1033 1034 for (i = 0; i < num_inputs; ++i) { 1035 PTOUCHINPUT input = &inputs[i]; 1036 1037 const SDL_TouchID touchId = (SDL_TouchID)((size_t)input->hSource); 1038 1039 /* TODO: Can we use GetRawInputDeviceInfo and HID info to 1040 determine if this is a direct or indirect touch device? 1041 */ 1042 if (SDL_AddTouch(touchId, SDL_TOUCH_DEVICE_DIRECT, "") < 0) { 1043 continue; 1044 } 1045 1046 /* Get the normalized coordinates for the window */ 1047 x = (float)(input->x - rect.left)/(rect.right - rect.left); 1048 y = (float)(input->y - rect.top)/(rect.bottom - rect.top); 1049 1050 if (input->dwFlags & TOUCHEVENTF_DOWN) { 1051 SDL_SendTouch(touchId, input->dwID, data->window, SDL_TRUE, x, y, 1.0f); 1052 } 1053 if (input->dwFlags & TOUCHEVENTF_MOVE) { 1054 SDL_SendTouchMotion(touchId, input->dwID, data->window, x, y, 1.0f); 1055 } 1056 if (input->dwFlags & TOUCHEVENTF_UP) { 1057 SDL_SendTouch(touchId, input->dwID, data->window, SDL_FALSE, x, y, 1.0f); 1058 } 1059 } 1060 } 1061 SDL_small_free(inputs, isstack); 1062 1063 data->videodata->CloseTouchInputHandle((HTOUCHINPUT)lParam); 1064 return 0; 1065 } 1066 break; 1067 1068 case WM_DROPFILES: 1069 { 1070 UINT i; 1071 HDROP drop = (HDROP) wParam; 1072 UINT count = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0); 1073 for (i = 0; i < count; ++i) { 1074 SDL_bool isstack; 1075 UINT size = DragQueryFile(drop, i, NULL, 0) + 1; 1076 LPTSTR buffer = SDL_small_alloc(TCHAR, size, &isstack); 1077 if (buffer) { 1078 if (DragQueryFile(drop, i, buffer, size)) { 1079 char *file = WIN_StringToUTF8(buffer); 1080 SDL_SendDropFile(data->window, file); 1081 SDL_free(file); 1082 } 1083 SDL_small_free(buffer, isstack); 1084 } 1085 } 1086 SDL_SendDropComplete(data->window); 1087 DragFinish(drop); 1088 return 0; 1089 } 1090 break; 1091 1092 case WM_DISPLAYCHANGE: 1093 { 1094 // Reacquire displays if any were added or removed 1095 WIN_RefreshDisplays(SDL_GetVideoDevice()); 1096 } 1097 break; 1098 1099 case WM_NCCALCSIZE: 1100 { 1101 Uint32 window_flags = SDL_GetWindowFlags(data->window); 1102 if (wParam == TRUE && (window_flags & SDL_WINDOW_BORDERLESS) && !(window_flags & SDL_WINDOW_FULLSCREEN)) { 1103 /* When borderless, need to tell windows that the size of the non-client area is 0 */ 1104 if (!(window_flags & SDL_WINDOW_RESIZABLE)) { 1105 int w, h; 1106 NCCALCSIZE_PARAMS *params = (NCCALCSIZE_PARAMS *)lParam; 1107 w = data->window->windowed.w; 1108 h = data->window->windowed.h; 1109 params->rgrc[0].right = params->rgrc[0].left + w; 1110 params->rgrc[0].bottom = params->rgrc[0].top + h; 1111 } 1112 return 0; 1113 } 1114 } 1115 break; 1116 1117 case WM_NCHITTEST: 1118 { 1119 SDL_Window *window = data->window; 1120 if (window->hit_test) { 1121 POINT winpoint = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; 1122 if (ScreenToClient(hwnd, &winpoint)) { 1123 const SDL_Point point = { (int) winpoint.x, (int) winpoint.y }; 1124 const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data); 1125 switch (rc) { 1126 #define POST_HIT_TEST(ret) { SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIT_TEST, 0, 0); return ret; } 1127 case SDL_HITTEST_DRAGGABLE: POST_HIT_TEST(HTCAPTION); 1128 case SDL_HITTEST_RESIZE_TOPLEFT: POST_HIT_TEST(HTTOPLEFT); 1129 case SDL_HITTEST_RESIZE_TOP: POST_HIT_TEST(HTTOP); 1130 case SDL_HITTEST_RESIZE_TOPRIGHT: POST_HIT_TEST(HTTOPRIGHT); 1131 case SDL_HITTEST_RESIZE_RIGHT: POST_HIT_TEST(HTRIGHT); 1132 case SDL_HITTEST_RESIZE_BOTTOMRIGHT: POST_HIT_TEST(HTBOTTOMRIGHT); 1133 case SDL_HITTEST_RESIZE_BOTTOM: POST_HIT_TEST(HTBOTTOM); 1134 case SDL_HITTEST_RESIZE_BOTTOMLEFT: POST_HIT_TEST(HTBOTTOMLEFT); 1135 case SDL_HITTEST_RESIZE_LEFT: POST_HIT_TEST(HTLEFT); 1136 #undef POST_HIT_TEST 1137 case SDL_HITTEST_NORMAL: return HTCLIENT; 1138 } 1139 } 1140 /* If we didn't return, this will call DefWindowProc below. */ 1141 } 1142 } 1143 break; 1144 } 1145 1146 /* If there's a window proc, assume it's going to handle messages */ 1147 if (data->wndproc) { 1148 return CallWindowProc(data->wndproc, hwnd, msg, wParam, lParam); 1149 } else if (returnCode >= 0) { 1150 return returnCode; 1151 } else { 1152 return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam); 1153 } 1154 } 1155 1156 static void WIN_UpdateClipCursorForWindows() 1157 { 1158 SDL_VideoDevice *_this = SDL_GetVideoDevice(); 1159 SDL_Window *window; 1160 Uint32 now = SDL_GetTicks(); 1161 const Uint32 CLIPCURSOR_UPDATE_INTERVAL_MS = 3000; 1162 1163 if (_this) { 1164 for (window = _this->windows; window; window = window->next) { 1165 SDL_WindowData *data = (SDL_WindowData *)window->driverdata; 1166 if (data) { 1167 if (data->skip_update_clipcursor) { 1168 data->skip_update_clipcursor = SDL_FALSE; 1169 WIN_UpdateClipCursor(window); 1170 } else if ((now - data->last_updated_clipcursor) >= CLIPCURSOR_UPDATE_INTERVAL_MS) { 1171 WIN_UpdateClipCursor(window); 1172 } 1173 } 1174 } 1175 } 1176 } 1177 1178 /* A message hook called before TranslateMessage() */ 1179 static SDL_WindowsMessageHook g_WindowsMessageHook = NULL; 1180 static void *g_WindowsMessageHookData = NULL; 1181 1182 void SDL_SetWindowsMessageHook(SDL_WindowsMessageHook callback, void *userdata) 1183 { 1184 g_WindowsMessageHook = callback; 1185 g_WindowsMessageHookData = userdata; 1186 } 1187 1188 void 1189 WIN_PumpEvents(_THIS) 1190 { 1191 const Uint8 *keystate; 1192 MSG msg; 1193 DWORD start_ticks = GetTickCount(); 1194 int new_messages = 0; 1195 1196 if (g_WindowsEnableMessageLoop) { 1197 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { 1198 if (g_WindowsMessageHook) { 1199 g_WindowsMessageHook(g_WindowsMessageHookData, msg.hwnd, msg.message, msg.wParam, msg.lParam); 1200 } 1201 1202 /* Always translate the message in case it's a non-SDL window (e.g. with Qt integration) */ 1203 TranslateMessage(&msg); 1204 DispatchMessage(&msg); 1205 1206 /* Make sure we don't busy loop here forever if there are lots of events coming in */ 1207 if (SDL_TICKS_PASSED(msg.time, start_ticks)) { 1208 /* We might get a few new messages generated by the Steam overlay or other application hooks 1209 In this case those messages will be processed before any pending input, so we want to continue after those messages. 1210 (thanks to Peter Deayton for his investigation here) 1211 */ 1212 const int MAX_NEW_MESSAGES = 3; 1213 ++new_messages; 1214 if (new_messages > MAX_NEW_MESSAGES) { 1215 break; 1216 } 1217 } 1218 } 1219 } 1220 1221 /* Windows loses a shift KEYUP event when you have both pressed at once and let go of one. 1222 You won't get a KEYUP until both are released, and that keyup will only be for the second 1223 key you released. Take heroic measures and check the keystate as of the last handled event, 1224 and if we think a key is pressed when Windows doesn't, unstick it in SDL's state. */ 1225 keystate = SDL_GetKeyboardState(NULL); 1226 if ((keystate[SDL_SCANCODE_LSHIFT] == SDL_PRESSED) && !(GetKeyState(VK_LSHIFT) & 0x8000)) { 1227 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT); 1228 } 1229 if ((keystate[SDL_SCANCODE_RSHIFT] == SDL_PRESSED) && !(GetKeyState(VK_RSHIFT) & 0x8000)) { 1230 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RSHIFT); 1231 } 1232 1233 /* Update the clipping rect in case someone else has stolen it */ 1234 WIN_UpdateClipCursorForWindows(); 1235 } 1236 1237 /* to work around #3931, a bug introduced in Win10 Fall Creators Update (build nr. 16299) 1238 we need to detect the windows version. this struct and the function below does that. 1239 usually this struct and the corresponding function (RtlGetVersion) are in <Ntddk.h> 1240 but here we just load it dynamically */ 1241 struct SDL_WIN_OSVERSIONINFOW { 1242 ULONG dwOSVersionInfoSize; 1243 ULONG dwMajorVersion; 1244 ULONG dwMinorVersion; 1245 ULONG dwBuildNumber; 1246 ULONG dwPlatformId; 1247 WCHAR szCSDVersion[128]; 1248 }; 1249 1250 static SDL_bool 1251 IsWin10FCUorNewer(void) 1252 { 1253 HMODULE handle = GetModuleHandleW(L"ntdll.dll"); 1254 if (handle) { 1255 typedef LONG(WINAPI* RtlGetVersionPtr)(struct SDL_WIN_OSVERSIONINFOW*); 1256 RtlGetVersionPtr getVersionPtr = (RtlGetVersionPtr)GetProcAddress(handle, "RtlGetVersion"); 1257 if (getVersionPtr != NULL) { 1258 struct SDL_WIN_OSVERSIONINFOW info; 1259 SDL_zero(info); 1260 info.dwOSVersionInfoSize = sizeof(info); 1261 if (getVersionPtr(&info) == 0) { /* STATUS_SUCCESS == 0 */ 1262 if ((info.dwMajorVersion == 10 && info.dwMinorVersion == 0 && info.dwBuildNumber >= 16299) || 1263 (info.dwMajorVersion == 10 && info.dwMinorVersion > 0) || 1264 (info.dwMajorVersion > 10)) 1265 { 1266 return SDL_TRUE; 1267 } 1268 } 1269 } 1270 } 1271 return SDL_FALSE; 1272 } 1273 1274 static int app_registered = 0; 1275 LPTSTR SDL_Appname = NULL; 1276 Uint32 SDL_Appstyle = 0; 1277 HINSTANCE SDL_Instance = NULL; 1278 1279 /* Register the class for this application */ 1280 int 1281 SDL_RegisterApp(char *name, Uint32 style, void *hInst) 1282 { 1283 const char *hint; 1284 WNDCLASSEX wcex; 1285 TCHAR path[MAX_PATH]; 1286 1287 /* Only do this once... */ 1288 if (app_registered) { 1289 ++app_registered; 1290 return (0); 1291 } 1292 if (!name && !SDL_Appname) { 1293 name = "SDL_app"; 1294 #if defined(CS_BYTEALIGNCLIENT) || defined(CS_OWNDC) 1295 SDL_Appstyle = (CS_BYTEALIGNCLIENT | CS_OWNDC); 1296 #endif 1297 SDL_Instance = hInst ? hInst : GetModuleHandle(NULL); 1298 } 1299 1300 if (name) { 1301 SDL_Appname = WIN_UTF8ToString(name); 1302 SDL_Appstyle = style; 1303 SDL_Instance = hInst ? hInst : GetModuleHandle(NULL); 1304 } 1305 1306 /* Register the application class */ 1307 wcex.cbSize = sizeof(WNDCLASSEX); 1308 wcex.hCursor = NULL; 1309 wcex.hIcon = NULL; 1310 wcex.hIconSm = NULL; 1311 wcex.lpszMenuName = NULL; 1312 wcex.lpszClassName = SDL_Appname; 1313 wcex.style = SDL_Appstyle; 1314 wcex.hbrBackground = NULL; 1315 wcex.lpfnWndProc = WIN_WindowProc; 1316 wcex.hInstance = SDL_Instance; 1317 wcex.cbClsExtra = 0; 1318 wcex.cbWndExtra = 0; 1319 1320 hint = SDL_GetHint(SDL_HINT_WINDOWS_INTRESOURCE_ICON); 1321 if (hint && *hint) { 1322 wcex.hIcon = LoadIcon(SDL_Instance, MAKEINTRESOURCE(SDL_atoi(hint))); 1323 1324 hint = SDL_GetHint(SDL_HINT_WINDOWS_INTRESOURCE_ICON_SMALL); 1325 if (hint && *hint) { 1326 wcex.hIconSm = LoadIcon(SDL_Instance, MAKEINTRESOURCE(SDL_atoi(hint))); 1327 } 1328 } else { 1329 /* Use the first icon as a default icon, like in the Explorer */ 1330 GetModuleFileName(SDL_Instance, path, MAX_PATH); 1331 ExtractIconEx(path, 0, &wcex.hIcon, &wcex.hIconSm, 1); 1332 } 1333 1334 if (!RegisterClassEx(&wcex)) { 1335 return SDL_SetError("Couldn't register application class"); 1336 } 1337 1338 isWin10FCUorNewer = IsWin10FCUorNewer(); 1339 1340 app_registered = 1; 1341 return 0; 1342 } 1343 1344 /* Unregisters the windowclass registered in SDL_RegisterApp above. */ 1345 void 1346 SDL_UnregisterApp() 1347 { 1348 WNDCLASSEX wcex; 1349 1350 /* SDL_RegisterApp might not have been called before */ 1351 if (!app_registered) { 1352 return; 1353 } 1354 --app_registered; 1355 if (app_registered == 0) { 1356 /* Check for any registered window classes. */ 1357 if (GetClassInfoEx(SDL_Instance, SDL_Appname, &wcex)) { 1358 UnregisterClass(SDL_Appname, SDL_Instance); 1359 if (wcex.hIcon) DestroyIcon(wcex.hIcon); 1360 if (wcex.hIconSm) DestroyIcon(wcex.hIconSm); 1361 } 1362 SDL_free(SDL_Appname); 1363 SDL_Appname = NULL; 1364 } 1365 } 1366 1367 #endif /* SDL_VIDEO_DRIVER_WINDOWS */ 1368 1369 /* vi: set ts=4 sw=4 expandtab: */