SDL_windowsevents.c (37406B)
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2014 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_syswm.h" 28#include "SDL_timer.h" 29#include "SDL_vkeys.h" 30#include "../../events/SDL_events_c.h" 31#include "../../events/SDL_touch_c.h" 32#include "../../events/scancodes_windows.h" 33#include "SDL_assert.h" 34 35/* Dropfile support */ 36#include <shellapi.h> 37 38/* For GET_X_LPARAM, GET_Y_LPARAM. */ 39#include <windowsx.h> 40 41/* #define WMMSG_DEBUG */ 42#ifdef WMMSG_DEBUG 43#include <stdio.h> 44#include "wmmsg.h" 45#endif 46 47/* Masks for processing the windows KEYDOWN and KEYUP messages */ 48#define REPEATED_KEYMASK (1<<30) 49#define EXTENDED_KEYMASK (1<<24) 50 51#define VK_ENTER 10 /* Keypad Enter ... no VKEY defined? */ 52#ifndef VK_OEM_NEC_EQUAL 53#define VK_OEM_NEC_EQUAL 0x92 54#endif 55 56/* Make sure XBUTTON stuff is defined that isn't in older Platform SDKs... */ 57#ifndef WM_XBUTTONDOWN 58#define WM_XBUTTONDOWN 0x020B 59#endif 60#ifndef WM_XBUTTONUP 61#define WM_XBUTTONUP 0x020C 62#endif 63#ifndef GET_XBUTTON_WPARAM 64#define GET_XBUTTON_WPARAM(w) (HIWORD(w)) 65#endif 66#ifndef WM_INPUT 67#define WM_INPUT 0x00ff 68#endif 69#ifndef WM_TOUCH 70#define WM_TOUCH 0x0240 71#endif 72#ifndef WM_MOUSEHWHEEL 73#define WM_MOUSEHWHEEL 0x020E 74#endif 75#ifndef WM_UNICHAR 76#define WM_UNICHAR 0x0109 77#endif 78 79static SDL_Scancode 80WindowsScanCodeToSDLScanCode(LPARAM lParam, WPARAM wParam) 81{ 82 SDL_Scancode code; 83 char bIsExtended; 84 int nScanCode = (lParam >> 16) & 0xFF; 85 86 /* 0x45 here to work around both pause and numlock sharing the same scancode, so use the VK key to tell them apart */ 87 if (nScanCode == 0 || nScanCode == 0x45) { 88 switch(wParam) { 89 case VK_CLEAR: return SDL_SCANCODE_CLEAR; 90 case VK_MODECHANGE: return SDL_SCANCODE_MODE; 91 case VK_SELECT: return SDL_SCANCODE_SELECT; 92 case VK_EXECUTE: return SDL_SCANCODE_EXECUTE; 93 case VK_HELP: return SDL_SCANCODE_HELP; 94 case VK_PAUSE: return SDL_SCANCODE_PAUSE; 95 case VK_NUMLOCK: return SDL_SCANCODE_NUMLOCKCLEAR; 96 97 case VK_F13: return SDL_SCANCODE_F13; 98 case VK_F14: return SDL_SCANCODE_F14; 99 case VK_F15: return SDL_SCANCODE_F15; 100 case VK_F16: return SDL_SCANCODE_F16; 101 case VK_F17: return SDL_SCANCODE_F17; 102 case VK_F18: return SDL_SCANCODE_F18; 103 case VK_F19: return SDL_SCANCODE_F19; 104 case VK_F20: return SDL_SCANCODE_F20; 105 case VK_F21: return SDL_SCANCODE_F21; 106 case VK_F22: return SDL_SCANCODE_F22; 107 case VK_F23: return SDL_SCANCODE_F23; 108 case VK_F24: return SDL_SCANCODE_F24; 109 110 case VK_OEM_NEC_EQUAL: return SDL_SCANCODE_KP_EQUALS; 111 case VK_BROWSER_BACK: return SDL_SCANCODE_AC_BACK; 112 case VK_BROWSER_FORWARD: return SDL_SCANCODE_AC_FORWARD; 113 case VK_BROWSER_REFRESH: return SDL_SCANCODE_AC_REFRESH; 114 case VK_BROWSER_STOP: return SDL_SCANCODE_AC_STOP; 115 case VK_BROWSER_SEARCH: return SDL_SCANCODE_AC_SEARCH; 116 case VK_BROWSER_FAVORITES: return SDL_SCANCODE_AC_BOOKMARKS; 117 case VK_BROWSER_HOME: return SDL_SCANCODE_AC_HOME; 118 case VK_VOLUME_MUTE: return SDL_SCANCODE_AUDIOMUTE; 119 case VK_VOLUME_DOWN: return SDL_SCANCODE_VOLUMEDOWN; 120 case VK_VOLUME_UP: return SDL_SCANCODE_VOLUMEUP; 121 122 case VK_MEDIA_NEXT_TRACK: return SDL_SCANCODE_AUDIONEXT; 123 case VK_MEDIA_PREV_TRACK: return SDL_SCANCODE_AUDIOPREV; 124 case VK_MEDIA_STOP: return SDL_SCANCODE_AUDIOSTOP; 125 case VK_MEDIA_PLAY_PAUSE: return SDL_SCANCODE_AUDIOPLAY; 126 case VK_LAUNCH_MAIL: return SDL_SCANCODE_MAIL; 127 case VK_LAUNCH_MEDIA_SELECT: return SDL_SCANCODE_MEDIASELECT; 128 129 case VK_OEM_102: return SDL_SCANCODE_NONUSBACKSLASH; 130 131 case VK_ATTN: return SDL_SCANCODE_SYSREQ; 132 case VK_CRSEL: return SDL_SCANCODE_CRSEL; 133 case VK_EXSEL: return SDL_SCANCODE_EXSEL; 134 case VK_OEM_CLEAR: return SDL_SCANCODE_CLEAR; 135 136 case VK_LAUNCH_APP1: return SDL_SCANCODE_APP1; 137 case VK_LAUNCH_APP2: return SDL_SCANCODE_APP2; 138 139 default: return SDL_SCANCODE_UNKNOWN; 140 } 141 } 142 143 if (nScanCode > 127) 144 return SDL_SCANCODE_UNKNOWN; 145 146 code = windows_scancode_table[nScanCode]; 147 148 bIsExtended = (lParam & (1 << 24)) != 0; 149 if (!bIsExtended) { 150 switch (code) { 151 case SDL_SCANCODE_HOME: 152 return SDL_SCANCODE_KP_7; 153 case SDL_SCANCODE_UP: 154 return SDL_SCANCODE_KP_8; 155 case SDL_SCANCODE_PAGEUP: 156 return SDL_SCANCODE_KP_9; 157 case SDL_SCANCODE_LEFT: 158 return SDL_SCANCODE_KP_4; 159 case SDL_SCANCODE_RIGHT: 160 return SDL_SCANCODE_KP_6; 161 case SDL_SCANCODE_END: 162 return SDL_SCANCODE_KP_1; 163 case SDL_SCANCODE_DOWN: 164 return SDL_SCANCODE_KP_2; 165 case SDL_SCANCODE_PAGEDOWN: 166 return SDL_SCANCODE_KP_3; 167 case SDL_SCANCODE_INSERT: 168 return SDL_SCANCODE_KP_0; 169 case SDL_SCANCODE_DELETE: 170 return SDL_SCANCODE_KP_PERIOD; 171 case SDL_SCANCODE_PRINTSCREEN: 172 return SDL_SCANCODE_KP_MULTIPLY; 173 default: 174 break; 175 } 176 } else { 177 switch (code) { 178 case SDL_SCANCODE_RETURN: 179 return SDL_SCANCODE_KP_ENTER; 180 case SDL_SCANCODE_LALT: 181 return SDL_SCANCODE_RALT; 182 case SDL_SCANCODE_LCTRL: 183 return SDL_SCANCODE_RCTRL; 184 case SDL_SCANCODE_SLASH: 185 return SDL_SCANCODE_KP_DIVIDE; 186 case SDL_SCANCODE_CAPSLOCK: 187 return SDL_SCANCODE_KP_PLUS; 188 default: 189 break; 190 } 191 } 192 193 return code; 194} 195 196 197void 198WIN_CheckWParamMouseButton(SDL_bool bwParamMousePressed, SDL_bool bSDLMousePressed, SDL_WindowData *data, Uint8 button) 199{ 200 if (data->focus_click_pending && button == SDL_BUTTON_LEFT && !bwParamMousePressed) { 201 data->focus_click_pending = SDL_FALSE; 202 WIN_UpdateClipCursor(data->window); 203 } 204 205 if (bwParamMousePressed && !bSDLMousePressed) { 206 SDL_SendMouseButton(data->window, 0, SDL_PRESSED, button); 207 } else if (!bwParamMousePressed && bSDLMousePressed) { 208 SDL_SendMouseButton(data->window, 0, SDL_RELEASED, button); 209 } 210} 211 212/* 213* Some windows systems fail to send a WM_LBUTTONDOWN sometimes, but each mouse move contains the current button state also 214* so this funciton reconciles our view of the world with the current buttons reported by windows 215*/ 216void 217WIN_CheckWParamMouseButtons(WPARAM wParam, SDL_WindowData *data) 218{ 219 if (wParam != data->mouse_button_flags) { 220 Uint32 mouseFlags = SDL_GetMouseState(NULL, NULL); 221 WIN_CheckWParamMouseButton((wParam & MK_LBUTTON), (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT); 222 WIN_CheckWParamMouseButton((wParam & MK_MBUTTON), (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE); 223 WIN_CheckWParamMouseButton((wParam & MK_RBUTTON), (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT); 224 WIN_CheckWParamMouseButton((wParam & MK_XBUTTON1), (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1); 225 WIN_CheckWParamMouseButton((wParam & MK_XBUTTON2), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2); 226 data->mouse_button_flags = wParam; 227 } 228} 229 230 231void 232WIN_CheckRawMouseButtons(ULONG rawButtons, SDL_WindowData *data) 233{ 234 if (rawButtons != data->mouse_button_flags) { 235 Uint32 mouseFlags = SDL_GetMouseState(NULL, NULL); 236 if ((rawButtons & RI_MOUSE_BUTTON_1_DOWN)) 237 WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_1_DOWN), (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT); 238 if ((rawButtons & RI_MOUSE_BUTTON_1_UP)) 239 WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_1_UP), (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT); 240 if ((rawButtons & RI_MOUSE_BUTTON_2_DOWN)) 241 WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_2_DOWN), (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT); 242 if ((rawButtons & RI_MOUSE_BUTTON_2_UP)) 243 WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_2_UP), (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT); 244 if ((rawButtons & RI_MOUSE_BUTTON_3_DOWN)) 245 WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_3_DOWN), (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE); 246 if ((rawButtons & RI_MOUSE_BUTTON_3_UP)) 247 WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_3_UP), (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE); 248 if ((rawButtons & RI_MOUSE_BUTTON_4_DOWN)) 249 WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_4_DOWN), (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1); 250 if ((rawButtons & RI_MOUSE_BUTTON_4_UP)) 251 WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_4_UP), (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1); 252 if ((rawButtons & RI_MOUSE_BUTTON_5_DOWN)) 253 WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_5_DOWN), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2); 254 if ((rawButtons & RI_MOUSE_BUTTON_5_UP)) 255 WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_5_UP), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2); 256 data->mouse_button_flags = rawButtons; 257 } 258} 259 260void 261WIN_CheckAsyncMouseRelease(SDL_WindowData *data) 262{ 263 Uint32 mouseFlags; 264 SHORT keyState; 265 266 /* mouse buttons may have changed state here, we need to resync them, 267 but we will get a WM_MOUSEMOVE right away which will fix things up if in non raw mode also 268 */ 269 mouseFlags = SDL_GetMouseState(NULL, NULL); 270 271 keyState = GetAsyncKeyState(VK_LBUTTON); 272 if (!(keyState & 0x8000)) { 273 WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT); 274 } 275 keyState = GetAsyncKeyState(VK_RBUTTON); 276 if (!(keyState & 0x8000)) { 277 WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT); 278 } 279 keyState = GetAsyncKeyState(VK_MBUTTON); 280 if (!(keyState & 0x8000)) { 281 WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE); 282 } 283 keyState = GetAsyncKeyState(VK_XBUTTON1); 284 if (!(keyState & 0x8000)) { 285 WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1); 286 } 287 keyState = GetAsyncKeyState(VK_XBUTTON2); 288 if (!(keyState & 0x8000)) { 289 WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2); 290 } 291 data->mouse_button_flags = 0; 292} 293 294SDL_FORCE_INLINE BOOL 295WIN_ConvertUTF32toUTF8(UINT32 codepoint, char * text) 296{ 297 if (codepoint <= 0x7F) { 298 text[0] = (char) codepoint; 299 text[1] = '\0'; 300 } else if (codepoint <= 0x7FF) { 301 text[0] = 0xC0 | (char) ((codepoint >> 6) & 0x1F); 302 text[1] = 0x80 | (char) (codepoint & 0x3F); 303 text[2] = '\0'; 304 } else if (codepoint <= 0xFFFF) { 305 text[0] = 0xE0 | (char) ((codepoint >> 12) & 0x0F); 306 text[1] = 0x80 | (char) ((codepoint >> 6) & 0x3F); 307 text[2] = 0x80 | (char) (codepoint & 0x3F); 308 text[3] = '\0'; 309 } else if (codepoint <= 0x10FFFF) { 310 text[0] = 0xF0 | (char) ((codepoint >> 18) & 0x0F); 311 text[1] = 0x80 | (char) ((codepoint >> 12) & 0x3F); 312 text[2] = 0x80 | (char) ((codepoint >> 6) & 0x3F); 313 text[3] = 0x80 | (char) (codepoint & 0x3F); 314 text[4] = '\0'; 315 } else { 316 return SDL_FALSE; 317 } 318 return SDL_TRUE; 319} 320 321LRESULT CALLBACK 322WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 323{ 324 SDL_WindowData *data; 325 LRESULT returnCode = -1; 326 327 /* Send a SDL_SYSWMEVENT if the application wants them */ 328 if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) { 329 SDL_SysWMmsg wmmsg; 330 331 SDL_VERSION(&wmmsg.version); 332 wmmsg.subsystem = SDL_SYSWM_WINDOWS; 333 wmmsg.msg.win.hwnd = hwnd; 334 wmmsg.msg.win.msg = msg; 335 wmmsg.msg.win.wParam = wParam; 336 wmmsg.msg.win.lParam = lParam; 337 SDL_SendSysWMEvent(&wmmsg); 338 } 339 340 /* Get the window data for the window */ 341 data = (SDL_WindowData *) GetProp(hwnd, TEXT("SDL_WindowData")); 342 if (!data) { 343 return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam); 344 } 345 346#ifdef WMMSG_DEBUG 347 { 348 char message[1024]; 349 if (msg > MAX_WMMSG) { 350 SDL_snprintf(message, sizeof(message), "Received windows message: %p UNKNOWN (%d) -- 0x%X, 0x%X\n", hwnd, msg, wParam, lParam); 351 } else { 352 SDL_snprintf(message, sizeof(message), "Received windows message: %p %s -- 0x%X, 0x%X\n", hwnd, wmtab[msg], wParam, lParam); 353 } 354 OutputDebugStringA(message); 355 } 356#endif /* WMMSG_DEBUG */ 357 358 if (IME_HandleMessage(hwnd, msg, wParam, &lParam, data->videodata)) 359 return 0; 360 361 switch (msg) { 362 363 case WM_SHOWWINDOW: 364 { 365 if (wParam) { 366 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0); 367 } else { 368 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0); 369 } 370 } 371 break; 372 373 case WM_ACTIVATE: 374 { 375 POINT cursorPos; 376 BOOL minimized; 377 378 minimized = HIWORD(wParam); 379 if (!minimized && (LOWORD(wParam) != WA_INACTIVE)) { 380 data->focus_click_pending = (GetAsyncKeyState(VK_LBUTTON) != 0); 381 382 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0); 383 if (SDL_GetKeyboardFocus() != data->window) { 384 SDL_SetKeyboardFocus(data->window); 385 } 386 387 GetCursorPos(&cursorPos); 388 ScreenToClient(hwnd, &cursorPos); 389 SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y); 390 391 WIN_CheckAsyncMouseRelease(data); 392 393 /* 394 * FIXME: Update keyboard state 395 */ 396 WIN_CheckClipboardUpdate(data->videodata); 397 } else { 398 if (SDL_GetKeyboardFocus() == data->window) { 399 SDL_SetKeyboardFocus(NULL); 400 } 401 402 ClipCursor(NULL); 403 } 404 } 405 returnCode = 0; 406 break; 407 408 case WM_MOUSEMOVE: 409 { 410 SDL_Mouse *mouse = SDL_GetMouse(); 411 if (!mouse->relative_mode || mouse->relative_mode_warp) { 412 SDL_SendMouseMotion(data->window, 0, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 413 } 414 } 415 /* don't break here, fall through to check the wParam like the button presses */ 416 case WM_LBUTTONUP: 417 case WM_RBUTTONUP: 418 case WM_MBUTTONUP: 419 case WM_XBUTTONUP: 420 case WM_LBUTTONDOWN: 421 case WM_LBUTTONDBLCLK: 422 case WM_RBUTTONDOWN: 423 case WM_RBUTTONDBLCLK: 424 case WM_MBUTTONDOWN: 425 case WM_MBUTTONDBLCLK: 426 case WM_XBUTTONDOWN: 427 case WM_XBUTTONDBLCLK: 428 { 429 SDL_Mouse *mouse = SDL_GetMouse(); 430 if (!mouse->relative_mode || mouse->relative_mode_warp) { 431 WIN_CheckWParamMouseButtons(wParam, data); 432 } 433 } 434 break; 435 436 case WM_INPUT: 437 { 438 SDL_Mouse *mouse = SDL_GetMouse(); 439 HRAWINPUT hRawInput = (HRAWINPUT)lParam; 440 RAWINPUT inp; 441 UINT size = sizeof(inp); 442 const SDL_bool isRelative = mouse->relative_mode || mouse->relative_mode_warp; 443 const SDL_bool isCapture = ((data->window->flags & SDL_WINDOW_MOUSE_CAPTURE) != 0); 444 445 if (!isRelative || mouse->focus != data->window) { 446 if (!isCapture) { 447 break; 448 } 449 } 450 451 GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER)); 452 453 /* Mouse data */ 454 if (inp.header.dwType == RIM_TYPEMOUSE) { 455 if (isRelative) { 456 RAWMOUSE* rawmouse = &inp.data.mouse; 457 458 if ((rawmouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE) { 459 SDL_SendMouseMotion(data->window, 0, 1, (int)rawmouse->lLastX, (int)rawmouse->lLastY); 460 } else { 461 /* synthesize relative moves from the abs position */ 462 static SDL_Point initialMousePoint; 463 if (initialMousePoint.x == 0 && initialMousePoint.y == 0) { 464 initialMousePoint.x = rawmouse->lLastX; 465 initialMousePoint.y = rawmouse->lLastY; 466 } 467 468 SDL_SendMouseMotion(data->window, 0, 1, (int)(rawmouse->lLastX-initialMousePoint.x), (int)(rawmouse->lLastY-initialMousePoint.y) ); 469 470 initialMousePoint.x = rawmouse->lLastX; 471 initialMousePoint.y = rawmouse->lLastY; 472 } 473 WIN_CheckRawMouseButtons(rawmouse->usButtonFlags, data); 474 } else if (isCapture) { 475 /* we check for where Windows thinks the system cursor lives in this case, so we don't really lose mouse accel, etc. */ 476 POINT pt; 477 GetCursorPos(&pt); 478 if (WindowFromPoint(pt) != hwnd) { /* if in the window, WM_MOUSEMOVE, etc, will cover it. */ 479 ScreenToClient(hwnd, &pt); 480 SDL_SendMouseMotion(data->window, 0, 0, (int) pt.x, (int) pt.y); 481 SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_LBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_LEFT); 482 SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_RBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_RIGHT); 483 SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_MIDDLE); 484 SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X1); 485 SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X2); 486 } 487 } else { 488 SDL_assert(0 && "Shouldn't happen"); 489 } 490 } 491 } 492 break; 493 494 case WM_MOUSEWHEEL: 495 { 496 static short s_AccumulatedMotion; 497 498 s_AccumulatedMotion += GET_WHEEL_DELTA_WPARAM(wParam); 499 if (s_AccumulatedMotion > 0) { 500 while (s_AccumulatedMotion >= WHEEL_DELTA) { 501 SDL_SendMouseWheel(data->window, 0, 0, 1); 502 s_AccumulatedMotion -= WHEEL_DELTA; 503 } 504 } else { 505 while (s_AccumulatedMotion <= -WHEEL_DELTA) { 506 SDL_SendMouseWheel(data->window, 0, 0, -1); 507 s_AccumulatedMotion += WHEEL_DELTA; 508 } 509 } 510 } 511 break; 512 513 case WM_MOUSEHWHEEL: 514 { 515 static short s_AccumulatedMotion; 516 517 s_AccumulatedMotion += GET_WHEEL_DELTA_WPARAM(wParam); 518 if (s_AccumulatedMotion > 0) { 519 while (s_AccumulatedMotion >= WHEEL_DELTA) { 520 SDL_SendMouseWheel(data->window, 0, 1, 0); 521 s_AccumulatedMotion -= WHEEL_DELTA; 522 } 523 } else { 524 while (s_AccumulatedMotion <= -WHEEL_DELTA) { 525 SDL_SendMouseWheel(data->window, 0, -1, 0); 526 s_AccumulatedMotion += WHEEL_DELTA; 527 } 528 } 529 } 530 break; 531 532#ifdef WM_MOUSELEAVE 533 case WM_MOUSELEAVE: 534 if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode && !(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) { 535 if (!IsIconic(hwnd)) { 536 POINT cursorPos; 537 GetCursorPos(&cursorPos); 538 ScreenToClient(hwnd, &cursorPos); 539 SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y); 540 } 541 SDL_SetMouseFocus(NULL); 542 } 543 returnCode = 0; 544 break; 545#endif /* WM_MOUSELEAVE */ 546 547 case WM_KEYDOWN: 548 case WM_SYSKEYDOWN: 549 { 550 SDL_Scancode code = WindowsScanCodeToSDLScanCode(lParam, wParam); 551 if (code != SDL_SCANCODE_UNKNOWN) { 552 SDL_SendKeyboardKey(SDL_PRESSED, code); 553 } 554 } 555 if (msg == WM_KEYDOWN) { 556 BYTE keyboardState[256]; 557 char text[5]; 558 UINT32 utf32 = 0; 559 560 GetKeyboardState(keyboardState); 561 if (ToUnicode(wParam, (lParam >> 16) & 0xff, keyboardState, (LPWSTR)&utf32, 1, 0) > 0) { 562 WORD repetition; 563 for (repetition = lParam & 0xffff; repetition > 0; repetition--) { 564 WIN_ConvertUTF32toUTF8(utf32, text); 565 SDL_SendKeyboardText(text); 566 } 567 } 568 } 569 returnCode = 0; 570 break; 571 572 case WM_SYSKEYUP: 573 case WM_KEYUP: 574 { 575 SDL_Scancode code = WindowsScanCodeToSDLScanCode(lParam, wParam); 576 const Uint8 *keyboardState = SDL_GetKeyboardState(NULL); 577 578 /* Detect relevant keyboard shortcuts */ 579 if (keyboardState[SDL_SCANCODE_LALT] == SDL_PRESSED || keyboardState[SDL_SCANCODE_RALT] == SDL_PRESSED) { 580 /* ALT+F4: Close window */ 581 if (code == SDL_SCANCODE_F4) { 582 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0); 583 } 584 } 585 586 if (code != SDL_SCANCODE_UNKNOWN) { 587 if (code == SDL_SCANCODE_PRINTSCREEN && 588 keyboardState[code] == SDL_RELEASED) { 589 SDL_SendKeyboardKey(SDL_PRESSED, code); 590 } 591 SDL_SendKeyboardKey(SDL_RELEASED, code); 592 } 593 } 594 returnCode = 0; 595 break; 596 597 case WM_UNICHAR: 598 case WM_CHAR: 599 /* Ignore WM_CHAR messages that come from TranslateMessage(), since we handle WM_KEY* messages directly */ 600 returnCode = 0; 601 break; 602 603#ifdef WM_INPUTLANGCHANGE 604 case WM_INPUTLANGCHANGE: 605 { 606 WIN_UpdateKeymap(); 607 } 608 returnCode = 1; 609 break; 610#endif /* WM_INPUTLANGCHANGE */ 611 612 case WM_NCLBUTTONDOWN: 613 { 614 data->in_title_click = SDL_TRUE; 615 } 616 break; 617 618 case WM_CAPTURECHANGED: 619 { 620 data->in_title_click = SDL_FALSE; 621 622 /* The mouse may have been released during a modal loop */ 623 WIN_CheckAsyncMouseRelease(data); 624 } 625 break; 626 627#ifdef WM_GETMINMAXINFO 628 case WM_GETMINMAXINFO: 629 { 630 MINMAXINFO *info; 631 RECT size; 632 int x, y; 633 int w, h; 634 int min_w, min_h; 635 int max_w, max_h; 636 int style; 637 BOOL menu; 638 BOOL constrain_max_size; 639 640 if (SDL_IsShapedWindow(data->window)) 641 Win32_ResizeWindowShape(data->window); 642 643 /* If this is an expected size change, allow it */ 644 if (data->expected_resize) { 645 break; 646 } 647 648 /* Get the current position of our window */ 649 GetWindowRect(hwnd, &size); 650 x = size.left; 651 y = size.top; 652 653 /* Calculate current size of our window */ 654 SDL_GetWindowSize(data->window, &w, &h); 655 SDL_GetWindowMinimumSize(data->window, &min_w, &min_h); 656 SDL_GetWindowMaximumSize(data->window, &max_w, &max_h); 657 658 /* Store in min_w and min_h difference between current size and minimal 659 size so we don't need to call AdjustWindowRectEx twice */ 660 min_w -= w; 661 min_h -= h; 662 if (max_w && max_h) { 663 max_w -= w; 664 max_h -= h; 665 constrain_max_size = TRUE; 666 } else { 667 constrain_max_size = FALSE; 668 } 669 670 size.top = 0; 671 size.left = 0; 672 size.bottom = h; 673 size.right = w; 674 675 style = GetWindowLong(hwnd, GWL_STYLE); 676 /* DJM - according to the docs for GetMenu(), the 677 return value is undefined if hwnd is a child window. 678 Apparently it's too difficult for MS to check 679 inside their function, so I have to do it here. 680 */ 681 menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); 682 AdjustWindowRectEx(&size, style, menu, 0); 683 w = size.right - size.left; 684 h = size.bottom - size.top; 685 686 /* Fix our size to the current size */ 687 info = (MINMAXINFO *) lParam; 688 if (SDL_GetWindowFlags(data->window) & SDL_WINDOW_RESIZABLE) { 689 info->ptMinTrackSize.x = w + min_w; 690 info->ptMinTrackSize.y = h + min_h; 691 if (constrain_max_size) { 692 info->ptMaxTrackSize.x = w + max_w; 693 info->ptMaxTrackSize.y = h + max_h; 694 } 695 } else { 696 info->ptMaxSize.x = w; 697 info->ptMaxSize.y = h; 698 info->ptMaxPosition.x = x; 699 info->ptMaxPosition.y = y; 700 info->ptMinTrackSize.x = w; 701 info->ptMinTrackSize.y = h; 702 info->ptMaxTrackSize.x = w; 703 info->ptMaxTrackSize.y = h; 704 } 705 } 706 returnCode = 0; 707 break; 708#endif /* WM_GETMINMAXINFO */ 709 710 case WM_WINDOWPOSCHANGED: 711 { 712 RECT rect; 713 int x, y; 714 int w, h; 715 716 if (data->in_border_change) { 717 break; 718 } 719 720 if (!GetClientRect(hwnd, &rect) || IsRectEmpty(&rect)) { 721 break; 722 } 723 ClientToScreen(hwnd, (LPPOINT) & rect); 724 ClientToScreen(hwnd, (LPPOINT) & rect + 1); 725 726 WIN_UpdateClipCursor(data->window); 727 728 x = rect.left; 729 y = rect.top; 730 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED, x, y); 731 732 w = rect.right - rect.left; 733 h = rect.bottom - rect.top; 734 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED, w, 735 h); 736 } 737 break; 738 739 case WM_SIZE: 740 { 741 switch (wParam) { 742 case SIZE_MAXIMIZED: 743 SDL_SendWindowEvent(data->window, 744 SDL_WINDOWEVENT_MAXIMIZED, 0, 0); 745 break; 746 case SIZE_MINIMIZED: 747 SDL_SendWindowEvent(data->window, 748 SDL_WINDOWEVENT_MINIMIZED, 0, 0); 749 break; 750 default: 751 SDL_SendWindowEvent(data->window, 752 SDL_WINDOWEVENT_RESTORED, 0, 0); 753 break; 754 } 755 } 756 break; 757 758 case WM_SETCURSOR: 759 { 760 Uint16 hittest; 761 762 hittest = LOWORD(lParam); 763 if (hittest == HTCLIENT) { 764 SetCursor(SDL_cursor); 765 returnCode = TRUE; 766 } else if (!g_WindowFrameUsableWhileCursorHidden && !SDL_cursor) { 767 SetCursor(NULL); 768 returnCode = TRUE; 769 } 770 } 771 break; 772 773 /* We were occluded, refresh our display */ 774 case WM_PAINT: 775 { 776 RECT rect; 777 if (GetUpdateRect(hwnd, &rect, FALSE)) { 778 ValidateRect(hwnd, NULL); 779 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED, 780 0, 0); 781 } 782 } 783 returnCode = 0; 784 break; 785 786 /* We'll do our own drawing, prevent flicker */ 787 case WM_ERASEBKGND: 788 { 789 } 790 return (1); 791 792#if defined(SC_SCREENSAVE) || defined(SC_MONITORPOWER) 793 case WM_SYSCOMMAND: 794 { 795 /* Don't start the screensaver or blank the monitor in fullscreen apps */ 796 if ((wParam & 0xFFF0) == SC_SCREENSAVE || 797 (wParam & 0xFFF0) == SC_MONITORPOWER) { 798 if (SDL_GetVideoDevice()->suspend_screensaver) { 799 return (0); 800 } 801 } 802 } 803 break; 804#endif /* System has screensaver support */ 805 806 case WM_CLOSE: 807 { 808 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0); 809 } 810 returnCode = 0; 811 break; 812 813 case WM_TOUCH: 814 { 815 UINT i, num_inputs = LOWORD(wParam); 816 PTOUCHINPUT inputs = SDL_stack_alloc(TOUCHINPUT, num_inputs); 817 if (data->videodata->GetTouchInputInfo((HTOUCHINPUT)lParam, num_inputs, inputs, sizeof(TOUCHINPUT))) { 818 RECT rect; 819 float x, y; 820 821 if (!GetClientRect(hwnd, &rect) || 822 (rect.right == rect.left && rect.bottom == rect.top)) { 823 if (inputs) { 824 SDL_stack_free(inputs); 825 } 826 break; 827 } 828 ClientToScreen(hwnd, (LPPOINT) & rect); 829 ClientToScreen(hwnd, (LPPOINT) & rect + 1); 830 rect.top *= 100; 831 rect.left *= 100; 832 rect.bottom *= 100; 833 rect.right *= 100; 834 835 for (i = 0; i < num_inputs; ++i) { 836 PTOUCHINPUT input = &inputs[i]; 837 838 const SDL_TouchID touchId = (SDL_TouchID)((size_t)input->hSource); 839 if (!SDL_GetTouch(touchId)) { 840 if (SDL_AddTouch(touchId, "") < 0) { 841 continue; 842 } 843 } 844 845 /* Get the normalized coordinates for the window */ 846 x = (float)(input->x - rect.left)/(rect.right - rect.left); 847 y = (float)(input->y - rect.top)/(rect.bottom - rect.top); 848 849 if (input->dwFlags & TOUCHEVENTF_DOWN) { 850 SDL_SendTouch(touchId, input->dwID, SDL_TRUE, x, y, 1.0f); 851 } 852 if (input->dwFlags & TOUCHEVENTF_MOVE) { 853 SDL_SendTouchMotion(touchId, input->dwID, x, y, 1.0f); 854 } 855 if (input->dwFlags & TOUCHEVENTF_UP) { 856 SDL_SendTouch(touchId, input->dwID, SDL_FALSE, x, y, 1.0f); 857 } 858 } 859 } 860 SDL_stack_free(inputs); 861 862 data->videodata->CloseTouchInputHandle((HTOUCHINPUT)lParam); 863 return 0; 864 } 865 break; 866 867 case WM_DROPFILES: 868 { 869 UINT i; 870 HDROP drop = (HDROP) wParam; 871 UINT count = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0); 872 for (i = 0; i < count; ++i) { 873 UINT size = DragQueryFile(drop, i, NULL, 0) + 1; 874 LPTSTR buffer = SDL_stack_alloc(TCHAR, size); 875 if (buffer) { 876 if (DragQueryFile(drop, i, buffer, size)) { 877 char *file = WIN_StringToUTF8(buffer); 878 SDL_SendDropFile(file); 879 SDL_free(file); 880 } 881 SDL_stack_free(buffer); 882 } 883 } 884 DragFinish(drop); 885 return 0; 886 } 887 break; 888 889 case WM_NCHITTEST: 890 { 891 SDL_Window *window = data->window; 892 if (window->hit_test) { 893 POINT winpoint = { (int) LOWORD(lParam), (int) HIWORD(lParam) }; 894 if (ScreenToClient(hwnd, &winpoint)) { 895 const SDL_Point point = { (int) winpoint.x, (int) winpoint.y }; 896 const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data); 897 switch (rc) { 898 case SDL_HITTEST_DRAGGABLE: return HTCAPTION; 899 case SDL_HITTEST_RESIZE_TOPLEFT: return HTTOPLEFT; 900 case SDL_HITTEST_RESIZE_TOP: return HTTOP; 901 case SDL_HITTEST_RESIZE_TOPRIGHT: return HTTOPRIGHT; 902 case SDL_HITTEST_RESIZE_RIGHT: return HTRIGHT; 903 case SDL_HITTEST_RESIZE_BOTTOMRIGHT: return HTBOTTOMRIGHT; 904 case SDL_HITTEST_RESIZE_BOTTOM: return HTBOTTOM; 905 case SDL_HITTEST_RESIZE_BOTTOMLEFT: return HTBOTTOMLEFT; 906 case SDL_HITTEST_RESIZE_LEFT: return HTLEFT; 907 case SDL_HITTEST_NORMAL: return HTCLIENT; 908 } 909 } 910 /* If we didn't return, this will call DefWindowProc below. */ 911 } 912 } 913 break; 914 915 } 916 917 /* If there's a window proc, assume it's going to handle messages */ 918 if (data->wndproc) { 919 return CallWindowProc(data->wndproc, hwnd, msg, wParam, lParam); 920 } else if (returnCode >= 0) { 921 return returnCode; 922 } else { 923 return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam); 924 } 925} 926 927void 928WIN_PumpEvents(_THIS) 929{ 930 const Uint8 *keystate; 931 MSG msg; 932 DWORD start_ticks = GetTickCount(); 933 934 if (g_WindowsEnableMessageLoop) { 935 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { 936 /* Always translate the message in case it's a non-SDL window (e.g. with Qt integration) */ 937 TranslateMessage(&msg); 938 DispatchMessage(&msg); 939 940 /* Make sure we don't busy loop here forever if there are lots of events coming in */ 941 if (SDL_TICKS_PASSED(msg.time, start_ticks)) { 942 break; 943 } 944 } 945 } 946 947 /* Windows loses a shift KEYUP event when you have both pressed at once and let go of one. 948 You won't get a KEYUP until both are released, and that keyup will only be for the second 949 key you released. Take heroic measures and check the keystate as of the last handled event, 950 and if we think a key is pressed when Windows doesn't, unstick it in SDL's state. */ 951 keystate = SDL_GetKeyboardState(NULL); 952 if ((keystate[SDL_SCANCODE_LSHIFT] == SDL_PRESSED) && !(GetKeyState(VK_LSHIFT) & 0x8000)) { 953 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT); 954 } 955 if ((keystate[SDL_SCANCODE_RSHIFT] == SDL_PRESSED) && !(GetKeyState(VK_RSHIFT) & 0x8000)) { 956 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RSHIFT); 957 } 958} 959 960static int app_registered = 0; 961LPTSTR SDL_Appname = NULL; 962Uint32 SDL_Appstyle = 0; 963HINSTANCE SDL_Instance = NULL; 964 965/* Register the class for this application */ 966int 967SDL_RegisterApp(char *name, Uint32 style, void *hInst) 968{ 969 WNDCLASS class; 970 971 /* Only do this once... */ 972 if (app_registered) { 973 ++app_registered; 974 return (0); 975 } 976 if (!name && !SDL_Appname) { 977 name = "SDL_app"; 978#if defined(CS_BYTEALIGNCLIENT) || defined(CS_OWNDC) 979 SDL_Appstyle = (CS_BYTEALIGNCLIENT | CS_OWNDC); 980#endif 981 SDL_Instance = hInst ? hInst : GetModuleHandle(NULL); 982 } 983 984 if (name) { 985 SDL_Appname = WIN_UTF8ToString(name); 986 SDL_Appstyle = style; 987 SDL_Instance = hInst ? hInst : GetModuleHandle(NULL); 988 } 989 990 /* Register the application class */ 991 class.hCursor = NULL; 992 class.hIcon = 993 LoadImage(SDL_Instance, SDL_Appname, IMAGE_ICON, 0, 0, 994 LR_DEFAULTCOLOR); 995 class.lpszMenuName = NULL; 996 class.lpszClassName = SDL_Appname; 997 class.hbrBackground = NULL; 998 class.hInstance = SDL_Instance; 999 class.style = SDL_Appstyle; 1000 class.lpfnWndProc = WIN_WindowProc; 1001 class.cbWndExtra = 0; 1002 class.cbClsExtra = 0; 1003 if (!RegisterClass(&class)) { 1004 return SDL_SetError("Couldn't register application class"); 1005 } 1006 1007 app_registered = 1; 1008 return 0; 1009} 1010 1011/* Unregisters the windowclass registered in SDL_RegisterApp above. */ 1012void 1013SDL_UnregisterApp() 1014{ 1015 WNDCLASS class; 1016 1017 /* SDL_RegisterApp might not have been called before */ 1018 if (!app_registered) { 1019 return; 1020 } 1021 --app_registered; 1022 if (app_registered == 0) { 1023 /* Check for any registered window classes. */ 1024 if (GetClassInfo(SDL_Instance, SDL_Appname, &class)) { 1025 UnregisterClass(SDL_Appname, SDL_Instance); 1026 } 1027 SDL_free(SDL_Appname); 1028 SDL_Appname = NULL; 1029 } 1030} 1031 1032#endif /* SDL_VIDEO_DRIVER_WINDOWS */ 1033 1034/* vi: set ts=4 sw=4 expandtab: */