src/video/windows/SDL_windowsevents.c
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Mon, 22 Apr 2013 18:14:55 -0700
changeset 7089 257fc4e541e1
parent 7060 9d96148c2e3e
child 7191 75360622e65f
permissions -rw-r--r--
Send mouse leave updates for Windows and X11.

We now generate a final SDL_MOUSEMOTION before the cursor leaves the
window, followed by a SDL_WINDOWEVENT_LEAVE.
     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) {
   450 			if (!SDL_GetMouse()->relative_mode) {
   451 				POINT cursorPos;
   452 				GetCursorPos(&cursorPos);
   453 				ScreenToClient(hwnd, &cursorPos);
   454 				SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y);
   455 			}
   456 
   457 			SDL_SetMouseFocus(NULL);
   458         }
   459         returnCode = 0;
   460         break;
   461 #endif /* WM_MOUSELEAVE */
   462 
   463     case WM_SYSKEYDOWN:
   464     case WM_KEYDOWN:
   465         {
   466 			SDL_Scancode code = WindowsScanCodeToSDLScanCode( lParam, wParam );
   467 			if ( code != SDL_SCANCODE_UNKNOWN ) {
   468                 SDL_SendKeyboardKey(SDL_PRESSED, code );
   469             }
   470         }
   471         returnCode = 0;
   472         break;
   473 
   474     case WM_SYSKEYUP:
   475     case WM_KEYUP:
   476         {
   477             SDL_Scancode code = WindowsScanCodeToSDLScanCode( lParam, wParam );
   478             if ( code != SDL_SCANCODE_UNKNOWN ) {
   479                 if (code == SDL_SCANCODE_PRINTSCREEN &&
   480                     SDL_GetKeyboardState(NULL)[code] == SDL_RELEASED) {
   481                     SDL_SendKeyboardKey(SDL_PRESSED, code);
   482                 }
   483                 SDL_SendKeyboardKey(SDL_RELEASED, code);
   484             }
   485         }
   486         returnCode = 0;
   487         break;
   488 
   489     case WM_CHAR:
   490         {
   491             char text[4];
   492 
   493             /* Convert to UTF-8 and send it on... */
   494             if (wParam <= 0x7F) {
   495                 text[0] = (char) wParam;
   496                 text[1] = '\0';
   497             } else if (wParam <= 0x7FF) {
   498                 text[0] = 0xC0 | (char) ((wParam >> 6) & 0x1F);
   499                 text[1] = 0x80 | (char) (wParam & 0x3F);
   500                 text[2] = '\0';
   501             } else {
   502                 text[0] = 0xE0 | (char) ((wParam >> 12) & 0x0F);
   503                 text[1] = 0x80 | (char) ((wParam >> 6) & 0x3F);
   504                 text[2] = 0x80 | (char) (wParam & 0x3F);
   505                 text[3] = '\0';
   506             }
   507             SDL_SendKeyboardText(text);
   508         }
   509         returnCode = 0;
   510         break;
   511 
   512 #ifdef WM_INPUTLANGCHANGE
   513     case WM_INPUTLANGCHANGE:
   514         {
   515             WIN_UpdateKeymap();
   516         }
   517         returnCode = 1;
   518         break;
   519 #endif /* WM_INPUTLANGCHANGE */
   520 
   521 #ifdef WM_GETMINMAXINFO
   522     case WM_GETMINMAXINFO:
   523         {
   524             MINMAXINFO *info;
   525             RECT size;
   526             int x, y;
   527             int w, h;
   528             int min_w, min_h;
   529             int max_w, max_h;
   530             int style;
   531             BOOL menu;
   532 			BOOL constrain_max_size;
   533 
   534             /* If we allow resizing, let the resize happen naturally */
   535             if (SDL_IsShapedWindow(data->window))
   536                 Win32_ResizeWindowShape(data->window);
   537 
   538             /* Get the current position of our window */
   539             GetWindowRect(hwnd, &size);
   540             x = size.left;
   541             y = size.top;
   542 
   543             /* Calculate current size of our window */
   544             SDL_GetWindowSize(data->window, &w, &h);
   545             SDL_GetWindowMinimumSize(data->window, &min_w, &min_h);
   546             SDL_GetWindowMaximumSize(data->window, &max_w, &max_h);
   547 
   548             /* Store in min_w and min_h difference between current size and minimal 
   549                size so we don't need to call AdjustWindowRectEx twice */
   550             min_w -= w;
   551             min_h -= h;
   552             if (max_w && max_h) {
   553                 max_w -= w;
   554                 max_h -= h;
   555                 constrain_max_size = TRUE;
   556             } else {
   557                 constrain_max_size = FALSE;
   558             }
   559 
   560             size.top = 0;
   561             size.left = 0;
   562             size.bottom = h;
   563             size.right = w;
   564 
   565             style = GetWindowLong(hwnd, GWL_STYLE);
   566             /* DJM - according to the docs for GetMenu(), the
   567                return value is undefined if hwnd is a child window.
   568                Aparently it's too difficult for MS to check
   569                inside their function, so I have to do it here.
   570              */
   571             menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
   572             AdjustWindowRectEx(&size, style, menu, 0);
   573             w = size.right - size.left;
   574             h = size.bottom - size.top;
   575 
   576             /* Fix our size to the current size */
   577             info = (MINMAXINFO *) lParam;
   578             if (SDL_GetWindowFlags(data->window) & SDL_WINDOW_RESIZABLE) {
   579                 info->ptMinTrackSize.x = w + min_w;
   580                 info->ptMinTrackSize.y = h + min_h;
   581                 if (constrain_max_size) {
   582                     info->ptMaxTrackSize.x = w + max_w;
   583                     info->ptMaxTrackSize.y = h + max_h;
   584                 }
   585             } else {
   586                 info->ptMaxSize.x = w;
   587                 info->ptMaxSize.y = h;
   588                 info->ptMaxPosition.x = x;
   589                 info->ptMaxPosition.y = y;
   590                 info->ptMinTrackSize.x = w;
   591                 info->ptMinTrackSize.y = h;
   592                 info->ptMaxTrackSize.x = w;
   593                 info->ptMaxTrackSize.y = h;
   594             }
   595         }
   596         returnCode = 0;
   597         break;
   598 #endif /* WM_GETMINMAXINFO */
   599 
   600     case WM_WINDOWPOSCHANGED:
   601         {
   602             RECT rect;
   603             int x, y;
   604             int w, h;
   605             Uint32 window_flags;
   606 
   607             if (!GetClientRect(hwnd, &rect) ||
   608                 (rect.right == rect.left && rect.bottom == rect.top)) {
   609                 break;
   610             }
   611             ClientToScreen(hwnd, (LPPOINT) & rect);
   612             ClientToScreen(hwnd, (LPPOINT) & rect + 1);
   613 
   614             window_flags = SDL_GetWindowFlags(data->window);
   615             if ((window_flags & SDL_WINDOW_INPUT_GRABBED) &&
   616                 (window_flags & SDL_WINDOW_INPUT_FOCUS)) {
   617                 ClipCursor(&rect);
   618             }
   619 
   620             x = rect.left;
   621             y = rect.top;
   622             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED, x, y);
   623 
   624             w = rect.right - rect.left;
   625             h = rect.bottom - rect.top;
   626             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED, w,
   627                                 h);
   628         }
   629         break;
   630 
   631     case WM_SETCURSOR:
   632         {
   633             Uint16 hittest;
   634 
   635             hittest = LOWORD(lParam);
   636             if (hittest == HTCLIENT) {
   637                 SetCursor(SDL_cursor);
   638                 returnCode = TRUE;
   639             }
   640         }
   641         break;
   642 
   643         /* We were occluded, refresh our display */
   644     case WM_PAINT:
   645         {
   646             RECT rect;
   647             if (GetUpdateRect(hwnd, &rect, FALSE)) {
   648                 ValidateRect(hwnd, &rect);
   649                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED,
   650                                     0, 0);
   651             }
   652         }
   653         returnCode = 0;
   654         break;
   655 
   656         /* We'll do our own drawing, prevent flicker */
   657     case WM_ERASEBKGND:
   658         {
   659         }
   660         return (1);
   661 
   662 #if defined(SC_SCREENSAVE) || defined(SC_MONITORPOWER)
   663     case WM_SYSCOMMAND:
   664         {
   665             /* Don't start the screensaver or blank the monitor in fullscreen apps */
   666             if ((wParam & 0xFFF0) == SC_SCREENSAVE ||
   667                 (wParam & 0xFFF0) == SC_MONITORPOWER) {
   668                 if (SDL_GetVideoDevice()->suspend_screensaver) {
   669                     return (0);
   670                 }
   671             }
   672         }
   673         break;
   674 #endif /* System has screensaver support */
   675 
   676     case WM_CLOSE:
   677         {
   678             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
   679         }
   680         returnCode = 0;
   681         break;
   682 
   683 	case WM_TOUCH:
   684 		{
   685 			UINT i, num_inputs = LOWORD(wParam);
   686 			PTOUCHINPUT inputs = SDL_stack_alloc(TOUCHINPUT, num_inputs);
   687 			if (data->videodata->GetTouchInputInfo((HTOUCHINPUT)lParam, num_inputs, inputs, sizeof(TOUCHINPUT))) {
   688 				RECT rect;
   689 				float x, y;
   690 
   691 				if (!GetClientRect(hwnd, &rect) ||
   692 				    (rect.right == rect.left && rect.bottom == rect.top)) {
   693 					break;
   694 				}
   695 				ClientToScreen(hwnd, (LPPOINT) & rect);
   696 				ClientToScreen(hwnd, (LPPOINT) & rect + 1);
   697 				rect.top *= 100;
   698 				rect.left *= 100;
   699 				rect.bottom *= 100;
   700 				rect.right *= 100;
   701 
   702 				for (i = 0; i < num_inputs; ++i) {
   703 					PTOUCHINPUT input = &inputs[i];
   704 
   705 					const SDL_TouchID touchId = (SDL_TouchID)input->hSource;
   706 					if (!SDL_GetTouch(touchId)) {
   707 						if (SDL_AddTouch(touchId, "") < 0) {
   708 							continue;
   709 						}
   710 					}
   711 
   712 					// Get the normalized coordinates for the window
   713 					x = (float)(input->x - rect.left)/(rect.right - rect.left);
   714 					y = (float)(input->y - rect.top)/(rect.bottom - rect.top);
   715 
   716 					if (input->dwFlags & TOUCHEVENTF_DOWN) {
   717 						SDL_SendTouch(touchId, input->dwID, SDL_TRUE, x, y, 1.0f);
   718 					}
   719 					if (input->dwFlags & TOUCHEVENTF_MOVE) {
   720 						SDL_SendTouchMotion(touchId, input->dwID, x, y, 1.0f);
   721 					}
   722 					if (input->dwFlags & TOUCHEVENTF_UP) {
   723 						SDL_SendTouch(touchId, input->dwID, SDL_FALSE, x, y, 1.0f);
   724 					}
   725 				}
   726 			}
   727 			SDL_stack_free(inputs);
   728 
   729 			data->videodata->CloseTouchInputHandle((HTOUCHINPUT)lParam);
   730 			return 0;
   731 		}
   732 		break;
   733 
   734     case WM_DROPFILES:
   735         {
   736             UINT i;
   737             HDROP drop = (HDROP) wParam;
   738             UINT count = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0);
   739             for (i = 0; i < count; ++i) {
   740                 UINT size = DragQueryFile(drop, i, NULL, 0) + 1;
   741                 LPTSTR buffer = SDL_stack_alloc(TCHAR, size);
   742                 if (buffer) {
   743                     if (DragQueryFile(drop, i, buffer, size)) {
   744                         char *file = WIN_StringToUTF8(buffer);
   745                         SDL_SendDropFile(file);
   746                         SDL_free(file);
   747                     }
   748                     SDL_stack_free(buffer);
   749                 }
   750             }
   751             DragFinish(drop);
   752             return 0;
   753         }
   754         break;
   755     }
   756 
   757     /* If there's a window proc, assume it's going to handle messages */
   758     if (data->wndproc) {
   759         return CallWindowProc(data->wndproc, hwnd, msg, wParam, lParam);
   760     } else if (returnCode >= 0) {
   761         return returnCode;
   762     } else {
   763         return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
   764     }
   765 }
   766 
   767 void
   768 WIN_PumpEvents(_THIS)
   769 {
   770     MSG msg;
   771     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
   772         TranslateMessage(&msg);
   773         DispatchMessage(&msg);
   774     }
   775 }
   776 
   777 static int app_registered = 0;
   778 LPTSTR SDL_Appname = NULL;
   779 Uint32 SDL_Appstyle = 0;
   780 HINSTANCE SDL_Instance = NULL;
   781 
   782 /* Register the class for this application */
   783 int
   784 SDL_RegisterApp(char *name, Uint32 style, void *hInst)
   785 {
   786     WNDCLASS class;
   787 
   788     /* Only do this once... */
   789     if (app_registered) {
   790         ++app_registered;
   791         return (0);
   792     }
   793     if (!name && !SDL_Appname) {
   794         name = "SDL_app";
   795 #if defined(CS_BYTEALIGNCLIENT) || defined(CS_OWNDC)
   796         SDL_Appstyle = (CS_BYTEALIGNCLIENT | CS_OWNDC);
   797 #endif
   798         SDL_Instance = hInst ? hInst : GetModuleHandle(NULL);
   799     }
   800 
   801     if (name) {
   802         SDL_Appname = WIN_UTF8ToString(name);
   803         SDL_Appstyle = style;
   804         SDL_Instance = hInst ? hInst : GetModuleHandle(NULL);
   805     }
   806 
   807     /* Register the application class */
   808     class.hCursor = NULL;
   809     class.hIcon =
   810         LoadImage(SDL_Instance, SDL_Appname, IMAGE_ICON, 0, 0,
   811                   LR_DEFAULTCOLOR);
   812     class.lpszMenuName = NULL;
   813     class.lpszClassName = SDL_Appname;
   814     class.hbrBackground = NULL;
   815     class.hInstance = SDL_Instance;
   816     class.style = SDL_Appstyle;
   817     class.lpfnWndProc = WIN_WindowProc;
   818     class.cbWndExtra = 0;
   819     class.cbClsExtra = 0;
   820     if (!RegisterClass(&class)) {
   821         return SDL_SetError("Couldn't register application class");
   822     }
   823 
   824     app_registered = 1;
   825     return 0;
   826 }
   827 
   828 /* Unregisters the windowclass registered in SDL_RegisterApp above. */
   829 void
   830 SDL_UnregisterApp()
   831 {
   832     WNDCLASS class;
   833 
   834     /* SDL_RegisterApp might not have been called before */
   835     if (!app_registered) {
   836         return;
   837     }
   838     --app_registered;
   839     if (app_registered == 0) {
   840         /* Check for any registered window classes. */
   841         if (GetClassInfo(SDL_Instance, SDL_Appname, &class)) {
   842             UnregisterClass(SDL_Appname, SDL_Instance);
   843         }
   844         SDL_free(SDL_Appname);
   845         SDL_Appname = NULL;
   846     }
   847 }
   848 
   849 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
   850 
   851 /* vi: set ts=4 sw=4 expandtab: */