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