src/video/windows/SDL_windowsevents.c
author Alex Szpakowski <slime73@gmail.com>
Sat, 10 Nov 2018 16:15:48 -0400
changeset 12404 eb60e952b13f
parent 12382 03d0bddca61b
child 12503 806492103856
permissions -rw-r--r--
Add SDL_TouchDeviceType enum and SDL_GetTouchDeviceType(SDL_TouchID id).

Touch device types include SDL_TOUCH_DEVICE_DIRECT (a touch screen with window-relative coordinates for touches), SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE (a trackpad-style device with absolute device coordinates), and SDL_TOUCH_DEVICE_INDIRECT_RELATIVE (a trackpad-style device with screen cursor-relative coordinates).

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