src/video/windows/SDL_windowsevents.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 07 Jul 2014 10:26:28 -0700
changeset 8976 1a5d959d7b32
parent 8953 dc80dc0bd22e
child 9257 6f41196c2d6b
permissions -rw-r--r--
Fixed mingw64 build and warnings
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 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_syswm.h"
    28 #include "SDL_timer.h"
    29 #include "SDL_vkeys.h"
    30 #include "../../events/SDL_events_c.h"
    31 #include "../../events/SDL_touch_c.h"
    32 #include "../../events/scancodes_windows.h"
    33 #include "SDL_assert.h"
    34 
    35 /* Dropfile support */
    36 #include <shellapi.h>
    37 
    38 /* For GET_X_LPARAM, GET_Y_LPARAM. */
    39 #include <windowsx.h>
    40 
    41 /* #define WMMSG_DEBUG */
    42 #ifdef WMMSG_DEBUG
    43 #include <stdio.h>
    44 #include "wmmsg.h"
    45 #endif
    46 
    47 /* Masks for processing the windows KEYDOWN and KEYUP messages */
    48 #define REPEATED_KEYMASK    (1<<30)
    49 #define EXTENDED_KEYMASK    (1<<24)
    50 
    51 #define VK_ENTER    10          /* Keypad Enter ... no VKEY defined? */
    52 #ifndef VK_OEM_NEC_EQUAL
    53 #define VK_OEM_NEC_EQUAL 0x92
    54 #endif
    55 
    56 /* Make sure XBUTTON stuff is defined that isn't in older Platform SDKs... */
    57 #ifndef WM_XBUTTONDOWN
    58 #define WM_XBUTTONDOWN 0x020B
    59 #endif
    60 #ifndef WM_XBUTTONUP
    61 #define WM_XBUTTONUP 0x020C
    62 #endif
    63 #ifndef GET_XBUTTON_WPARAM
    64 #define GET_XBUTTON_WPARAM(w) (HIWORD(w))
    65 #endif
    66 #ifndef WM_INPUT
    67 #define WM_INPUT 0x00ff
    68 #endif
    69 #ifndef WM_TOUCH
    70 #define WM_TOUCH 0x0240
    71 #endif
    72 #ifndef WM_MOUSEHWHEEL
    73 #define WM_MOUSEHWHEEL 0x020E
    74 #endif
    75 #ifndef WM_UNICHAR
    76 #define WM_UNICHAR 0x0109
    77 #endif
    78 
    79 static SDL_Scancode
    80 WindowsScanCodeToSDLScanCode(LPARAM lParam, WPARAM wParam)
    81 {
    82     SDL_Scancode code;
    83     char bIsExtended;
    84     int nScanCode = (lParam >> 16) & 0xFF;
    85 
    86     /* 0x45 here to work around both pause and numlock sharing the same scancode, so use the VK key to tell them apart */
    87     if (nScanCode == 0 || nScanCode == 0x45) {
    88         switch(wParam) {
    89         case VK_CLEAR: return SDL_SCANCODE_CLEAR;
    90         case VK_MODECHANGE: return SDL_SCANCODE_MODE;
    91         case VK_SELECT: return SDL_SCANCODE_SELECT;
    92         case VK_EXECUTE: return SDL_SCANCODE_EXECUTE;
    93         case VK_HELP: return SDL_SCANCODE_HELP;
    94         case VK_PAUSE: return SDL_SCANCODE_PAUSE;
    95         case VK_NUMLOCK: return SDL_SCANCODE_NUMLOCKCLEAR;
    96 
    97         case VK_F13: return SDL_SCANCODE_F13;
    98         case VK_F14: return SDL_SCANCODE_F14;
    99         case VK_F15: return SDL_SCANCODE_F15;
   100         case VK_F16: return SDL_SCANCODE_F16;
   101         case VK_F17: return SDL_SCANCODE_F17;
   102         case VK_F18: return SDL_SCANCODE_F18;
   103         case VK_F19: return SDL_SCANCODE_F19;
   104         case VK_F20: return SDL_SCANCODE_F20;
   105         case VK_F21: return SDL_SCANCODE_F21;
   106         case VK_F22: return SDL_SCANCODE_F22;
   107         case VK_F23: return SDL_SCANCODE_F23;
   108         case VK_F24: return SDL_SCANCODE_F24;
   109 
   110         case VK_OEM_NEC_EQUAL: return SDL_SCANCODE_KP_EQUALS;
   111         case VK_BROWSER_BACK: return SDL_SCANCODE_AC_BACK;
   112         case VK_BROWSER_FORWARD: return SDL_SCANCODE_AC_FORWARD;
   113         case VK_BROWSER_REFRESH: return SDL_SCANCODE_AC_REFRESH;
   114         case VK_BROWSER_STOP: return SDL_SCANCODE_AC_STOP;
   115         case VK_BROWSER_SEARCH: return SDL_SCANCODE_AC_SEARCH;
   116         case VK_BROWSER_FAVORITES: return SDL_SCANCODE_AC_BOOKMARKS;
   117         case VK_BROWSER_HOME: return SDL_SCANCODE_AC_HOME;
   118         case VK_VOLUME_MUTE: return SDL_SCANCODE_AUDIOMUTE;
   119         case VK_VOLUME_DOWN: return SDL_SCANCODE_VOLUMEDOWN;
   120         case VK_VOLUME_UP: return SDL_SCANCODE_VOLUMEUP;
   121 
   122         case VK_MEDIA_NEXT_TRACK: return SDL_SCANCODE_AUDIONEXT;
   123         case VK_MEDIA_PREV_TRACK: return SDL_SCANCODE_AUDIOPREV;
   124         case VK_MEDIA_STOP: return SDL_SCANCODE_AUDIOSTOP;
   125         case VK_MEDIA_PLAY_PAUSE: return SDL_SCANCODE_AUDIOPLAY;
   126         case VK_LAUNCH_MAIL: return SDL_SCANCODE_MAIL;
   127         case VK_LAUNCH_MEDIA_SELECT: return SDL_SCANCODE_MEDIASELECT;
   128 
   129         case VK_OEM_102: return SDL_SCANCODE_NONUSBACKSLASH;
   130 
   131         case VK_ATTN: return SDL_SCANCODE_SYSREQ;
   132         case VK_CRSEL: return SDL_SCANCODE_CRSEL;
   133         case VK_EXSEL: return SDL_SCANCODE_EXSEL;
   134         case VK_OEM_CLEAR: return SDL_SCANCODE_CLEAR;
   135 
   136         case VK_LAUNCH_APP1: return SDL_SCANCODE_APP1;
   137         case VK_LAUNCH_APP2: return SDL_SCANCODE_APP2;
   138 
   139         default: return SDL_SCANCODE_UNKNOWN;
   140         }
   141     }
   142 
   143     if (nScanCode > 127)
   144         return SDL_SCANCODE_UNKNOWN;
   145 
   146     code = windows_scancode_table[nScanCode];
   147 
   148     bIsExtended = (lParam & (1 << 24)) != 0;
   149     if (!bIsExtended) {
   150         switch (code) {
   151         case SDL_SCANCODE_HOME:
   152             return SDL_SCANCODE_KP_7;
   153         case SDL_SCANCODE_UP:
   154             return SDL_SCANCODE_KP_8;
   155         case SDL_SCANCODE_PAGEUP:
   156             return SDL_SCANCODE_KP_9;
   157         case SDL_SCANCODE_LEFT:
   158             return SDL_SCANCODE_KP_4;
   159         case SDL_SCANCODE_RIGHT:
   160             return SDL_SCANCODE_KP_6;
   161         case SDL_SCANCODE_END:
   162             return SDL_SCANCODE_KP_1;
   163         case SDL_SCANCODE_DOWN:
   164             return SDL_SCANCODE_KP_2;
   165         case SDL_SCANCODE_PAGEDOWN:
   166             return SDL_SCANCODE_KP_3;
   167         case SDL_SCANCODE_INSERT:
   168             return SDL_SCANCODE_KP_0;
   169         case SDL_SCANCODE_DELETE:
   170             return SDL_SCANCODE_KP_PERIOD;
   171         case SDL_SCANCODE_PRINTSCREEN:
   172             return SDL_SCANCODE_KP_MULTIPLY;
   173         default:
   174             break;
   175         }
   176     } else {
   177         switch (code) {
   178         case SDL_SCANCODE_RETURN:
   179             return SDL_SCANCODE_KP_ENTER;
   180         case SDL_SCANCODE_LALT:
   181             return SDL_SCANCODE_RALT;
   182         case SDL_SCANCODE_LCTRL:
   183             return SDL_SCANCODE_RCTRL;
   184         case SDL_SCANCODE_SLASH:
   185             return SDL_SCANCODE_KP_DIVIDE;
   186         case SDL_SCANCODE_CAPSLOCK:
   187             return SDL_SCANCODE_KP_PLUS;
   188         default:
   189             break;
   190         }
   191     }
   192 
   193     return code;
   194 }
   195 
   196 
   197 void
   198 WIN_CheckWParamMouseButton(SDL_bool bwParamMousePressed, SDL_bool bSDLMousePressed, SDL_WindowData *data, Uint8 button)
   199 {
   200     if (data->focus_click_pending && button == SDL_BUTTON_LEFT && !bwParamMousePressed) {
   201         data->focus_click_pending = SDL_FALSE;
   202         WIN_UpdateClipCursor(data->window);
   203     }
   204 
   205     if (bwParamMousePressed && !bSDLMousePressed) {
   206         SDL_SendMouseButton(data->window, 0, SDL_PRESSED, button);
   207     } else if (!bwParamMousePressed && bSDLMousePressed) {
   208         SDL_SendMouseButton(data->window, 0, SDL_RELEASED, button);
   209     }
   210 }
   211 
   212 /*
   213 * Some windows systems fail to send a WM_LBUTTONDOWN sometimes, but each mouse move contains the current button state also
   214 *  so this funciton reconciles our view of the world with the current buttons reported by windows
   215 */
   216 void
   217 WIN_CheckWParamMouseButtons(WPARAM wParam, SDL_WindowData *data)
   218 {
   219     if (wParam != data->mouse_button_flags) {
   220         Uint32 mouseFlags = SDL_GetMouseState(NULL, NULL);
   221         WIN_CheckWParamMouseButton((wParam & MK_LBUTTON), (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT);
   222         WIN_CheckWParamMouseButton((wParam & MK_MBUTTON), (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE);
   223         WIN_CheckWParamMouseButton((wParam & MK_RBUTTON), (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT);
   224         WIN_CheckWParamMouseButton((wParam & MK_XBUTTON1), (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1);
   225         WIN_CheckWParamMouseButton((wParam & MK_XBUTTON2), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2);
   226         data->mouse_button_flags = wParam;
   227     }
   228 }
   229 
   230 
   231 void
   232 WIN_CheckRawMouseButtons(ULONG rawButtons, SDL_WindowData *data)
   233 {
   234     if (rawButtons != data->mouse_button_flags) {
   235         Uint32 mouseFlags = SDL_GetMouseState(NULL, NULL);
   236         if ((rawButtons & RI_MOUSE_BUTTON_1_DOWN))
   237             WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_1_DOWN), (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT);
   238         if ((rawButtons & RI_MOUSE_BUTTON_1_UP))
   239             WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_1_UP), (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT);
   240         if ((rawButtons & RI_MOUSE_BUTTON_2_DOWN))
   241             WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_2_DOWN), (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT);
   242         if ((rawButtons & RI_MOUSE_BUTTON_2_UP))
   243             WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_2_UP), (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT);
   244         if ((rawButtons & RI_MOUSE_BUTTON_3_DOWN))
   245             WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_3_DOWN), (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE);
   246         if ((rawButtons & RI_MOUSE_BUTTON_3_UP))
   247             WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_3_UP), (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE);
   248         if ((rawButtons & RI_MOUSE_BUTTON_4_DOWN))
   249             WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_4_DOWN), (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1);
   250         if ((rawButtons & RI_MOUSE_BUTTON_4_UP))
   251             WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_4_UP), (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1);
   252         if ((rawButtons & RI_MOUSE_BUTTON_5_DOWN))
   253             WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_5_DOWN), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2);
   254         if ((rawButtons & RI_MOUSE_BUTTON_5_UP))
   255             WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_5_UP), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2);
   256         data->mouse_button_flags = rawButtons;
   257     }
   258 }
   259 
   260 void
   261 WIN_CheckAsyncMouseRelease(SDL_WindowData *data)
   262 {
   263     Uint32 mouseFlags;
   264     SHORT keyState;
   265 
   266     /* mouse buttons may have changed state here, we need to resync them,
   267        but we will get a WM_MOUSEMOVE right away which will fix things up if in non raw mode also
   268     */
   269     mouseFlags = SDL_GetMouseState(NULL, NULL);
   270 
   271     keyState = GetAsyncKeyState(VK_LBUTTON);
   272     if (!(keyState & 0x8000)) {
   273         WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT);
   274     }
   275     keyState = GetAsyncKeyState(VK_RBUTTON);
   276     if (!(keyState & 0x8000)) {
   277         WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT);
   278     }
   279     keyState = GetAsyncKeyState(VK_MBUTTON);
   280     if (!(keyState & 0x8000)) {
   281         WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE);
   282     }
   283     keyState = GetAsyncKeyState(VK_XBUTTON1);
   284     if (!(keyState & 0x8000)) {
   285         WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1);
   286     }
   287     keyState = GetAsyncKeyState(VK_XBUTTON2);
   288     if (!(keyState & 0x8000)) {
   289         WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2);
   290     }
   291     data->mouse_button_flags = 0;
   292 }
   293 
   294 SDL_FORCE_INLINE BOOL
   295 WIN_ConvertUTF32toUTF8(UINT32 codepoint, char * text)
   296 {
   297     if (codepoint <= 0x7F) {
   298         text[0] = (char) codepoint;
   299         text[1] = '\0';
   300     } else if (codepoint <= 0x7FF) {
   301         text[0] = 0xC0 | (char) ((codepoint >> 6) & 0x1F);
   302         text[1] = 0x80 | (char) (codepoint & 0x3F);
   303         text[2] = '\0';
   304     } else if (codepoint <= 0xFFFF) {
   305         text[0] = 0xE0 | (char) ((codepoint >> 12) & 0x0F);
   306         text[1] = 0x80 | (char) ((codepoint >> 6) & 0x3F);
   307         text[2] = 0x80 | (char) (codepoint & 0x3F);
   308         text[3] = '\0';
   309     } else if (codepoint <= 0x10FFFF) {
   310         text[0] = 0xF0 | (char) ((codepoint >> 18) & 0x0F);
   311         text[1] = 0x80 | (char) ((codepoint >> 12) & 0x3F);
   312         text[2] = 0x80 | (char) ((codepoint >> 6) & 0x3F);
   313         text[3] = 0x80 | (char) (codepoint & 0x3F);
   314         text[4] = '\0';
   315     } else {
   316         return SDL_FALSE;
   317     }
   318     return SDL_TRUE;
   319 }
   320 
   321 LRESULT CALLBACK
   322 WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
   323 {
   324     SDL_WindowData *data;
   325     LRESULT returnCode = -1;
   326 
   327     /* Send a SDL_SYSWMEVENT if the application wants them */
   328     if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) {
   329         SDL_SysWMmsg wmmsg;
   330 
   331         SDL_VERSION(&wmmsg.version);
   332         wmmsg.subsystem = SDL_SYSWM_WINDOWS;
   333         wmmsg.msg.win.hwnd = hwnd;
   334         wmmsg.msg.win.msg = msg;
   335         wmmsg.msg.win.wParam = wParam;
   336         wmmsg.msg.win.lParam = lParam;
   337         SDL_SendSysWMEvent(&wmmsg);
   338     }
   339 
   340     /* Get the window data for the window */
   341     data = (SDL_WindowData *) GetProp(hwnd, TEXT("SDL_WindowData"));
   342     if (!data) {
   343         return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
   344     }
   345 
   346 #ifdef WMMSG_DEBUG
   347     {
   348         char message[1024];
   349         if (msg > MAX_WMMSG) {
   350             SDL_snprintf(message, sizeof(message), "Received windows message: %p UNKNOWN (%d) -- 0x%X, 0x%X\n", hwnd, msg, wParam, lParam);
   351         } else {
   352             SDL_snprintf(message, sizeof(message), "Received windows message: %p %s -- 0x%X, 0x%X\n", hwnd, wmtab[msg], wParam, lParam);
   353         }
   354         OutputDebugStringA(message);
   355     }
   356 #endif /* WMMSG_DEBUG */
   357 
   358     if (IME_HandleMessage(hwnd, msg, wParam, &lParam, data->videodata))
   359         return 0;
   360 
   361     switch (msg) {
   362 
   363     case WM_SHOWWINDOW:
   364         {
   365             if (wParam) {
   366                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
   367             } else {
   368                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
   369             }
   370         }
   371         break;
   372 
   373     case WM_ACTIVATE:
   374         {
   375             POINT cursorPos;
   376             BOOL minimized;
   377 
   378             minimized = HIWORD(wParam);
   379             if (!minimized && (LOWORD(wParam) != WA_INACTIVE)) {
   380                 data->focus_click_pending = (GetAsyncKeyState(VK_LBUTTON) != 0);
   381 
   382                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
   383                 if (SDL_GetKeyboardFocus() != data->window) {
   384                     SDL_SetKeyboardFocus(data->window);
   385                 }
   386                 
   387                 GetCursorPos(&cursorPos);
   388                 ScreenToClient(hwnd, &cursorPos);
   389                 SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y);
   390                 
   391                 WIN_CheckAsyncMouseRelease(data);
   392 
   393                 /*
   394                  * FIXME: Update keyboard state
   395                  */
   396                 WIN_CheckClipboardUpdate(data->videodata);
   397             } else {
   398                 if (SDL_GetKeyboardFocus() == data->window) {
   399                     SDL_SetKeyboardFocus(NULL);
   400                 }
   401 
   402                 ClipCursor(NULL);
   403             }
   404         }
   405         returnCode = 0;
   406         break;
   407 
   408     case WM_MOUSEMOVE:
   409         {
   410             SDL_Mouse *mouse = SDL_GetMouse();
   411             if (!mouse->relative_mode || mouse->relative_mode_warp) {
   412                 SDL_SendMouseMotion(data->window, 0, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
   413             }
   414         }
   415         /* don't break here, fall through to check the wParam like the button presses */
   416     case WM_LBUTTONUP:
   417     case WM_RBUTTONUP:
   418     case WM_MBUTTONUP:
   419     case WM_XBUTTONUP:
   420     case WM_LBUTTONDOWN:
   421     case WM_LBUTTONDBLCLK:
   422     case WM_RBUTTONDOWN:
   423     case WM_RBUTTONDBLCLK:
   424     case WM_MBUTTONDOWN:
   425     case WM_MBUTTONDBLCLK:
   426     case WM_XBUTTONDOWN:
   427     case WM_XBUTTONDBLCLK:
   428         {
   429             SDL_Mouse *mouse = SDL_GetMouse();
   430             if (!mouse->relative_mode || mouse->relative_mode_warp) {
   431                 WIN_CheckWParamMouseButtons(wParam, data);
   432             }
   433         }
   434         break;
   435 
   436     case WM_INPUT:
   437         {
   438             SDL_Mouse *mouse = SDL_GetMouse();
   439             HRAWINPUT hRawInput = (HRAWINPUT)lParam;
   440             RAWINPUT inp;
   441             UINT size = sizeof(inp);
   442             const SDL_bool isRelative = mouse->relative_mode || mouse->relative_mode_warp;
   443             const SDL_bool isCapture = ((data->window->flags & SDL_WINDOW_MOUSE_CAPTURE) != 0);
   444 
   445             if (!isRelative || mouse->focus != data->window) {
   446                 if (!isCapture) {
   447                     break;
   448                 }
   449             }
   450 
   451             GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER));
   452 
   453             /* Mouse data */
   454             if (inp.header.dwType == RIM_TYPEMOUSE) {
   455                 if (isRelative) {
   456                     RAWMOUSE* rawmouse = &inp.data.mouse;
   457 
   458                     if ((rawmouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE) {
   459                         SDL_SendMouseMotion(data->window, 0, 1, (int)rawmouse->lLastX, (int)rawmouse->lLastY);
   460                     } else {
   461                         /* synthesize relative moves from the abs position */
   462                         static SDL_Point initialMousePoint;
   463                         if (initialMousePoint.x == 0 && initialMousePoint.y == 0) {
   464                             initialMousePoint.x = rawmouse->lLastX;
   465                             initialMousePoint.y = rawmouse->lLastY;
   466                         }
   467 
   468                         SDL_SendMouseMotion(data->window, 0, 1, (int)(rawmouse->lLastX-initialMousePoint.x), (int)(rawmouse->lLastY-initialMousePoint.y) );
   469 
   470                         initialMousePoint.x = rawmouse->lLastX;
   471                         initialMousePoint.y = rawmouse->lLastY;
   472                     }
   473                     WIN_CheckRawMouseButtons(rawmouse->usButtonFlags, data);
   474                 } else if (isCapture) {
   475                     /* we check for where Windows thinks the system cursor lives in this case, so we don't really lose mouse accel, etc. */
   476                     POINT pt;
   477                     GetCursorPos(&pt);
   478                     if (WindowFromPoint(pt) != hwnd) {  /* if in the window, WM_MOUSEMOVE, etc, will cover it. */
   479                         ScreenToClient(hwnd, &pt);
   480                         SDL_SendMouseMotion(data->window, 0, 0, (int) pt.x, (int) pt.y);
   481                         SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_LBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_LEFT);
   482                         SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_RBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_RIGHT);
   483                         SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_MIDDLE);
   484                         SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X1);
   485                         SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X2);
   486                     }
   487                 } else {
   488                     SDL_assert(0 && "Shouldn't happen");
   489                 }
   490             }
   491         }
   492         break;
   493 
   494     case WM_MOUSEWHEEL:
   495         {
   496             static short s_AccumulatedMotion;
   497 
   498             s_AccumulatedMotion += GET_WHEEL_DELTA_WPARAM(wParam);
   499             if (s_AccumulatedMotion > 0) {
   500                 while (s_AccumulatedMotion >= WHEEL_DELTA) {
   501                     SDL_SendMouseWheel(data->window, 0, 0, 1);
   502                     s_AccumulatedMotion -= WHEEL_DELTA;
   503                 }
   504             } else {
   505                 while (s_AccumulatedMotion <= -WHEEL_DELTA) {
   506                     SDL_SendMouseWheel(data->window, 0, 0, -1);
   507                     s_AccumulatedMotion += WHEEL_DELTA;
   508                 }
   509             }
   510         }
   511         break;
   512 
   513     case WM_MOUSEHWHEEL:
   514         {
   515             static short s_AccumulatedMotion;
   516 
   517             s_AccumulatedMotion += GET_WHEEL_DELTA_WPARAM(wParam);
   518             if (s_AccumulatedMotion > 0) {
   519                 while (s_AccumulatedMotion >= WHEEL_DELTA) {
   520                     SDL_SendMouseWheel(data->window, 0, 1, 0);
   521                     s_AccumulatedMotion -= WHEEL_DELTA;
   522                 }
   523             } else {
   524                 while (s_AccumulatedMotion <= -WHEEL_DELTA) {
   525                     SDL_SendMouseWheel(data->window, 0, -1, 0);
   526                     s_AccumulatedMotion += WHEEL_DELTA;
   527                 }
   528             }
   529         }
   530         break;
   531 
   532 #ifdef WM_MOUSELEAVE
   533     case WM_MOUSELEAVE:
   534         if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode && !(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
   535             if (!IsIconic(hwnd)) {
   536                 POINT cursorPos;
   537                 GetCursorPos(&cursorPos);
   538                 ScreenToClient(hwnd, &cursorPos);
   539                 SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y);
   540             }
   541             SDL_SetMouseFocus(NULL);
   542         }
   543         returnCode = 0;
   544         break;
   545 #endif /* WM_MOUSELEAVE */
   546 
   547     case WM_KEYDOWN:
   548     case WM_SYSKEYDOWN:
   549         {
   550             SDL_Scancode code = WindowsScanCodeToSDLScanCode(lParam, wParam);
   551             if (code != SDL_SCANCODE_UNKNOWN) {
   552                 SDL_SendKeyboardKey(SDL_PRESSED, code);
   553             }
   554         }
   555         if (msg == WM_KEYDOWN) {
   556             BYTE keyboardState[256];
   557             char text[5];
   558             UINT32 utf32 = 0;
   559 
   560             GetKeyboardState(keyboardState);
   561             if (ToUnicode(wParam, (lParam >> 16) & 0xff, keyboardState, (LPWSTR)&utf32, 1, 0) > 0) {
   562                 WORD repetition;
   563                 for (repetition = lParam & 0xffff; repetition > 0; repetition--) {
   564                     WIN_ConvertUTF32toUTF8(utf32, text);
   565                     SDL_SendKeyboardText(text);
   566                 }
   567             }
   568         }
   569         returnCode = 0;
   570         break;
   571 
   572     case WM_SYSKEYUP:
   573     case WM_KEYUP:
   574         {
   575             SDL_Scancode code = WindowsScanCodeToSDLScanCode(lParam, wParam);
   576             const Uint8 *keyboardState = SDL_GetKeyboardState(NULL);
   577 
   578             /* Detect relevant keyboard shortcuts */
   579             if (keyboardState[SDL_SCANCODE_LALT] == SDL_PRESSED || keyboardState[SDL_SCANCODE_RALT] == SDL_PRESSED) {
   580                 /* ALT+F4: Close window */
   581                 if (code == SDL_SCANCODE_F4) {
   582                     SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
   583                 }
   584             }
   585 
   586             if (code != SDL_SCANCODE_UNKNOWN) {
   587                 if (code == SDL_SCANCODE_PRINTSCREEN &&
   588                     keyboardState[code] == SDL_RELEASED) {
   589                     SDL_SendKeyboardKey(SDL_PRESSED, code);
   590                 }
   591                 SDL_SendKeyboardKey(SDL_RELEASED, code);
   592             }
   593         }
   594         returnCode = 0;
   595         break;
   596 
   597     case WM_UNICHAR:
   598     case WM_CHAR:
   599         /* Ignore WM_CHAR messages that come from TranslateMessage(), since we handle WM_KEY* messages directly */
   600         returnCode = 0;
   601         break;
   602 
   603 #ifdef WM_INPUTLANGCHANGE
   604     case WM_INPUTLANGCHANGE:
   605         {
   606             WIN_UpdateKeymap();
   607         }
   608         returnCode = 1;
   609         break;
   610 #endif /* WM_INPUTLANGCHANGE */
   611 
   612     case WM_NCLBUTTONDOWN:
   613         {
   614             data->in_title_click = SDL_TRUE;
   615         }
   616         break;
   617 
   618     case WM_CAPTURECHANGED:
   619         {
   620             data->in_title_click = SDL_FALSE;
   621 
   622             /* The mouse may have been released during a modal loop */
   623             WIN_CheckAsyncMouseRelease(data);
   624         }
   625         break;
   626 
   627 #ifdef WM_GETMINMAXINFO
   628     case WM_GETMINMAXINFO:
   629         {
   630             MINMAXINFO *info;
   631             RECT size;
   632             int x, y;
   633             int w, h;
   634             int min_w, min_h;
   635             int max_w, max_h;
   636             int style;
   637             BOOL menu;
   638             BOOL constrain_max_size;
   639 
   640             if (SDL_IsShapedWindow(data->window))
   641                 Win32_ResizeWindowShape(data->window);
   642 
   643             /* If this is an expected size change, allow it */
   644             if (data->expected_resize) {
   645                 break;
   646             }
   647 
   648             /* Get the current position of our window */
   649             GetWindowRect(hwnd, &size);
   650             x = size.left;
   651             y = size.top;
   652 
   653             /* Calculate current size of our window */
   654             SDL_GetWindowSize(data->window, &w, &h);
   655             SDL_GetWindowMinimumSize(data->window, &min_w, &min_h);
   656             SDL_GetWindowMaximumSize(data->window, &max_w, &max_h);
   657 
   658             /* Store in min_w and min_h difference between current size and minimal
   659                size so we don't need to call AdjustWindowRectEx twice */
   660             min_w -= w;
   661             min_h -= h;
   662             if (max_w && max_h) {
   663                 max_w -= w;
   664                 max_h -= h;
   665                 constrain_max_size = TRUE;
   666             } else {
   667                 constrain_max_size = FALSE;
   668             }
   669 
   670             size.top = 0;
   671             size.left = 0;
   672             size.bottom = h;
   673             size.right = w;
   674 
   675             style = GetWindowLong(hwnd, GWL_STYLE);
   676             /* DJM - according to the docs for GetMenu(), the
   677                return value is undefined if hwnd is a child window.
   678                Apparently it's too difficult for MS to check
   679                inside their function, so I have to do it here.
   680              */
   681             menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
   682             AdjustWindowRectEx(&size, style, menu, 0);
   683             w = size.right - size.left;
   684             h = size.bottom - size.top;
   685 
   686             /* Fix our size to the current size */
   687             info = (MINMAXINFO *) lParam;
   688             if (SDL_GetWindowFlags(data->window) & SDL_WINDOW_RESIZABLE) {
   689                 info->ptMinTrackSize.x = w + min_w;
   690                 info->ptMinTrackSize.y = h + min_h;
   691                 if (constrain_max_size) {
   692                     info->ptMaxTrackSize.x = w + max_w;
   693                     info->ptMaxTrackSize.y = h + max_h;
   694                 }
   695             } else {
   696                 info->ptMaxSize.x = w;
   697                 info->ptMaxSize.y = h;
   698                 info->ptMaxPosition.x = x;
   699                 info->ptMaxPosition.y = y;
   700                 info->ptMinTrackSize.x = w;
   701                 info->ptMinTrackSize.y = h;
   702                 info->ptMaxTrackSize.x = w;
   703                 info->ptMaxTrackSize.y = h;
   704             }
   705         }
   706         returnCode = 0;
   707         break;
   708 #endif /* WM_GETMINMAXINFO */
   709 
   710     case WM_WINDOWPOSCHANGED:
   711         {
   712             RECT rect;
   713             int x, y;
   714             int w, h;
   715             
   716             if (data->in_border_change) {
   717                 break;
   718             }
   719 
   720             if (!GetClientRect(hwnd, &rect) || IsRectEmpty(&rect)) {
   721                 break;
   722             }
   723             ClientToScreen(hwnd, (LPPOINT) & rect);
   724             ClientToScreen(hwnd, (LPPOINT) & rect + 1);
   725 
   726             WIN_UpdateClipCursor(data->window);
   727 
   728             x = rect.left;
   729             y = rect.top;
   730             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED, x, y);
   731 
   732             w = rect.right - rect.left;
   733             h = rect.bottom - rect.top;
   734             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED, w,
   735                                 h);
   736         }
   737         break;
   738 
   739     case WM_SIZE:
   740         {
   741             switch (wParam) {
   742             case SIZE_MAXIMIZED:
   743                 SDL_SendWindowEvent(data->window,
   744                     SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
   745                 break;
   746             case SIZE_MINIMIZED:
   747                 SDL_SendWindowEvent(data->window,
   748                     SDL_WINDOWEVENT_MINIMIZED, 0, 0);
   749                 break;
   750             default:
   751                 SDL_SendWindowEvent(data->window,
   752                     SDL_WINDOWEVENT_RESTORED, 0, 0);
   753                 break;
   754             }
   755         }
   756         break;
   757 
   758     case WM_SETCURSOR:
   759         {
   760             Uint16 hittest;
   761 
   762             hittest = LOWORD(lParam);
   763             if (hittest == HTCLIENT) {
   764                 SetCursor(SDL_cursor);
   765                 returnCode = TRUE;
   766             } else if (!g_WindowFrameUsableWhileCursorHidden && !SDL_cursor) {
   767                 SetCursor(NULL);
   768                 returnCode = TRUE;
   769             }
   770         }
   771         break;
   772 
   773         /* We were occluded, refresh our display */
   774     case WM_PAINT:
   775         {
   776             RECT rect;
   777             if (GetUpdateRect(hwnd, &rect, FALSE)) {
   778                 ValidateRect(hwnd, NULL);
   779                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED,
   780                                     0, 0);
   781             }
   782         }
   783         returnCode = 0;
   784         break;
   785 
   786         /* We'll do our own drawing, prevent flicker */
   787     case WM_ERASEBKGND:
   788         {
   789         }
   790         return (1);
   791 
   792 #if defined(SC_SCREENSAVE) || defined(SC_MONITORPOWER)
   793     case WM_SYSCOMMAND:
   794         {
   795             /* Don't start the screensaver or blank the monitor in fullscreen apps */
   796             if ((wParam & 0xFFF0) == SC_SCREENSAVE ||
   797                 (wParam & 0xFFF0) == SC_MONITORPOWER) {
   798                 if (SDL_GetVideoDevice()->suspend_screensaver) {
   799                     return (0);
   800                 }
   801             }
   802         }
   803         break;
   804 #endif /* System has screensaver support */
   805 
   806     case WM_CLOSE:
   807         {
   808             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
   809         }
   810         returnCode = 0;
   811         break;
   812 
   813     case WM_TOUCH:
   814         {
   815             UINT i, num_inputs = LOWORD(wParam);
   816             PTOUCHINPUT inputs = SDL_stack_alloc(TOUCHINPUT, num_inputs);
   817             if (data->videodata->GetTouchInputInfo((HTOUCHINPUT)lParam, num_inputs, inputs, sizeof(TOUCHINPUT))) {
   818                 RECT rect;
   819                 float x, y;
   820 
   821                 if (!GetClientRect(hwnd, &rect) ||
   822                     (rect.right == rect.left && rect.bottom == rect.top)) {
   823                     if (inputs) {
   824                         SDL_stack_free(inputs);
   825                     }
   826                     break;
   827                 }
   828                 ClientToScreen(hwnd, (LPPOINT) & rect);
   829                 ClientToScreen(hwnd, (LPPOINT) & rect + 1);
   830                 rect.top *= 100;
   831                 rect.left *= 100;
   832                 rect.bottom *= 100;
   833                 rect.right *= 100;
   834 
   835                 for (i = 0; i < num_inputs; ++i) {
   836                     PTOUCHINPUT input = &inputs[i];
   837 
   838                     const SDL_TouchID touchId = (SDL_TouchID)((size_t)input->hSource);
   839                     if (!SDL_GetTouch(touchId)) {
   840                         if (SDL_AddTouch(touchId, "") < 0) {
   841                             continue;
   842                         }
   843                     }
   844 
   845                     /* Get the normalized coordinates for the window */
   846                     x = (float)(input->x - rect.left)/(rect.right - rect.left);
   847                     y = (float)(input->y - rect.top)/(rect.bottom - rect.top);
   848 
   849                     if (input->dwFlags & TOUCHEVENTF_DOWN) {
   850                         SDL_SendTouch(touchId, input->dwID, SDL_TRUE, x, y, 1.0f);
   851                     }
   852                     if (input->dwFlags & TOUCHEVENTF_MOVE) {
   853                         SDL_SendTouchMotion(touchId, input->dwID, x, y, 1.0f);
   854                     }
   855                     if (input->dwFlags & TOUCHEVENTF_UP) {
   856                         SDL_SendTouch(touchId, input->dwID, SDL_FALSE, x, y, 1.0f);
   857                     }
   858                 }
   859             }
   860             SDL_stack_free(inputs);
   861 
   862             data->videodata->CloseTouchInputHandle((HTOUCHINPUT)lParam);
   863             return 0;
   864         }
   865         break;
   866 
   867     case WM_DROPFILES:
   868         {
   869             UINT i;
   870             HDROP drop = (HDROP) wParam;
   871             UINT count = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0);
   872             for (i = 0; i < count; ++i) {
   873                 UINT size = DragQueryFile(drop, i, NULL, 0) + 1;
   874                 LPTSTR buffer = SDL_stack_alloc(TCHAR, size);
   875                 if (buffer) {
   876                     if (DragQueryFile(drop, i, buffer, size)) {
   877                         char *file = WIN_StringToUTF8(buffer);
   878                         SDL_SendDropFile(file);
   879                         SDL_free(file);
   880                     }
   881                     SDL_stack_free(buffer);
   882                 }
   883             }
   884             DragFinish(drop);
   885             return 0;
   886         }
   887         break;
   888 
   889     case WM_NCHITTEST:
   890         {
   891             SDL_Window *window = data->window;
   892             if (window->hit_test) {
   893                 POINT winpoint = { (int) LOWORD(lParam), (int) HIWORD(lParam) };
   894                 if (ScreenToClient(hwnd, &winpoint)) {
   895                     const SDL_Point point = { (int) winpoint.x, (int) winpoint.y };
   896                     const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
   897                     switch (rc) {
   898                         case SDL_HITTEST_DRAGGABLE: return HTCAPTION;
   899                         case SDL_HITTEST_RESIZE_TOPLEFT: return HTTOPLEFT;
   900                         case SDL_HITTEST_RESIZE_TOP: return HTTOP;
   901                         case SDL_HITTEST_RESIZE_TOPRIGHT: return HTTOPRIGHT;
   902                         case SDL_HITTEST_RESIZE_RIGHT: return HTRIGHT;
   903                         case SDL_HITTEST_RESIZE_BOTTOMRIGHT: return HTBOTTOMRIGHT;
   904                         case SDL_HITTEST_RESIZE_BOTTOM: return HTBOTTOM;
   905                         case SDL_HITTEST_RESIZE_BOTTOMLEFT: return HTBOTTOMLEFT;
   906                         case SDL_HITTEST_RESIZE_LEFT: return HTLEFT;
   907                         case SDL_HITTEST_NORMAL: return HTCLIENT;
   908                     }
   909                 }
   910                 /* If we didn't return, this will call DefWindowProc below. */
   911             }
   912         }
   913         break;
   914 
   915     }
   916 
   917     /* If there's a window proc, assume it's going to handle messages */
   918     if (data->wndproc) {
   919         return CallWindowProc(data->wndproc, hwnd, msg, wParam, lParam);
   920     } else if (returnCode >= 0) {
   921         return returnCode;
   922     } else {
   923         return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
   924     }
   925 }
   926 
   927 void
   928 WIN_PumpEvents(_THIS)
   929 {
   930     const Uint8 *keystate;
   931     MSG msg;
   932     DWORD start_ticks = GetTickCount();
   933 
   934     if (g_WindowsEnableMessageLoop) {
   935         while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
   936             /* Always translate the message in case it's a non-SDL window (e.g. with Qt integration) */
   937             TranslateMessage(&msg);
   938             DispatchMessage(&msg);
   939 
   940             /* Make sure we don't busy loop here forever if there are lots of events coming in */
   941             if (SDL_TICKS_PASSED(msg.time, start_ticks)) {
   942                 break;
   943             }
   944         }
   945     }
   946 
   947     /* Windows loses a shift KEYUP event when you have both pressed at once and let go of one.
   948        You won't get a KEYUP until both are released, and that keyup will only be for the second
   949        key you released. Take heroic measures and check the keystate as of the last handled event,
   950        and if we think a key is pressed when Windows doesn't, unstick it in SDL's state. */
   951     keystate = SDL_GetKeyboardState(NULL);
   952     if ((keystate[SDL_SCANCODE_LSHIFT] == SDL_PRESSED) && !(GetKeyState(VK_LSHIFT) & 0x8000)) {
   953         SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
   954     }
   955     if ((keystate[SDL_SCANCODE_RSHIFT] == SDL_PRESSED) && !(GetKeyState(VK_RSHIFT) & 0x8000)) {
   956         SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RSHIFT);
   957     }
   958 }
   959 
   960 static int app_registered = 0;
   961 LPTSTR SDL_Appname = NULL;
   962 Uint32 SDL_Appstyle = 0;
   963 HINSTANCE SDL_Instance = NULL;
   964 
   965 /* Register the class for this application */
   966 int
   967 SDL_RegisterApp(char *name, Uint32 style, void *hInst)
   968 {
   969     WNDCLASS class;
   970 
   971     /* Only do this once... */
   972     if (app_registered) {
   973         ++app_registered;
   974         return (0);
   975     }
   976     if (!name && !SDL_Appname) {
   977         name = "SDL_app";
   978 #if defined(CS_BYTEALIGNCLIENT) || defined(CS_OWNDC)
   979         SDL_Appstyle = (CS_BYTEALIGNCLIENT | CS_OWNDC);
   980 #endif
   981         SDL_Instance = hInst ? hInst : GetModuleHandle(NULL);
   982     }
   983 
   984     if (name) {
   985         SDL_Appname = WIN_UTF8ToString(name);
   986         SDL_Appstyle = style;
   987         SDL_Instance = hInst ? hInst : GetModuleHandle(NULL);
   988     }
   989 
   990     /* Register the application class */
   991     class.hCursor = NULL;
   992     class.hIcon =
   993         LoadImage(SDL_Instance, SDL_Appname, IMAGE_ICON, 0, 0,
   994                   LR_DEFAULTCOLOR);
   995     class.lpszMenuName = NULL;
   996     class.lpszClassName = SDL_Appname;
   997     class.hbrBackground = NULL;
   998     class.hInstance = SDL_Instance;
   999     class.style = SDL_Appstyle;
  1000     class.lpfnWndProc = WIN_WindowProc;
  1001     class.cbWndExtra = 0;
  1002     class.cbClsExtra = 0;
  1003     if (!RegisterClass(&class)) {
  1004         return SDL_SetError("Couldn't register application class");
  1005     }
  1006 
  1007     app_registered = 1;
  1008     return 0;
  1009 }
  1010 
  1011 /* Unregisters the windowclass registered in SDL_RegisterApp above. */
  1012 void
  1013 SDL_UnregisterApp()
  1014 {
  1015     WNDCLASS class;
  1016 
  1017     /* SDL_RegisterApp might not have been called before */
  1018     if (!app_registered) {
  1019         return;
  1020     }
  1021     --app_registered;
  1022     if (app_registered == 0) {
  1023         /* Check for any registered window classes. */
  1024         if (GetClassInfo(SDL_Instance, SDL_Appname, &class)) {
  1025             UnregisterClass(SDL_Appname, SDL_Instance);
  1026         }
  1027         SDL_free(SDL_Appname);
  1028         SDL_Appname = NULL;
  1029     }
  1030 }
  1031 
  1032 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
  1033 
  1034 /* vi: set ts=4 sw=4 expandtab: */