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