1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2012 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 1
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
69LPWSTR SDL_Appname = NULL;
70#else
71LPSTR SDL_Appname = NULL;
72#endif
73Uint32 SDL_Appstyle = 0;
74HINSTANCE SDL_Instance = NULL;
75HWND SDL_Window = NULL;
76RECT SDL_bounds = {0, 0, 0, 0};
77int SDL_windowX = 0;
78int SDL_windowY = 0;
79int SDL_resizing = 0;
80int mouse_relative = 0;
81int posted = 0;
82#ifndef NO_CHANGEDISPLAYSETTINGS
83DEVMODE SDL_desktop_mode;
84DEVMODE SDL_fullscreen_mode;
85#endif
86WORD *gamma_saved = NULL;
87
88
89/* Functions called by the message processing function */
90LONG (*HandleMessage)(_THIS, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)=NULL;
91void (*WIN_Activate)(_THIS, BOOL active, BOOL iconic);
92void (*WIN_RealizePalette)(_THIS);
93void (*WIN_PaletteChanged)(_THIS, HWND window);
94void (*WIN_WinPAINT)(_THIS, HDC hdc);
95extern void DIB_SwapGamma(_THIS);
96
97#ifndef NO_GETKEYBOARDSTATE
98#ifndef _WIN64
99/* Variables and support functions for SDL_ToUnicode() */
100static int codepage;
101static int Is9xME();
102static int GetCodePage();
103static int WINAPI ToUnicode9xME(UINT vkey, UINT scancode, const BYTE *keystate, LPWSTR wchars, int wsize, UINT flags);
104
105ToUnicodeFN 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
116HINSTANCE aygshell = NULL;
117BOOL (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
126static 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*/
143void 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
153static 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}
178static 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*/
218static BOOL (WINAPI *_TrackMouseEvent)(TRACKMOUSEEVENT *ptme) = NULL;
219
220static VOID CALLBACK
221TrackMouseTimerProc(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}
236static 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
246int sysevents_mouse_pressed = 0;
247
248/* The main Win32 event handler
249DJM: This is no longer static as (DX5/DIB)_CreateWindow needs it
250*/
251LRESULT CALLBACK WinMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
252{
253	SDL_VideoDevice *this = current_video;
254#ifdef WMMSG_DEBUG
255	fprintf(stderr, "Received windows message:  ");
256	if ( msg > MAX_WMMSG ) {
257		fprintf(stderr, "%d", msg);
258	} else {
259		fprintf(stderr, "%s", wmtab[msg]);
260	}
261	fprintf(stderr, " -- 0x%X, 0x%X\n", wParam, lParam);
262#endif
263	switch (msg) {
264
265		case WM_ACTIVATE: {
266			SDL_VideoDevice *this = current_video;
267			BOOL active, minimized;
268			Uint8 appstate;
269
270			minimized = HIWORD(wParam);
271			active = (LOWORD(wParam) != WA_INACTIVE) && !minimized;
272			if ( active ) {
273				/* Gain the following states */
274				appstate = SDL_APPACTIVE|SDL_APPINPUTFOCUS;
275				if ( !(SDL_GetAppState() & SDL_APPINPUTFOCUS) ) {
276					if ( this->input_grab != SDL_GRAB_OFF ) {
277						WIN_GrabInput(this, SDL_GRAB_ON);
278					}
279					if ( ! DDRAW_FULLSCREEN() ) {
280						DIB_SwapGamma(this);
281					}
282					if ( WINDIB_FULLSCREEN() ) {
283						SDL_RestoreGameMode();
284					}
285				}
286#if defined(_WIN32_WCE)
287				if ( WINDIB_FULLSCREEN() ) {
288					LoadAygshell();
289					if( SHFullScreen )
290						SHFullScreen(SDL_Window, SHFS_HIDESTARTICON|SHFS_HIDETASKBAR|SHFS_HIDESIPBUTTON);
291					else
292						ShowWindow(FindWindow(TEXT("HHTaskBar"),NULL),SW_HIDE);
293				}
294#endif
295				posted = SDL_PrivateAppActive(1, appstate);
296			} else {
297				/* Lose the following states */
298				appstate = SDL_APPINPUTFOCUS;
299				if ( minimized ) {
300					appstate |= SDL_APPACTIVE;
301				}
302
303				if ( SDL_GetAppState() & SDL_APPINPUTFOCUS ) {
304					if ( this->input_grab != SDL_GRAB_OFF ) {
305						WIN_GrabInput(this, SDL_GRAB_OFF);
306					}
307					if ( ! DDRAW_FULLSCREEN() ) {
308						DIB_SwapGamma(this);
309					}
310					if ( WINDIB_FULLSCREEN() ) {
311						appstate |= SDL_APPMOUSEFOCUS;
312						SDL_RestoreDesktopMode();
313						/* A fullscreen app gets hidden but will not get a minimize event */
314						appstate |= (SDL_APPACTIVE | SDL_APPMOUSEFOCUS);
315#if defined(_WIN32_WCE)
316						LoadAygshell();
317						if( SHFullScreen )
318							SHFullScreen(SDL_Window, SHFS_SHOWSTARTICON|SHFS_SHOWTASKBAR|SHFS_SHOWSIPBUTTON);
319						else
320							ShowWindow(FindWindow(TEXT("HHTaskBar"),NULL),SW_SHOW);
321#endif
322					}
323				}
324				posted = SDL_PrivateAppActive(0, appstate);
325			}
326			WIN_Activate(this, active, minimized);
327			return(0);
328		}
329		break;
330
331		case WM_MOUSEMOVE: {
332
333#ifdef WM_MOUSELEAVE
334			if ( SDL_VideoSurface ) {
335				/* mouse has entered the window */
336
337				if ( !(SDL_GetAppState() & SDL_APPMOUSEFOCUS) ) {
338					TRACKMOUSEEVENT tme;
339
340					tme.cbSize = sizeof(tme);
341					tme.dwFlags = TME_LEAVE;
342					tme.hwndTrack = SDL_Window;
343					_TrackMouseEvent(&tme);
344				}
345			}
346#endif /* WM_MOUSELEAVE */
347
348			/* Mouse motion is handled in DIB_PumpEvents or
349			 * DX5_PumpEvents, depending on the video driver
350			 * in use */
351
352			posted = SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
353		}
354		return(0);
355
356#ifdef WM_MOUSELEAVE
357		case WM_MOUSELEAVE: {
358
359			if ( SDL_VideoSurface ) {
360				/* mouse has left the window */
361				posted = SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
362			}
363		}
364		return(0);
365#endif /* WM_MOUSELEAVE */
366
367		case WM_LBUTTONDOWN:
368		case WM_LBUTTONUP:
369		case WM_MBUTTONDOWN:
370		case WM_MBUTTONUP:
371		case WM_RBUTTONDOWN:
372		case WM_RBUTTONUP:
373		case WM_XBUTTONDOWN:
374		case WM_XBUTTONUP: {
375			/* Mouse is handled by DirectInput when fullscreen */
376			if ( SDL_VideoSurface && ! DINPUT() ) {
377				WORD xbuttonval = 0;
378				Uint8 button, state;
379                int x, y;
380
381				/* DJM:
382				   We want the SDL window to take focus so that
383				   it acts like a normal windows "component"
384				   (e.g. gains keyboard focus on a mouse click).
385				 */
386				SetFocus(SDL_Window);
387
388				/* Figure out which button to use */
389				switch (msg) {
390					case WM_LBUTTONDOWN:
391						button = SDL_BUTTON_LEFT;
392						state = SDL_PRESSED;
393						break;
394					case WM_LBUTTONUP:
395						button = SDL_BUTTON_LEFT;
396						state = SDL_RELEASED;
397						break;
398					case WM_MBUTTONDOWN:
399						button = SDL_BUTTON_MIDDLE;
400						state = SDL_PRESSED;
401						break;
402					case WM_MBUTTONUP:
403						button = SDL_BUTTON_MIDDLE;
404						state = SDL_RELEASED;
405						break;
406					case WM_RBUTTONDOWN:
407						button = SDL_BUTTON_RIGHT;
408						state = SDL_PRESSED;
409						break;
410					case WM_RBUTTONUP:
411						button = SDL_BUTTON_RIGHT;
412						state = SDL_RELEASED;
413						break;
414					case WM_XBUTTONDOWN:
415						xbuttonval = GET_XBUTTON_WPARAM(wParam);
416						button = SDL_BUTTON_X1 + xbuttonval - 1;
417						state = SDL_PRESSED;
418						break;
419					case WM_XBUTTONUP:
420						xbuttonval = GET_XBUTTON_WPARAM(wParam);
421						button = SDL_BUTTON_X1 + xbuttonval - 1;
422						state = SDL_RELEASED;
423						break;
424					default:
425						/* Eh? Unknown button? */
426						return(0);
427				}
428				if ( state == SDL_PRESSED ) {
429					/* Grab mouse so we get up events */
430					if ( ++sysevents_mouse_pressed > 0 ) {
431						SetCapture(hwnd);
432					}
433				} else {
434					/* Release mouse after all up events */
435					if ( --sysevents_mouse_pressed <= 0 ) {
436						ReleaseCapture();
437						sysevents_mouse_pressed = 0;
438					}
439				}
440				if ( mouse_relative ) {
441				/*	RJR: March 28, 2000
442					report internal mouse position if in relative mode */
443					x = 0; y = 0;
444				} else {
445					x = (Sint16)LOWORD(lParam);
446					y = (Sint16)HIWORD(lParam);
447#ifdef _WIN32_WCE
448					if (SDL_VideoSurface)
449						GapiTransform(this->hidden->userOrientation,
450this->hidden->hiresFix, &x, &y);
451#endif
452				}
453				posted = SDL_PrivateMouseButton(
454							state, button, x, y);
455
456				/*
457				 * MSDN says:
458				 *  "Unlike the WM_LBUTTONUP, WM_MBUTTONUP, and WM_RBUTTONUP
459				 *   messages, an application should return TRUE from [an
460				 *   XBUTTON message] if it processes it. Doing so will allow
461				 *   software that simulates this message on Microsoft Windows
462				 *   systems earlier than Windows 2000 to determine whether
463				 *   the window procedure processed the message or called
464				 *   DefWindowProc to process it.
465				 */
466				if (xbuttonval > 0)
467					return(TRUE);
468			}
469		}
470		return(0);
471
472
473#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
474		case WM_MOUSEWHEEL:
475			if ( SDL_VideoSurface && ! DINPUT() ) {
476				int move = (short)HIWORD(wParam);
477				if ( move ) {
478					Uint8 button;
479					if ( move > 0 )
480						button = SDL_BUTTON_WHEELUP;
481					else
482						button = SDL_BUTTON_WHEELDOWN;
483					posted = SDL_PrivateMouseButton(
484						SDL_PRESSED, button, 0, 0);
485					posted |= SDL_PrivateMouseButton(
486						SDL_RELEASED, button, 0, 0);
487				}
488			}
489			return(0);
490#endif
491
492#ifdef WM_GETMINMAXINFO
493		/* This message is sent as a way for us to "check" the values
494		 * of a position change.  If we don't like it, we can adjust
495		 * the values before they are changed.
496		 */
497		case WM_GETMINMAXINFO: {
498			MINMAXINFO *info;
499			RECT        size;
500			int x, y;
501			int style;
502			int width;
503			int height;
504
505			/* We don't want to clobber an internal resize */
506			if ( SDL_resizing )
507				return(0);
508
509			/* We allow resizing with the SDL_RESIZABLE flag */
510			if ( SDL_PublicSurface &&
511				(SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
512				return(0);
513			}
514
515			/* Get the current position of our window */
516			GetWindowRect(SDL_Window, &size);
517			x = size.left;
518			y = size.top;
519
520			/* Calculate current width and height of our window */
521			size.top = 0;
522			size.left = 0;
523			if ( SDL_PublicSurface != NULL ) {
524				size.bottom = SDL_PublicSurface->h;
525				size.right = SDL_PublicSurface->w;
526			} else {
527				size.bottom = 0;
528				size.right = 0;
529			}
530
531			/* DJM - according to the docs for GetMenu(), the
532			   return value is undefined if hwnd is a child window.
533			   Aparently it's too difficult for MS to check
534			   inside their function, so I have to do it here.
535          		 */
536         		style = GetWindowLong(hwnd, GWL_STYLE);
537         		AdjustWindowRect(
538				&size,
539				style,
540            			style & WS_CHILDWINDOW ? FALSE
541						       : GetMenu(hwnd) != NULL);
542
543			width = size.right - size.left;
544			height = size.bottom - size.top;
545
546			/* Fix our size to the current size */
547			info = (MINMAXINFO *)lParam;
548			info->ptMaxSize.x = width;
549			info->ptMaxSize.y = height;
550			info->ptMaxPosition.x = x;
551			info->ptMaxPosition.y = y;
552			info->ptMinTrackSize.x = width;
553			info->ptMinTrackSize.y = height;
554			info->ptMaxTrackSize.x = width;
555			info->ptMaxTrackSize.y = height;
556		}
557		return(0);
558#endif /* WM_GETMINMAXINFO */
559
560		case WM_WINDOWPOSCHANGING: {
561			WINDOWPOS *windowpos = (WINDOWPOS*)lParam;
562
563			/* When menu is at the side or top, Windows likes
564			   to try to reposition the fullscreen window when
565			   changing video modes.
566			 */
567			if ( !SDL_resizing &&
568			     SDL_PublicSurface &&
569			     (SDL_PublicSurface->flags & SDL_FULLSCREEN) ) {
570				windowpos->x = 0;
571				windowpos->y = 0;
572			}
573		}
574		return(0);
575
576		case WM_WINDOWPOSCHANGED: {
577			SDL_VideoDevice *this = current_video;
578			POINT pt;
579			int w, h;
580
581			GetClientRect(SDL_Window, &SDL_bounds);
582
583			/* avoiding type-punning here... */
584			pt.x = SDL_bounds.left;
585			pt.y = SDL_bounds.top;
586			ClientToScreen(SDL_Window, &pt);
587			SDL_bounds.left = pt.x;
588			SDL_bounds.top = pt.y;
589
590			pt.x = SDL_bounds.right;
591			pt.y = SDL_bounds.bottom;
592			ClientToScreen(SDL_Window, &pt);
593			SDL_bounds.right = pt.x;
594			SDL_bounds.bottom = pt.y;
595
596			if ( !SDL_resizing && !IsZoomed(SDL_Window) &&
597			     SDL_PublicSurface &&
598				!(SDL_PublicSurface->flags & SDL_FULLSCREEN) ) {
599				SDL_windowX = SDL_bounds.left;
600				SDL_windowY = SDL_bounds.top;
601			}
602			w = SDL_bounds.right-SDL_bounds.left;
603			h = SDL_bounds.bottom-SDL_bounds.top;
604			if ( this->input_grab != SDL_GRAB_OFF ) {
605				ClipCursor(&SDL_bounds);
606			}
607			if ( SDL_PublicSurface &&
608				(SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
609				SDL_PrivateResize(w, h);
610			}
611		}
612		break;
613
614		/* We need to set the cursor */
615		case WM_SETCURSOR: {
616			Uint16 hittest;
617
618			hittest = LOWORD(lParam);
619			if ( hittest == HTCLIENT ) {
620				SetCursor(SDL_hcursor);
621				return(TRUE);
622			}
623		}
624		break;
625
626		/* We are about to get palette focus! */
627		case WM_QUERYNEWPALETTE: {
628			WIN_RealizePalette(current_video);
629			return(TRUE);
630		}
631		break;
632
633		/* Another application changed the palette */
634		case WM_PALETTECHANGED: {
635			WIN_PaletteChanged(current_video, (HWND)wParam);
636		}
637		break;
638
639		/* We were occluded, refresh our display */
640		case WM_PAINT: {
641			HDC hdc;
642			PAINTSTRUCT ps;
643
644			hdc = BeginPaint(SDL_Window, &ps);
645			if ( current_video->screen &&
646			     !(current_video->screen->flags & SDL_OPENGL) ) {
647				WIN_WinPAINT(current_video, hdc);
648			}
649			EndPaint(SDL_Window, &ps);
650		}
651		return(0);
652
653		/* DJM: Send an expose event in this case */
654		case WM_ERASEBKGND: {
655			posted = SDL_PrivateExpose();
656		}
657		return(0);
658
659		case WM_CLOSE: {
660			if ( (posted = SDL_PrivateQuit()) )
661				PostQuitMessage(0);
662		}
663		return(0);
664
665		case WM_DESTROY: {
666			PostQuitMessage(0);
667		}
668		return(0);
669
670#ifndef NO_GETKEYBOARDSTATE
671		case WM_INPUTLANGCHANGE:
672#ifndef _WIN64
673			codepage = GetCodePage();
674#endif
675		return(TRUE);
676#endif
677
678		default: {
679			/* Special handling by the video driver */
680			if (HandleMessage) {
681				return(HandleMessage(current_video,
682			                     hwnd, msg, wParam, lParam));
683			}
684		}
685		break;
686	}
687	return(DefWindowProc(hwnd, msg, wParam, lParam));
688}
689
690/* Allow the application handle to be stored and retrieved later */
691static void *SDL_handle = NULL;
692
693void SDL_SetModuleHandle(void *handle)
694{
695	SDL_handle = handle;
696}
697void *SDL_GetModuleHandle(void)
698{
699	void *handle;
700
701	if ( SDL_handle ) {
702		handle = SDL_handle;
703	} else {
704		handle = GetModuleHandle(NULL);
705	}
706	return(handle);
707}
708
709/* This allows the SDL_WINDOWID hack */
710BOOL SDL_windowid = FALSE;
711
712static int app_registered = 0;
713
714/* Register the class for this application -- exported for winmain.c */
715int SDL_RegisterApp(char *name, Uint32 style, void *hInst)
716{
717	WNDCLASS class;
718#ifdef WM_MOUSELEAVE
719	HMODULE handle;
720#endif
721
722	/* Only do this once... */
723	if ( app_registered ) {
724		++app_registered;
725		return(0);
726	}
727
728#ifndef CS_BYTEALIGNCLIENT
729#define CS_BYTEALIGNCLIENT	0
730#endif
731	if ( ! name && ! SDL_Appname ) {
732		name = "SDL_app";
733		SDL_Appstyle = CS_BYTEALIGNCLIENT;
734		SDL_Instance = hInst ? hInst : SDL_GetModuleHandle();
735	}
736
737	if ( name ) {
738#ifdef _WIN32_WCE
739		/* WinCE uses the UNICODE version */
740		SDL_Appname = SDL_iconv_utf8_ucs2(name);
741#else
742		SDL_Appname = SDL_iconv_utf8_locale(name);
743#endif /* _WIN32_WCE */
744		SDL_Appstyle = style;
745		SDL_Instance = hInst ? hInst : SDL_GetModuleHandle();
746	}
747
748	/* Register the application class */
749	class.hCursor		= NULL;
750	class.hIcon		= LoadImage(SDL_Instance, SDL_Appname,
751				            IMAGE_ICON,
752	                                    0, 0, LR_DEFAULTCOLOR);
753	class.lpszMenuName	= NULL;
754	class.lpszClassName	= SDL_Appname;
755	class.hbrBackground	= NULL;
756	class.hInstance		= SDL_Instance;
757	class.style		= SDL_Appstyle;
758#if SDL_VIDEO_OPENGL
759	class.style		|= CS_OWNDC;
760#endif
761	class.lpfnWndProc	= WinMessage;
762	class.cbWndExtra	= 0;
763	class.cbClsExtra	= 0;
764	if ( ! RegisterClass(&class) ) {
765		SDL_SetError("Couldn't register application class");
766		return(-1);
767	}
768
769#ifdef WM_MOUSELEAVE
770	/* Get the version of TrackMouseEvent() we use */
771	_TrackMouseEvent = NULL;
772	handle = GetModuleHandle("USER32.DLL");
773	if ( handle ) {
774		_TrackMouseEvent = (BOOL (WINAPI *)(TRACKMOUSEEVENT *))GetProcAddress(handle, "TrackMouseEvent");
775	}
776	if ( _TrackMouseEvent == NULL ) {
777		_TrackMouseEvent = WIN_TrackMouseEvent;
778	}
779#endif /* WM_MOUSELEAVE */
780
781#ifndef NO_GETKEYBOARDSTATE
782#ifndef _WIN64
783	/* Initialise variables for SDL_ToUnicode() */
784	codepage = GetCodePage();
785
786	/* Cygwin headers don't match windows.h, so we have to cast around a
787	   const issue here... */
788	SDL_ToUnicode = Is9xME() ? ToUnicode9xME : (ToUnicodeFN) ToUnicode;
789#endif
790#endif /* NO_GETKEYBOARDSTATE */
791
792	app_registered = 1;
793	return(0);
794}
795
796/* Unregisters the windowclass registered in SDL_RegisterApp above. */
797void SDL_UnregisterApp()
798{
799	WNDCLASS class;
800
801	/* SDL_RegisterApp might not have been called before */
802	if ( !app_registered ) {
803		return;
804	}
805	--app_registered;
806	if ( app_registered == 0 ) {
807		/* Check for any registered window classes. */
808		if ( GetClassInfo(SDL_Instance, SDL_Appname, &class) ) {
809			UnregisterClass(SDL_Appname, SDL_Instance);
810		}
811		SDL_free(SDL_Appname);
812		SDL_Appname = NULL;
813	}
814}
815
816#ifndef NO_GETKEYBOARDSTATE
817#ifndef _WIN64
818/* JFP: Implementation of ToUnicode() that works on 9x/ME/2K/XP */
819
820static int Is9xME()
821{
822	OSVERSIONINFO   info;
823
824	SDL_memset(&info, 0, sizeof(info));
825	info.dwOSVersionInfoSize = sizeof(info);
826	if (!GetVersionEx(&info)) {
827		return 0;
828	}
829	return (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
830}
831
832static int GetCodePage()
833{
834	char	buff[8];
835	int	lcid = MAKELCID(LOWORD(GetKeyboardLayout(0)), SORT_DEFAULT);
836	int	cp = GetACP();
837
838	if (GetLocaleInfo(lcid, LOCALE_IDEFAULTANSICODEPAGE, buff, sizeof(buff))) {
839		cp = SDL_atoi(buff);
840	}
841	return cp;
842}
843
844static int WINAPI ToUnicode9xME(UINT vkey, UINT scancode, const BYTE *keystate, LPWSTR wchars, int wsize, UINT flags)
845{
846	BYTE	chars[2];
847
848	/* arg #3 should be const BYTE *, but cygwin lists it as PBYTE. */
849	if (ToAsciiEx(vkey, scancode, (PBYTE) keystate, (WORD*)chars, 0, GetKeyboardLayout(0)) == 1) {
850		return MultiByteToWideChar(codepage, 0, (LPCSTR) chars, 1, wchars, wsize);
851	}
852	return 0;
853}
854#endif
855#endif /* !NO_GETKEYBOARDSTATE */
856