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