src/video/wincommon/SDL_sysevents.c
author Sam Lantinga
Thu, 29 Dec 2011 04:29:53 -0500
branchSDL-1.2
changeset 6110 676a54593bc4
parent 5988 bf927e528813
child 6125 760a3162e180
permissions -rw-r--r--
Fixed bugs 1034 and 1035

zicodxx@gmx.de 2010-07-28 12:59:27 PDT
Again another bug I encountered on Windows 7:

Assuming I hide my mouse cusor with SDL_ShowCursor(SDL_DISABLE).
Now if my app runs on Fullscreen, I tend to "get out" of it using ALT+TAB. This
will minimize the app. However SDL_GetAppState will STILL report SDL_APPACTIVE,
SDL_APPINPUTFOCUS and SDL_APPMOUSEFOCUS.

Also if I check event.active.gain, this *seems* (as much as I could find out)
to be set twice (first 0 then 1 again) while going out of the app with ALT+TAB.
I am not perfectly sure here but even if the app is minimized because of
ALT+TAB (or also CTRL+ESC), event.active.gain is 1.

Alex Volkov 2011-04-28 17:19:26 PDT
This happens with the windib driver. Windows posts the WM_ACTIVATE WA_INACTIVE
event with minimized==false when you Alt+Tab out of a fullscreen app.
Responding to this event, wincommon/SDL_sysevents.c:WinMessage() calls
ShowWindow(,SW_MINIMIZE) via SDL_RestoreDesktopMode, but another WM_ACTIVATE
event is not posted in response to that.
Whatever the case may be, WinMessage() should just treat a fullscreen app
receiving WA_INACTIVE the same as a minimized app. Additionally, it's probably
a good idea to clear SDL_APPMOUSEFOCUS at the same time, and that should take
care of bug #1035.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2009 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 #define WIN32_LEAN_AND_MEAN
    25 #include <windows.h>
    26 
    27 /* Make sure XBUTTON stuff is defined that isn't in older Platform SDKs... */
    28 #ifndef WM_XBUTTONDOWN
    29 #define WM_XBUTTONDOWN 0x020B
    30 #endif
    31 #ifndef WM_XBUTTONUP
    32 #define WM_XBUTTONUP 0x020C
    33 #endif
    34 #ifndef GET_XBUTTON_WPARAM
    35 #define GET_XBUTTON_WPARAM(w) (HIWORD(w))
    36 #endif
    37 
    38 #include "SDL_events.h"
    39 #include "SDL_video.h"
    40 #include "SDL_syswm.h"
    41 #include "../SDL_sysvideo.h"
    42 #include "../../events/SDL_sysevents.h"
    43 #include "../../events/SDL_events_c.h"
    44 #include "SDL_lowvideo.h"
    45 #include "SDL_syswm_c.h"
    46 #include "SDL_main.h"
    47 #include "SDL_loadso.h"
    48 
    49 #ifdef WMMSG_DEBUG
    50 #include "wmmsg.h"
    51 #endif
    52 
    53 #include "../windib/SDL_gapidibvideo.h"
    54 
    55 #ifdef SDL_VIDEO_DRIVER_GAPI
    56 #include "../gapi/SDL_gapivideo.h"
    57 #endif
    58 
    59 #ifdef _WIN32_WCE
    60 #define IsZoomed(HWND) 1
    61 #define NO_GETKEYBOARDSTATE
    62 #if _WIN32_WCE < 420
    63 #define NO_CHANGEDISPLAYSETTINGS
    64 #endif
    65 #endif
    66 
    67 /* The window we use for everything... */
    68 #ifdef _WIN32_WCE
    69 LPWSTR SDL_Appname = NULL;
    70 #else
    71 LPSTR SDL_Appname = NULL;
    72 #endif
    73 Uint32 SDL_Appstyle = 0;
    74 HINSTANCE SDL_Instance = NULL;
    75 HWND SDL_Window = NULL;
    76 RECT SDL_bounds = {0, 0, 0, 0};
    77 int SDL_windowX = 0;
    78 int SDL_windowY = 0;
    79 int SDL_resizing = 0;
    80 int mouse_relative = 0;
    81 int posted = 0;
    82 #ifndef NO_CHANGEDISPLAYSETTINGS
    83 DEVMODE SDL_desktop_mode;
    84 DEVMODE SDL_fullscreen_mode;
    85 #endif
    86 WORD *gamma_saved = NULL;
    87 
    88 
    89 /* Functions called by the message processing function */
    90 LONG (*HandleMessage)(_THIS, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)=NULL;
    91 void (*WIN_Activate)(_THIS, BOOL active, BOOL iconic);
    92 void (*WIN_RealizePalette)(_THIS);
    93 void (*WIN_PaletteChanged)(_THIS, HWND window);
    94 void (*WIN_WinPAINT)(_THIS, HDC hdc);
    95 extern void DIB_SwapGamma(_THIS);
    96 
    97 #ifndef NO_GETKEYBOARDSTATE
    98 #ifndef _WIN64
    99 /* Variables and support functions for SDL_ToUnicode() */
   100 static int codepage;
   101 static int Is9xME();
   102 static int GetCodePage();
   103 static int WINAPI ToUnicode9xME(UINT vkey, UINT scancode, const BYTE *keystate, LPWSTR wchars, int wsize, UINT flags);
   104 
   105 ToUnicodeFN SDL_ToUnicode = ToUnicode9xME;
   106 #endif
   107 #endif /* !NO_GETKEYBOARDSTATE */
   108 
   109 
   110 #if defined(_WIN32_WCE)
   111 
   112 //AdjustWindowRect is not available under WinCE 2003
   113 #define AdjustWindowRect(a,b,c) (AdjustWindowRectEx((a),(b),(c),0))
   114 
   115 // dynamically load aygshell dll because we want SDL to work on HPC and be300
   116 HINSTANCE aygshell = NULL;
   117 BOOL (WINAPI *SHFullScreen)(HWND hwndRequester, DWORD dwState) = 0;
   118 
   119 #define SHFS_SHOWTASKBAR            0x0001
   120 #define SHFS_HIDETASKBAR            0x0002
   121 #define SHFS_SHOWSIPBUTTON          0x0004
   122 #define SHFS_HIDESIPBUTTON          0x0008
   123 #define SHFS_SHOWSTARTICON          0x0010
   124 #define SHFS_HIDESTARTICON          0x0020
   125 
   126 static void LoadAygshell(void)
   127 {
   128 	if( !aygshell )
   129 		 aygshell = SDL_LoadObject("aygshell.dll");
   130 	if( (aygshell != 0) && (SHFullScreen == 0) )
   131 	{
   132 		SHFullScreen = (int (WINAPI *)(struct HWND__ *,unsigned long)) SDL_LoadFunction(aygshell, "SHFullScreen");
   133 	}
   134 }
   135 
   136 #endif
   137 
   138 /* JC 14 Mar 2006
   139    This is used all over the place, in the windib driver and in the dx5 driver
   140    So we may as well stick it here instead of having multiple copies scattered
   141    about
   142 */
   143 void WIN_FlushMessageQueue()
   144 {
   145 	MSG  msg;
   146 	while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) {
   147 		if ( msg.message == WM_QUIT ) break;
   148 		TranslateMessage( &msg );
   149 		DispatchMessage( &msg );
   150 	}
   151 }
   152 
   153 static void SDL_RestoreGameMode(void)
   154 {
   155 #ifdef _WIN32_WCE //Under ce we don't minimize, therefore no restore
   156 	
   157 #ifdef SDL_VIDEO_DRIVER_GAPI
   158 	SDL_VideoDevice *this = current_video;
   159 	if(SDL_strcmp(this->name, "gapi") == 0)
   160 	{
   161 		if( this->hidden->gapiInfo->suspended )
   162 		{
   163 			this->hidden->gapiInfo->suspended = 0;
   164 		}
   165 	}
   166 #endif
   167 	
   168 #else
   169 	ShowWindow(SDL_Window, SW_RESTORE);
   170 #endif
   171 
   172 #ifndef NO_CHANGEDISPLAYSETTINGS
   173 #ifndef _WIN32_WCE
   174 	ChangeDisplaySettings(&SDL_fullscreen_mode, CDS_FULLSCREEN);
   175 #endif
   176 #endif /* NO_CHANGEDISPLAYSETTINGS */
   177 }
   178 static void SDL_RestoreDesktopMode(void)
   179 {
   180 
   181 #ifdef _WIN32_WCE
   182 	
   183 #ifdef SDL_VIDEO_DRIVER_GAPI
   184 	SDL_VideoDevice *this = current_video;
   185 	if(SDL_strcmp(this->name, "gapi") == 0)
   186 	{
   187 		if( !this->hidden->gapiInfo->suspended )
   188 		{
   189 			this->hidden->gapiInfo->suspended = 1;
   190 		}
   191 	}
   192 #endif
   193 	
   194 #else
   195 	/* WinCE does not have a taskbar, so minimizing is not convenient */
   196 	ShowWindow(SDL_Window, SW_MINIMIZE);
   197 #endif
   198 
   199 #ifndef NO_CHANGEDISPLAYSETTINGS
   200 #ifndef _WIN32_WCE
   201 	ChangeDisplaySettings(NULL, 0);
   202 #endif
   203 #endif /* NO_CHANGEDISPLAYSETTINGS */
   204 }
   205 
   206 #ifdef WM_MOUSELEAVE
   207 /* 
   208    Special code to handle mouse leave events - this sucks...
   209    http://support.microsoft.com/support/kb/articles/q183/1/07.asp
   210 
   211    TrackMouseEvent() is only available on Win98 and WinNT.
   212    _TrackMouseEvent() is available on Win95, but isn't yet in the mingw32
   213    development environment, and only works on systems that have had IE 3.0
   214    or newer installed on them (which is not the case with the base Win95).
   215    Therefore, we implement our own version of _TrackMouseEvent() which
   216    uses our own implementation if TrackMouseEvent() is not available.
   217 */
   218 static BOOL (WINAPI *_TrackMouseEvent)(TRACKMOUSEEVENT *ptme) = NULL;
   219 
   220 static VOID CALLBACK
   221 TrackMouseTimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
   222 {
   223 	union { RECT rect; POINT pt; } rectpt;  /* prevent type-punning issue. */
   224 	POINT pt;
   225 
   226 	GetClientRect(hWnd, &rectpt.rect);
   227 	MapWindowPoints(hWnd, NULL, &rectpt.pt, 2);
   228 	GetCursorPos(&pt);
   229 	if ( !PtInRect(&rectpt.rect, pt) || (WindowFromPoint(pt) != hWnd) ) {
   230 		if ( !KillTimer(hWnd, idEvent) ) {
   231 			/* Error killing the timer! */
   232 		}
   233 		PostMessage(hWnd, WM_MOUSELEAVE, 0, 0);
   234 	}
   235 }
   236 static BOOL WINAPI WIN_TrackMouseEvent(TRACKMOUSEEVENT *ptme)
   237 {
   238 	if ( ptme->dwFlags == TME_LEAVE ) {
   239 		return SetTimer(ptme->hwndTrack, ptme->dwFlags, 100,
   240 		                (TIMERPROC)TrackMouseTimerProc) != 0;
   241 	}
   242 	return FALSE;
   243 }
   244 #endif /* WM_MOUSELEAVE */
   245 
   246 /* The main Win32 event handler
   247 DJM: This is no longer static as (DX5/DIB)_CreateWindow needs it
   248 */
   249 LRESULT CALLBACK WinMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
   250 {
   251 	SDL_VideoDevice *this = current_video;
   252 	static int mouse_pressed = 0;
   253 #ifdef WMMSG_DEBUG
   254 	fprintf(stderr, "Received windows message:  ");
   255 	if ( msg > MAX_WMMSG ) {
   256 		fprintf(stderr, "%d", msg);
   257 	} else {
   258 		fprintf(stderr, "%s", wmtab[msg]);
   259 	}
   260 	fprintf(stderr, " -- 0x%X, 0x%X\n", wParam, lParam);
   261 #endif
   262 	switch (msg) {
   263 
   264 		case WM_ACTIVATE: {
   265 			SDL_VideoDevice *this = current_video;
   266 			BOOL active, minimized;
   267 			Uint8 appstate;
   268 
   269 			minimized = HIWORD(wParam);
   270 			active = (LOWORD(wParam) != WA_INACTIVE) && !minimized;
   271 			if ( active ) {
   272 				/* Gain the following states */
   273 				appstate = SDL_APPACTIVE|SDL_APPINPUTFOCUS;
   274 				if ( !(SDL_GetAppState() & SDL_APPINPUTFOCUS) ) {
   275 					if ( this->input_grab != SDL_GRAB_OFF ) {
   276 						WIN_GrabInput(this, SDL_GRAB_ON);
   277 					}
   278 					if ( ! DDRAW_FULLSCREEN() ) {
   279 						DIB_SwapGamma(this);
   280 					}
   281 					if ( WINDIB_FULLSCREEN() ) {
   282 						SDL_RestoreGameMode();
   283 					}
   284 				}
   285 #if defined(_WIN32_WCE)
   286 				if ( WINDIB_FULLSCREEN() ) {
   287 					LoadAygshell();
   288 					if( SHFullScreen )
   289 						SHFullScreen(SDL_Window, SHFS_HIDESTARTICON|SHFS_HIDETASKBAR|SHFS_HIDESIPBUTTON);
   290 					else
   291 						ShowWindow(FindWindow(TEXT("HHTaskBar"),NULL),SW_HIDE);
   292 				}
   293 #endif
   294 				posted = SDL_PrivateAppActive(1, appstate);
   295 			} else {
   296 				/* Lose the following states */
   297 				appstate = SDL_APPINPUTFOCUS;
   298 				if ( minimized ) {
   299 					appstate |= SDL_APPACTIVE;
   300 				}
   301 
   302 				if ( SDL_GetAppState() & SDL_APPINPUTFOCUS ) {
   303 					if ( this->input_grab != SDL_GRAB_OFF ) {
   304 						WIN_GrabInput(this, SDL_GRAB_OFF);
   305 					}
   306 					if ( ! DDRAW_FULLSCREEN() ) {
   307 						DIB_SwapGamma(this);
   308 					}
   309 					if ( WINDIB_FULLSCREEN() ) {
   310 						appstate |= SDL_APPMOUSEFOCUS;
   311 						SDL_RestoreDesktopMode();
   312 						/* A fullscreen app gets hidden but will not get a minimize event */
   313 						appstate |= (SDL_APPACTIVE | SDL_APPMOUSEFOCUS);
   314 #if defined(_WIN32_WCE)
   315 						LoadAygshell();
   316 						if( SHFullScreen ) 
   317 							SHFullScreen(SDL_Window, SHFS_SHOWSTARTICON|SHFS_SHOWTASKBAR|SHFS_SHOWSIPBUTTON);
   318 						else
   319 							ShowWindow(FindWindow(TEXT("HHTaskBar"),NULL),SW_SHOW);
   320 #endif
   321 					}
   322 				}
   323 				posted = SDL_PrivateAppActive(0, appstate);
   324 			}
   325 			WIN_Activate(this, active, minimized);
   326 			return(0);
   327 		}
   328 		break;
   329 
   330 		case WM_MOUSEMOVE: {
   331 
   332 #ifdef WM_MOUSELEAVE
   333 			if ( SDL_VideoSurface ) {
   334 				/* mouse has entered the window */
   335 
   336 				if ( !(SDL_GetAppState() & SDL_APPMOUSEFOCUS) ) {
   337 					TRACKMOUSEEVENT tme;
   338 
   339 					tme.cbSize = sizeof(tme);
   340 					tme.dwFlags = TME_LEAVE;
   341 					tme.hwndTrack = SDL_Window;
   342 					_TrackMouseEvent(&tme);
   343 				}
   344 			}
   345 #endif /* WM_MOUSELEAVE */
   346 
   347 			/* Mouse motion is handled in DIB_PumpEvents or
   348 			 * DX5_PumpEvents, depending on the video driver
   349 			 * in use */
   350 
   351 			posted = SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
   352 		}
   353 		return(0);
   354 
   355 #ifdef WM_MOUSELEAVE
   356 		case WM_MOUSELEAVE: {
   357 
   358 			if ( SDL_VideoSurface ) {
   359 				/* mouse has left the window */
   360 				posted = SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
   361 			}
   362 		}
   363 		return(0);
   364 #endif /* WM_MOUSELEAVE */
   365 
   366 		case WM_LBUTTONDOWN:
   367 		case WM_LBUTTONUP:
   368 		case WM_MBUTTONDOWN:
   369 		case WM_MBUTTONUP:
   370 		case WM_RBUTTONDOWN:
   371 		case WM_RBUTTONUP:
   372 		case WM_XBUTTONDOWN:
   373 		case WM_XBUTTONUP: {
   374 			/* Mouse is handled by DirectInput when fullscreen */
   375 			if ( SDL_VideoSurface && ! DINPUT() ) {
   376 				WORD xbuttonval = 0;
   377 				Uint8 button, state;
   378                 int x, y;
   379 
   380 				/* DJM:
   381 				   We want the SDL window to take focus so that
   382 				   it acts like a normal windows "component"
   383 				   (e.g. gains keyboard focus on a mouse click).
   384 				 */
   385 				SetFocus(SDL_Window);
   386 
   387 				/* Figure out which button to use */
   388 				switch (msg) {
   389 					case WM_LBUTTONDOWN:
   390 						button = SDL_BUTTON_LEFT;
   391 						state = SDL_PRESSED;
   392 						break;
   393 					case WM_LBUTTONUP:
   394 						button = SDL_BUTTON_LEFT;
   395 						state = SDL_RELEASED;
   396 						break;
   397 					case WM_MBUTTONDOWN:
   398 						button = SDL_BUTTON_MIDDLE;
   399 						state = SDL_PRESSED;
   400 						break;
   401 					case WM_MBUTTONUP:
   402 						button = SDL_BUTTON_MIDDLE;
   403 						state = SDL_RELEASED;
   404 						break;
   405 					case WM_RBUTTONDOWN:
   406 						button = SDL_BUTTON_RIGHT;
   407 						state = SDL_PRESSED;
   408 						break;
   409 					case WM_RBUTTONUP:
   410 						button = SDL_BUTTON_RIGHT;
   411 						state = SDL_RELEASED;
   412 						break;
   413 					case WM_XBUTTONDOWN:
   414 						xbuttonval = GET_XBUTTON_WPARAM(wParam);
   415 						button = SDL_BUTTON_X1 + xbuttonval - 1;
   416 						state = SDL_PRESSED;
   417 						break;
   418 					case WM_XBUTTONUP:
   419 						xbuttonval = GET_XBUTTON_WPARAM(wParam);
   420 						button = SDL_BUTTON_X1 + xbuttonval - 1;
   421 						state = SDL_RELEASED;
   422 						break;
   423 					default:
   424 						/* Eh? Unknown button? */
   425 						return(0);
   426 				}
   427 				if ( state == SDL_PRESSED ) {
   428 					/* Grab mouse so we get up events */
   429 					if ( ++mouse_pressed > 0 ) {
   430 						SetCapture(hwnd);
   431 					}
   432 				} else {
   433 					/* Release mouse after all up events */
   434 					if ( --mouse_pressed <= 0 ) {
   435 						ReleaseCapture();
   436 						mouse_pressed = 0;
   437 					}
   438 				}
   439 				if ( mouse_relative ) {
   440 				/*	RJR: March 28, 2000
   441 					report internal mouse position if in relative mode */
   442 					x = 0; y = 0;
   443 				} else {
   444 					x = (Sint16)LOWORD(lParam);
   445 					y = (Sint16)HIWORD(lParam);
   446 #ifdef _WIN32_WCE
   447 					if (SDL_VideoSurface)
   448 						GapiTransform(this->hidden->userOrientation,
   449 this->hidden->hiresFix, &x, &y);
   450 #endif
   451 				}
   452 				posted = SDL_PrivateMouseButton(
   453 							state, button, x, y);
   454 
   455 				/*
   456 				 * MSDN says:
   457 				 *  "Unlike the WM_LBUTTONUP, WM_MBUTTONUP, and WM_RBUTTONUP
   458 				 *   messages, an application should return TRUE from [an 
   459 				 *   XBUTTON message] if it processes it. Doing so will allow
   460 				 *   software that simulates this message on Microsoft Windows
   461 				 *   systems earlier than Windows 2000 to determine whether
   462 				 *   the window procedure processed the message or called
   463 				 *   DefWindowProc to process it.
   464 				 */
   465 				if (xbuttonval > 0)
   466 					return(TRUE);
   467 			}
   468 		}
   469 		return(0);
   470 
   471 
   472 #if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
   473 		case WM_MOUSEWHEEL: 
   474 			if ( SDL_VideoSurface && ! DINPUT() ) {
   475 				int move = (short)HIWORD(wParam);
   476 				if ( move ) {
   477 					Uint8 button;
   478 					if ( move > 0 )
   479 						button = SDL_BUTTON_WHEELUP;
   480 					else
   481 						button = SDL_BUTTON_WHEELDOWN;
   482 					posted = SDL_PrivateMouseButton(
   483 						SDL_PRESSED, button, 0, 0);
   484 					posted |= SDL_PrivateMouseButton(
   485 						SDL_RELEASED, button, 0, 0);
   486 				}
   487 			}
   488 			return(0);
   489 #endif
   490 
   491 #ifdef WM_GETMINMAXINFO
   492 		/* This message is sent as a way for us to "check" the values
   493 		 * of a position change.  If we don't like it, we can adjust
   494 		 * the values before they are changed.
   495 		 */
   496 		case WM_GETMINMAXINFO: {
   497 			MINMAXINFO *info;
   498 			RECT        size;
   499 			int x, y;
   500 			int style;
   501 			int width;
   502 			int height;
   503 
   504 			/* We don't want to clobber an internal resize */
   505 			if ( SDL_resizing )
   506 				return(0);
   507 
   508 			/* We allow resizing with the SDL_RESIZABLE flag */
   509 			if ( SDL_PublicSurface &&
   510 				(SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
   511 				return(0);
   512 			}
   513 
   514 			/* Get the current position of our window */
   515 			GetWindowRect(SDL_Window, &size);
   516 			x = size.left;
   517 			y = size.top;
   518 
   519 			/* Calculate current width and height of our window */
   520 			size.top = 0;
   521 			size.left = 0;
   522 			if ( SDL_PublicSurface != NULL ) {
   523 				size.bottom = SDL_PublicSurface->h;
   524 				size.right = SDL_PublicSurface->w;
   525 			} else {
   526 				size.bottom = 0;
   527 				size.right = 0;
   528 			}
   529 
   530 			/* DJM - according to the docs for GetMenu(), the
   531 			   return value is undefined if hwnd is a child window.
   532 			   Aparently it's too difficult for MS to check
   533 			   inside their function, so I have to do it here.
   534           		 */
   535          		style = GetWindowLong(hwnd, GWL_STYLE);
   536          		AdjustWindowRect(
   537 				&size,
   538 				style,
   539             			style & WS_CHILDWINDOW ? FALSE
   540 						       : GetMenu(hwnd) != NULL);
   541 
   542 			width = size.right - size.left;
   543 			height = size.bottom - size.top;
   544 
   545 			/* Fix our size to the current size */
   546 			info = (MINMAXINFO *)lParam;
   547 			info->ptMaxSize.x = width;
   548 			info->ptMaxSize.y = height;
   549 			info->ptMaxPosition.x = x;
   550 			info->ptMaxPosition.y = y;
   551 			info->ptMinTrackSize.x = width;
   552 			info->ptMinTrackSize.y = height;
   553 			info->ptMaxTrackSize.x = width;
   554 			info->ptMaxTrackSize.y = height;
   555 		}
   556 		return(0);
   557 #endif /* WM_GETMINMAXINFO */
   558 
   559 		case WM_WINDOWPOSCHANGING: {
   560 			WINDOWPOS *windowpos = (WINDOWPOS*)lParam;
   561 
   562 			/* When menu is at the side or top, Windows likes
   563 			   to try to reposition the fullscreen window when
   564 			   changing video modes.
   565 			 */
   566 			if ( !SDL_resizing &&
   567 			     SDL_PublicSurface &&
   568 			     (SDL_PublicSurface->flags & SDL_FULLSCREEN) ) {
   569 				windowpos->x = 0;
   570 				windowpos->y = 0;
   571 			}
   572 		}
   573 		return(0);
   574 
   575 		case WM_WINDOWPOSCHANGED: {
   576 			SDL_VideoDevice *this = current_video;
   577 			POINT pt;
   578 			int w, h;
   579 
   580 			GetClientRect(SDL_Window, &SDL_bounds);
   581 
   582 			/* avoiding type-punning here... */
   583 			pt.x = SDL_bounds.left;
   584 			pt.y = SDL_bounds.top;
   585 			ClientToScreen(SDL_Window, &pt);
   586 			SDL_bounds.left = pt.x;
   587 			SDL_bounds.top = pt.y;
   588 
   589 			pt.x = SDL_bounds.right;
   590 			pt.y = SDL_bounds.bottom;
   591 			ClientToScreen(SDL_Window, &pt);
   592 			SDL_bounds.right = pt.x;
   593 			SDL_bounds.bottom = pt.y;
   594 
   595 			if ( !SDL_resizing && !IsZoomed(SDL_Window) &&
   596 			     SDL_PublicSurface &&
   597 				!(SDL_PublicSurface->flags & SDL_FULLSCREEN) ) {
   598 				SDL_windowX = SDL_bounds.left;
   599 				SDL_windowY = SDL_bounds.top;
   600 			}
   601 			w = SDL_bounds.right-SDL_bounds.left;
   602 			h = SDL_bounds.bottom-SDL_bounds.top;
   603 			if ( this->input_grab != SDL_GRAB_OFF ) {
   604 				ClipCursor(&SDL_bounds);
   605 			}
   606 			if ( SDL_PublicSurface && 
   607 				(SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
   608 				SDL_PrivateResize(w, h);
   609 			}
   610 		}
   611 		break;
   612 
   613 		/* We need to set the cursor */
   614 		case WM_SETCURSOR: {
   615 			Uint16 hittest;
   616 
   617 			hittest = LOWORD(lParam);
   618 			if ( hittest == HTCLIENT ) {
   619 				SetCursor(SDL_hcursor);
   620 				return(TRUE);
   621 			}
   622 		}
   623 		break;
   624 
   625 		/* We are about to get palette focus! */
   626 		case WM_QUERYNEWPALETTE: {
   627 			WIN_RealizePalette(current_video);
   628 			return(TRUE);
   629 		}
   630 		break;
   631 
   632 		/* Another application changed the palette */
   633 		case WM_PALETTECHANGED: {
   634 			WIN_PaletteChanged(current_video, (HWND)wParam);
   635 		}
   636 		break;
   637 
   638 		/* We were occluded, refresh our display */
   639 		case WM_PAINT: {
   640 			HDC hdc;
   641 			PAINTSTRUCT ps;
   642 
   643 			hdc = BeginPaint(SDL_Window, &ps);
   644 			if ( current_video->screen &&
   645 			     !(current_video->screen->flags & SDL_OPENGL) ) {
   646 				WIN_WinPAINT(current_video, hdc);
   647 			}
   648 			EndPaint(SDL_Window, &ps);
   649 		}
   650 		return(0);
   651 
   652 		/* DJM: Send an expose event in this case */
   653 		case WM_ERASEBKGND: {
   654 			posted = SDL_PrivateExpose();
   655 		}
   656 		return(0);
   657 
   658 		case WM_CLOSE: {
   659 			if ( (posted = SDL_PrivateQuit()) )
   660 				PostQuitMessage(0);
   661 		}
   662 		return(0);
   663 
   664 		case WM_DESTROY: {
   665 			PostQuitMessage(0);
   666 		}
   667 		return(0);
   668 
   669 #ifndef NO_GETKEYBOARDSTATE
   670 		case WM_INPUTLANGCHANGE:
   671 #ifndef _WIN64
   672 			codepage = GetCodePage();
   673 #endif
   674 		return(TRUE);
   675 #endif
   676 
   677 		default: {
   678 			/* Special handling by the video driver */
   679 			if (HandleMessage) {
   680 				return(HandleMessage(current_video,
   681 			                     hwnd, msg, wParam, lParam));
   682 			}
   683 		}
   684 		break;
   685 	}
   686 	return(DefWindowProc(hwnd, msg, wParam, lParam));
   687 }
   688 
   689 /* Allow the application handle to be stored and retrieved later */
   690 static void *SDL_handle = NULL;
   691 
   692 void SDL_SetModuleHandle(void *handle)
   693 {
   694 	SDL_handle = handle;
   695 }
   696 void *SDL_GetModuleHandle(void)
   697 {
   698 	void *handle;
   699 
   700 	if ( SDL_handle ) {
   701 		handle = SDL_handle;
   702 	} else {
   703 		handle = GetModuleHandle(NULL);
   704 	}
   705 	return(handle);
   706 }
   707 
   708 /* This allows the SDL_WINDOWID hack */
   709 BOOL SDL_windowid = FALSE;
   710 
   711 static int app_registered = 0;
   712 
   713 /* Register the class for this application -- exported for winmain.c */
   714 int SDL_RegisterApp(char *name, Uint32 style, void *hInst)
   715 {
   716 	WNDCLASS class;
   717 #ifdef WM_MOUSELEAVE
   718 	HMODULE handle;
   719 #endif
   720 
   721 	/* Only do this once... */
   722 	if ( app_registered ) {
   723 		++app_registered;
   724 		return(0);
   725 	}
   726 
   727 #ifndef CS_BYTEALIGNCLIENT
   728 #define CS_BYTEALIGNCLIENT	0
   729 #endif
   730 	if ( ! name && ! SDL_Appname ) {
   731 		name = "SDL_app";
   732 		SDL_Appstyle = CS_BYTEALIGNCLIENT;
   733 		SDL_Instance = hInst ? hInst : SDL_GetModuleHandle();
   734 	}
   735 
   736 	if ( name ) {
   737 #ifdef _WIN32_WCE
   738 		/* WinCE uses the UNICODE version */
   739 		SDL_Appname = SDL_iconv_utf8_ucs2(name);
   740 #else
   741 		SDL_Appname = SDL_iconv_utf8_locale(name);
   742 #endif /* _WIN32_WCE */
   743 		SDL_Appstyle = style;
   744 		SDL_Instance = hInst ? hInst : SDL_GetModuleHandle();
   745 	}
   746 
   747 	/* Register the application class */
   748 	class.hCursor		= NULL;
   749 	class.hIcon		= LoadImage(SDL_Instance, SDL_Appname,
   750 				            IMAGE_ICON,
   751 	                                    0, 0, LR_DEFAULTCOLOR);
   752 	class.lpszMenuName	= NULL;
   753 	class.lpszClassName	= SDL_Appname;
   754 	class.hbrBackground	= NULL;
   755 	class.hInstance		= SDL_Instance;
   756 	class.style		= SDL_Appstyle;
   757 #if SDL_VIDEO_OPENGL
   758 	class.style		|= CS_OWNDC;
   759 #endif
   760 	class.lpfnWndProc	= WinMessage;
   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 #ifdef WM_MOUSELEAVE
   769 	/* Get the version of TrackMouseEvent() we use */
   770 	_TrackMouseEvent = NULL;
   771 	handle = GetModuleHandle("USER32.DLL");
   772 	if ( handle ) {
   773 		_TrackMouseEvent = (BOOL (WINAPI *)(TRACKMOUSEEVENT *))GetProcAddress(handle, "TrackMouseEvent");
   774 	}
   775 	if ( _TrackMouseEvent == NULL ) {
   776 		_TrackMouseEvent = WIN_TrackMouseEvent;
   777 	}
   778 #endif /* WM_MOUSELEAVE */
   779 
   780 #ifndef NO_GETKEYBOARDSTATE
   781 #ifndef _WIN64
   782 	/* Initialise variables for SDL_ToUnicode() */
   783 	codepage = GetCodePage();
   784 
   785 	/* Cygwin headers don't match windows.h, so we have to cast around a
   786 	   const issue here... */
   787 	SDL_ToUnicode = Is9xME() ? ToUnicode9xME : (ToUnicodeFN) ToUnicode;
   788 #endif
   789 #endif /* NO_GETKEYBOARDSTATE */
   790 
   791 	app_registered = 1;
   792 	return(0);
   793 }
   794 
   795 /* Unregisters the windowclass registered in SDL_RegisterApp above. */
   796 void SDL_UnregisterApp()
   797 {
   798 	WNDCLASS class;
   799 
   800 	/* SDL_RegisterApp might not have been called before */
   801 	if ( !app_registered ) {
   802 		return;
   803 	}
   804 	--app_registered;
   805 	if ( app_registered == 0 ) {
   806 		/* Check for any registered window classes. */
   807 		if ( GetClassInfo(SDL_Instance, SDL_Appname, &class) ) {
   808 			UnregisterClass(SDL_Appname, SDL_Instance);
   809 		}
   810 		SDL_free(SDL_Appname);
   811 		SDL_Appname = NULL;
   812 	}
   813 }
   814 
   815 #ifndef NO_GETKEYBOARDSTATE
   816 #ifndef _WIN64
   817 /* JFP: Implementation of ToUnicode() that works on 9x/ME/2K/XP */
   818 
   819 static int Is9xME()
   820 {
   821 	OSVERSIONINFO   info;
   822 
   823 	SDL_memset(&info, 0, sizeof(info));
   824 	info.dwOSVersionInfoSize = sizeof(info);
   825 	if (!GetVersionEx(&info)) {
   826 		return 0;
   827 	}
   828 	return (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
   829 }
   830 
   831 static int GetCodePage()
   832 {
   833 	char	buff[8];
   834 	int	lcid = MAKELCID(LOWORD(GetKeyboardLayout(0)), SORT_DEFAULT);
   835 	int	cp = GetACP();
   836 
   837 	if (GetLocaleInfo(lcid, LOCALE_IDEFAULTANSICODEPAGE, buff, sizeof(buff))) {
   838 		cp = SDL_atoi(buff);
   839 	}
   840 	return cp;
   841 }
   842 
   843 static int WINAPI ToUnicode9xME(UINT vkey, UINT scancode, const BYTE *keystate, LPWSTR wchars, int wsize, UINT flags)
   844 {
   845 	BYTE	chars[2];
   846 
   847 	/* arg #3 should be const BYTE *, but cygwin lists it as PBYTE. */
   848 	if (ToAsciiEx(vkey, scancode, (PBYTE) keystate, (WORD*)chars, 0, GetKeyboardLayout(0)) == 1) {
   849 		return MultiByteToWideChar(codepage, 0, (LPCSTR) chars, 1, wchars, wsize);
   850 	}
   851 	return 0;
   852 }
   853 #endif
   854 #endif /* !NO_GETKEYBOARDSTATE */