src/video/windows/SDL_windowsevents.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 19 May 2019 12:06:58 -0700
changeset 12740 8a533504ed44
parent 12684 16a389d01a4a
permissions -rw-r--r--
Fixed bug 4474 - Add support for an ASUS Gamepad variation

Trent Gamblin

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