src/video/windows/SDL_windowsevents.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 18 May 2018 13:09:30 -0700
changeset 11983 3a50eb90e4b2
parent 11959 4cc8aad8d434
child 12153 de4288fa5b0b
permissions -rw-r--r--
Merged latest changes from Steam Link app
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 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             /* Don't mark the window as shown if it's activated before being shown */
   425             if (!IsWindowVisible(hwnd)) {
   426                 break;
   427             }
   428 
   429             minimized = HIWORD(wParam);
   430             if (!minimized && (LOWORD(wParam) != WA_INACTIVE)) {
   431                 if (LOWORD(wParam) == WA_CLICKACTIVE) {
   432                     if (GetAsyncKeyState(VK_LBUTTON)) {
   433                         data->focus_click_pending |= SDL_BUTTON_LMASK;
   434                     }
   435                     if (GetAsyncKeyState(VK_RBUTTON)) {
   436                         data->focus_click_pending |= SDL_BUTTON_RMASK;
   437                     }
   438                     if (GetAsyncKeyState(VK_MBUTTON)) {
   439                         data->focus_click_pending |= SDL_BUTTON_MMASK;
   440                     }
   441                     if (GetAsyncKeyState(VK_XBUTTON1)) {
   442                         data->focus_click_pending |= SDL_BUTTON_X1MASK;
   443                     }
   444                     if (GetAsyncKeyState(VK_XBUTTON2)) {
   445                         data->focus_click_pending |= SDL_BUTTON_X2MASK;
   446                     }
   447                 }
   448                 
   449                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
   450                 if (SDL_GetKeyboardFocus() != data->window) {
   451                     SDL_SetKeyboardFocus(data->window);
   452                 }
   453 
   454                 GetCursorPos(&cursorPos);
   455                 ScreenToClient(hwnd, &cursorPos);
   456                 SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y);
   457 
   458                 WIN_CheckAsyncMouseRelease(data);
   459 
   460                 /*
   461                  * FIXME: Update keyboard state
   462                  */
   463                 WIN_CheckClipboardUpdate(data->videodata);
   464 
   465                 SDL_ToggleModState(KMOD_CAPS, (GetKeyState(VK_CAPITAL) & 0x0001) != 0);
   466                 SDL_ToggleModState(KMOD_NUM, (GetKeyState(VK_NUMLOCK) & 0x0001) != 0);
   467             } else {
   468                 data->in_window_deactivation = SDL_TRUE;
   469 
   470                 if (SDL_GetKeyboardFocus() == data->window) {
   471                     SDL_SetKeyboardFocus(NULL);
   472                     WIN_ResetDeadKeys();
   473                 }
   474 
   475                 ClipCursor(NULL);
   476 
   477                 data->in_window_deactivation = SDL_FALSE;
   478             }
   479         }
   480         returnCode = 0;
   481         break;
   482 
   483     case WM_MOUSEMOVE:
   484         {
   485             SDL_Mouse *mouse = SDL_GetMouse();
   486             if (!mouse->relative_mode || mouse->relative_mode_warp) {
   487                 SDL_MouseID mouseID = (((GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH) == MOUSEEVENTF_FROMTOUCH) ? SDL_TOUCH_MOUSEID : 0);
   488                 SDL_SendMouseMotion(data->window, mouseID, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
   489                 if (isWin10FCUorNewer && mouseID != SDL_TOUCH_MOUSEID && mouse->relative_mode_warp) {
   490                     /* To work around #3931, Win10 bug introduced in Fall Creators Update, where
   491                        SetCursorPos() (SDL_WarpMouseInWindow()) doesn't reliably generate mouse events anymore,
   492                        after each windows mouse event generate a fake event for the middle of the window
   493                        if relative_mode_warp is used */
   494                     int center_x = 0, center_y = 0;
   495                     SDL_GetWindowSize(data->window, &center_x, &center_y);
   496                     center_x /= 2;
   497                     center_y /= 2;
   498                     SDL_SendMouseMotion(data->window, mouseID, 0, center_x, center_y);
   499                 }
   500             }
   501         }
   502         /* don't break here, fall through to check the wParam like the button presses */
   503     case WM_LBUTTONUP:
   504     case WM_RBUTTONUP:
   505     case WM_MBUTTONUP:
   506     case WM_XBUTTONUP:
   507     case WM_LBUTTONDOWN:
   508     case WM_LBUTTONDBLCLK:
   509     case WM_RBUTTONDOWN:
   510     case WM_RBUTTONDBLCLK:
   511     case WM_MBUTTONDOWN:
   512     case WM_MBUTTONDBLCLK:
   513     case WM_XBUTTONDOWN:
   514     case WM_XBUTTONDBLCLK:
   515         {
   516             SDL_Mouse *mouse = SDL_GetMouse();
   517             if (!mouse->relative_mode || mouse->relative_mode_warp) {
   518                 SDL_MouseID mouseID = (((GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH) == MOUSEEVENTF_FROMTOUCH) ? SDL_TOUCH_MOUSEID : 0);
   519                 WIN_CheckWParamMouseButtons(wParam, data, mouseID);
   520             }
   521         }
   522         break;
   523 
   524     case WM_INPUT:
   525         {
   526             SDL_Mouse *mouse = SDL_GetMouse();
   527             HRAWINPUT hRawInput = (HRAWINPUT)lParam;
   528             RAWINPUT inp;
   529             UINT size = sizeof(inp);
   530             const SDL_bool isRelative = mouse->relative_mode || mouse->relative_mode_warp;
   531             const SDL_bool isCapture = ((data->window->flags & SDL_WINDOW_MOUSE_CAPTURE) != 0);
   532 
   533             if (!isRelative || mouse->focus != data->window) {
   534                 if (!isCapture) {
   535                     break;
   536                 }
   537             }
   538 
   539             GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER));
   540 
   541             /* Mouse data */
   542             if (inp.header.dwType == RIM_TYPEMOUSE) {
   543                 if (isRelative) {
   544                     RAWMOUSE* rawmouse = &inp.data.mouse;
   545 
   546                     if ((rawmouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE) {
   547                         SDL_SendMouseMotion(data->window, 0, 1, (int)rawmouse->lLastX, (int)rawmouse->lLastY);
   548                     } else {
   549                         /* synthesize relative moves from the abs position */
   550                         static SDL_Point initialMousePoint;
   551                         if (initialMousePoint.x == 0 && initialMousePoint.y == 0) {
   552                             initialMousePoint.x = rawmouse->lLastX;
   553                             initialMousePoint.y = rawmouse->lLastY;
   554                         }
   555 
   556                         SDL_SendMouseMotion(data->window, 0, 1, (int)(rawmouse->lLastX-initialMousePoint.x), (int)(rawmouse->lLastY-initialMousePoint.y));
   557 
   558                         initialMousePoint.x = rawmouse->lLastX;
   559                         initialMousePoint.y = rawmouse->lLastY;
   560                     }
   561                     WIN_CheckRawMouseButtons(rawmouse->usButtonFlags, data);
   562                 } else if (isCapture) {
   563                     /* we check for where Windows thinks the system cursor lives in this case, so we don't really lose mouse accel, etc. */
   564                     POINT pt;
   565                     RECT hwndRect;
   566                     HWND currentHnd;
   567 
   568                     GetCursorPos(&pt);
   569                     currentHnd = WindowFromPoint(pt);
   570                     ScreenToClient(hwnd, &pt);
   571                     GetClientRect(hwnd, &hwndRect);
   572 
   573                     /* if in the window, WM_MOUSEMOVE, etc, will cover it. */
   574                     if(currentHnd != hwnd || pt.x < 0 || pt.y < 0 || pt.x > hwndRect.right || pt.y > hwndRect.right) {
   575                         SDL_SendMouseMotion(data->window, 0, 0, (int)pt.x, (int)pt.y);
   576                         SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_LBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_LEFT);
   577                         SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_RBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_RIGHT);
   578                         SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_MIDDLE);
   579                         SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X1);
   580                         SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X2);
   581                     }
   582                 } else {
   583                     SDL_assert(0 && "Shouldn't happen");
   584                 }
   585             }
   586         }
   587         break;
   588 
   589     case WM_MOUSEWHEEL:
   590     case WM_MOUSEHWHEEL:
   591         {
   592             short amount = GET_WHEEL_DELTA_WPARAM(wParam);
   593             float fAmount = (float) amount / WHEEL_DELTA;
   594             if (msg == WM_MOUSEWHEEL)
   595                 SDL_SendMouseWheel(data->window, 0, 0.0f, fAmount, SDL_MOUSEWHEEL_NORMAL);
   596             else
   597                 SDL_SendMouseWheel(data->window, 0, fAmount, 0.0f, SDL_MOUSEWHEEL_NORMAL);
   598         }
   599         break;
   600 
   601 #ifdef WM_MOUSELEAVE
   602     case WM_MOUSELEAVE:
   603         if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode && !(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
   604             if (!IsIconic(hwnd)) {
   605                 POINT cursorPos;
   606                 GetCursorPos(&cursorPos);
   607                 ScreenToClient(hwnd, &cursorPos);
   608                 SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y);
   609             }
   610             SDL_SetMouseFocus(NULL);
   611         }
   612         returnCode = 0;
   613         break;
   614 #endif /* WM_MOUSELEAVE */
   615 
   616     case WM_KEYDOWN:
   617     case WM_SYSKEYDOWN:
   618         {
   619             SDL_Scancode code = WindowsScanCodeToSDLScanCode(lParam, wParam);
   620             const Uint8 *keyboardState = SDL_GetKeyboardState(NULL);
   621 
   622             /* Detect relevant keyboard shortcuts */
   623             if (keyboardState[SDL_SCANCODE_LALT] == SDL_PRESSED || keyboardState[SDL_SCANCODE_RALT] == SDL_PRESSED) {
   624                 /* ALT+F4: Close window */
   625                 if (code == SDL_SCANCODE_F4 && ShouldGenerateWindowCloseOnAltF4()) {
   626                     SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
   627                 }
   628             }
   629 
   630             if (code != SDL_SCANCODE_UNKNOWN) {
   631                 SDL_SendKeyboardKey(SDL_PRESSED, code);
   632             }
   633         }
   634 
   635         returnCode = 0;
   636         break;
   637 
   638     case WM_SYSKEYUP:
   639     case WM_KEYUP:
   640         {
   641             SDL_Scancode code = WindowsScanCodeToSDLScanCode(lParam, wParam);
   642             const Uint8 *keyboardState = SDL_GetKeyboardState(NULL);
   643 
   644             if (code != SDL_SCANCODE_UNKNOWN) {
   645                 if (code == SDL_SCANCODE_PRINTSCREEN &&
   646                     keyboardState[code] == SDL_RELEASED) {
   647                     SDL_SendKeyboardKey(SDL_PRESSED, code);
   648                 }
   649                 SDL_SendKeyboardKey(SDL_RELEASED, code);
   650             }
   651         }
   652         returnCode = 0;
   653         break;
   654 
   655     case WM_UNICHAR:
   656         if ( wParam == UNICODE_NOCHAR ) {
   657             returnCode = 1;
   658             break;
   659         }
   660         /* otherwise fall through to below */
   661     case WM_CHAR:
   662         {
   663             char text[5];
   664             if ( WIN_ConvertUTF32toUTF8( (UINT32)wParam, text ) ) {
   665                 SDL_SendKeyboardText( text );
   666             }
   667         }
   668         returnCode = 0;
   669         break;
   670 
   671 #ifdef WM_INPUTLANGCHANGE
   672     case WM_INPUTLANGCHANGE:
   673         {
   674             WIN_UpdateKeymap();
   675             SDL_SendKeymapChangedEvent();
   676         }
   677         returnCode = 1;
   678         break;
   679 #endif /* WM_INPUTLANGCHANGE */
   680 
   681     case WM_NCLBUTTONDOWN:
   682         {
   683             data->in_title_click = SDL_TRUE;
   684         }
   685         break;
   686 
   687     case WM_CAPTURECHANGED:
   688         {
   689             data->in_title_click = SDL_FALSE;
   690 
   691             /* The mouse may have been released during a modal loop */
   692             WIN_CheckAsyncMouseRelease(data);
   693         }
   694         break;
   695 
   696 #ifdef WM_GETMINMAXINFO
   697     case WM_GETMINMAXINFO:
   698         {
   699             MINMAXINFO *info;
   700             RECT size;
   701             int x, y;
   702             int w, h;
   703             int min_w, min_h;
   704             int max_w, max_h;
   705             BOOL constrain_max_size;
   706 
   707             if (SDL_IsShapedWindow(data->window))
   708                 Win32_ResizeWindowShape(data->window);
   709 
   710             /* If this is an expected size change, allow it */
   711             if (data->expected_resize) {
   712                 break;
   713             }
   714 
   715             /* Get the current position of our window */
   716             GetWindowRect(hwnd, &size);
   717             x = size.left;
   718             y = size.top;
   719 
   720             /* Calculate current size of our window */
   721             SDL_GetWindowSize(data->window, &w, &h);
   722             SDL_GetWindowMinimumSize(data->window, &min_w, &min_h);
   723             SDL_GetWindowMaximumSize(data->window, &max_w, &max_h);
   724 
   725             /* Store in min_w and min_h difference between current size and minimal
   726                size so we don't need to call AdjustWindowRectEx twice */
   727             min_w -= w;
   728             min_h -= h;
   729             if (max_w && max_h) {
   730                 max_w -= w;
   731                 max_h -= h;
   732                 constrain_max_size = TRUE;
   733             } else {
   734                 constrain_max_size = FALSE;
   735             }
   736 
   737             if (!(SDL_GetWindowFlags(data->window) & SDL_WINDOW_BORDERLESS)) {
   738                 LONG style = GetWindowLong(hwnd, GWL_STYLE);
   739                 /* DJM - according to the docs for GetMenu(), the
   740                    return value is undefined if hwnd is a child window.
   741                    Apparently it's too difficult for MS to check
   742                    inside their function, so I have to do it here.
   743                  */
   744                 BOOL menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
   745                 size.top = 0;
   746                 size.left = 0;
   747                 size.bottom = h;
   748                 size.right = w;
   749 
   750                 AdjustWindowRectEx(&size, style, menu, 0);
   751                 w = size.right - size.left;
   752                 h = size.bottom - size.top;
   753             }
   754 
   755             /* Fix our size to the current size */
   756             info = (MINMAXINFO *) lParam;
   757             if (SDL_GetWindowFlags(data->window) & SDL_WINDOW_RESIZABLE) {
   758                 info->ptMinTrackSize.x = w + min_w;
   759                 info->ptMinTrackSize.y = h + min_h;
   760                 if (constrain_max_size) {
   761                     info->ptMaxTrackSize.x = w + max_w;
   762                     info->ptMaxTrackSize.y = h + max_h;
   763                 }
   764             } else {
   765                 info->ptMaxSize.x = w;
   766                 info->ptMaxSize.y = h;
   767                 info->ptMaxPosition.x = x;
   768                 info->ptMaxPosition.y = y;
   769                 info->ptMinTrackSize.x = w;
   770                 info->ptMinTrackSize.y = h;
   771                 info->ptMaxTrackSize.x = w;
   772                 info->ptMaxTrackSize.y = h;
   773             }
   774         }
   775         returnCode = 0;
   776         break;
   777 #endif /* WM_GETMINMAXINFO */
   778 
   779     case WM_WINDOWPOSCHANGING:
   780 
   781         if (data->expected_resize) {
   782             returnCode = 0;
   783         }
   784         break;
   785 
   786     case WM_WINDOWPOSCHANGED:
   787         {
   788             RECT rect;
   789             int x, y;
   790             int w, h;
   791 
   792             if (data->initializing || data->in_border_change) {
   793                 break;
   794             }
   795 
   796             if (!GetClientRect(hwnd, &rect) || IsRectEmpty(&rect)) {
   797                 break;
   798             }
   799             ClientToScreen(hwnd, (LPPOINT) & rect);
   800             ClientToScreen(hwnd, (LPPOINT) & rect + 1);
   801 
   802             WIN_UpdateClipCursor(data->window);
   803 
   804             x = rect.left;
   805             y = rect.top;
   806             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED, x, y);
   807 
   808             w = rect.right - rect.left;
   809             h = rect.bottom - rect.top;
   810             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED, w,
   811                                 h);
   812 
   813             /* Forces a WM_PAINT event */
   814             InvalidateRect(hwnd, NULL, FALSE);
   815         }
   816         break;
   817 
   818     case WM_SIZE:
   819         {
   820             switch (wParam) {
   821             case SIZE_MAXIMIZED:
   822                 SDL_SendWindowEvent(data->window,
   823                     SDL_WINDOWEVENT_RESTORED, 0, 0);
   824                 SDL_SendWindowEvent(data->window,
   825                     SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
   826                 break;
   827             case SIZE_MINIMIZED:
   828                 SDL_SendWindowEvent(data->window,
   829                     SDL_WINDOWEVENT_MINIMIZED, 0, 0);
   830                 break;
   831             default:
   832                 SDL_SendWindowEvent(data->window,
   833                     SDL_WINDOWEVENT_RESTORED, 0, 0);
   834                 break;
   835             }
   836         }
   837         break;
   838 
   839     case WM_SETCURSOR:
   840         {
   841             Uint16 hittest;
   842 
   843             hittest = LOWORD(lParam);
   844             if (hittest == HTCLIENT) {
   845                 SetCursor(SDL_cursor);
   846                 returnCode = TRUE;
   847             } else if (!g_WindowFrameUsableWhileCursorHidden && !SDL_cursor) {
   848                 SetCursor(NULL);
   849                 returnCode = TRUE;
   850             }
   851         }
   852         break;
   853 
   854         /* We were occluded, refresh our display */
   855     case WM_PAINT:
   856         {
   857             RECT rect;
   858             if (GetUpdateRect(hwnd, &rect, FALSE)) {
   859                 ValidateRect(hwnd, NULL);
   860                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED,
   861                                     0, 0);
   862             }
   863         }
   864         returnCode = 0;
   865         break;
   866 
   867         /* We'll do our own drawing, prevent flicker */
   868     case WM_ERASEBKGND:
   869         {
   870         }
   871         return (1);
   872 
   873     case WM_SYSCOMMAND:
   874         {
   875             if ((wParam & 0xFFF0) == SC_KEYMENU) {
   876                 return (0);
   877             }
   878 
   879 #if defined(SC_SCREENSAVE) || defined(SC_MONITORPOWER)
   880             /* Don't start the screensaver or blank the monitor in fullscreen apps */
   881             if ((wParam & 0xFFF0) == SC_SCREENSAVE ||
   882                 (wParam & 0xFFF0) == SC_MONITORPOWER) {
   883                 if (SDL_GetVideoDevice()->suspend_screensaver) {
   884                     return (0);
   885                 }
   886             }
   887 #endif /* System has screensaver support */
   888         }
   889         break;
   890 
   891     case WM_CLOSE:
   892         {
   893             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
   894         }
   895         returnCode = 0;
   896         break;
   897 
   898     case WM_TOUCH:
   899         if (data->videodata->GetTouchInputInfo && data->videodata->CloseTouchInputHandle) {
   900             UINT i, num_inputs = LOWORD(wParam);
   901             PTOUCHINPUT inputs = SDL_stack_alloc(TOUCHINPUT, num_inputs);
   902             if (data->videodata->GetTouchInputInfo((HTOUCHINPUT)lParam, num_inputs, inputs, sizeof(TOUCHINPUT))) {
   903                 RECT rect;
   904                 float x, y;
   905 
   906                 if (!GetClientRect(hwnd, &rect) ||
   907                     (rect.right == rect.left && rect.bottom == rect.top)) {
   908                     if (inputs) {
   909                         SDL_stack_free(inputs);
   910                     }
   911                     break;
   912                 }
   913                 ClientToScreen(hwnd, (LPPOINT) & rect);
   914                 ClientToScreen(hwnd, (LPPOINT) & rect + 1);
   915                 rect.top *= 100;
   916                 rect.left *= 100;
   917                 rect.bottom *= 100;
   918                 rect.right *= 100;
   919 
   920                 for (i = 0; i < num_inputs; ++i) {
   921                     PTOUCHINPUT input = &inputs[i];
   922 
   923                     const SDL_TouchID touchId = (SDL_TouchID)((size_t)input->hSource);
   924                     if (SDL_AddTouch(touchId, "") < 0) {
   925                         continue;
   926                     }
   927 
   928                     /* Get the normalized coordinates for the window */
   929                     x = (float)(input->x - rect.left)/(rect.right - rect.left);
   930                     y = (float)(input->y - rect.top)/(rect.bottom - rect.top);
   931 
   932                     if (input->dwFlags & TOUCHEVENTF_DOWN) {
   933                         SDL_SendTouch(touchId, input->dwID, SDL_TRUE, x, y, 1.0f);
   934                     }
   935                     if (input->dwFlags & TOUCHEVENTF_MOVE) {
   936                         SDL_SendTouchMotion(touchId, input->dwID, x, y, 1.0f);
   937                     }
   938                     if (input->dwFlags & TOUCHEVENTF_UP) {
   939                         SDL_SendTouch(touchId, input->dwID, SDL_FALSE, x, y, 1.0f);
   940                     }
   941                 }
   942             }
   943             SDL_stack_free(inputs);
   944 
   945             data->videodata->CloseTouchInputHandle((HTOUCHINPUT)lParam);
   946             return 0;
   947         }
   948         break;
   949 
   950     case WM_DROPFILES:
   951         {
   952             UINT i;
   953             HDROP drop = (HDROP) wParam;
   954             UINT count = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0);
   955             for (i = 0; i < count; ++i) {
   956                 UINT size = DragQueryFile(drop, i, NULL, 0) + 1;
   957                 LPTSTR buffer = SDL_stack_alloc(TCHAR, size);
   958                 if (buffer) {
   959                     if (DragQueryFile(drop, i, buffer, size)) {
   960                         char *file = WIN_StringToUTF8(buffer);
   961                         SDL_SendDropFile(data->window, file);
   962                         SDL_free(file);
   963                     }
   964                     SDL_stack_free(buffer);
   965                 }
   966             }
   967             SDL_SendDropComplete(data->window);
   968             DragFinish(drop);
   969             return 0;
   970         }
   971         break;
   972 
   973     case WM_NCCALCSIZE:
   974         {
   975             Uint32 window_flags = SDL_GetWindowFlags(data->window);
   976             if (wParam == TRUE && (window_flags & SDL_WINDOW_BORDERLESS) && !(window_flags & SDL_WINDOW_FULLSCREEN)) {
   977                 /* When borderless, need to tell windows that the size of the non-client area is 0 */
   978                 if (!(window_flags & SDL_WINDOW_RESIZABLE)) {
   979                     int w, h;
   980                     NCCALCSIZE_PARAMS *params = (NCCALCSIZE_PARAMS *)lParam;
   981                     w = data->window->windowed.w;
   982                     h = data->window->windowed.h;
   983                     params->rgrc[0].right = params->rgrc[0].left + w;
   984                     params->rgrc[0].bottom = params->rgrc[0].top + h;
   985                 }
   986                 return 0;
   987             }
   988         }
   989         break;
   990 
   991     case WM_NCHITTEST:
   992         {
   993             SDL_Window *window = data->window;
   994             if (window->hit_test) {
   995                 POINT winpoint = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
   996                 if (ScreenToClient(hwnd, &winpoint)) {
   997                     const SDL_Point point = { (int) winpoint.x, (int) winpoint.y };
   998                     const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
   999                     switch (rc) {
  1000                         #define POST_HIT_TEST(ret) { SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIT_TEST, 0, 0); return ret; }
  1001                         case SDL_HITTEST_DRAGGABLE: POST_HIT_TEST(HTCAPTION);
  1002                         case SDL_HITTEST_RESIZE_TOPLEFT: POST_HIT_TEST(HTTOPLEFT);
  1003                         case SDL_HITTEST_RESIZE_TOP: POST_HIT_TEST(HTTOP);
  1004                         case SDL_HITTEST_RESIZE_TOPRIGHT: POST_HIT_TEST(HTTOPRIGHT);
  1005                         case SDL_HITTEST_RESIZE_RIGHT: POST_HIT_TEST(HTRIGHT);
  1006                         case SDL_HITTEST_RESIZE_BOTTOMRIGHT: POST_HIT_TEST(HTBOTTOMRIGHT);
  1007                         case SDL_HITTEST_RESIZE_BOTTOM: POST_HIT_TEST(HTBOTTOM);
  1008                         case SDL_HITTEST_RESIZE_BOTTOMLEFT: POST_HIT_TEST(HTBOTTOMLEFT);
  1009                         case SDL_HITTEST_RESIZE_LEFT: POST_HIT_TEST(HTLEFT);
  1010                         #undef POST_HIT_TEST
  1011                         case SDL_HITTEST_NORMAL: return HTCLIENT;
  1012                     }
  1013                 }
  1014                 /* If we didn't return, this will call DefWindowProc below. */
  1015             }
  1016         }
  1017         break;
  1018     }
  1019 
  1020     /* If there's a window proc, assume it's going to handle messages */
  1021     if (data->wndproc) {
  1022         return CallWindowProc(data->wndproc, hwnd, msg, wParam, lParam);
  1023     } else if (returnCode >= 0) {
  1024         return returnCode;
  1025     } else {
  1026         return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
  1027     }
  1028 }
  1029 
  1030 /* A message hook called before TranslateMessage() */
  1031 static SDL_WindowsMessageHook g_WindowsMessageHook = NULL;
  1032 static void *g_WindowsMessageHookData = NULL;
  1033 
  1034 void SDL_SetWindowsMessageHook(SDL_WindowsMessageHook callback, void *userdata)
  1035 {
  1036     g_WindowsMessageHook = callback;
  1037     g_WindowsMessageHookData = userdata;
  1038 }
  1039 
  1040 void
  1041 WIN_PumpEvents(_THIS)
  1042 {
  1043     const Uint8 *keystate;
  1044     MSG msg;
  1045     DWORD start_ticks = GetTickCount();
  1046 
  1047     if (g_WindowsEnableMessageLoop) {
  1048         while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  1049             if (g_WindowsMessageHook) {
  1050                 g_WindowsMessageHook(g_WindowsMessageHookData, msg.hwnd, msg.message, msg.wParam, msg.lParam);
  1051             }
  1052 
  1053             /* Always translate the message in case it's a non-SDL window (e.g. with Qt integration) */
  1054             TranslateMessage(&msg);
  1055             DispatchMessage(&msg);
  1056 
  1057             /* Make sure we don't busy loop here forever if there are lots of events coming in */
  1058             if (SDL_TICKS_PASSED(msg.time, start_ticks)) {
  1059                 break;
  1060             }
  1061         }
  1062     }
  1063 
  1064     /* Windows loses a shift KEYUP event when you have both pressed at once and let go of one.
  1065        You won't get a KEYUP until both are released, and that keyup will only be for the second
  1066        key you released. Take heroic measures and check the keystate as of the last handled event,
  1067        and if we think a key is pressed when Windows doesn't, unstick it in SDL's state. */
  1068     keystate = SDL_GetKeyboardState(NULL);
  1069     if ((keystate[SDL_SCANCODE_LSHIFT] == SDL_PRESSED) && !(GetKeyState(VK_LSHIFT) & 0x8000)) {
  1070         SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
  1071     }
  1072     if ((keystate[SDL_SCANCODE_RSHIFT] == SDL_PRESSED) && !(GetKeyState(VK_RSHIFT) & 0x8000)) {
  1073         SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RSHIFT);
  1074     }
  1075 }
  1076 
  1077 /* to work around #3931, a bug introduced in Win10 Fall Creators Update (build nr. 16299)
  1078    we need to detect the windows version. this struct and the function below does that.
  1079    usually this struct and the corresponding function (RtlGetVersion) are in <Ntddk.h>
  1080    but here we just load it dynamically */
  1081 struct SDL_WIN_OSVERSIONINFOW {
  1082     ULONG dwOSVersionInfoSize;
  1083     ULONG dwMajorVersion;
  1084     ULONG dwMinorVersion;
  1085     ULONG dwBuildNumber;
  1086     ULONG dwPlatformId;
  1087     WCHAR szCSDVersion[128];
  1088 };
  1089 
  1090 static SDL_bool
  1091 IsWin10FCUorNewer(void)
  1092 {
  1093     HMODULE handle = GetModuleHandleW(L"ntdll.dll");
  1094     if (handle) {
  1095         typedef LONG(WINAPI* RtlGetVersionPtr)(struct SDL_WIN_OSVERSIONINFOW*);
  1096         RtlGetVersionPtr getVersionPtr = (RtlGetVersionPtr)GetProcAddress(handle, "RtlGetVersion");
  1097         if (getVersionPtr != NULL) {
  1098             struct SDL_WIN_OSVERSIONINFOW info;
  1099             SDL_zero(info);
  1100             info.dwOSVersionInfoSize = sizeof(info);
  1101             if (getVersionPtr(&info) == 0) { /* STATUS_SUCCESS == 0 */
  1102                 if (   (info.dwMajorVersion == 10 && info.dwMinorVersion == 0 && info.dwBuildNumber >= 16299)
  1103                     || (info.dwMajorVersion == 10 && info.dwMinorVersion > 0)
  1104                     || (info.dwMajorVersion > 10) )
  1105                 {
  1106                     return SDL_TRUE;
  1107                 }
  1108             }
  1109         }
  1110     }
  1111     return SDL_FALSE;
  1112 }
  1113 
  1114 static int app_registered = 0;
  1115 LPTSTR SDL_Appname = NULL;
  1116 Uint32 SDL_Appstyle = 0;
  1117 HINSTANCE SDL_Instance = NULL;
  1118 
  1119 /* Register the class for this application */
  1120 int
  1121 SDL_RegisterApp(char *name, Uint32 style, void *hInst)
  1122 {
  1123     const char *hint;
  1124     WNDCLASSEX wcex;
  1125     TCHAR path[MAX_PATH];
  1126 
  1127     /* Only do this once... */
  1128     if (app_registered) {
  1129         ++app_registered;
  1130         return (0);
  1131     }
  1132     if (!name && !SDL_Appname) {
  1133         name = "SDL_app";
  1134 #if defined(CS_BYTEALIGNCLIENT) || defined(CS_OWNDC)
  1135         SDL_Appstyle = (CS_BYTEALIGNCLIENT | CS_OWNDC);
  1136 #endif
  1137         SDL_Instance = hInst ? hInst : GetModuleHandle(NULL);
  1138     }
  1139 
  1140     if (name) {
  1141         SDL_Appname = WIN_UTF8ToString(name);
  1142         SDL_Appstyle = style;
  1143         SDL_Instance = hInst ? hInst : GetModuleHandle(NULL);
  1144     }
  1145 
  1146     /* Register the application class */
  1147     wcex.cbSize         = sizeof(WNDCLASSEX);
  1148     wcex.hCursor        = NULL;
  1149     wcex.hIcon          = NULL;
  1150     wcex.hIconSm        = NULL;
  1151     wcex.lpszMenuName   = NULL;
  1152     wcex.lpszClassName  = SDL_Appname;
  1153     wcex.style          = SDL_Appstyle;
  1154     wcex.hbrBackground  = NULL;
  1155     wcex.lpfnWndProc    = WIN_WindowProc;
  1156     wcex.hInstance      = SDL_Instance;
  1157     wcex.cbClsExtra     = 0;
  1158     wcex.cbWndExtra     = 0;
  1159 
  1160     hint = SDL_GetHint(SDL_HINT_WINDOWS_INTRESOURCE_ICON);
  1161     if (hint && *hint) {
  1162         wcex.hIcon = LoadIcon(SDL_Instance, MAKEINTRESOURCE(SDL_atoi(hint)));
  1163 
  1164         hint = SDL_GetHint(SDL_HINT_WINDOWS_INTRESOURCE_ICON_SMALL);
  1165         if (hint && *hint) {
  1166             wcex.hIconSm = LoadIcon(SDL_Instance, MAKEINTRESOURCE(SDL_atoi(hint)));
  1167         }
  1168     } else {
  1169         /* Use the first icon as a default icon, like in the Explorer */
  1170         GetModuleFileName(SDL_Instance, path, MAX_PATH);
  1171         ExtractIconEx(path, 0, &wcex.hIcon, &wcex.hIconSm, 1);
  1172     }
  1173 
  1174     if (!RegisterClassEx(&wcex)) {
  1175         return SDL_SetError("Couldn't register application class");
  1176     }
  1177 
  1178     isWin10FCUorNewer = IsWin10FCUorNewer();
  1179 
  1180     app_registered = 1;
  1181     return 0;
  1182 }
  1183 
  1184 /* Unregisters the windowclass registered in SDL_RegisterApp above. */
  1185 void
  1186 SDL_UnregisterApp()
  1187 {
  1188     WNDCLASSEX wcex;
  1189 
  1190     /* SDL_RegisterApp might not have been called before */
  1191     if (!app_registered) {
  1192         return;
  1193     }
  1194     --app_registered;
  1195     if (app_registered == 0) {
  1196         /* Check for any registered window classes. */
  1197         if (GetClassInfoEx(SDL_Instance, SDL_Appname, &wcex)) {
  1198             UnregisterClass(SDL_Appname, SDL_Instance);
  1199             if (wcex.hIcon) DestroyIcon(wcex.hIcon);
  1200             if (wcex.hIconSm) DestroyIcon(wcex.hIconSm);
  1201         }
  1202         SDL_free(SDL_Appname);
  1203         SDL_Appname = NULL;
  1204     }
  1205 }
  1206 
  1207 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
  1208 
  1209 /* vi: set ts=4 sw=4 expandtab: */