sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_windowsevents.c (51232B)


      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_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 = GetModuleHandleW(L"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: */