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