src/video/windows/SDL_windowsevents.c
author Ozkan Sezer <sezeroz@gmail.com>
Sat, 11 Jul 2020 08:10:02 +0300
changeset 13945 465afae5eb7e
parent 13907 ff702ce6ab48
permissions -rw-r--r--
fix bug #5228 -- Add AltiVec detection for FreeBSD.

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