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