src/video/windows/SDL_windowsevents.c
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Wed, 05 Jun 2013 12:00:15 -0700
changeset 7275 0a6b3b998814
parent 7191 75360622e65f
child 7276 37814e7eeff3
permissions -rw-r--r--
Win32: Ignore WM_MOUSELEAVE in relative mode.

We get an WM_MOUSELEAVE when we switch to relative mode, even though the cursor is still in the window.
Ignoring this event to not end up with a NULL mouse focus.

This fixes http://bugzilla.libsdl.org/show_bug.cgi?id=1861
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 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_config.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_vkeys.h"
    29 #include "../../events/SDL_events_c.h"
    30 #include "../../events/SDL_touch_c.h"
    31 #include "../../events/scancodes_windows.h"
    32 
    33 /* Dropfile support */
    34 #include <shellapi.h>
    35 
    36 
    37 /*#define WMMSG_DEBUG*/
    38 #ifdef WMMSG_DEBUG
    39 #include <stdio.h>
    40 #include "wmmsg.h"
    41 #endif
    42 
    43 /* Masks for processing the windows KEYDOWN and KEYUP messages */
    44 #define REPEATED_KEYMASK    (1<<30)
    45 #define EXTENDED_KEYMASK    (1<<24)
    46 
    47 #define VK_ENTER    10          /* Keypad Enter ... no VKEY defined? */
    48 #ifndef VK_OEM_NEC_EQUAL
    49 #define VK_OEM_NEC_EQUAL 0x92
    50 #endif
    51 
    52 /* Make sure XBUTTON stuff is defined that isn't in older Platform SDKs... */
    53 #ifndef WM_XBUTTONDOWN
    54 #define WM_XBUTTONDOWN 0x020B
    55 #endif
    56 #ifndef WM_XBUTTONUP
    57 #define WM_XBUTTONUP 0x020C
    58 #endif
    59 #ifndef GET_XBUTTON_WPARAM
    60 #define GET_XBUTTON_WPARAM(w) (HIWORD(w))
    61 #endif
    62 #ifndef WM_INPUT
    63 #define WM_INPUT 0x00ff
    64 #endif
    65 #ifndef WM_TOUCH
    66 #define WM_TOUCH 0x0240
    67 #endif
    68 
    69 static SDL_Scancode
    70 WindowsScanCodeToSDLScanCode( LPARAM lParam, WPARAM wParam )
    71 {
    72     SDL_Scancode code;
    73     char bIsExtended;
    74     int nScanCode = ( lParam >> 16 ) & 0xFF;
    75 
    76     /* 0x45 here to work around both pause and numlock sharing the same scancode, so use the VK key to tell them apart */
    77     if ( nScanCode == 0 || nScanCode == 0x45 )
    78     {
    79         switch( wParam )
    80         {
    81         case VK_CLEAR: return SDL_SCANCODE_CLEAR;
    82         case VK_MODECHANGE: return SDL_SCANCODE_MODE;
    83         case VK_SELECT: return SDL_SCANCODE_SELECT;
    84         case VK_EXECUTE: return SDL_SCANCODE_EXECUTE;
    85         case VK_HELP: return SDL_SCANCODE_HELP;
    86         case VK_PAUSE: return SDL_SCANCODE_PAUSE;
    87         case VK_NUMLOCK: return SDL_SCANCODE_NUMLOCKCLEAR;
    88 
    89         case VK_F13: return SDL_SCANCODE_F13;
    90         case VK_F14: return SDL_SCANCODE_F14;
    91         case VK_F15: return SDL_SCANCODE_F15;
    92         case VK_F16: return SDL_SCANCODE_F16;
    93         case VK_F17: return SDL_SCANCODE_F17;
    94         case VK_F18: return SDL_SCANCODE_F18;
    95         case VK_F19: return SDL_SCANCODE_F19;
    96         case VK_F20: return SDL_SCANCODE_F20;
    97         case VK_F21: return SDL_SCANCODE_F21;
    98         case VK_F22: return SDL_SCANCODE_F22;
    99         case VK_F23: return SDL_SCANCODE_F23;
   100         case VK_F24: return SDL_SCANCODE_F24;
   101 
   102         case VK_OEM_NEC_EQUAL: return SDL_SCANCODE_KP_EQUALS;
   103         case VK_BROWSER_BACK: return SDL_SCANCODE_AC_BACK;
   104         case VK_BROWSER_FORWARD: return SDL_SCANCODE_AC_FORWARD;
   105         case VK_BROWSER_REFRESH: return SDL_SCANCODE_AC_REFRESH;
   106         case VK_BROWSER_STOP: return SDL_SCANCODE_AC_STOP;
   107         case VK_BROWSER_SEARCH: return SDL_SCANCODE_AC_SEARCH;
   108         case VK_BROWSER_FAVORITES: return SDL_SCANCODE_AC_BOOKMARKS;
   109         case VK_BROWSER_HOME: return SDL_SCANCODE_AC_HOME;
   110         case VK_VOLUME_MUTE: return SDL_SCANCODE_AUDIOMUTE;
   111         case VK_VOLUME_DOWN: return SDL_SCANCODE_VOLUMEDOWN;
   112         case VK_VOLUME_UP: return SDL_SCANCODE_VOLUMEUP;
   113 
   114         case VK_MEDIA_NEXT_TRACK: return SDL_SCANCODE_AUDIONEXT;
   115         case VK_MEDIA_PREV_TRACK: return SDL_SCANCODE_AUDIOPREV;
   116         case VK_MEDIA_STOP: return SDL_SCANCODE_AUDIOSTOP;
   117         case VK_MEDIA_PLAY_PAUSE: return SDL_SCANCODE_AUDIOPLAY;
   118         case VK_LAUNCH_MAIL: return SDL_SCANCODE_MAIL;
   119         case VK_LAUNCH_MEDIA_SELECT: return SDL_SCANCODE_MEDIASELECT;
   120 
   121         case VK_OEM_102: return SDL_SCANCODE_NONUSBACKSLASH;
   122 
   123         case VK_ATTN: return SDL_SCANCODE_SYSREQ;
   124         case VK_CRSEL: return SDL_SCANCODE_CRSEL;
   125         case VK_EXSEL: return SDL_SCANCODE_EXSEL;
   126         case VK_OEM_CLEAR: return SDL_SCANCODE_CLEAR;
   127 
   128         case VK_LAUNCH_APP1: return SDL_SCANCODE_APP1;
   129         case VK_LAUNCH_APP2: return SDL_SCANCODE_APP2;
   130 
   131         default: return SDL_SCANCODE_UNKNOWN;
   132         }
   133     }
   134 
   135     if ( nScanCode > 127 )
   136         return SDL_SCANCODE_UNKNOWN;
   137 
   138     code = windows_scancode_table[nScanCode];
   139 
   140     bIsExtended = ( lParam & ( 1 << 24 ) ) != 0;
   141     if ( !bIsExtended )
   142     {
   143         switch ( code )
   144         {
   145         case SDL_SCANCODE_HOME:
   146             return SDL_SCANCODE_KP_7;
   147         case SDL_SCANCODE_UP:
   148             return SDL_SCANCODE_KP_8;
   149         case SDL_SCANCODE_PAGEUP:
   150             return SDL_SCANCODE_KP_9;
   151         case SDL_SCANCODE_LEFT:
   152             return SDL_SCANCODE_KP_4;
   153         case SDL_SCANCODE_RIGHT:
   154             return SDL_SCANCODE_KP_6;
   155         case SDL_SCANCODE_END:
   156             return SDL_SCANCODE_KP_1;
   157         case SDL_SCANCODE_DOWN:
   158             return SDL_SCANCODE_KP_2;
   159         case SDL_SCANCODE_PAGEDOWN:
   160             return SDL_SCANCODE_KP_3;
   161         case SDL_SCANCODE_INSERT:
   162             return SDL_SCANCODE_KP_0;
   163         case SDL_SCANCODE_DELETE:
   164             return SDL_SCANCODE_KP_PERIOD;
   165         case SDL_SCANCODE_PRINTSCREEN:
   166             return SDL_SCANCODE_KP_MULTIPLY;
   167         default:
   168             break;
   169         }
   170     }
   171     else
   172     {
   173         switch ( code )
   174         {
   175         case SDL_SCANCODE_RETURN:
   176             return SDL_SCANCODE_KP_ENTER;
   177         case SDL_SCANCODE_LALT:
   178             return SDL_SCANCODE_RALT;
   179         case SDL_SCANCODE_LCTRL:
   180             return SDL_SCANCODE_RCTRL;
   181         case SDL_SCANCODE_SLASH:
   182             return SDL_SCANCODE_KP_DIVIDE;
   183         case SDL_SCANCODE_CAPSLOCK:
   184             return SDL_SCANCODE_KP_PLUS;
   185         default:
   186             break;
   187         }
   188     }
   189 
   190     return code;
   191 }
   192 
   193 
   194 void
   195 WIN_CheckWParamMouseButton( SDL_bool bwParamMousePressed, SDL_bool bSDLMousePressed, SDL_WindowData *data, Uint8 button )
   196 {
   197     if ( bwParamMousePressed && !bSDLMousePressed )
   198     {
   199         SDL_SendMouseButton(data->window, 0, SDL_PRESSED, button);
   200     }
   201     else if ( !bwParamMousePressed && bSDLMousePressed )
   202     {
   203         SDL_SendMouseButton(data->window, 0, SDL_RELEASED, button);
   204     }
   205 }
   206 
   207 /*
   208 * Some windows systems fail to send a WM_LBUTTONDOWN sometimes, but each mouse move contains the current button state also
   209 *  so this funciton reconciles our view of the world with the current buttons reported by windows
   210 */
   211 void
   212 WIN_CheckWParamMouseButtons( WPARAM wParam, SDL_WindowData *data )
   213 {
   214     if ( wParam != data->mouse_button_flags )
   215     {
   216         Uint32 mouseFlags = SDL_GetMouseState( NULL, NULL );
   217         WIN_CheckWParamMouseButton(  (wParam & MK_LBUTTON), (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT );
   218         WIN_CheckWParamMouseButton(  (wParam & MK_MBUTTON), (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE );
   219         WIN_CheckWParamMouseButton(  (wParam & MK_RBUTTON), (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT );
   220         WIN_CheckWParamMouseButton(  (wParam & MK_XBUTTON1), (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1 );
   221         WIN_CheckWParamMouseButton(  (wParam & MK_XBUTTON2), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2 );
   222         data->mouse_button_flags = wParam;
   223     }
   224 }
   225 
   226 
   227 void
   228 WIN_CheckRawMouseButtons( ULONG rawButtons, SDL_WindowData *data )
   229 {
   230     if ( rawButtons != data->mouse_button_flags )
   231     {
   232         Uint32 mouseFlags = SDL_GetMouseState( NULL, NULL );
   233         if ( (rawButtons & RI_MOUSE_BUTTON_1_DOWN) )
   234             WIN_CheckWParamMouseButton(  (rawButtons & RI_MOUSE_BUTTON_1_DOWN), (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT );
   235         if ( (rawButtons & RI_MOUSE_BUTTON_1_UP) )
   236             WIN_CheckWParamMouseButton(  !(rawButtons & RI_MOUSE_BUTTON_1_UP), (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT );
   237         if ( (rawButtons & RI_MOUSE_BUTTON_2_DOWN) )
   238             WIN_CheckWParamMouseButton(  (rawButtons & RI_MOUSE_BUTTON_2_DOWN), (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT );
   239         if ( (rawButtons & RI_MOUSE_BUTTON_2_UP) )
   240             WIN_CheckWParamMouseButton(  !(rawButtons & RI_MOUSE_BUTTON_2_UP), (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT );
   241         if ( (rawButtons & RI_MOUSE_BUTTON_3_DOWN) )
   242             WIN_CheckWParamMouseButton(  (rawButtons & RI_MOUSE_BUTTON_3_DOWN), (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE );
   243         if ( (rawButtons & RI_MOUSE_BUTTON_3_UP) )
   244             WIN_CheckWParamMouseButton(  !(rawButtons & RI_MOUSE_BUTTON_3_UP), (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE );
   245         if ( (rawButtons & RI_MOUSE_BUTTON_4_DOWN) )
   246             WIN_CheckWParamMouseButton(  (rawButtons & RI_MOUSE_BUTTON_4_DOWN), (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1 );
   247         if ( (rawButtons & RI_MOUSE_BUTTON_4_UP) )
   248             WIN_CheckWParamMouseButton(  !(rawButtons & RI_MOUSE_BUTTON_4_UP), (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1 );
   249         if ( (rawButtons & RI_MOUSE_BUTTON_5_DOWN) )
   250             WIN_CheckWParamMouseButton(  (rawButtons & RI_MOUSE_BUTTON_5_DOWN), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2 );
   251         if ( (rawButtons & RI_MOUSE_BUTTON_5_UP) )
   252             WIN_CheckWParamMouseButton(  !(rawButtons & RI_MOUSE_BUTTON_5_UP), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2 );
   253         data->mouse_button_flags = rawButtons;
   254     }
   255 }
   256 
   257 LRESULT CALLBACK
   258 WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
   259 {
   260     SDL_WindowData *data;
   261     LRESULT returnCode = -1;
   262 
   263     /* Send a SDL_SYSWMEVENT if the application wants them */
   264     if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) {
   265         SDL_SysWMmsg wmmsg;
   266 
   267         SDL_VERSION(&wmmsg.version);
   268         wmmsg.subsystem = SDL_SYSWM_WINDOWS;
   269         wmmsg.msg.win.hwnd = hwnd;
   270         wmmsg.msg.win.msg = msg;
   271         wmmsg.msg.win.wParam = wParam;
   272         wmmsg.msg.win.lParam = lParam;
   273         SDL_SendSysWMEvent(&wmmsg);
   274     }
   275 
   276     /* Get the window data for the window */
   277     data = (SDL_WindowData *) GetProp(hwnd, TEXT("SDL_WindowData"));
   278     if (!data) {
   279         return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
   280     }
   281 
   282 #ifdef WMMSG_DEBUG
   283     {
   284         FILE *log = fopen("wmmsg.txt", "a");
   285         fprintf(log, "Received windows message: %p ", hwnd);
   286         if (msg > MAX_WMMSG) {
   287             fprintf(log, "%d", msg);
   288         } else {
   289             fprintf(log, "%s", wmtab[msg]);
   290         }
   291         fprintf(log, " -- 0x%X, 0x%X\n", wParam, lParam);
   292         fclose(log);
   293     }
   294 #endif
   295 
   296     if (IME_HandleMessage(hwnd, msg, wParam, &lParam, data->videodata))
   297         return 0;
   298 
   299     switch (msg) {
   300 
   301     case WM_SHOWWINDOW:
   302         {
   303             if (wParam) {
   304                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
   305             } else {
   306                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
   307             }
   308         }
   309         break;
   310 
   311     case WM_ACTIVATE:
   312         {
   313             BOOL minimized;
   314 
   315             minimized = HIWORD(wParam);
   316             if (!minimized && (LOWORD(wParam) != WA_INACTIVE)) {
   317                 Uint32 mouseFlags;
   318                 SHORT keyState;
   319 
   320                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
   321                 SDL_SendWindowEvent(data->window,
   322                                     SDL_WINDOWEVENT_RESTORED, 0, 0);
   323                 if (IsZoomed(hwnd)) {
   324                     SDL_SendWindowEvent(data->window,
   325                                         SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
   326                 }
   327                 if (SDL_GetKeyboardFocus() != data->window) {
   328                     SDL_SetKeyboardFocus(data->window);
   329                 }
   330                 /* mouse buttons may have changed state here, we need
   331                 to resync them, but we will get a WM_MOUSEMOVE right away which will fix
   332                 things up if in non raw mode also
   333                 */
   334                 mouseFlags = SDL_GetMouseState( NULL, NULL );
   335 
   336                 keyState = GetAsyncKeyState( VK_LBUTTON );
   337                 WIN_CheckWParamMouseButton( ( keyState & 0x8000 ), (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT );
   338                 keyState = GetAsyncKeyState( VK_RBUTTON );
   339                 WIN_CheckWParamMouseButton( ( keyState & 0x8000 ), (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT );
   340                 keyState = GetAsyncKeyState( VK_MBUTTON );
   341                 WIN_CheckWParamMouseButton( ( keyState & 0x8000 ), (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE );
   342                 keyState = GetAsyncKeyState( VK_XBUTTON1 );
   343                 WIN_CheckWParamMouseButton( ( keyState & 0x8000 ), (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1 );
   344                 keyState = GetAsyncKeyState( VK_XBUTTON2 );
   345                 WIN_CheckWParamMouseButton( ( keyState & 0x8000 ), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2 );
   346                 data->mouse_button_flags = 0;
   347 
   348                 if(SDL_GetMouse()->relative_mode) {
   349                     LONG cx, cy;
   350                     RECT rect;
   351                     GetWindowRect(hwnd, &rect);
   352 
   353                     cx = (rect.left + rect.right) / 2;
   354                     cy = (rect.top + rect.bottom) / 2;
   355 
   356                     /* Make an absurdly small clip rect */
   357                     rect.left = cx-1;
   358                     rect.right = cx+1;
   359                     rect.top = cy-1;
   360                     rect.bottom = cy+1;
   361 
   362                     ClipCursor(&rect);
   363                 }
   364 
   365                 /*
   366                  * FIXME: Update keyboard state
   367                  */
   368                 WIN_CheckClipboardUpdate(data->videodata);
   369             } else {
   370                 if (SDL_GetKeyboardFocus() == data->window) {
   371                     SDL_SetKeyboardFocus(NULL);
   372                 }
   373                 if (minimized) {
   374                     SDL_SendWindowEvent(data->window,
   375                                         SDL_WINDOWEVENT_MINIMIZED, 0, 0);
   376                 }
   377             }
   378         }
   379         returnCode = 0;
   380         break;
   381 
   382     case WM_MOUSEMOVE:
   383         if( !SDL_GetMouse()->relative_mode )
   384             SDL_SendMouseMotion(data->window, 0, 0, LOWORD(lParam), HIWORD(lParam));
   385         /* don't break here, fall through to check the wParam like the button presses */
   386     case WM_LBUTTONUP:
   387     case WM_RBUTTONUP:
   388     case WM_MBUTTONUP:
   389     case WM_XBUTTONUP:
   390     case WM_LBUTTONDOWN:
   391     case WM_RBUTTONDOWN:
   392     case WM_MBUTTONDOWN:
   393     case WM_XBUTTONDOWN:
   394         if( !SDL_GetMouse()->relative_mode )
   395             WIN_CheckWParamMouseButtons( wParam, data );
   396         break;
   397 
   398     case WM_INPUT:
   399     {
   400         HRAWINPUT hRawInput = (HRAWINPUT)lParam;
   401         RAWINPUT inp;
   402         UINT size = sizeof(inp);
   403 
   404         if(!SDL_GetMouse()->relative_mode)
   405             break;
   406 
   407         GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER));
   408 
   409         /* Mouse data */
   410         if(inp.header.dwType == RIM_TYPEMOUSE)
   411         {
   412             RAWMOUSE* mouse = &inp.data.mouse;
   413 
   414             if((mouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE)
   415             {
   416                 SDL_SendMouseMotion(data->window, 0, 1, (int)mouse->lLastX, (int)mouse->lLastY);
   417             }
   418             else
   419             {
   420                 /* synthesize relative moves from the abs position */
   421                 static SDL_Point initialMousePoint;
   422                 if ( initialMousePoint.x == 0 && initialMousePoint.y == 0 )
   423                 {
   424                     initialMousePoint.x = mouse->lLastX;
   425                     initialMousePoint.y = mouse->lLastY;
   426                 }
   427 
   428                 SDL_SendMouseMotion(data->window, 0, 1, (int)(mouse->lLastX-initialMousePoint.x), (int)(mouse->lLastY-initialMousePoint.y) );
   429 
   430                 initialMousePoint.x = mouse->lLastX;
   431                 initialMousePoint.y = mouse->lLastY;
   432             }
   433             WIN_CheckRawMouseButtons( mouse->usButtonFlags, data );
   434         }
   435         break;
   436     }
   437 
   438     case WM_MOUSEWHEEL:
   439         {
   440             /* FIXME: This may need to accumulate deltas up to WHEEL_DELTA */
   441             short motion = GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA;
   442 
   443             SDL_SendMouseWheel(data->window, 0, 0, motion);
   444             break;
   445         }
   446 
   447 #ifdef WM_MOUSELEAVE
   448     case WM_MOUSELEAVE:
   449         if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode) {
   450             POINT cursorPos;
   451             GetCursorPos(&cursorPos);
   452             ScreenToClient(hwnd, &cursorPos);
   453             SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y);
   454             SDL_SetMouseFocus(NULL);
   455         }
   456         returnCode = 0;
   457         break;
   458 #endif /* WM_MOUSELEAVE */
   459 
   460     case WM_SYSKEYDOWN:
   461     case WM_KEYDOWN:
   462         {
   463             SDL_Scancode code = WindowsScanCodeToSDLScanCode( lParam, wParam );
   464             if ( code != SDL_SCANCODE_UNKNOWN ) {
   465                 SDL_SendKeyboardKey(SDL_PRESSED, code );
   466             }
   467         }
   468         returnCode = 0;
   469         break;
   470 
   471     case WM_SYSKEYUP:
   472     case WM_KEYUP:
   473         {
   474             SDL_Scancode code = WindowsScanCodeToSDLScanCode( lParam, wParam );
   475             if ( code != SDL_SCANCODE_UNKNOWN ) {
   476                 if (code == SDL_SCANCODE_PRINTSCREEN &&
   477                     SDL_GetKeyboardState(NULL)[code] == SDL_RELEASED) {
   478                     SDL_SendKeyboardKey(SDL_PRESSED, code);
   479                 }
   480                 SDL_SendKeyboardKey(SDL_RELEASED, code);
   481             }
   482         }
   483         returnCode = 0;
   484         break;
   485 
   486     case WM_CHAR:
   487         {
   488             char text[4];
   489 
   490             /* Convert to UTF-8 and send it on... */
   491             if (wParam <= 0x7F) {
   492                 text[0] = (char) wParam;
   493                 text[1] = '\0';
   494             } else if (wParam <= 0x7FF) {
   495                 text[0] = 0xC0 | (char) ((wParam >> 6) & 0x1F);
   496                 text[1] = 0x80 | (char) (wParam & 0x3F);
   497                 text[2] = '\0';
   498             } else {
   499                 text[0] = 0xE0 | (char) ((wParam >> 12) & 0x0F);
   500                 text[1] = 0x80 | (char) ((wParam >> 6) & 0x3F);
   501                 text[2] = 0x80 | (char) (wParam & 0x3F);
   502                 text[3] = '\0';
   503             }
   504             SDL_SendKeyboardText(text);
   505         }
   506         returnCode = 0;
   507         break;
   508 
   509 #ifdef WM_INPUTLANGCHANGE
   510     case WM_INPUTLANGCHANGE:
   511         {
   512             WIN_UpdateKeymap();
   513         }
   514         returnCode = 1;
   515         break;
   516 #endif /* WM_INPUTLANGCHANGE */
   517 
   518 #ifdef WM_GETMINMAXINFO
   519     case WM_GETMINMAXINFO:
   520         {
   521             MINMAXINFO *info;
   522             RECT size;
   523             int x, y;
   524             int w, h;
   525             int min_w, min_h;
   526             int max_w, max_h;
   527             int style;
   528             BOOL menu;
   529             BOOL constrain_max_size;
   530 
   531             /* If we allow resizing, let the resize happen naturally */
   532             if (SDL_IsShapedWindow(data->window))
   533                 Win32_ResizeWindowShape(data->window);
   534 
   535             /* Get the current position of our window */
   536             GetWindowRect(hwnd, &size);
   537             x = size.left;
   538             y = size.top;
   539 
   540             /* Calculate current size of our window */
   541             SDL_GetWindowSize(data->window, &w, &h);
   542             SDL_GetWindowMinimumSize(data->window, &min_w, &min_h);
   543             SDL_GetWindowMaximumSize(data->window, &max_w, &max_h);
   544 
   545             /* Store in min_w and min_h difference between current size and minimal
   546                size so we don't need to call AdjustWindowRectEx twice */
   547             min_w -= w;
   548             min_h -= h;
   549             if (max_w && max_h) {
   550                 max_w -= w;
   551                 max_h -= h;
   552                 constrain_max_size = TRUE;
   553             } else {
   554                 constrain_max_size = FALSE;
   555             }
   556 
   557             size.top = 0;
   558             size.left = 0;
   559             size.bottom = h;
   560             size.right = w;
   561 
   562             style = GetWindowLong(hwnd, GWL_STYLE);
   563             /* DJM - according to the docs for GetMenu(), the
   564                return value is undefined if hwnd is a child window.
   565                Aparently it's too difficult for MS to check
   566                inside their function, so I have to do it here.
   567              */
   568             menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
   569             AdjustWindowRectEx(&size, style, menu, 0);
   570             w = size.right - size.left;
   571             h = size.bottom - size.top;
   572 
   573             /* Fix our size to the current size */
   574             info = (MINMAXINFO *) lParam;
   575             if (SDL_GetWindowFlags(data->window) & SDL_WINDOW_RESIZABLE) {
   576                 info->ptMinTrackSize.x = w + min_w;
   577                 info->ptMinTrackSize.y = h + min_h;
   578                 if (constrain_max_size) {
   579                     info->ptMaxTrackSize.x = w + max_w;
   580                     info->ptMaxTrackSize.y = h + max_h;
   581                 }
   582             } else {
   583                 info->ptMaxSize.x = w;
   584                 info->ptMaxSize.y = h;
   585                 info->ptMaxPosition.x = x;
   586                 info->ptMaxPosition.y = y;
   587                 info->ptMinTrackSize.x = w;
   588                 info->ptMinTrackSize.y = h;
   589                 info->ptMaxTrackSize.x = w;
   590                 info->ptMaxTrackSize.y = h;
   591             }
   592         }
   593         returnCode = 0;
   594         break;
   595 #endif /* WM_GETMINMAXINFO */
   596 
   597     case WM_WINDOWPOSCHANGED:
   598         {
   599             RECT rect;
   600             int x, y;
   601             int w, h;
   602             Uint32 window_flags;
   603 
   604             if (!GetClientRect(hwnd, &rect) ||
   605                 (rect.right == rect.left && rect.bottom == rect.top)) {
   606                 break;
   607             }
   608             ClientToScreen(hwnd, (LPPOINT) & rect);
   609             ClientToScreen(hwnd, (LPPOINT) & rect + 1);
   610 
   611             window_flags = SDL_GetWindowFlags(data->window);
   612             if ((window_flags & SDL_WINDOW_INPUT_GRABBED) &&
   613                 (window_flags & SDL_WINDOW_INPUT_FOCUS)) {
   614                 ClipCursor(&rect);
   615             }
   616 
   617             x = rect.left;
   618             y = rect.top;
   619             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED, x, y);
   620 
   621             w = rect.right - rect.left;
   622             h = rect.bottom - rect.top;
   623             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED, w,
   624                                 h);
   625         }
   626         break;
   627 
   628     case WM_SETCURSOR:
   629         {
   630             Uint16 hittest;
   631 
   632             hittest = LOWORD(lParam);
   633             if (hittest == HTCLIENT) {
   634                 SetCursor(SDL_cursor);
   635                 returnCode = TRUE;
   636             }
   637         }
   638         break;
   639 
   640         /* We were occluded, refresh our display */
   641     case WM_PAINT:
   642         {
   643             RECT rect;
   644             if (GetUpdateRect(hwnd, &rect, FALSE)) {
   645                 ValidateRect(hwnd, &rect);
   646                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED,
   647                                     0, 0);
   648             }
   649         }
   650         returnCode = 0;
   651         break;
   652 
   653         /* We'll do our own drawing, prevent flicker */
   654     case WM_ERASEBKGND:
   655         {
   656         }
   657         return (1);
   658 
   659 #if defined(SC_SCREENSAVE) || defined(SC_MONITORPOWER)
   660     case WM_SYSCOMMAND:
   661         {
   662             /* Don't start the screensaver or blank the monitor in fullscreen apps */
   663             if ((wParam & 0xFFF0) == SC_SCREENSAVE ||
   664                 (wParam & 0xFFF0) == SC_MONITORPOWER) {
   665                 if (SDL_GetVideoDevice()->suspend_screensaver) {
   666                     return (0);
   667                 }
   668             }
   669         }
   670         break;
   671 #endif /* System has screensaver support */
   672 
   673     case WM_CLOSE:
   674         {
   675             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
   676         }
   677         returnCode = 0;
   678         break;
   679 
   680     case WM_TOUCH:
   681         {
   682             UINT i, num_inputs = LOWORD(wParam);
   683             PTOUCHINPUT inputs = SDL_stack_alloc(TOUCHINPUT, num_inputs);
   684             if (data->videodata->GetTouchInputInfo((HTOUCHINPUT)lParam, num_inputs, inputs, sizeof(TOUCHINPUT))) {
   685                 RECT rect;
   686                 float x, y;
   687 
   688                 if (!GetClientRect(hwnd, &rect) ||
   689                     (rect.right == rect.left && rect.bottom == rect.top)) {
   690                     break;
   691                 }
   692                 ClientToScreen(hwnd, (LPPOINT) & rect);
   693                 ClientToScreen(hwnd, (LPPOINT) & rect + 1);
   694                 rect.top *= 100;
   695                 rect.left *= 100;
   696                 rect.bottom *= 100;
   697                 rect.right *= 100;
   698 
   699                 for (i = 0; i < num_inputs; ++i) {
   700                     PTOUCHINPUT input = &inputs[i];
   701 
   702                     const SDL_TouchID touchId = (SDL_TouchID)input->hSource;
   703                     if (!SDL_GetTouch(touchId)) {
   704                         if (SDL_AddTouch(touchId, "") < 0) {
   705                             continue;
   706                         }
   707                     }
   708 
   709                     /* Get the normalized coordinates for the window */
   710                     x = (float)(input->x - rect.left)/(rect.right - rect.left);
   711                     y = (float)(input->y - rect.top)/(rect.bottom - rect.top);
   712 
   713                     if (input->dwFlags & TOUCHEVENTF_DOWN) {
   714                         SDL_SendTouch(touchId, input->dwID, SDL_TRUE, x, y, 1.0f);
   715                     }
   716                     if (input->dwFlags & TOUCHEVENTF_MOVE) {
   717                         SDL_SendTouchMotion(touchId, input->dwID, x, y, 1.0f);
   718                     }
   719                     if (input->dwFlags & TOUCHEVENTF_UP) {
   720                         SDL_SendTouch(touchId, input->dwID, SDL_FALSE, x, y, 1.0f);
   721                     }
   722                 }
   723             }
   724             SDL_stack_free(inputs);
   725 
   726             data->videodata->CloseTouchInputHandle((HTOUCHINPUT)lParam);
   727             return 0;
   728         }
   729         break;
   730 
   731     case WM_DROPFILES:
   732         {
   733             UINT i;
   734             HDROP drop = (HDROP) wParam;
   735             UINT count = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0);
   736             for (i = 0; i < count; ++i) {
   737                 UINT size = DragQueryFile(drop, i, NULL, 0) + 1;
   738                 LPTSTR buffer = SDL_stack_alloc(TCHAR, size);
   739                 if (buffer) {
   740                     if (DragQueryFile(drop, i, buffer, size)) {
   741                         char *file = WIN_StringToUTF8(buffer);
   742                         SDL_SendDropFile(file);
   743                         SDL_free(file);
   744                     }
   745                     SDL_stack_free(buffer);
   746                 }
   747             }
   748             DragFinish(drop);
   749             return 0;
   750         }
   751         break;
   752     }
   753 
   754     /* If there's a window proc, assume it's going to handle messages */
   755     if (data->wndproc) {
   756         return CallWindowProc(data->wndproc, hwnd, msg, wParam, lParam);
   757     } else if (returnCode >= 0) {
   758         return returnCode;
   759     } else {
   760         return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
   761     }
   762 }
   763 
   764 void
   765 WIN_PumpEvents(_THIS)
   766 {
   767     MSG msg;
   768     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
   769         TranslateMessage(&msg);
   770         DispatchMessage(&msg);
   771     }
   772 }
   773 
   774 static int app_registered = 0;
   775 LPTSTR SDL_Appname = NULL;
   776 Uint32 SDL_Appstyle = 0;
   777 HINSTANCE SDL_Instance = NULL;
   778 
   779 /* Register the class for this application */
   780 int
   781 SDL_RegisterApp(char *name, Uint32 style, void *hInst)
   782 {
   783     WNDCLASS class;
   784 
   785     /* Only do this once... */
   786     if (app_registered) {
   787         ++app_registered;
   788         return (0);
   789     }
   790     if (!name && !SDL_Appname) {
   791         name = "SDL_app";
   792 #if defined(CS_BYTEALIGNCLIENT) || defined(CS_OWNDC)
   793         SDL_Appstyle = (CS_BYTEALIGNCLIENT | CS_OWNDC);
   794 #endif
   795         SDL_Instance = hInst ? hInst : GetModuleHandle(NULL);
   796     }
   797 
   798     if (name) {
   799         SDL_Appname = WIN_UTF8ToString(name);
   800         SDL_Appstyle = style;
   801         SDL_Instance = hInst ? hInst : GetModuleHandle(NULL);
   802     }
   803 
   804     /* Register the application class */
   805     class.hCursor = NULL;
   806     class.hIcon =
   807         LoadImage(SDL_Instance, SDL_Appname, IMAGE_ICON, 0, 0,
   808                   LR_DEFAULTCOLOR);
   809     class.lpszMenuName = NULL;
   810     class.lpszClassName = SDL_Appname;
   811     class.hbrBackground = NULL;
   812     class.hInstance = SDL_Instance;
   813     class.style = SDL_Appstyle;
   814     class.lpfnWndProc = WIN_WindowProc;
   815     class.cbWndExtra = 0;
   816     class.cbClsExtra = 0;
   817     if (!RegisterClass(&class)) {
   818         return SDL_SetError("Couldn't register application class");
   819     }
   820 
   821     app_registered = 1;
   822     return 0;
   823 }
   824 
   825 /* Unregisters the windowclass registered in SDL_RegisterApp above. */
   826 void
   827 SDL_UnregisterApp()
   828 {
   829     WNDCLASS class;
   830 
   831     /* SDL_RegisterApp might not have been called before */
   832     if (!app_registered) {
   833         return;
   834     }
   835     --app_registered;
   836     if (app_registered == 0) {
   837         /* Check for any registered window classes. */
   838         if (GetClassInfo(SDL_Instance, SDL_Appname, &class)) {
   839             UnregisterClass(SDL_Appname, SDL_Instance);
   840         }
   841         SDL_free(SDL_Appname);
   842         SDL_Appname = NULL;
   843     }
   844 }
   845 
   846 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
   847 
   848 /* vi: set ts=4 sw=4 expandtab: */