2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2009 Sam Lantinga
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.
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.
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
22 #include "SDL_config.h"
24 #define WIN32_LEAN_AND_MEAN
27 /* Make sure XBUTTON stuff is defined that isn't in older Platform SDKs... */
28 #ifndef WM_XBUTTONDOWN
29 #define WM_XBUTTONDOWN 0x020B
32 #define WM_XBUTTONUP 0x020C
34 #ifndef GET_XBUTTON_WPARAM
35 #define GET_XBUTTON_WPARAM(w) (HIWORD(w))
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"
47 #include "SDL_loadso.h"
53 #include "../windib/SDL_gapidibvideo.h"
55 #ifdef SDL_VIDEO_DRIVER_GAPI
56 #include "../gapi/SDL_gapivideo.h"
60 #define IsZoomed(HWND) 1
61 #define NO_GETKEYBOARDSTATE
63 #define NO_CHANGEDISPLAYSETTINGS
67 /* The window we use for everything... */
69 LPWSTR SDL_Appname = NULL;
71 LPSTR SDL_Appname = NULL;
73 Uint32 SDL_Appstyle = 0;
74 HINSTANCE SDL_Instance = NULL;
75 HWND SDL_Window = NULL;
76 RECT SDL_bounds = {0, 0, 0, 0};
80 int mouse_relative = 0;
82 #ifndef NO_CHANGEDISPLAYSETTINGS
83 DEVMODE SDL_desktop_mode;
84 DEVMODE SDL_fullscreen_mode;
86 WORD *gamma_saved = NULL;
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);
97 #ifndef NO_GETKEYBOARDSTATE
98 /* Variables and support functions for SDL_ToUnicode() */
101 static int GetCodePage();
102 static int WINAPI ToUnicode9xME(UINT vkey, UINT scancode, BYTE *keystate, LPWSTR wchars, int wsize, UINT flags);
104 ToUnicodeFN SDL_ToUnicode = ToUnicode9xME;
105 #endif /* !NO_GETKEYBOARDSTATE */
108 #if defined(_WIN32_WCE)
110 //AdjustWindowRect is not available under WinCE 2003
111 #define AdjustWindowRect(a,b,c) (AdjustWindowRectEx((a),(b),(c),0))
113 // dynamically load aygshell dll because we want SDL to work on HPC and be300
114 HINSTANCE aygshell = NULL;
115 BOOL (WINAPI *SHFullScreen)(HWND hwndRequester, DWORD dwState) = 0;
117 #define SHFS_SHOWTASKBAR 0x0001
118 #define SHFS_HIDETASKBAR 0x0002
119 #define SHFS_SHOWSIPBUTTON 0x0004
120 #define SHFS_HIDESIPBUTTON 0x0008
121 #define SHFS_SHOWSTARTICON 0x0010
122 #define SHFS_HIDESTARTICON 0x0020
124 static void LoadAygshell(void)
127 aygshell = SDL_LoadObject("aygshell.dll");
128 if( (aygshell != 0) && (SHFullScreen == 0) )
130 SHFullScreen = (int (WINAPI *)(struct HWND__ *,unsigned long)) SDL_LoadFunction(aygshell, "SHFullScreen");
137 This is used all over the place, in the windib driver and in the dx5 driver
138 So we may as well stick it here instead of having multiple copies scattered
141 void WIN_FlushMessageQueue()
144 while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) {
145 if ( msg.message == WM_QUIT ) break;
146 TranslateMessage( &msg );
147 DispatchMessage( &msg );
151 static void SDL_RestoreGameMode(void)
153 #ifdef _WIN32_WCE //Under ce we don't minimize, therefore no restore
155 #ifdef SDL_VIDEO_DRIVER_GAPI
156 SDL_VideoDevice *this = current_video;
157 if(SDL_strcmp(this->name, "gapi") == 0)
159 if( this->hidden->gapiInfo->suspended )
161 this->hidden->gapiInfo->suspended = 0;
167 ShowWindow(SDL_Window, SW_RESTORE);
170 #ifndef NO_CHANGEDISPLAYSETTINGS
172 ChangeDisplaySettings(&SDL_fullscreen_mode, CDS_FULLSCREEN);
174 #endif /* NO_CHANGEDISPLAYSETTINGS */
176 static void SDL_RestoreDesktopMode(void)
181 #ifdef SDL_VIDEO_DRIVER_GAPI
182 SDL_VideoDevice *this = current_video;
183 if(SDL_strcmp(this->name, "gapi") == 0)
185 if( !this->hidden->gapiInfo->suspended )
187 this->hidden->gapiInfo->suspended = 1;
193 /* WinCE does not have a taskbar, so minimizing is not convenient */
194 ShowWindow(SDL_Window, SW_MINIMIZE);
197 #ifndef NO_CHANGEDISPLAYSETTINGS
199 ChangeDisplaySettings(NULL, 0);
201 #endif /* NO_CHANGEDISPLAYSETTINGS */
206 Special code to handle mouse leave events - this sucks...
207 http://support.microsoft.com/support/kb/articles/q183/1/07.asp
209 TrackMouseEvent() is only available on Win98 and WinNT.
210 _TrackMouseEvent() is available on Win95, but isn't yet in the mingw32
211 development environment, and only works on systems that have had IE 3.0
212 or newer installed on them (which is not the case with the base Win95).
213 Therefore, we implement our own version of _TrackMouseEvent() which
214 uses our own implementation if TrackMouseEvent() is not available.
216 static BOOL (WINAPI *_TrackMouseEvent)(TRACKMOUSEEVENT *ptme) = NULL;
219 TrackMouseTimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
224 GetClientRect(hWnd, &rect);
225 MapWindowPoints(hWnd, NULL, (LPPOINT)&rect, 2);
227 if ( !PtInRect(&rect, pt) || (WindowFromPoint(pt) != hWnd) ) {
228 if ( !KillTimer(hWnd, idEvent) ) {
229 /* Error killing the timer! */
231 PostMessage(hWnd, WM_MOUSELEAVE, 0, 0);
234 static BOOL WINAPI WIN_TrackMouseEvent(TRACKMOUSEEVENT *ptme)
236 if ( ptme->dwFlags == TME_LEAVE ) {
237 return SetTimer(ptme->hwndTrack, ptme->dwFlags, 100,
238 (TIMERPROC)TrackMouseTimerProc) != 0;
242 #endif /* WM_MOUSELEAVE */
244 /* Function to retrieve the current keyboard modifiers */
245 static void WIN_GetKeyboardState(void)
247 #ifndef NO_GETKEYBOARDSTATE
250 Uint8 *kstate = SDL_GetKeyState(NULL);
253 if ( GetKeyboardState(keyboard) ) {
254 if ( keyboard[VK_LSHIFT] & 0x80) {
255 state |= KMOD_LSHIFT;
256 kstate[SDLK_LSHIFT] = SDL_PRESSED;
258 if ( keyboard[VK_RSHIFT] & 0x80) {
259 state |= KMOD_RSHIFT;
260 kstate[SDLK_RSHIFT] = SDL_PRESSED;
262 if ( keyboard[VK_LCONTROL] & 0x80) {
264 kstate[SDLK_LCTRL] = SDL_PRESSED;
266 if ( keyboard[VK_RCONTROL] & 0x80) {
268 kstate[SDLK_RCTRL] = SDL_PRESSED;
270 if ( keyboard[VK_LMENU] & 0x80) {
272 kstate[SDLK_LALT] = SDL_PRESSED;
274 if ( keyboard[VK_RMENU] & 0x80) {
276 kstate[SDLK_RALT] = SDL_PRESSED;
278 if ( keyboard[VK_NUMLOCK] & 0x01) {
280 kstate[SDLK_NUMLOCK] = SDL_PRESSED;
282 if ( keyboard[VK_CAPITAL] & 0x01) {
284 kstate[SDLK_CAPSLOCK] = SDL_PRESSED;
287 SDL_SetModState(state);
288 #endif /* !NO_GETKEYBOARDSTATE */
291 /* The main Win32 event handler
292 DJM: This is no longer static as (DX5/DIB)_CreateWindow needs it
294 LRESULT CALLBACK WinMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
296 SDL_VideoDevice *this = current_video;
297 static int mouse_pressed = 0;
299 fprintf(stderr, "Received windows message: ");
300 if ( msg > MAX_WMMSG ) {
301 fprintf(stderr, "%d", msg);
303 fprintf(stderr, "%s", wmtab[msg]);
305 fprintf(stderr, " -- 0x%X, 0x%X\n", wParam, lParam);
310 SDL_VideoDevice *this = current_video;
311 BOOL active, minimized;
314 minimized = HIWORD(wParam);
315 active = (LOWORD(wParam) != WA_INACTIVE) && !minimized;
317 /* Gain the following states */
318 appstate = SDL_APPACTIVE|SDL_APPINPUTFOCUS;
319 if ( this->input_grab != SDL_GRAB_OFF ) {
320 WIN_GrabInput(this, SDL_GRAB_ON);
322 if ( !(SDL_GetAppState()&SDL_APPINPUTFOCUS) ) {
323 if ( ! DDRAW_FULLSCREEN() ) {
326 if ( WINDIB_FULLSCREEN() ) {
327 SDL_RestoreGameMode();
330 #if defined(_WIN32_WCE)
331 if ( WINDIB_FULLSCREEN() ) {
334 SHFullScreen(SDL_Window, SHFS_HIDESTARTICON|SHFS_HIDETASKBAR|SHFS_HIDESIPBUTTON);
336 ShowWindow(FindWindow(TEXT("HHTaskBar"),NULL),SW_HIDE);
339 posted = SDL_PrivateAppActive(1, appstate);
340 WIN_GetKeyboardState();
342 /* Lose the following states */
343 appstate = SDL_APPINPUTFOCUS;
345 appstate |= SDL_APPACTIVE;
347 if ( this->input_grab != SDL_GRAB_OFF ) {
348 WIN_GrabInput(this, SDL_GRAB_OFF);
350 if ( SDL_GetAppState() & SDL_APPINPUTFOCUS ) {
351 if ( ! DDRAW_FULLSCREEN() ) {
354 if ( WINDIB_FULLSCREEN() ) {
355 SDL_RestoreDesktopMode();
356 #if defined(_WIN32_WCE)
359 SHFullScreen(SDL_Window, SHFS_SHOWSTARTICON|SHFS_SHOWTASKBAR|SHFS_SHOWSIPBUTTON);
361 ShowWindow(FindWindow(TEXT("HHTaskBar"),NULL),SW_SHOW);
365 posted = SDL_PrivateAppActive(0, appstate);
367 WIN_Activate(this, active, minimized);
375 /* No need to handle SDL_APPMOUSEFOCUS when fullscreen */
376 if ( SDL_VideoSurface && !FULLSCREEN() ) {
377 /* mouse has entered the window */
379 if ( !(SDL_GetAppState() & SDL_APPMOUSEFOCUS) ) {
382 tme.cbSize = sizeof(tme);
383 tme.dwFlags = TME_LEAVE;
384 tme.hwndTrack = SDL_Window;
385 _TrackMouseEvent(&tme);
388 #endif /* WM_MOUSELEAVE */
390 /* Mouse motion is handled in DIB_PumpEvents or
391 * DX5_PumpEvents, depending on the video driver
394 posted = SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
399 case WM_MOUSELEAVE: {
401 /* No need to handle SDL_APPMOUSEFOCUS when fullscreen */
402 if ( SDL_VideoSurface && !FULLSCREEN() ) {
403 /* mouse has left the window */
405 /* Elvis has left the building! */
406 posted = SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
410 #endif /* WM_MOUSELEAVE */
420 /* Mouse is handled by DirectInput when fullscreen */
421 if ( SDL_VideoSurface && ! DINPUT() ) {
426 We want the SDL window to take focus so that
427 it acts like a normal windows "component"
428 (e.g. gains keyboard focus on a mouse click).
430 SetFocus(SDL_Window);
432 /* Figure out which button to use */
435 button = SDL_BUTTON_LEFT;
439 button = SDL_BUTTON_LEFT;
440 state = SDL_RELEASED;
443 button = SDL_BUTTON_MIDDLE;
447 button = SDL_BUTTON_MIDDLE;
448 state = SDL_RELEASED;
451 button = SDL_BUTTON_RIGHT;
455 button = SDL_BUTTON_RIGHT;
456 state = SDL_RELEASED;
459 xbuttonval = GET_XBUTTON_WPARAM(wParam);
460 button = SDL_BUTTON_X1 + xbuttonval - 1;
464 xbuttonval = GET_XBUTTON_WPARAM(wParam);
465 button = SDL_BUTTON_X1 + xbuttonval - 1;
466 state = SDL_RELEASED;
469 /* Eh? Unknown button? */
472 if ( state == SDL_PRESSED ) {
473 /* Grab mouse so we get up events */
474 if ( ++mouse_pressed > 0 ) {
478 /* Release mouse after all up events */
479 if ( --mouse_pressed <= 0 ) {
484 posted = SDL_PrivateMouseButton(
485 state, button, 0, 0);
489 * "Unlike the WM_LBUTTONUP, WM_MBUTTONUP, and WM_RBUTTONUP
490 * messages, an application should return TRUE from [an
491 * XBUTTON message] if it processes it. Doing so will allow
492 * software that simulates this message on Microsoft Windows
493 * systems earlier than Windows 2000 to determine whether
494 * the window procedure processed the message or called
495 * DefWindowProc to process it.
504 #if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
506 if ( SDL_VideoSurface && ! DINPUT() ) {
507 int move = (short)HIWORD(wParam);
511 button = SDL_BUTTON_WHEELUP;
513 button = SDL_BUTTON_WHEELDOWN;
514 posted = SDL_PrivateMouseButton(
515 SDL_PRESSED, button, 0, 0);
516 posted |= SDL_PrivateMouseButton(
517 SDL_RELEASED, button, 0, 0);
523 #ifdef WM_GETMINMAXINFO
524 /* This message is sent as a way for us to "check" the values
525 * of a position change. If we don't like it, we can adjust
526 * the values before they are changed.
528 case WM_GETMINMAXINFO: {
536 /* We don't want to clobber an internal resize */
540 /* We allow resizing with the SDL_RESIZABLE flag */
541 if ( SDL_PublicSurface &&
542 (SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
546 /* Get the current position of our window */
547 GetWindowRect(SDL_Window, &size);
551 /* Calculate current width and height of our window */
554 if ( SDL_PublicSurface != NULL ) {
555 size.bottom = SDL_PublicSurface->h;
556 size.right = SDL_PublicSurface->w;
562 /* DJM - according to the docs for GetMenu(), the
563 return value is undefined if hwnd is a child window.
564 Aparently it's too difficult for MS to check
565 inside their function, so I have to do it here.
567 style = GetWindowLong(hwnd, GWL_STYLE);
571 style & WS_CHILDWINDOW ? FALSE
572 : GetMenu(hwnd) != NULL);
574 width = size.right - size.left;
575 height = size.bottom - size.top;
577 /* Fix our size to the current size */
578 info = (MINMAXINFO *)lParam;
579 info->ptMaxSize.x = width;
580 info->ptMaxSize.y = height;
581 info->ptMaxPosition.x = x;
582 info->ptMaxPosition.y = y;
583 info->ptMinTrackSize.x = width;
584 info->ptMinTrackSize.y = height;
585 info->ptMaxTrackSize.x = width;
586 info->ptMaxTrackSize.y = height;
589 #endif /* WM_GETMINMAXINFO */
591 case WM_WINDOWPOSCHANGING: {
592 WINDOWPOS *windowpos = (WINDOWPOS*)lParam;
594 /* When menu is at the side or top, Windows likes
595 to try to reposition the fullscreen window when
596 changing video modes.
598 if ( !SDL_resizing &&
600 (SDL_PublicSurface->flags & SDL_FULLSCREEN) ) {
607 case WM_WINDOWPOSCHANGED: {
608 SDL_VideoDevice *this = current_video;
611 GetClientRect(SDL_Window, &SDL_bounds);
612 ClientToScreen(SDL_Window, (LPPOINT)&SDL_bounds);
613 ClientToScreen(SDL_Window, (LPPOINT)&SDL_bounds+1);
614 if ( !SDL_resizing && !IsZoomed(SDL_Window) &&
616 !(SDL_PublicSurface->flags & SDL_FULLSCREEN) ) {
617 SDL_windowX = SDL_bounds.left;
618 SDL_windowY = SDL_bounds.top;
620 w = SDL_bounds.right-SDL_bounds.left;
621 h = SDL_bounds.bottom-SDL_bounds.top;
622 if ( this->input_grab != SDL_GRAB_OFF ) {
623 ClipCursor(&SDL_bounds);
625 if ( SDL_PublicSurface &&
626 (SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
627 SDL_PrivateResize(w, h);
632 /* We need to set the cursor */
636 hittest = LOWORD(lParam);
637 if ( hittest == HTCLIENT ) {
638 SetCursor(SDL_hcursor);
644 /* We are about to get palette focus! */
645 case WM_QUERYNEWPALETTE: {
646 WIN_RealizePalette(current_video);
651 /* Another application changed the palette */
652 case WM_PALETTECHANGED: {
653 WIN_PaletteChanged(current_video, (HWND)wParam);
657 /* We were occluded, refresh our display */
662 hdc = BeginPaint(SDL_Window, &ps);
663 if ( current_video->screen &&
664 !(current_video->screen->flags & SDL_OPENGL) ) {
665 WIN_WinPAINT(current_video, hdc);
667 EndPaint(SDL_Window, &ps);
671 /* DJM: Send an expose event in this case */
672 case WM_ERASEBKGND: {
673 posted = SDL_PrivateExpose();
678 if ( (posted = SDL_PrivateQuit()) )
688 #ifndef NO_GETKEYBOARDSTATE
689 case WM_INPUTLANGCHANGE: {
690 codepage = GetCodePage();
696 /* Special handling by the video driver */
698 return(HandleMessage(current_video,
699 hwnd, msg, wParam, lParam));
704 return(DefWindowProc(hwnd, msg, wParam, lParam));
707 /* Allow the application handle to be stored and retrieved later */
708 static void *SDL_handle = NULL;
710 void SDL_SetModuleHandle(void *handle)
714 void *SDL_GetModuleHandle(void)
721 handle = GetModuleHandle(NULL);
726 /* This allows the SDL_WINDOWID hack */
727 BOOL SDL_windowid = FALSE;
729 static int app_registered = 0;
731 /* Register the class for this application -- exported for winmain.c */
732 int SDL_RegisterApp(char *name, Uint32 style, void *hInst)
739 /* Only do this once... */
740 if ( app_registered ) {
745 #ifndef CS_BYTEALIGNCLIENT
746 #define CS_BYTEALIGNCLIENT 0
748 if ( ! name && ! SDL_Appname ) {
750 SDL_Appstyle = CS_BYTEALIGNCLIENT;
751 SDL_Instance = hInst ? hInst : SDL_GetModuleHandle();
756 /* WinCE uses the UNICODE version */
757 SDL_Appname = SDL_iconv_utf8_ucs2(name);
759 SDL_Appname = SDL_iconv_utf8_locale(name);
760 #endif /* _WIN32_WCE */
761 SDL_Appstyle = style;
762 SDL_Instance = hInst ? hInst : SDL_GetModuleHandle();
765 /* Register the application class */
766 class.hCursor = NULL;
767 class.hIcon = LoadImage(SDL_Instance, SDL_Appname,
769 0, 0, LR_DEFAULTCOLOR);
770 class.lpszMenuName = NULL;
771 class.lpszClassName = SDL_Appname;
772 class.hbrBackground = NULL;
773 class.hInstance = SDL_Instance;
774 class.style = SDL_Appstyle;
776 class.style |= CS_OWNDC;
778 class.lpfnWndProc = WinMessage;
779 class.cbWndExtra = 0;
780 class.cbClsExtra = 0;
781 if ( ! RegisterClass(&class) ) {
782 SDL_SetError("Couldn't register application class");
787 /* Get the version of TrackMouseEvent() we use */
788 _TrackMouseEvent = NULL;
789 handle = GetModuleHandle("USER32.DLL");
791 _TrackMouseEvent = (BOOL (WINAPI *)(TRACKMOUSEEVENT *))GetProcAddress(handle, "TrackMouseEvent");
793 if ( _TrackMouseEvent == NULL ) {
794 _TrackMouseEvent = WIN_TrackMouseEvent;
796 #endif /* WM_MOUSELEAVE */
798 #ifndef NO_GETKEYBOARDSTATE
799 /* Initialise variables for SDL_ToUnicode() */
800 codepage = GetCodePage();
801 SDL_ToUnicode = Is9xME() ? ToUnicode9xME : ToUnicode;
808 /* Unregisters the windowclass registered in SDL_RegisterApp above. */
809 void SDL_UnregisterApp()
813 /* SDL_RegisterApp might not have been called before */
814 if ( !app_registered ) {
818 if ( app_registered == 0 ) {
819 /* Check for any registered window classes. */
820 if ( GetClassInfo(SDL_Instance, SDL_Appname, &class) ) {
821 UnregisterClass(SDL_Appname, SDL_Instance);
823 SDL_free(SDL_Appname);
828 #ifndef NO_GETKEYBOARDSTATE
829 /* JFP: Implementation of ToUnicode() that works on 9x/ME/2K/XP */
835 SDL_memset(&info, 0, sizeof(info));
836 info.dwOSVersionInfoSize = sizeof(info);
837 if (!GetVersionEx(&info)) {
840 return (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
843 static int GetCodePage()
846 int lcid = MAKELCID(LOWORD(GetKeyboardLayout(0)), SORT_DEFAULT);
849 if (GetLocaleInfo(lcid, LOCALE_IDEFAULTANSICODEPAGE, buff, sizeof(buff))) {
855 static int WINAPI ToUnicode9xME(UINT vkey, UINT scancode, PBYTE keystate, LPWSTR wchars, int wsize, UINT flags)
859 if (ToAsciiEx(vkey, scancode, keystate, (WORD*)chars, 0, GetKeyboardLayout(0)) == 1) {
860 return MultiByteToWideChar(codepage, 0, chars, 1, wchars, wsize);
865 #endif /* !NO_GETKEYBOARDSTATE */