src/video/windows/SDL_windowsevents.c
author Cameron Gutman <aicommander@gmail.com>
Sun, 07 Jul 2019 11:23:16 -0700
changeset 12930 92e72926b7f5
parent 12824 2f882d435abf
child 12943 49190e92b7d1
permissions -rw-r--r--
Ignore synthetic mouse events generated for touchscreens

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