src/video/wincommon/SDL_sysevents.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 31 Mar 2002 03:34:16 +0000
changeset 327 13fc64213765
parent 304 ec53caed9fb2
child 332 3d6dd1b7b7ba
permissions -rw-r--r--
*** empty log message ***
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997, 1998, 1999  Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 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     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 #ifdef SAVE_RCSID
    24 static char rcsid =
    25  "@(#) $Id$";
    26 #endif
    27 
    28 #include <stdlib.h>
    29 #include <stdio.h>
    30 #include <windows.h>
    31 
    32 #include "SDL_getenv.h"
    33 #include "SDL_events.h"
    34 #include "SDL_video.h"
    35 #include "SDL_error.h"
    36 #include "SDL_syswm.h"
    37 #include "SDL_sysevents.h"
    38 #include "SDL_events_c.h"
    39 #include "SDL_sysvideo.h"
    40 #include "SDL_lowvideo.h"
    41 #include "SDL_syswm_c.h"
    42 #include "SDL_main.h"
    43 
    44 #ifdef WMMSG_DEBUG
    45 #include "wmmsg.h"
    46 #endif
    47 
    48 #ifdef _WIN32_WCE
    49 #define NO_GETKEYBOARDSTATE
    50 #endif
    51 
    52 /* The window we use for everything... */
    53 const char *SDL_Appname = NULL;
    54 HINSTANCE SDL_Instance = NULL;
    55 HWND SDL_Window = NULL;
    56 RECT SDL_bounds = {0, 0, 0, 0};
    57 int SDL_resizing = 0;
    58 int mouse_relative = 0;
    59 int posted = 0;
    60 #ifndef NO_CHANGEDISPLAYSETTINGS
    61 DEVMODE SDL_fullscreen_mode;
    62 #endif
    63 
    64 
    65 /* Functions called by the message processing function */
    66 LONG
    67 (*HandleMessage)(_THIS, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)=NULL;
    68 void (*WIN_RealizePalette)(_THIS);
    69 void (*WIN_PaletteChanged)(_THIS, HWND window);
    70 void (*WIN_SwapGamma)(_THIS);
    71 void (*WIN_WinPAINT)(_THIS, HDC hdc);
    72 
    73 static void SDL_RestoreGameMode(void)
    74 {
    75 #ifndef NO_CHANGEDISPLAYSETTINGS
    76 	ShowWindow(SDL_Window, SW_RESTORE);
    77 	ChangeDisplaySettings(&SDL_fullscreen_mode, CDS_FULLSCREEN);
    78 #endif
    79 }
    80 static void SDL_RestoreDesktopMode(void)
    81 {
    82 #ifndef NO_CHANGEDISPLAYSETTINGS
    83 	ShowWindow(SDL_Window, SW_MINIMIZE);
    84 	ChangeDisplaySettings(NULL, 0);
    85 #endif
    86 }
    87 
    88 #ifdef WM_MOUSELEAVE
    89 /* 
    90    Special code to handle mouse leave events - this sucks...
    91    http://support.microsoft.com/support/kb/articles/q183/1/07.asp
    92 
    93    TrackMouseEvent() is only available on Win98 and WinNT.
    94    _TrackMouseEvent() is available on Win95, but isn't yet in the mingw32
    95    development environment, and only works on systems that have had IE 3.0
    96    or newer installed on them (which is not the case with the base Win95).
    97    Therefore, we implement our own version of _TrackMouseEvent() which
    98    uses our own implementation if TrackMouseEvent() is not available.
    99 */
   100 static BOOL (WINAPI *_TrackMouseEvent)(TRACKMOUSEEVENT *ptme) = NULL;
   101 
   102 static VOID CALLBACK
   103 TrackMouseTimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
   104 {
   105 	RECT rect;
   106 	POINT pt;
   107 
   108 	GetClientRect(hWnd, &rect);
   109 	MapWindowPoints(hWnd, NULL, (LPPOINT)&rect, 2);
   110 	GetCursorPos(&pt);
   111 	if ( !PtInRect(&rect, pt) || (WindowFromPoint(pt) != hWnd) ) {
   112 		if ( !KillTimer(hWnd, idEvent) ) {
   113 			/* Error killing the timer! */
   114 		}
   115 		PostMessage(hWnd, WM_MOUSELEAVE, 0, 0);
   116 	}
   117 }
   118 static BOOL WINAPI WIN_TrackMouseEvent(TRACKMOUSEEVENT *ptme)
   119 {
   120 	if ( ptme->dwFlags == TME_LEAVE ) {
   121 		return SetTimer(ptme->hwndTrack, ptme->dwFlags, 100,
   122 		                (TIMERPROC)TrackMouseTimerProc);
   123 	}
   124 	return FALSE;
   125 }
   126 #endif /* WM_MOUSELEAVE */
   127 
   128 /* Function to retrieve the current keyboard modifiers */
   129 static void WIN_GetKeyboardState(void)
   130 {
   131 #ifndef NO_GETKEYBOARDSTATE
   132 	SDLMod state;
   133 	BYTE keyboard[256];
   134 	Uint8 *kstate = SDL_GetKeyState(NULL);
   135 
   136 	state = KMOD_NONE;
   137 	if ( GetKeyboardState(keyboard) ) {
   138 		if ( keyboard[VK_LSHIFT] & 0x80) {
   139 			state |= KMOD_LSHIFT;
   140 		}
   141 		if ( keyboard[VK_RSHIFT] & 0x80) {
   142 			state |= KMOD_RSHIFT;
   143 		}
   144 		if ( keyboard[VK_LCONTROL] & 0x80) {
   145 			state |= KMOD_LCTRL;
   146 		}
   147 		if ( keyboard[VK_RCONTROL] & 0x80) {
   148 			state |= KMOD_RCTRL;
   149 		}
   150 		if ( keyboard[VK_LMENU] & 0x80) {
   151 			state |= KMOD_LALT;
   152 		}
   153 		if ( keyboard[VK_RMENU] & 0x80) {
   154 			state |= KMOD_RALT;
   155 		}
   156 		if ( keyboard[VK_NUMLOCK] & 0x01) {
   157 			state |= KMOD_NUM;
   158 			kstate[SDLK_NUMLOCK] = SDL_PRESSED;
   159 		}
   160 		if ( keyboard[VK_CAPITAL] & 0x01) {
   161 			state |= KMOD_CAPS;
   162 			kstate[SDLK_CAPSLOCK] = SDL_PRESSED;
   163 		}
   164 	}
   165 	SDL_SetModState(state);
   166 #endif /* !NO_GETKEYBOARDSTATE */
   167 }
   168 
   169 /* The main Win32 event handler
   170 DJM: This is no longer static as (DX5/DIB)_CreateWindow needs it
   171 */
   172 LONG CALLBACK WinMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
   173 {
   174 	SDL_VideoDevice *this = current_video;
   175 	static int mouse_pressed = 0;
   176 	static int in_window = 0;
   177 #ifdef WMMSG_DEBUG
   178 	fprintf(stderr, "Received windows message:  ");
   179 	if ( msg > MAX_WMMSG ) {
   180 		fprintf(stderr, "%d", msg);
   181 	} else {
   182 		fprintf(stderr, "%s", wmtab[msg]);
   183 	}
   184 	fprintf(stderr, " -- 0x%X, 0x%X\n", wParam, lParam);
   185 #endif
   186 	switch (msg) {
   187 
   188 		case WM_ACTIVATE: {
   189 			SDL_VideoDevice *this = current_video;
   190 			BOOL minimized;
   191 			Uint8 appstate;
   192 
   193 			minimized = HIWORD(wParam);
   194 			if ( !minimized && (LOWORD(wParam) != WA_INACTIVE) ) {
   195 				/* Gain the following states */
   196 				appstate = SDL_APPACTIVE|SDL_APPINPUTFOCUS;
   197 				if ( this->input_grab != SDL_GRAB_OFF ) {
   198 					WIN_GrabInput(this, SDL_GRAB_ON);
   199 				}
   200 				if ( !(SDL_GetAppState()&SDL_APPINPUTFOCUS) ) {
   201 					WIN_SwapGamma(this);
   202 					if ( WINDIB_FULLSCREEN() ) {
   203 						SDL_RestoreGameMode();
   204 					}
   205 				}
   206 				posted = SDL_PrivateAppActive(1, appstate);
   207 				WIN_GetKeyboardState();
   208 			} else {
   209 				/* Lose the following states */
   210 				appstate = SDL_APPINPUTFOCUS;
   211 				if ( minimized ) {
   212 					appstate |= SDL_APPACTIVE;
   213 				}
   214 				if ( this->input_grab != SDL_GRAB_OFF ) {
   215 					WIN_GrabInput(this, SDL_GRAB_OFF);
   216 				}
   217 				if ( SDL_GetAppState() & SDL_APPINPUTFOCUS ) {
   218 					WIN_SwapGamma(this);
   219 					if ( WINDIB_FULLSCREEN() ) {
   220 						SDL_RestoreDesktopMode();
   221 					}
   222 				}
   223 				posted = SDL_PrivateAppActive(0, appstate);
   224 			}
   225 			return(0);
   226 		}
   227 		break;
   228 
   229 		case WM_MOUSEMOVE: {
   230 			
   231 			/* Mouse is handled by DirectInput when fullscreen */
   232 			if ( SDL_VideoSurface && ! DINPUT_FULLSCREEN() ) {
   233 				Sint16 x, y;
   234 
   235 				/* mouse has entered the window */
   236 				if ( ! in_window ) {
   237 #ifdef WM_MOUSELEAVE
   238 					TRACKMOUSEEVENT tme;
   239 
   240 					tme.cbSize = sizeof(tme);
   241 					tme.dwFlags = TME_LEAVE;
   242 					tme.hwndTrack = SDL_Window;
   243 					_TrackMouseEvent(&tme);
   244 #endif /* WM_MOUSELEAVE */
   245 					in_window = TRUE;
   246 
   247 					posted = SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
   248 				}
   249 
   250 				/* mouse has moved within the window */
   251 				x = LOWORD(lParam);
   252 				y = HIWORD(lParam);
   253 				if ( mouse_relative ) {
   254 					POINT center;
   255 					center.x = (SDL_VideoSurface->w/2);
   256 					center.y = (SDL_VideoSurface->h/2);
   257 					x -= (Sint16)center.x;
   258 					y -= (Sint16)center.y;
   259 					if ( x || y ) {
   260 						ClientToScreen(SDL_Window, &center);
   261 						SetCursorPos(center.x, center.y);
   262 						posted = SDL_PrivateMouseMotion(0, 1, x, y);
   263 					}
   264 				} else {
   265 					posted = SDL_PrivateMouseMotion(0, 0, x, y);
   266 				}
   267 			}
   268 		}
   269 		return(0);
   270 
   271 #ifdef WM_MOUSELEAVE
   272 		case WM_MOUSELEAVE: {
   273 
   274 			/* Mouse is handled by DirectInput when fullscreen */
   275 			if ( SDL_VideoSurface && ! DINPUT_FULLSCREEN() ) {
   276 				/* mouse has left the window */
   277 				/* or */
   278 				/* Elvis has left the building! */
   279 				in_window = FALSE;
   280 				posted = SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
   281 			}
   282 		}
   283 		return(0);
   284 #endif /* WM_MOUSELEAVE */
   285 
   286 		case WM_LBUTTONDOWN:
   287 		case WM_LBUTTONUP:
   288 		case WM_MBUTTONDOWN:
   289 		case WM_MBUTTONUP:
   290 		case WM_RBUTTONDOWN:
   291 		case WM_RBUTTONUP: {
   292 			/* Mouse is handled by DirectInput when fullscreen */
   293 			if ( SDL_VideoSurface && ! DINPUT_FULLSCREEN() ) {
   294 				Sint16 x, y;
   295 				Uint8 button, state;
   296 
   297 				/* DJM:
   298 				   We want the SDL window to take focus so that
   299 				   it acts like a normal windows "component"
   300 				   (e.g. gains keyboard focus on a mouse click).
   301 				 */
   302 				SetFocus(SDL_Window);
   303 
   304 				/* Figure out which button to use */
   305 				switch (msg) {
   306 					case WM_LBUTTONDOWN:
   307 						button = 1;
   308 						state = SDL_PRESSED;
   309 						break;
   310 					case WM_LBUTTONUP:
   311 						button = 1;
   312 						state = SDL_RELEASED;
   313 						break;
   314 					case WM_MBUTTONDOWN:
   315 						button = 2;
   316 						state = SDL_PRESSED;
   317 						break;
   318 					case WM_MBUTTONUP:
   319 						button = 2;
   320 						state = SDL_RELEASED;
   321 						break;
   322 					case WM_RBUTTONDOWN:
   323 						button = 3;
   324 						state = SDL_PRESSED;
   325 						break;
   326 					case WM_RBUTTONUP:
   327 						button = 3;
   328 						state = SDL_RELEASED;
   329 						break;
   330 					default:
   331 						/* Eh? Unknown button? */
   332 						return(0);
   333 				}
   334 				if ( state == SDL_PRESSED ) {
   335 					/* Grab mouse so we get up events */
   336 					if ( ++mouse_pressed > 0 ) {
   337 						SetCapture(hwnd);
   338 					}
   339 				} else {
   340 					/* Release mouse after all up events */
   341 					if ( --mouse_pressed <= 0 ) {
   342 						ReleaseCapture();
   343 						mouse_pressed = 0;
   344 					}
   345 				}
   346 				if ( mouse_relative ) {
   347 				/*	RJR: March 28, 2000
   348 					report internal mouse position if in relative mode */
   349 					x = 0; y = 0;
   350 				} else {
   351 					x = (Sint16)LOWORD(lParam);
   352 					y = (Sint16)HIWORD(lParam);
   353 				}
   354 				posted = SDL_PrivateMouseButton(
   355 							state, button, x, y);
   356 			}
   357 		}
   358 		return(0);
   359 
   360 
   361 #if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
   362 		case WM_MOUSEWHEEL: 
   363 			if ( SDL_VideoSurface && ! DINPUT_FULLSCREEN() ) {
   364 				int move = (short)HIWORD(wParam);
   365 				if ( move ) {
   366 					Uint8 button;
   367 					if ( move > 0 )
   368 						button = 4;
   369 					else
   370 						button = 5;
   371 					posted = SDL_PrivateMouseButton(
   372 						SDL_PRESSED, button, 0, 0);
   373 				}
   374 			}
   375 			return(0);
   376 #endif
   377 
   378 #ifdef WM_GETMINMAXINFO
   379 		/* This message is sent as a way for us to "check" the values
   380 		 * of a position change.  If we don't like it, we can adjust
   381 		 * the values before they are changed.
   382 		 */
   383 		case WM_GETMINMAXINFO: {
   384 			MINMAXINFO *info;
   385 			RECT        size;
   386 			int x, y;
   387 			int width;
   388 			int height;
   389 
   390 			/* We don't want to clobber an internal resize */
   391 			if ( SDL_resizing )
   392 				return(0);
   393 
   394 			/* We allow resizing with the SDL_RESIZABLE flag */
   395 			if ( SDL_PublicSurface &&
   396 				(SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
   397 				return(0);
   398 			}
   399 
   400 			/* Get the current position of our window */
   401 			GetWindowRect(SDL_Window, &size);
   402 			x = size.left;
   403 			y = size.top;
   404 
   405 			/* Calculate current width and height of our window */
   406 			size.top = 0;
   407 			size.left = 0;
   408 			if ( SDL_PublicSurface != NULL ) {
   409 				size.bottom = SDL_PublicSurface->h;
   410 				size.right = SDL_PublicSurface->w;
   411 			} else {
   412 				size.bottom = 0;
   413 				size.right = 0;
   414 			}
   415 			AdjustWindowRect(&size, GetWindowLong(hwnd, GWL_STYLE),
   416 									FALSE);
   417 			width = size.right - size.left;
   418 			height = size.bottom - size.top;
   419 
   420 			/* Fix our size to the current size */
   421 			info = (MINMAXINFO *)lParam;
   422 			info->ptMaxSize.x = width;
   423 			info->ptMaxSize.y = height;
   424 			info->ptMaxPosition.x = x;
   425 			info->ptMaxPosition.y = y;
   426 			info->ptMinTrackSize.x = width;
   427 			info->ptMinTrackSize.y = height;
   428 			info->ptMaxTrackSize.x = width;
   429 			info->ptMaxTrackSize.y = height;
   430 		}
   431 		return(0);
   432 #endif /* WM_GETMINMAXINFO */
   433 
   434 		case WM_MOVE: {
   435 			SDL_VideoDevice *this = current_video;
   436 
   437 			GetClientRect(SDL_Window, &SDL_bounds);
   438 			ClientToScreen(SDL_Window, (LPPOINT)&SDL_bounds);
   439 			ClientToScreen(SDL_Window, (LPPOINT)&SDL_bounds+1);
   440 			if ( this->input_grab != SDL_GRAB_OFF ) {
   441 				ClipCursor(&SDL_bounds);
   442 			}
   443 		}
   444 		break;
   445 	
   446 		case WM_SIZE: {
   447 			if ( SDL_PublicSurface &&
   448 				(SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
   449 				SDL_PrivateResize(LOWORD(lParam), HIWORD(lParam));
   450 			}
   451 			return(0);
   452 		}
   453 		break;
   454 
   455 		/* We need to set the cursor */
   456 		case WM_SETCURSOR: {
   457 			Uint16 hittest;
   458 
   459 			hittest = LOWORD(lParam);
   460 			if ( hittest == HTCLIENT ) {
   461 				SetCursor(SDL_hcursor);
   462 				return(TRUE);
   463 			}
   464 		}
   465 		break;
   466 
   467 		/* We are about to get palette focus! */
   468 		case WM_QUERYNEWPALETTE: {
   469 			WIN_RealizePalette(current_video);
   470 			return(TRUE);
   471 		}
   472 		break;
   473 
   474 		/* Another application changed the palette */
   475 		case WM_PALETTECHANGED: {
   476 			WIN_PaletteChanged(current_video, (HWND)wParam);
   477 		}
   478 		break;
   479 
   480 		/* We were occluded, refresh our display */
   481 		case WM_PAINT: {
   482 			HDC hdc;
   483 			PAINTSTRUCT ps;
   484 
   485 			hdc = BeginPaint(SDL_Window, &ps);
   486 			if ( current_video->screen &&
   487 			     !(current_video->screen->flags & SDL_OPENGL) ) {
   488 				WIN_WinPAINT(current_video, hdc);
   489 			}
   490 			EndPaint(SDL_Window, &ps);
   491 		}
   492 		return(0);
   493 
   494 		/* DJM: Send an expose event in this case */
   495 		case WM_ERASEBKGND: {
   496 			posted = SDL_PrivateExpose();
   497 		}
   498 		return(0);
   499 
   500 		case WM_CLOSE: {
   501 			if ( (posted = SDL_PrivateQuit()) )
   502 				PostQuitMessage(0);
   503 		}
   504 		return(0);
   505 
   506 		case WM_DESTROY: {
   507 			PostQuitMessage(0);
   508 		}
   509 		return(0);
   510 
   511 		default: {
   512 			/* Special handling by the video driver */
   513 			if (HandleMessage) {
   514 				return(HandleMessage(current_video,
   515 			                     hwnd, msg, wParam, lParam));
   516 			}
   517 		}
   518 		break;
   519 	}
   520 	return(DefWindowProc(hwnd, msg, wParam, lParam));
   521 }
   522 
   523 /* Allow the application handle to be stored and retrieved later */
   524 static void *SDL_handle = NULL;
   525 
   526 void SDL_SetModuleHandle(void *handle)
   527 {
   528 	SDL_handle = handle;
   529 }
   530 void *SDL_GetModuleHandle(void)
   531 {
   532 	void *handle;
   533 
   534 	if ( SDL_handle ) {
   535 		handle = SDL_handle;
   536 	} else {
   537 		/* Warning:
   538 		   If SDL is built as a DLL, this will return a handle to
   539 		   the DLL, not the application, and DirectInput may fail
   540 		   to initialize.
   541 		 */
   542 		handle = GetModuleHandle(NULL);
   543 	}
   544 	return(handle);
   545 }
   546 
   547 /* This allows the SDL_WINDOWID hack */
   548 const char *SDL_windowid = NULL;
   549 
   550 /* Register the class for this application -- exported for winmain.c */
   551 int SDL_RegisterApp(char *name, Uint32 style, void *hInst)
   552 {
   553 	static int initialized = 0;
   554 	WNDCLASS class;
   555 #ifdef WM_MOUSELEAVE
   556 	HMODULE handle;
   557 #endif
   558 
   559 	/* Only do this once... */
   560 	if ( initialized ) {
   561 		return(0);
   562 	}
   563 
   564 	/* This function needs to be passed the correct process handle
   565 	   by the application.
   566 	 */
   567 	if ( ! hInst ) {
   568 		hInst = SDL_GetModuleHandle();
   569 	}
   570 
   571 	/* Register the application class */
   572 	class.hCursor		= NULL;
   573 #ifdef _WIN32_WCE
   574     {
   575 	/* WinCE uses the UNICODE version */
   576 	int nLen = strlen(name)+1;
   577 	LPWSTR lpszW = alloca(nLen*2);
   578 	MultiByteToWideChar(CP_ACP, 0, name, -1, lpszW, nLen);
   579 	class.hIcon		= LoadImage(hInst, lpszW, IMAGE_ICON,
   580 	                                    0, 0, LR_DEFAULTCOLOR);
   581 	class.lpszMenuName	= NULL;
   582 	class.lpszClassName	= lpszW;
   583     }
   584 #else
   585 	class.hIcon		= LoadImage(hInst, name, IMAGE_ICON,
   586 	                                    0, 0, LR_DEFAULTCOLOR);
   587 	class.lpszMenuName	= "(none)";
   588 	class.lpszClassName	= name;
   589 #endif /* _WIN32_WCE */
   590 	class.hbrBackground	= NULL;
   591 	class.hInstance		= hInst;
   592 	class.style		= style;
   593 #ifdef HAVE_OPENGL
   594 	class.style		|= CS_OWNDC;
   595 #endif
   596 	class.lpfnWndProc	= WinMessage;
   597 	class.cbWndExtra	= 0;
   598 	class.cbClsExtra	= 0;
   599 	if ( ! RegisterClass(&class) ) {
   600 		SDL_SetError("Couldn't register application class");
   601 		return(-1);
   602 	}
   603 	SDL_Appname = name;
   604 	SDL_Instance = hInst;
   605 
   606 #ifdef WM_MOUSELEAVE
   607 	/* Get the version of TrackMouseEvent() we use */
   608 	_TrackMouseEvent = NULL;
   609 	handle = GetModuleHandle("USER32.DLL");
   610 	if ( handle ) {
   611 		_TrackMouseEvent = (BOOL (WINAPI *)(TRACKMOUSEEVENT *))GetProcAddress(handle, "TrackMouseEvent");
   612 	}
   613 	if ( _TrackMouseEvent == NULL ) {
   614 		_TrackMouseEvent = WIN_TrackMouseEvent;
   615 	}
   616 #endif /* WM_MOUSELEAVE */
   617 
   618 	/* Check for SDL_WINDOWID hack */
   619 	SDL_windowid = getenv("SDL_WINDOWID");
   620 
   621 	initialized = 1;
   622 	return(0);
   623 }
   624