src/video/wincommon/SDL_sysevents.c
author Sam Lantinga <slouken@lokigames.com>
Thu, 26 Apr 2001 16:45:43 +0000
changeset 0 74212992fb08
child 1 cf2af46e9e2a
permissions -rw-r--r--
Initial revision
     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@devolution.com
    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 
    61 
    62 /* Functions called by the message processing function */
    63 LONG
    64 (*HandleMessage)(_THIS, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)=NULL;
    65 void (*WIN_RealizePalette)(_THIS);
    66 void (*WIN_PaletteChanged)(_THIS, HWND window);
    67 void (*WIN_SwapGamma)(_THIS);
    68 void (*WIN_WinPAINT)(_THIS, HDC hdc);
    69 
    70 #ifdef WM_MOUSELEAVE
    71 /* 
    72    Special code to handle mouse leave events - this sucks...
    73    http://support.microsoft.com/support/kb/articles/q183/1/07.asp
    74 
    75    TrackMouseEvent() is only available on Win98 and WinNT.
    76    _TrackMouseEvent() is available on Win95, but isn't yet in the mingw32
    77    development environment, and only works on systems that have had IE 3.0
    78    or newer installed on them (which is not the case with the base Win95).
    79    Therefore, we implement our own version of _TrackMouseEvent() which
    80    uses our own implementation if TrackMouseEvent() is not available.
    81 */
    82 static BOOL (WINAPI *_TrackMouseEvent)(TRACKMOUSEEVENT *ptme) = NULL;
    83 
    84 static VOID CALLBACK
    85 TrackMouseTimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
    86 {
    87 	RECT rect;
    88 	POINT pt;
    89 
    90 	GetClientRect(hWnd, &rect);
    91 	MapWindowPoints(hWnd, NULL, (LPPOINT)&rect, 2);
    92 	GetCursorPos(&pt);
    93 	if ( !PtInRect(&rect, pt) || (WindowFromPoint(pt) != hWnd) ) {
    94 		if ( !KillTimer(hWnd, idEvent) ) {
    95 			/* Error killing the timer! */
    96 		}
    97 		PostMessage(hWnd, WM_MOUSELEAVE, 0, 0);
    98 	}
    99 }
   100 static BOOL WINAPI WIN_TrackMouseEvent(TRACKMOUSEEVENT *ptme)
   101 {
   102 	if ( ptme->dwFlags == TME_LEAVE ) {
   103 		return SetTimer(ptme->hwndTrack, ptme->dwFlags, 100,
   104 		                (TIMERPROC)TrackMouseTimerProc);
   105 	}
   106 	return FALSE;
   107 }
   108 #endif /* WM_MOUSELEAVE */
   109 
   110 /* Function to retrieve the current keyboard modifiers */
   111 static void WIN_GetKeyboardState(void)
   112 {
   113 #ifndef NO_GETKEYBOARDSTATE
   114 	SDLMod state;
   115 	BYTE keyboard[256];
   116 
   117 	state = KMOD_NONE;
   118 	if ( GetKeyboardState(keyboard) ) {
   119 		if ( keyboard[VK_LSHIFT] & 0x80) {
   120 			state |= KMOD_LSHIFT;
   121 		}
   122 		if ( keyboard[VK_RSHIFT] & 0x80) {
   123 			state |= KMOD_RSHIFT;
   124 		}
   125 		if ( keyboard[VK_LCONTROL] & 0x80) {
   126 			state |= KMOD_LCTRL;
   127 		}
   128 		if ( keyboard[VK_RCONTROL] & 0x80) {
   129 			state |= KMOD_RCTRL;
   130 		}
   131 		if ( keyboard[VK_LMENU] & 0x80) {
   132 			state |= KMOD_LALT;
   133 		}
   134 		if ( keyboard[VK_RMENU] & 0x80) {
   135 			state |= KMOD_RALT;
   136 		}
   137 		if ( keyboard[VK_NUMLOCK] & 0x80) {
   138 			state |= KMOD_NUM;
   139 		}
   140 		if ( keyboard[VK_CAPITAL] & 0x80) {
   141 			state |= KMOD_CAPS;
   142 		}
   143 	}
   144 	SDL_SetModState(state);
   145 #endif /* !NO_GETKEYBOARDSTATE */
   146 }
   147 
   148 /* The main Win32 event handler */
   149 static LONG CALLBACK WinMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
   150 {
   151 	SDL_VideoDevice *this = current_video;
   152 	static int mouse_pressed = 0;
   153 	static int in_window = 0;
   154 #ifdef WMMSG_DEBUG
   155 	fprintf(stderr, "Received windows message:  ");
   156 	if ( msg > MAX_WMMSG ) {
   157 		fprintf(stderr, "%d", msg);
   158 	} else {
   159 		fprintf(stderr, "%s", wmtab[msg]);
   160 	}
   161 	fprintf(stderr, " -- 0x%X, 0x%X\n", wParam, lParam);
   162 #endif
   163 	switch (msg) {
   164 
   165 		case WM_ACTIVATE: {
   166 			SDL_VideoDevice *this = current_video;
   167 			BOOL minimized;
   168 			Uint8 appstate;
   169 
   170 			minimized = HIWORD(wParam);
   171 			if ( !minimized && (LOWORD(wParam) != WA_INACTIVE) ) {
   172 				/* Gain the following states */
   173 				appstate = SDL_APPACTIVE|SDL_APPINPUTFOCUS;
   174 				if ( this->input_grab != SDL_GRAB_OFF ) {
   175 					WIN_GrabInput(this, SDL_GRAB_ON);
   176 				}
   177 				if ( !(SDL_GetAppState()&SDL_APPINPUTFOCUS) ) {
   178 					WIN_SwapGamma(this);
   179 				}
   180 				posted = SDL_PrivateAppActive(1, appstate);
   181 				WIN_GetKeyboardState();
   182 			} else {
   183 				/* Lose the following states */
   184 				appstate = SDL_APPINPUTFOCUS;
   185 				if ( minimized ) {
   186 					appstate |= SDL_APPACTIVE;
   187 				}
   188 				if ( this->input_grab != SDL_GRAB_OFF ) {
   189 					WIN_GrabInput(this, SDL_GRAB_OFF);
   190 				}
   191 				if ( SDL_GetAppState() & SDL_APPINPUTFOCUS ) {
   192 					WIN_SwapGamma(this);
   193 				}
   194 				posted = SDL_PrivateAppActive(0, appstate);
   195 			}
   196 			return(0);
   197 		}
   198 		break;
   199 
   200 		case WM_MOUSEMOVE: {
   201 			
   202 			/* Mouse is handled by DirectInput when fullscreen */
   203 			if ( SDL_VideoSurface && ! DIRECTX_FULLSCREEN() ) {
   204 				Sint16 x, y;
   205 
   206 				/* mouse has entered the window */
   207 				if ( ! in_window ) {
   208 #ifdef WM_MOUSELEAVE
   209 					TRACKMOUSEEVENT tme;
   210 
   211 					tme.cbSize = sizeof(tme);
   212 					tme.dwFlags = TME_LEAVE;
   213 					tme.hwndTrack = SDL_Window;
   214 					_TrackMouseEvent(&tme);
   215 #endif /* WM_MOUSELEAVE */
   216 					in_window = TRUE;
   217 
   218 					posted = SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
   219 				}
   220 
   221 				/* mouse has moved within the window */
   222 				x = LOWORD(lParam);
   223 				y = HIWORD(lParam);
   224 				if ( mouse_relative ) {
   225 					POINT center;
   226 					center.x = (SDL_VideoSurface->w/2);
   227 					center.y = (SDL_VideoSurface->h/2);
   228 					x -= (Sint16)center.x;
   229 					y -= (Sint16)center.y;
   230 					if ( x || y ) {
   231 						ClientToScreen(SDL_Window, &center);
   232 						SetCursorPos(center.x, center.y);
   233 						posted = SDL_PrivateMouseMotion(0, 1, x, y);
   234 					}
   235 				} else {
   236 					posted = SDL_PrivateMouseMotion(0, 0, x, y);
   237 				}
   238 			}
   239 		}
   240 		return(0);
   241 
   242 #ifdef WM_MOUSELEAVE
   243 		case WM_MOUSELEAVE: {
   244 
   245 			/* Mouse is handled by DirectInput when fullscreen */
   246 			if ( SDL_VideoSurface && ! DIRECTX_FULLSCREEN() ) {
   247 				/* mouse has left the window */
   248 				/* or */
   249 				/* Elvis has left the building! */
   250 				in_window = FALSE;
   251 				posted = SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
   252 			}
   253 		}
   254 		return(0);
   255 #endif /* WM_MOUSELEAVE */
   256 
   257 		case WM_LBUTTONDOWN:
   258 		case WM_LBUTTONUP:
   259 		case WM_MBUTTONDOWN:
   260 		case WM_MBUTTONUP:
   261 		case WM_RBUTTONDOWN:
   262 		case WM_RBUTTONUP: {
   263 			/* Mouse is handled by DirectInput when fullscreen */
   264 			if ( SDL_VideoSurface && ! DIRECTX_FULLSCREEN() ) {
   265 				Sint16 x, y;
   266 				Uint8 button, state;
   267 
   268 				/* Figure out which button to use */
   269 				switch (msg) {
   270 					case WM_LBUTTONDOWN:
   271 						button = 1;
   272 						state = SDL_PRESSED;
   273 						break;
   274 					case WM_LBUTTONUP:
   275 						button = 1;
   276 						state = SDL_RELEASED;
   277 						break;
   278 					case WM_MBUTTONDOWN:
   279 						button = 2;
   280 						state = SDL_PRESSED;
   281 						break;
   282 					case WM_MBUTTONUP:
   283 						button = 2;
   284 						state = SDL_RELEASED;
   285 						break;
   286 					case WM_RBUTTONDOWN:
   287 						button = 3;
   288 						state = SDL_PRESSED;
   289 						break;
   290 					case WM_RBUTTONUP:
   291 						button = 3;
   292 						state = SDL_RELEASED;
   293 						break;
   294 					default:
   295 						/* Eh? Unknown button? */
   296 						return(0);
   297 				}
   298 				if ( state == SDL_PRESSED ) {
   299 					/* Grab mouse so we get up events */
   300 					if ( ++mouse_pressed > 0 ) {
   301 						SetCapture(hwnd);
   302 					}
   303 				} else {
   304 					/* Release mouse after all up events */
   305 					if ( --mouse_pressed <= 0 ) {
   306 						ReleaseCapture();
   307 						mouse_pressed = 0;
   308 					}
   309 				}
   310 				if ( mouse_relative ) {
   311 				/*	RJR: March 28, 2000
   312 					report internal mouse position if in relative mode */
   313 					x = 0; y = 0;
   314 				} else {
   315 					x = (Sint16)LOWORD(lParam);
   316 					y = (Sint16)HIWORD(lParam);
   317 				}
   318 				posted = SDL_PrivateMouseButton(
   319 							state, button, x, y);
   320 			}
   321 		}
   322 		return(0);
   323 
   324 #ifdef WM_GETMINMAXINFO
   325 		/* This message is sent as a way for us to "check" the values
   326 		 * of a position change.  If we don't like it, we can adjust
   327 		 * the values before they are changed.
   328 		 */
   329 		case WM_GETMINMAXINFO: {
   330 			MINMAXINFO *info;
   331 			RECT        size;
   332 			int x, y;
   333 			int width;
   334 			int height;
   335 
   336 			/* We don't want to clobber an internal resize */
   337 			if ( SDL_resizing )
   338 				return(0);
   339 
   340 			/* We allow resizing with the SDL_RESIZABLE flag */
   341 			if ( SDL_PublicSurface &&
   342 				(SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
   343 				return(0);
   344 			}
   345 
   346 			/* Get the current position of our window */
   347 			GetWindowRect(SDL_Window, &size);
   348 			x = size.left;
   349 			y = size.top;
   350 
   351 			/* Calculate current width and height of our window */
   352 			size.top = 0;
   353 			size.left = 0;
   354 			if ( SDL_PublicSurface != NULL ) {
   355 				size.bottom = SDL_PublicSurface->h;
   356 				size.right = SDL_PublicSurface->w;
   357 			} else {
   358 				size.bottom = 0;
   359 				size.right = 0;
   360 			}
   361 			AdjustWindowRect(&size, GetWindowLong(hwnd, GWL_STYLE),
   362 									FALSE);
   363 			width = size.right - size.left;
   364 			height = size.bottom - size.top;
   365 
   366 			/* Fix our size to the current size */
   367 			info = (MINMAXINFO *)lParam;
   368 			info->ptMaxSize.x = width;
   369 			info->ptMaxSize.y = height;
   370 			info->ptMaxPosition.x = x;
   371 			info->ptMaxPosition.y = y;
   372 			info->ptMinTrackSize.x = width;
   373 			info->ptMinTrackSize.y = height;
   374 			info->ptMaxTrackSize.x = width;
   375 			info->ptMaxTrackSize.y = height;
   376 		}
   377 		return(0);
   378 #endif /* WM_GETMINMAXINFO */
   379 
   380 		case WM_MOVE: {
   381 			SDL_VideoDevice *this = current_video;
   382 
   383 			GetClientRect(SDL_Window, &SDL_bounds);
   384 			ClientToScreen(SDL_Window, (LPPOINT)&SDL_bounds);
   385 			ClientToScreen(SDL_Window, (LPPOINT)&SDL_bounds+1);
   386 			if ( this->input_grab != SDL_GRAB_OFF ) {
   387 				ClipCursor(&SDL_bounds);
   388 			}
   389 		}
   390 		break;
   391 	
   392 		case WM_SIZE: {
   393 			if ( SDL_PublicSurface &&
   394 				(SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
   395 				SDL_PrivateResize(LOWORD(lParam), HIWORD(lParam));
   396 			}
   397 			return(0);
   398 		}
   399 		break;
   400 
   401 		/* We need to set the cursor */
   402 		case WM_SETCURSOR: {
   403 			Uint16 hittest;
   404 
   405 			hittest = LOWORD(lParam);
   406 			if ( hittest == HTCLIENT ) {
   407 				SetCursor(SDL_hcursor);
   408 				return(TRUE);
   409 			}
   410 		}
   411 		break;
   412 
   413 		/* We are about to get palette focus! */
   414 		case WM_QUERYNEWPALETTE: {
   415 			WIN_RealizePalette(current_video);
   416 			return(TRUE);
   417 		}
   418 		break;
   419 
   420 		/* Another application changed the palette */
   421 		case WM_PALETTECHANGED: {
   422 			WIN_PaletteChanged(current_video, (HWND)wParam);
   423 		}
   424 		break;
   425 
   426 		/* We were occluded, refresh our display */
   427 		case WM_PAINT: {
   428 			HDC hdc;
   429 			PAINTSTRUCT ps;
   430 
   431 			hdc = BeginPaint(SDL_Window, &ps);
   432 			if ( current_video->screen &&
   433 			     !(current_video->screen->flags & SDL_OPENGL) ) {
   434 				WIN_WinPAINT(current_video, hdc);
   435 			}
   436 			EndPaint(SDL_Window, &ps);
   437 		}
   438 		return(0);
   439 
   440 		case WM_ERASEBKGND: {
   441 			/* Just do nothing */ ;
   442 		}
   443 		return(1);
   444 
   445 		case WM_CLOSE: {
   446 			if ( (posted = SDL_PrivateQuit()) )
   447 				PostQuitMessage(0);
   448 		}
   449 		return(0);
   450 
   451 		case WM_DESTROY: {
   452 			PostQuitMessage(0);
   453 		}
   454 		return(0);
   455 
   456 		default: {
   457 			/* Special handling by the video driver */
   458 			if (HandleMessage) {
   459 				return(HandleMessage(current_video,
   460 			                     hwnd, msg, wParam, lParam));
   461 			}
   462 		}
   463 		break;
   464 	}
   465 	return(DefWindowProc(hwnd, msg, wParam, lParam));
   466 }
   467 
   468 /* This allows the SDL_WINDOWID hack */
   469 const char *SDL_windowid = NULL;
   470 
   471 /* Register the class for this application -- exported for winmain.c */
   472 int SDL_RegisterApp(char *name, Uint32 style, void *hInst)
   473 {
   474 	static int initialized = 0;
   475 	WNDCLASS class;
   476 #ifdef WM_MOUSELEAVE
   477 	HMODULE handle;
   478 #endif
   479 
   480 	/* Only do this once... */
   481 	if ( initialized ) {
   482 		return(0);
   483 	}
   484 
   485 	/* This function needs to be passed the correct process handle
   486 	   by the application.  The following call just returns a handle
   487 	   to the SDL DLL, which is useless for our purposes and causes
   488 	   DirectInput to fail to initialize.
   489 	 */
   490 	if ( ! hInst ) {
   491 		hInst = GetModuleHandle(NULL);
   492 	}
   493 
   494 	/* Register the application class */
   495 	class.hCursor		= NULL;
   496 #ifdef _WIN32_WCE
   497     {
   498 	/* WinCE uses the UNICODE version */
   499 	int nLen = strlen(name);
   500 	LPWSTR lpszW = alloca((nLen+1)*2);
   501 	MultiByteToWideChar(CP_ACP, 0, name, -1, lpszW, nLen);
   502 	class.hIcon		= LoadImage(hInst, lpszW, IMAGE_ICON,
   503 	                                    0, 0, LR_DEFAULTCOLOR);
   504 	class.lpszMenuName	= lpszW;
   505 	class.lpszClassName	= lpszW;
   506     }
   507 #else
   508 	class.hIcon		= LoadImage(hInst, name, IMAGE_ICON,
   509 	                                    0, 0, LR_DEFAULTCOLOR);
   510 	class.lpszMenuName	= "(none)";
   511 	class.lpszClassName	= name;
   512 #endif /* _WIN32_WCE */
   513 	class.hbrBackground	= NULL;
   514 	class.hInstance		= hInst ? hInst : GetModuleHandle(0);
   515 	class.style		= style;
   516 #ifdef HAVE_OPENGL
   517 	class.style		|= CS_OWNDC;
   518 #endif
   519 	class.lpfnWndProc	= WinMessage;
   520 	class.cbWndExtra	= 0;
   521 	class.cbClsExtra	= 0;
   522 	if ( ! RegisterClass(&class) ) {
   523 		SDL_SetError("Couldn't register application class");
   524 		return(-1);
   525 	}
   526 	SDL_Appname = name;
   527 	SDL_Instance = hInst;
   528 
   529 #ifdef WM_MOUSELEAVE
   530 	/* Get the version of TrackMouseEvent() we use */
   531 	_TrackMouseEvent = NULL;
   532 	handle = GetModuleHandle("USER32.DLL");
   533 	if ( handle ) {
   534 		_TrackMouseEvent = (BOOL (WINAPI *)(TRACKMOUSEEVENT *))GetProcAddress(handle, "TrackMouseEvent");
   535 	}
   536 	if ( _TrackMouseEvent == NULL ) {
   537 		_TrackMouseEvent = WIN_TrackMouseEvent;
   538 	}
   539 #endif /* WM_MOUSELEAVE */
   540 
   541 	/* Check for SDL_WINDOWID hack */
   542 	SDL_windowid = getenv("SDL_WINDOWID");
   543 
   544 	initialized = 1;
   545 	return(0);
   546 }
   547