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
99 /* Variables and support functions for SDL_ToUnicode() */
102 static int GetCodePage();
103 static int WINAPI ToUnicode9xME(UINT vkey, UINT scancode, const BYTE *keystate, LPWSTR wchars, int wsize, UINT flags);
105 ToUnicodeFN SDL_ToUnicode = ToUnicode9xME;
107 #endif /* !NO_GETKEYBOARDSTATE */
110 #if defined(_WIN32_WCE)
112 //AdjustWindowRect is not available under WinCE 2003
113 #define AdjustWindowRect(a,b,c) (AdjustWindowRectEx((a),(b),(c),0))
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;
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
126 static void LoadAygshell(void)
129 aygshell = SDL_LoadObject("aygshell.dll");
130 if( (aygshell != 0) && (SHFullScreen == 0) )
132 SHFullScreen = (int (WINAPI *)(struct HWND__ *,unsigned long)) SDL_LoadFunction(aygshell, "SHFullScreen");
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
143 void WIN_FlushMessageQueue()
146 while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) {
147 if ( msg.message == WM_QUIT ) break;
148 TranslateMessage( &msg );
149 DispatchMessage( &msg );
153 static void SDL_RestoreGameMode(void)
155 #ifdef _WIN32_WCE //Under ce we don't minimize, therefore no restore
157 #ifdef SDL_VIDEO_DRIVER_GAPI
158 SDL_VideoDevice *this = current_video;
159 if(SDL_strcmp(this->name, "gapi") == 0)
161 if( this->hidden->gapiInfo->suspended )
163 this->hidden->gapiInfo->suspended = 0;
169 ShowWindow(SDL_Window, SW_RESTORE);
172 #ifndef NO_CHANGEDISPLAYSETTINGS
174 ChangeDisplaySettings(&SDL_fullscreen_mode, CDS_FULLSCREEN);
176 #endif /* NO_CHANGEDISPLAYSETTINGS */
178 static void SDL_RestoreDesktopMode(void)
183 #ifdef SDL_VIDEO_DRIVER_GAPI
184 SDL_VideoDevice *this = current_video;
185 if(SDL_strcmp(this->name, "gapi") == 0)
187 if( !this->hidden->gapiInfo->suspended )
189 this->hidden->gapiInfo->suspended = 1;
195 /* WinCE does not have a taskbar, so minimizing is not convenient */
196 ShowWindow(SDL_Window, SW_MINIMIZE);
199 #ifndef NO_CHANGEDISPLAYSETTINGS
201 ChangeDisplaySettings(NULL, 0);
203 #endif /* NO_CHANGEDISPLAYSETTINGS */
208 Special code to handle mouse leave events - this sucks...
209 http://support.microsoft.com/support/kb/articles/q183/1/07.asp
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.
218 static BOOL (WINAPI *_TrackMouseEvent)(TRACKMOUSEEVENT *ptme) = NULL;
221 TrackMouseTimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
223 union { RECT rect; POINT pt; } rectpt; /* prevent type-punning issue. */
226 GetClientRect(hWnd, &rectpt.rect);
227 MapWindowPoints(hWnd, NULL, &rectpt.pt, 2);
229 if ( !PtInRect(&rectpt.rect, pt) || (WindowFromPoint(pt) != hWnd) ) {
230 if ( !KillTimer(hWnd, idEvent) ) {
231 /* Error killing the timer! */
233 PostMessage(hWnd, WM_MOUSELEAVE, 0, 0);
236 static BOOL WINAPI WIN_TrackMouseEvent(TRACKMOUSEEVENT *ptme)
238 if ( ptme->dwFlags == TME_LEAVE ) {
239 return SetTimer(ptme->hwndTrack, ptme->dwFlags, 100,
240 (TIMERPROC)TrackMouseTimerProc) != 0;
244 #endif /* WM_MOUSELEAVE */
246 /* The main Win32 event handler
247 DJM: This is no longer static as (DX5/DIB)_CreateWindow needs it
249 LRESULT CALLBACK WinMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
251 SDL_VideoDevice *this = current_video;
252 static int mouse_pressed = 0;
254 fprintf(stderr, "Received windows message: ");
255 if ( msg > MAX_WMMSG ) {
256 fprintf(stderr, "%d", msg);
258 fprintf(stderr, "%s", wmtab[msg]);
260 fprintf(stderr, " -- 0x%X, 0x%X\n", wParam, lParam);
265 SDL_VideoDevice *this = current_video;
266 BOOL active, minimized;
269 minimized = HIWORD(wParam);
270 active = (LOWORD(wParam) != WA_INACTIVE) && !minimized;
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);
278 if ( ! DDRAW_FULLSCREEN() ) {
281 if ( WINDIB_FULLSCREEN() ) {
282 SDL_RestoreGameMode();
285 #if defined(_WIN32_WCE)
286 if ( WINDIB_FULLSCREEN() ) {
289 SHFullScreen(SDL_Window, SHFS_HIDESTARTICON|SHFS_HIDETASKBAR|SHFS_HIDESIPBUTTON);
291 ShowWindow(FindWindow(TEXT("HHTaskBar"),NULL),SW_HIDE);
294 posted = SDL_PrivateAppActive(1, appstate);
296 /* Lose the following states */
297 appstate = SDL_APPINPUTFOCUS;
299 appstate |= SDL_APPACTIVE;
302 if ( SDL_GetAppState() & SDL_APPINPUTFOCUS ) {
303 if ( this->input_grab != SDL_GRAB_OFF ) {
304 WIN_GrabInput(this, SDL_GRAB_OFF);
306 if ( ! DDRAW_FULLSCREEN() ) {
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)
317 SHFullScreen(SDL_Window, SHFS_SHOWSTARTICON|SHFS_SHOWTASKBAR|SHFS_SHOWSIPBUTTON);
319 ShowWindow(FindWindow(TEXT("HHTaskBar"),NULL),SW_SHOW);
323 posted = SDL_PrivateAppActive(0, appstate);
325 WIN_Activate(this, active, minimized);
333 if ( SDL_VideoSurface ) {
334 /* mouse has entered the window */
336 if ( !(SDL_GetAppState() & SDL_APPMOUSEFOCUS) ) {
339 tme.cbSize = sizeof(tme);
340 tme.dwFlags = TME_LEAVE;
341 tme.hwndTrack = SDL_Window;
342 _TrackMouseEvent(&tme);
345 #endif /* WM_MOUSELEAVE */
347 /* Mouse motion is handled in DIB_PumpEvents or
348 * DX5_PumpEvents, depending on the video driver
351 posted = SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
356 case WM_MOUSELEAVE: {
358 if ( SDL_VideoSurface ) {
359 /* mouse has left the window */
360 posted = SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
364 #endif /* WM_MOUSELEAVE */
374 /* Mouse is handled by DirectInput when fullscreen */
375 if ( SDL_VideoSurface && ! DINPUT() ) {
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).
385 SetFocus(SDL_Window);
387 /* Figure out which button to use */
390 button = SDL_BUTTON_LEFT;
394 button = SDL_BUTTON_LEFT;
395 state = SDL_RELEASED;
398 button = SDL_BUTTON_MIDDLE;
402 button = SDL_BUTTON_MIDDLE;
403 state = SDL_RELEASED;
406 button = SDL_BUTTON_RIGHT;
410 button = SDL_BUTTON_RIGHT;
411 state = SDL_RELEASED;
414 xbuttonval = GET_XBUTTON_WPARAM(wParam);
415 button = SDL_BUTTON_X1 + xbuttonval - 1;
419 xbuttonval = GET_XBUTTON_WPARAM(wParam);
420 button = SDL_BUTTON_X1 + xbuttonval - 1;
421 state = SDL_RELEASED;
424 /* Eh? Unknown button? */
427 if ( state == SDL_PRESSED ) {
428 /* Grab mouse so we get up events */
429 if ( ++mouse_pressed > 0 ) {
433 /* Release mouse after all up events */
434 if ( --mouse_pressed <= 0 ) {
439 if ( mouse_relative ) {
440 /* RJR: March 28, 2000
441 report internal mouse position if in relative mode */
444 x = (Sint16)LOWORD(lParam);
445 y = (Sint16)HIWORD(lParam);
447 if (SDL_VideoSurface)
448 GapiTransform(this->hidden->userOrientation,
449 this->hidden->hiresFix, &x, &y);
452 posted = SDL_PrivateMouseButton(
453 state, button, x, y);
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.
472 #if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
474 if ( SDL_VideoSurface && ! DINPUT() ) {
475 int move = (short)HIWORD(wParam);
479 button = SDL_BUTTON_WHEELUP;
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);
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.
496 case WM_GETMINMAXINFO: {
504 /* We don't want to clobber an internal resize */
508 /* We allow resizing with the SDL_RESIZABLE flag */
509 if ( SDL_PublicSurface &&
510 (SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
514 /* Get the current position of our window */
515 GetWindowRect(SDL_Window, &size);
519 /* Calculate current width and height of our window */
522 if ( SDL_PublicSurface != NULL ) {
523 size.bottom = SDL_PublicSurface->h;
524 size.right = SDL_PublicSurface->w;
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.
535 style = GetWindowLong(hwnd, GWL_STYLE);
539 style & WS_CHILDWINDOW ? FALSE
540 : GetMenu(hwnd) != NULL);
542 width = size.right - size.left;
543 height = size.bottom - size.top;
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;
557 #endif /* WM_GETMINMAXINFO */
559 case WM_WINDOWPOSCHANGING: {
560 WINDOWPOS *windowpos = (WINDOWPOS*)lParam;
562 /* When menu is at the side or top, Windows likes
563 to try to reposition the fullscreen window when
564 changing video modes.
566 if ( !SDL_resizing &&
568 (SDL_PublicSurface->flags & SDL_FULLSCREEN) ) {
575 case WM_WINDOWPOSCHANGED: {
576 SDL_VideoDevice *this = current_video;
580 GetClientRect(SDL_Window, &SDL_bounds);
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;
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;
595 if ( !SDL_resizing && !IsZoomed(SDL_Window) &&
597 !(SDL_PublicSurface->flags & SDL_FULLSCREEN) ) {
598 SDL_windowX = SDL_bounds.left;
599 SDL_windowY = SDL_bounds.top;
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);
606 if ( SDL_PublicSurface &&
607 (SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
608 SDL_PrivateResize(w, h);
613 /* We need to set the cursor */
617 hittest = LOWORD(lParam);
618 if ( hittest == HTCLIENT ) {
619 SetCursor(SDL_hcursor);
625 /* We are about to get palette focus! */
626 case WM_QUERYNEWPALETTE: {
627 WIN_RealizePalette(current_video);
632 /* Another application changed the palette */
633 case WM_PALETTECHANGED: {
634 WIN_PaletteChanged(current_video, (HWND)wParam);
638 /* We were occluded, refresh our display */
643 hdc = BeginPaint(SDL_Window, &ps);
644 if ( current_video->screen &&
645 !(current_video->screen->flags & SDL_OPENGL) ) {
646 WIN_WinPAINT(current_video, hdc);
648 EndPaint(SDL_Window, &ps);
652 /* DJM: Send an expose event in this case */
653 case WM_ERASEBKGND: {
654 posted = SDL_PrivateExpose();
659 if ( (posted = SDL_PrivateQuit()) )
669 #ifndef NO_GETKEYBOARDSTATE
670 case WM_INPUTLANGCHANGE:
672 codepage = GetCodePage();
678 /* Special handling by the video driver */
680 return(HandleMessage(current_video,
681 hwnd, msg, wParam, lParam));
686 return(DefWindowProc(hwnd, msg, wParam, lParam));
689 /* Allow the application handle to be stored and retrieved later */
690 static void *SDL_handle = NULL;
692 void SDL_SetModuleHandle(void *handle)
696 void *SDL_GetModuleHandle(void)
703 handle = GetModuleHandle(NULL);
708 /* This allows the SDL_WINDOWID hack */
709 BOOL SDL_windowid = FALSE;
711 static int app_registered = 0;
713 /* Register the class for this application -- exported for winmain.c */
714 int SDL_RegisterApp(char *name, Uint32 style, void *hInst)
721 /* Only do this once... */
722 if ( app_registered ) {
727 #ifndef CS_BYTEALIGNCLIENT
728 #define CS_BYTEALIGNCLIENT 0
730 if ( ! name && ! SDL_Appname ) {
732 SDL_Appstyle = CS_BYTEALIGNCLIENT;
733 SDL_Instance = hInst ? hInst : SDL_GetModuleHandle();
738 /* WinCE uses the UNICODE version */
739 SDL_Appname = SDL_iconv_utf8_ucs2(name);
741 SDL_Appname = SDL_iconv_utf8_locale(name);
742 #endif /* _WIN32_WCE */
743 SDL_Appstyle = style;
744 SDL_Instance = hInst ? hInst : SDL_GetModuleHandle();
747 /* Register the application class */
748 class.hCursor = NULL;
749 class.hIcon = LoadImage(SDL_Instance, SDL_Appname,
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;
758 class.style |= CS_OWNDC;
760 class.lpfnWndProc = WinMessage;
761 class.cbWndExtra = 0;
762 class.cbClsExtra = 0;
763 if ( ! RegisterClass(&class) ) {
764 SDL_SetError("Couldn't register application class");
769 /* Get the version of TrackMouseEvent() we use */
770 _TrackMouseEvent = NULL;
771 handle = GetModuleHandle("USER32.DLL");
773 _TrackMouseEvent = (BOOL (WINAPI *)(TRACKMOUSEEVENT *))GetProcAddress(handle, "TrackMouseEvent");
775 if ( _TrackMouseEvent == NULL ) {
776 _TrackMouseEvent = WIN_TrackMouseEvent;
778 #endif /* WM_MOUSELEAVE */
780 #ifndef NO_GETKEYBOARDSTATE
782 /* Initialise variables for SDL_ToUnicode() */
783 codepage = GetCodePage();
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;
789 #endif /* NO_GETKEYBOARDSTATE */
795 /* Unregisters the windowclass registered in SDL_RegisterApp above. */
796 void SDL_UnregisterApp()
800 /* SDL_RegisterApp might not have been called before */
801 if ( !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);
810 SDL_free(SDL_Appname);
815 #ifndef NO_GETKEYBOARDSTATE
817 /* JFP: Implementation of ToUnicode() that works on 9x/ME/2K/XP */
823 SDL_memset(&info, 0, sizeof(info));
824 info.dwOSVersionInfoSize = sizeof(info);
825 if (!GetVersionEx(&info)) {
828 return (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
831 static int GetCodePage()
834 int lcid = MAKELCID(LOWORD(GetKeyboardLayout(0)), SORT_DEFAULT);
837 if (GetLocaleInfo(lcid, LOCALE_IDEFAULTANSICODEPAGE, buff, sizeof(buff))) {
843 static int WINAPI ToUnicode9xME(UINT vkey, UINT scancode, const BYTE *keystate, LPWSTR wchars, int wsize, UINT flags)
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);
854 #endif /* !NO_GETKEYBOARDSTATE */