src/video/windows/SDL_windowsevents.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 27 Feb 2013 11:39:39 -0800
changeset 6943 ce87e12970f5
parent 6931 843f9c1b16b8
child 6944 e8effbc1a10e
permissions -rw-r--r--
sdl - use the wParam and rawinput data for mouse state rather than just the message type, fixes missing mouse up events when alt-tabing out of the window

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