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