1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2006 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#ifdef _WIN32_WCE
54#include "../gapi/SDL_gapivideo.h"
55
56#define IsZoomed(HWND) 1
57#define NO_GETKEYBOARDSTATE
58#if _WIN32_WCE < 420
59#define NO_CHANGEDISPLAYSETTINGS
60#endif
61#endif
62
63/* The window we use for everything... */
64#ifdef _WIN32_WCE
65LPWSTR SDL_Appname = NULL;
66#else
67LPSTR SDL_Appname = NULL;
68#endif
69Uint32 SDL_Appstyle = 0;
70HINSTANCE SDL_Instance = NULL;
71HWND SDL_Window = NULL;
72RECT SDL_bounds = {0, 0, 0, 0};
73int SDL_windowX = 0;
74int SDL_windowY = 0;
75int SDL_resizing = 0;
76int mouse_relative = 0;
77int posted = 0;
78#ifndef NO_CHANGEDISPLAYSETTINGS
79DEVMODE SDL_desktop_mode;
80DEVMODE SDL_fullscreen_mode;
81#endif
82WORD *gamma_saved = NULL;
83
84
85/* Functions called by the message processing function */
86LONG (*HandleMessage)(_THIS, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)=NULL;
87void (*WIN_Activate)(_THIS, BOOL active, BOOL iconic);
88void (*WIN_RealizePalette)(_THIS);
89void (*WIN_PaletteChanged)(_THIS, HWND window);
90void (*WIN_WinPAINT)(_THIS, HDC hdc);
91extern void DIB_SwapGamma(_THIS);
92
93#ifndef NO_GETKEYBOARDSTATE
94/* Variables and support functions for SDL_ToUnicode() */
95static int codepage;
96static int Is9xME();
97static int GetCodePage();
98static int WINAPI ToUnicode9xME(UINT vkey, UINT scancode, BYTE *keystate, LPWSTR wchars, int wsize, UINT flags);
99
100ToUnicodeFN SDL_ToUnicode = ToUnicode9xME;
101#endif /* !NO_GETKEYBOARDSTATE */
102
103
104#if defined(_WIN32_WCE)
105
106// dynamically load aygshell dll because we want SDL to work on HPC and be300
107HINSTANCE aygshell = NULL;
108BOOL (WINAPI *SHFullScreen)(HWND hwndRequester, DWORD dwState) = 0;
109
110#define SHFS_SHOWTASKBAR            0x0001
111#define SHFS_HIDETASKBAR            0x0002
112#define SHFS_SHOWSIPBUTTON          0x0004
113#define SHFS_HIDESIPBUTTON          0x0008
114#define SHFS_SHOWSTARTICON          0x0010
115#define SHFS_HIDESTARTICON          0x0020
116
117static void LoadAygshell(void)
118{
119	if( !aygshell )
120		 aygshell = SDL_LoadObject("aygshell.dll");
121	if( (aygshell != 0) && (SHFullScreen == 0) )
122	{
123		SHFullScreen = (int (WINAPI *)(struct HWND__ *,unsigned long)) SDL_LoadFunction(aygshell, "SHFullScreen");
124	}
125}
126
127/* for gapi landscape mode */
128static void GapiTransform(SDL_ScreenOrientation rotate, char hires, Sint16 *x, Sint16 *y) {
129	Sint16 rotatedX;
130	Sint16 rotatedY;
131
132	if (hires) {
133		*x = *x * 2;
134		*y = *y * 2;
135	}
136
137	switch(rotate) {
138		case SDL_ORIENTATION_UP:
139			{
140/* this code needs testing on a real device!
141   So it will be enabled later */
142/*
143#ifdef _WIN32_WCE
144#if _WIN32_WCE >= 420
145				// test device orientation
146				// FIXME: do not check every mouse message
147				DEVMODE settings;
148				SDL_memset(&settings, 0, sizeof(DEVMODE));
149				settings.dmSize = sizeof(DEVMODE);
150				settings.dmFields = DM_DISPLAYORIENTATION;
151				ChangeDisplaySettingsEx(NULL, &settings, NULL, CDS_TEST, NULL);
152				if( settings.dmOrientation == DMDO_90 )
153				{
154					rotatedX = SDL_VideoSurface->h - *x;
155					rotatedY = *y;
156					*x = rotatedX;
157					*y = rotatedY;
158				}
159#endif
160#endif */
161			}
162			break;
163		case SDL_ORIENTATION_RIGHT:
164			if (!SDL_VideoSurface)
165				break;
166			rotatedX = SDL_VideoSurface->w - *y;
167			rotatedY = *x;
168			*x = rotatedX;
169			*y = rotatedY;
170			break;
171		case SDL_ORIENTATION_LEFT:
172			if (!SDL_VideoSurface)
173				break;
174			rotatedX = *y;
175			rotatedY = SDL_VideoSurface->h - *x;
176			*x = rotatedX;
177			*y = rotatedY;
178			break;
179	}
180}
181
182#endif
183
184/* JC 14 Mar 2006
185   This is used all over the place, in the windib driver and in the dx5 driver
186   So we may as well stick it here instead of having multiple copies scattered
187   about
188*/
189void WIN_FlushMessageQueue()
190{
191	MSG  msg;
192	while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) {
193		if ( msg.message == WM_QUIT ) break;
194		TranslateMessage( &msg );
195		DispatchMessage( &msg );
196	}
197}
198
199static void SDL_RestoreGameMode(void)
200{
201#ifdef _WIN32_WCE
202	SDL_VideoDevice *this = current_video;
203	if(SDL_strcmp(this->name, "gapi") == 0)
204	{
205		if( this->hidden->suspended )
206		{
207			this->hidden->suspended = 0;
208		}
209	}
210#else
211	ShowWindow(SDL_Window, SW_RESTORE);
212#endif
213
214#ifndef NO_CHANGEDISPLAYSETTINGS
215#ifndef _WIN32_WCE
216	ChangeDisplaySettings(&SDL_fullscreen_mode, CDS_FULLSCREEN);
217#endif
218#endif /* NO_CHANGEDISPLAYSETTINGS */
219}
220static void SDL_RestoreDesktopMode(void)
221{
222
223#ifdef _WIN32_WCE
224	SDL_VideoDevice *this = current_video;
225	if(SDL_strcmp(this->name, "gapi") == 0)
226	{
227		if( !this->hidden->suspended )
228		{
229			this->hidden->suspended = 1;
230		}
231	}
232#else
233	/* WinCE does not have a taskbar, so minimizing is not convenient */
234	ShowWindow(SDL_Window, SW_MINIMIZE);
235#endif
236
237#ifndef NO_CHANGEDISPLAYSETTINGS
238#ifndef _WIN32_WCE
239	ChangeDisplaySettings(NULL, 0);
240#endif
241#endif /* NO_CHANGEDISPLAYSETTINGS */
242}
243
244#ifdef WM_MOUSELEAVE
245/*
246   Special code to handle mouse leave events - this sucks...
247   http://support.microsoft.com/support/kb/articles/q183/1/07.asp
248
249   TrackMouseEvent() is only available on Win98 and WinNT.
250   _TrackMouseEvent() is available on Win95, but isn't yet in the mingw32
251   development environment, and only works on systems that have had IE 3.0
252   or newer installed on them (which is not the case with the base Win95).
253   Therefore, we implement our own version of _TrackMouseEvent() which
254   uses our own implementation if TrackMouseEvent() is not available.
255*/
256static BOOL (WINAPI *_TrackMouseEvent)(TRACKMOUSEEVENT *ptme) = NULL;
257
258static VOID CALLBACK
259TrackMouseTimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
260{
261	RECT rect;
262	POINT pt;
263
264	GetClientRect(hWnd, &rect);
265	MapWindowPoints(hWnd, NULL, (LPPOINT)&rect, 2);
266	GetCursorPos(&pt);
267	if ( !PtInRect(&rect, pt) || (WindowFromPoint(pt) != hWnd) ) {
268		if ( !KillTimer(hWnd, idEvent) ) {
269			/* Error killing the timer! */
270		}
271		PostMessage(hWnd, WM_MOUSELEAVE, 0, 0);
272	}
273}
274static BOOL WINAPI WIN_TrackMouseEvent(TRACKMOUSEEVENT *ptme)
275{
276	if ( ptme->dwFlags == TME_LEAVE ) {
277		return SetTimer(ptme->hwndTrack, ptme->dwFlags, 100,
278		                (TIMERPROC)TrackMouseTimerProc) != 0;
279	}
280	return FALSE;
281}
282#endif /* WM_MOUSELEAVE */
283
284/* Function to retrieve the current keyboard modifiers */
285static void WIN_GetKeyboardState(void)
286{
287#ifndef NO_GETKEYBOARDSTATE
288	SDLMod state;
289	BYTE keyboard[256];
290	Uint8 *kstate = SDL_GetKeyState(NULL);
291
292	state = KMOD_NONE;
293	if ( GetKeyboardState(keyboard) ) {
294		if ( keyboard[VK_LSHIFT] & 0x80) {
295			state |= KMOD_LSHIFT;
296			kstate[SDLK_LSHIFT] = SDL_PRESSED;
297		}
298		if ( keyboard[VK_RSHIFT] & 0x80) {
299			state |= KMOD_RSHIFT;
300			kstate[SDLK_RSHIFT] = SDL_PRESSED;
301		}
302		if ( keyboard[VK_LCONTROL] & 0x80) {
303			state |= KMOD_LCTRL;
304			kstate[SDLK_LCTRL] = SDL_PRESSED;
305		}
306		if ( keyboard[VK_RCONTROL] & 0x80) {
307			state |= KMOD_RCTRL;
308			kstate[SDLK_RCTRL] = SDL_PRESSED;
309		}
310		if ( keyboard[VK_LMENU] & 0x80) {
311			state |= KMOD_LALT;
312			kstate[SDLK_LALT] = SDL_PRESSED;
313		}
314		if ( keyboard[VK_RMENU] & 0x80) {
315			state |= KMOD_RALT;
316			kstate[SDLK_RALT] = SDL_PRESSED;
317		}
318		if ( keyboard[VK_NUMLOCK] & 0x01) {
319			state |= KMOD_NUM;
320			kstate[SDLK_NUMLOCK] = SDL_PRESSED;
321		}
322		if ( keyboard[VK_CAPITAL] & 0x01) {
323			state |= KMOD_CAPS;
324			kstate[SDLK_CAPSLOCK] = SDL_PRESSED;
325		}
326	}
327	SDL_SetModState(state);
328#endif /* !NO_GETKEYBOARDSTATE */
329}
330
331/* The main Win32 event handler
332DJM: This is no longer static as (DX5/DIB)_CreateWindow needs it
333*/
334LRESULT CALLBACK WinMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
335{
336	SDL_VideoDevice *this = current_video;
337	static int mouse_pressed = 0;
338	static int in_window = 0;
339#ifdef WMMSG_DEBUG
340	fprintf(stderr, "Received windows message:  ");
341	if ( msg > MAX_WMMSG ) {
342		fprintf(stderr, "%d", msg);
343	} else {
344		fprintf(stderr, "%s", wmtab[msg]);
345	}
346	fprintf(stderr, " -- 0x%X, 0x%X\n", wParam, lParam);
347#endif
348	switch (msg) {
349
350		case WM_ACTIVATE: {
351			SDL_VideoDevice *this = current_video;
352			BOOL active, minimized;
353			Uint8 appstate;
354
355			minimized = HIWORD(wParam);
356			active = (LOWORD(wParam) != WA_INACTIVE) && !minimized;
357			if ( active ) {
358				/* Gain the following states */
359				appstate = SDL_APPACTIVE|SDL_APPINPUTFOCUS;
360				if ( this->input_grab != SDL_GRAB_OFF ) {
361					WIN_GrabInput(this, SDL_GRAB_ON);
362				}
363				if ( !(SDL_GetAppState()&SDL_APPINPUTFOCUS) ) {
364					if ( ! DDRAW_FULLSCREEN() ) {
365						DIB_SwapGamma(this);
366					}
367					if ( WINDIB_FULLSCREEN() ) {
368						SDL_RestoreGameMode();
369					}
370				}
371#if defined(_WIN32_WCE)
372				if ( WINDIB_FULLSCREEN() ) {
373					LoadAygshell();
374					if( SHFullScreen )
375						SHFullScreen(SDL_Window, SHFS_HIDESTARTICON|SHFS_HIDETASKBAR|SHFS_HIDESIPBUTTON);
376					else
377						ShowWindow(FindWindow(TEXT("HHTaskBar"),NULL),SW_HIDE);
378				}
379#endif
380				posted = SDL_PrivateAppActive(1, appstate);
381				WIN_GetKeyboardState();
382			} else {
383				/* Lose the following states */
384				appstate = SDL_APPINPUTFOCUS;
385				if ( minimized ) {
386					appstate |= SDL_APPACTIVE;
387				}
388				if ( this->input_grab != SDL_GRAB_OFF ) {
389					WIN_GrabInput(this, SDL_GRAB_OFF);
390				}
391				if ( SDL_GetAppState() & SDL_APPINPUTFOCUS ) {
392					if ( ! DDRAW_FULLSCREEN() ) {
393						DIB_SwapGamma(this);
394					}
395					if ( WINDIB_FULLSCREEN() ) {
396						SDL_RestoreDesktopMode();
397#if defined(_WIN32_WCE)
398						LoadAygshell();
399						if( SHFullScreen )
400							SHFullScreen(SDL_Window, SHFS_SHOWSTARTICON|SHFS_SHOWTASKBAR|SHFS_SHOWSIPBUTTON);
401						else
402							ShowWindow(FindWindow(TEXT("HHTaskBar"),NULL),SW_SHOW);
403#endif
404					}
405				}
406				posted = SDL_PrivateAppActive(0, appstate);
407			}
408			WIN_Activate(this, active, minimized);
409			return(0);
410		}
411		break;
412
413		case WM_MOUSEMOVE: {
414
415			/* Mouse is handled by DirectInput when fullscreen */
416			if ( SDL_VideoSurface && ! DINPUT_FULLSCREEN() ) {
417				Sint16 x, y;
418
419				/* mouse has entered the window */
420				if ( ! in_window ) {
421#ifdef WM_MOUSELEAVE
422					TRACKMOUSEEVENT tme;
423
424					tme.cbSize = sizeof(tme);
425					tme.dwFlags = TME_LEAVE;
426					tme.hwndTrack = SDL_Window;
427					_TrackMouseEvent(&tme);
428#endif /* WM_MOUSELEAVE */
429					in_window = TRUE;
430
431					posted = SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
432				}
433
434				/* mouse has moved within the window */
435				x = LOWORD(lParam);
436				y = HIWORD(lParam);
437				if ( mouse_relative ) {
438					POINT center;
439					center.x = (SDL_VideoSurface->w/2);
440					center.y = (SDL_VideoSurface->h/2);
441					x -= (Sint16)center.x;
442					y -= (Sint16)center.y;
443					if ( x || y ) {
444						ClientToScreen(SDL_Window, &center);
445						SetCursorPos(center.x, center.y);
446						posted = SDL_PrivateMouseMotion(0, 1, x, y);
447					}
448				} else {
449#ifdef _WIN32_WCE
450					if (SDL_VideoSurface)
451						GapiTransform(this->hidden->userOrientation, this->hidden->hiresFix, &x, &y);
452#endif
453					posted = SDL_PrivateMouseMotion(0, 0, x, y);
454				}
455			}
456		}
457		return(0);
458
459#ifdef WM_MOUSELEAVE
460		case WM_MOUSELEAVE: {
461
462			/* Mouse is handled by DirectInput when fullscreen */
463			if ( SDL_VideoSurface && ! DINPUT_FULLSCREEN() ) {
464				/* mouse has left the window */
465				/* or */
466				/* Elvis has left the building! */
467				posted = SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
468			}
469			in_window = FALSE;
470		}
471		return(0);
472#endif /* WM_MOUSELEAVE */
473
474		case WM_LBUTTONDOWN:
475		case WM_LBUTTONUP:
476		case WM_MBUTTONDOWN:
477		case WM_MBUTTONUP:
478		case WM_RBUTTONDOWN:
479		case WM_RBUTTONUP:
480		case WM_XBUTTONDOWN:
481		case WM_XBUTTONUP: {
482			/* Mouse is handled by DirectInput when fullscreen */
483			if ( SDL_VideoSurface && ! DINPUT_FULLSCREEN() ) {
484				WORD xbuttonval = 0;
485				Sint16 x, y;
486				Uint8 button, state;
487
488				/* DJM:
489				   We want the SDL window to take focus so that
490				   it acts like a normal windows "component"
491				   (e.g. gains keyboard focus on a mouse click).
492				 */
493				SetFocus(SDL_Window);
494
495				/* Figure out which button to use */
496				switch (msg) {
497					case WM_LBUTTONDOWN:
498						button = SDL_BUTTON_LEFT;
499						state = SDL_PRESSED;
500						break;
501					case WM_LBUTTONUP:
502						button = SDL_BUTTON_LEFT;
503						state = SDL_RELEASED;
504						break;
505					case WM_MBUTTONDOWN:
506						button = SDL_BUTTON_MIDDLE;
507						state = SDL_PRESSED;
508						break;
509					case WM_MBUTTONUP:
510						button = SDL_BUTTON_MIDDLE;
511						state = SDL_RELEASED;
512						break;
513					case WM_RBUTTONDOWN:
514						button = SDL_BUTTON_RIGHT;
515						state = SDL_PRESSED;
516						break;
517					case WM_RBUTTONUP:
518						button = SDL_BUTTON_RIGHT;
519						state = SDL_RELEASED;
520						break;
521					case WM_XBUTTONDOWN:
522						xbuttonval = GET_XBUTTON_WPARAM(wParam);
523						button = SDL_BUTTON_WHEELDOWN + xbuttonval;
524						state = SDL_PRESSED;
525						break;
526					case WM_XBUTTONUP:
527						xbuttonval = GET_XBUTTON_WPARAM(wParam);
528						button = SDL_BUTTON_WHEELDOWN + xbuttonval;
529						state = SDL_RELEASED;
530						break;
531					default:
532						/* Eh? Unknown button? */
533						return(0);
534				}
535				if ( state == SDL_PRESSED ) {
536					/* Grab mouse so we get up events */
537					if ( ++mouse_pressed > 0 ) {
538						SetCapture(hwnd);
539					}
540				} else {
541					/* Release mouse after all up events */
542					if ( --mouse_pressed <= 0 ) {
543						ReleaseCapture();
544						mouse_pressed = 0;
545					}
546				}
547				if ( mouse_relative ) {
548				/*	RJR: March 28, 2000
549					report internal mouse position if in relative mode */
550					x = 0; y = 0;
551				} else {
552					x = (Sint16)LOWORD(lParam);
553					y = (Sint16)HIWORD(lParam);
554#ifdef _WIN32_WCE
555					if (SDL_VideoSurface)
556						GapiTransform(this->hidden->userOrientation, this->hidden->hiresFix, &x, &y);
557#endif
558				}
559				posted = SDL_PrivateMouseButton(
560							state, button, x, y);
561
562				/*
563				 * MSDN says:
564				 *  "Unlike the WM_LBUTTONUP, WM_MBUTTONUP, and WM_RBUTTONUP
565				 *   messages, an application should return TRUE from [an
566				 *   XBUTTON message] if it processes it. Doing so will allow
567				 *   software that simulates this message on Microsoft Windows
568				 *   systems earlier than Windows 2000 to determine whether
569				 *   the window procedure processed the message or called
570				 *   DefWindowProc to process it.
571				 */
572				if (xbuttonval > 0)
573					return(TRUE);
574			}
575		}
576		return(0);
577
578
579#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
580		case WM_MOUSEWHEEL:
581			if ( SDL_VideoSurface && ! DINPUT_FULLSCREEN() ) {
582				int move = (short)HIWORD(wParam);
583				if ( move ) {
584					Uint8 button;
585					if ( move > 0 )
586						button = SDL_BUTTON_WHEELUP;
587					else
588						button = SDL_BUTTON_WHEELDOWN;
589					posted = SDL_PrivateMouseButton(
590						SDL_PRESSED, button, 0, 0);
591					posted |= SDL_PrivateMouseButton(
592						SDL_RELEASED, button, 0, 0);
593				}
594			}
595			return(0);
596#endif
597
598#ifdef WM_GETMINMAXINFO
599		/* This message is sent as a way for us to "check" the values
600		 * of a position change.  If we don't like it, we can adjust
601		 * the values before they are changed.
602		 */
603		case WM_GETMINMAXINFO: {
604			MINMAXINFO *info;
605			RECT        size;
606			int x, y;
607			int style;
608			int width;
609			int height;
610
611			/* We don't want to clobber an internal resize */
612			if ( SDL_resizing )
613				return(0);
614
615			/* We allow resizing with the SDL_RESIZABLE flag */
616			if ( SDL_PublicSurface &&
617				(SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
618				return(0);
619			}
620
621			/* Get the current position of our window */
622			GetWindowRect(SDL_Window, &size);
623			x = size.left;
624			y = size.top;
625
626			/* Calculate current width and height of our window */
627			size.top = 0;
628			size.left = 0;
629			if ( SDL_PublicSurface != NULL ) {
630				size.bottom = SDL_PublicSurface->h;
631				size.right = SDL_PublicSurface->w;
632			} else {
633				size.bottom = 0;
634				size.right = 0;
635			}
636
637			/* DJM - according to the docs for GetMenu(), the
638			   return value is undefined if hwnd is a child window.
639			   Aparently it's too difficult for MS to check
640			   inside their function, so I have to do it here.
641          		 */
642         		style = GetWindowLong(hwnd, GWL_STYLE);
643         		AdjustWindowRect(
644				&size,
645				style,
646            			style & WS_CHILDWINDOW ? FALSE
647						       : GetMenu(hwnd) != NULL);
648
649			width = size.right - size.left;
650			height = size.bottom - size.top;
651
652			/* Fix our size to the current size */
653			info = (MINMAXINFO *)lParam;
654			info->ptMaxSize.x = width;
655			info->ptMaxSize.y = height;
656			info->ptMaxPosition.x = x;
657			info->ptMaxPosition.y = y;
658			info->ptMinTrackSize.x = width;
659			info->ptMinTrackSize.y = height;
660			info->ptMaxTrackSize.x = width;
661			info->ptMaxTrackSize.y = height;
662		}
663		return(0);
664#endif /* WM_GETMINMAXINFO */
665
666		case WM_WINDOWPOSCHANGED: {
667			SDL_VideoDevice *this = current_video;
668			int w, h;
669
670			GetClientRect(SDL_Window, &SDL_bounds);
671			ClientToScreen(SDL_Window, (LPPOINT)&SDL_bounds);
672			ClientToScreen(SDL_Window, (LPPOINT)&SDL_bounds+1);
673			if ( !SDL_resizing && !IsZoomed(SDL_Window) &&
674			     SDL_PublicSurface &&
675				!(SDL_PublicSurface->flags & SDL_FULLSCREEN) ) {
676				SDL_windowX = SDL_bounds.left;
677				SDL_windowY = SDL_bounds.top;
678			}
679			w = SDL_bounds.right-SDL_bounds.left;
680			h = SDL_bounds.bottom-SDL_bounds.top;
681			if ( this->input_grab != SDL_GRAB_OFF ) {
682				ClipCursor(&SDL_bounds);
683			}
684			if ( SDL_PublicSurface &&
685				(SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
686				SDL_PrivateResize(w, h);
687			}
688		}
689		break;
690
691		/* We need to set the cursor */
692		case WM_SETCURSOR: {
693			Uint16 hittest;
694
695			hittest = LOWORD(lParam);
696			if ( hittest == HTCLIENT ) {
697				SetCursor(SDL_hcursor);
698				return(TRUE);
699			}
700		}
701		break;
702
703		/* We are about to get palette focus! */
704		case WM_QUERYNEWPALETTE: {
705			WIN_RealizePalette(current_video);
706			return(TRUE);
707		}
708		break;
709
710		/* Another application changed the palette */
711		case WM_PALETTECHANGED: {
712			WIN_PaletteChanged(current_video, (HWND)wParam);
713		}
714		break;
715
716		/* We were occluded, refresh our display */
717		case WM_PAINT: {
718			HDC hdc;
719			PAINTSTRUCT ps;
720
721			hdc = BeginPaint(SDL_Window, &ps);
722			if ( current_video->screen &&
723			     !(current_video->screen->flags & SDL_OPENGL) ) {
724				WIN_WinPAINT(current_video, hdc);
725			}
726			EndPaint(SDL_Window, &ps);
727		}
728		return(0);
729
730		/* DJM: Send an expose event in this case */
731		case WM_ERASEBKGND: {
732			posted = SDL_PrivateExpose();
733		}
734		return(0);
735
736		case WM_CLOSE: {
737			if ( (posted = SDL_PrivateQuit()) )
738				PostQuitMessage(0);
739		}
740		return(0);
741
742		case WM_DESTROY: {
743			PostQuitMessage(0);
744		}
745		return(0);
746
747#ifndef NO_GETKEYBOARDSTATE
748		case WM_INPUTLANGCHANGE: {
749			codepage = GetCodePage();
750		}
751		return(TRUE);
752#endif
753
754		default: {
755			/* Special handling by the video driver */
756			if (HandleMessage) {
757				return(HandleMessage(current_video,
758			                     hwnd, msg, wParam, lParam));
759			}
760		}
761		break;
762	}
763	return(DefWindowProc(hwnd, msg, wParam, lParam));
764}
765
766/* Allow the application handle to be stored and retrieved later */
767static void *SDL_handle = NULL;
768
769void SDL_SetModuleHandle(void *handle)
770{
771	SDL_handle = handle;
772}
773void *SDL_GetModuleHandle(void)
774{
775	void *handle;
776
777	if ( SDL_handle ) {
778		handle = SDL_handle;
779	} else {
780		handle = GetModuleHandle(NULL);
781	}
782	return(handle);
783}
784
785/* This allows the SDL_WINDOWID hack */
786BOOL SDL_windowid = FALSE;
787
788static int app_registered = 0;
789
790/* Register the class for this application -- exported for winmain.c */
791int SDL_RegisterApp(char *name, Uint32 style, void *hInst)
792{
793	WNDCLASS class;
794#ifdef WM_MOUSELEAVE
795	HMODULE handle;
796#endif
797
798	/* Only do this once... */
799	if ( app_registered ) {
800		++app_registered;
801		return(0);
802	}
803
804#ifndef CS_BYTEALIGNCLIENT
805#define CS_BYTEALIGNCLIENT	0
806#endif
807	if ( ! name && ! SDL_Appname ) {
808		name = "SDL_app";
809		SDL_Appstyle = CS_BYTEALIGNCLIENT;
810		SDL_Instance = hInst ? hInst : SDL_GetModuleHandle();
811	}
812
813	if ( name ) {
814#ifdef _WIN32_WCE
815		/* WinCE uses the UNICODE version */
816		SDL_Appname = SDL_iconv_utf8_ucs2(name);
817#else
818		SDL_Appname = SDL_iconv_utf8_locale(name);
819#endif /* _WIN32_WCE */
820		SDL_Appstyle = style;
821		SDL_Instance = hInst ? hInst : SDL_GetModuleHandle();
822	}
823
824	/* Register the application class */
825	class.hCursor		= NULL;
826	class.hIcon		= LoadImage(SDL_Instance, SDL_Appname,
827				            IMAGE_ICON,
828	                                    0, 0, LR_DEFAULTCOLOR);
829	class.lpszMenuName	= NULL;
830	class.lpszClassName	= SDL_Appname;
831	class.hbrBackground	= NULL;
832	class.hInstance		= SDL_Instance;
833	class.style		= SDL_Appstyle;
834#if SDL_VIDEO_OPENGL
835	class.style		|= CS_OWNDC;
836#endif
837	class.lpfnWndProc	= WinMessage;
838	class.cbWndExtra	= 0;
839	class.cbClsExtra	= 0;
840	if ( ! RegisterClass(&class) ) {
841		SDL_SetError("Couldn't register application class");
842		return(-1);
843	}
844
845#ifdef WM_MOUSELEAVE
846	/* Get the version of TrackMouseEvent() we use */
847	_TrackMouseEvent = NULL;
848	handle = GetModuleHandle("USER32.DLL");
849	if ( handle ) {
850		_TrackMouseEvent = (BOOL (WINAPI *)(TRACKMOUSEEVENT *))GetProcAddress(handle, "TrackMouseEvent");
851	}
852	if ( _TrackMouseEvent == NULL ) {
853		_TrackMouseEvent = WIN_TrackMouseEvent;
854	}
855#endif /* WM_MOUSELEAVE */
856
857#ifndef NO_GETKEYBOARDSTATE
858	/* Initialise variables for SDL_ToUnicode() */
859	codepage = GetCodePage();
860	SDL_ToUnicode = Is9xME() ? ToUnicode9xME : ToUnicode;
861#endif
862
863	app_registered = 1;
864	return(0);
865}
866
867/* Unregisters the windowclass registered in SDL_RegisterApp above. */
868void SDL_UnregisterApp()
869{
870	WNDCLASS class;
871
872	/* SDL_RegisterApp might not have been called before */
873	if ( !app_registered ) {
874		return;
875	}
876	--app_registered;
877	if ( app_registered == 0 ) {
878		/* Check for any registered window classes. */
879		if ( GetClassInfo(SDL_Instance, SDL_Appname, &class) ) {
880			UnregisterClass(SDL_Appname, SDL_Instance);
881		}
882		SDL_free(SDL_Appname);
883		SDL_Appname = NULL;
884	}
885}
886
887#ifndef NO_GETKEYBOARDSTATE
888/* JFP: Implementation of ToUnicode() that works on 9x/ME/2K/XP */
889
890static int Is9xME()
891{
892	OSVERSIONINFO   info;
893
894	SDL_memset(&info, 0, sizeof(info));
895	info.dwOSVersionInfoSize = sizeof(info);
896	if (!GetVersionEx(&info)) {
897		return 0;
898	}
899	return (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
900}
901
902static int GetCodePage()
903{
904	char	buff[8];
905	int	lcid = MAKELCID(LOWORD(GetKeyboardLayout(0)), SORT_DEFAULT);
906	int	cp = GetACP();
907
908	if (GetLocaleInfo(lcid, LOCALE_IDEFAULTANSICODEPAGE, buff, sizeof(buff))) {
909		cp = SDL_atoi(buff);
910	}
911	return cp;
912}
913
914static int WINAPI ToUnicode9xME(UINT vkey, UINT scancode, PBYTE keystate, LPWSTR wchars, int wsize, UINT flags)
915{
916	BYTE	chars[2];
917
918	if (ToAsciiEx(vkey, scancode, keystate, (WORD*)chars, 0, GetKeyboardLayout(0)) == 1) {
919		return MultiByteToWideChar(codepage, 0, chars, 1, wchars, wsize);
920	}
921	return 0;
922}
923
924#endif /* !NO_GETKEYBOARDSTATE */
925