src/video/wincommon/SDL_sysevents.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 14 Dec 2001 12:38:15 +0000
changeset 252 e8157fcb3114
parent 179 ec9ec33673a2
child 304 ec53caed9fb2
permissions -rw-r--r--
Updated the source with the correct e-mail address
     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 
    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] & 0x01) {
   138 			state |= KMOD_NUM;
   139 		}
   140 		if ( keyboard[VK_CAPITAL] & 0x01) {
   141 			state |= KMOD_CAPS;
   142 		}
   143 	}
   144 	SDL_SetModState(state);
   145 #endif /* !NO_GETKEYBOARDSTATE */
   146 }
   147 
   148 /* The main Win32 event handler
   149 DJM: This is no longer static as (DX5/DIB)_CreateWindow needs it
   150 */
   151 LONG CALLBACK WinMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
   152 {
   153 	SDL_VideoDevice *this = current_video;
   154 	static int mouse_pressed = 0;
   155 	static int in_window = 0;
   156 #ifdef WMMSG_DEBUG
   157 	fprintf(stderr, "Received windows message:  ");
   158 	if ( msg > MAX_WMMSG ) {
   159 		fprintf(stderr, "%d", msg);
   160 	} else {
   161 		fprintf(stderr, "%s", wmtab[msg]);
   162 	}
   163 	fprintf(stderr, " -- 0x%X, 0x%X\n", wParam, lParam);
   164 #endif
   165 	switch (msg) {
   166 
   167 		case WM_ACTIVATE: {
   168 			SDL_VideoDevice *this = current_video;
   169 			BOOL minimized;
   170 			Uint8 appstate;
   171 
   172 			minimized = HIWORD(wParam);
   173 			if ( !minimized && (LOWORD(wParam) != WA_INACTIVE) ) {
   174 				/* Gain the following states */
   175 				appstate = SDL_APPACTIVE|SDL_APPINPUTFOCUS;
   176 				if ( this->input_grab != SDL_GRAB_OFF ) {
   177 					WIN_GrabInput(this, SDL_GRAB_ON);
   178 				}
   179 				if ( !(SDL_GetAppState()&SDL_APPINPUTFOCUS) ) {
   180 					WIN_SwapGamma(this);
   181 				}
   182 				posted = SDL_PrivateAppActive(1, appstate);
   183 				WIN_GetKeyboardState();
   184 			} else {
   185 				/* Lose the following states */
   186 				appstate = SDL_APPINPUTFOCUS;
   187 				if ( minimized ) {
   188 					appstate |= SDL_APPACTIVE;
   189 				}
   190 				if ( this->input_grab != SDL_GRAB_OFF ) {
   191 					WIN_GrabInput(this, SDL_GRAB_OFF);
   192 				}
   193 				if ( SDL_GetAppState() & SDL_APPINPUTFOCUS ) {
   194 					WIN_SwapGamma(this);
   195 				}
   196 				posted = SDL_PrivateAppActive(0, appstate);
   197 			}
   198 			return(0);
   199 		}
   200 		break;
   201 
   202 		case WM_MOUSEMOVE: {
   203 			
   204 			/* Mouse is handled by DirectInput when fullscreen */
   205 			if ( SDL_VideoSurface && ! DINPUT_FULLSCREEN() ) {
   206 				Sint16 x, y;
   207 
   208 				/* mouse has entered the window */
   209 				if ( ! in_window ) {
   210 #ifdef WM_MOUSELEAVE
   211 					TRACKMOUSEEVENT tme;
   212 
   213 					tme.cbSize = sizeof(tme);
   214 					tme.dwFlags = TME_LEAVE;
   215 					tme.hwndTrack = SDL_Window;
   216 					_TrackMouseEvent(&tme);
   217 #endif /* WM_MOUSELEAVE */
   218 					in_window = TRUE;
   219 
   220 					posted = SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
   221 				}
   222 
   223 				/* mouse has moved within the window */
   224 				x = LOWORD(lParam);
   225 				y = HIWORD(lParam);
   226 				if ( mouse_relative ) {
   227 					POINT center;
   228 					center.x = (SDL_VideoSurface->w/2);
   229 					center.y = (SDL_VideoSurface->h/2);
   230 					x -= (Sint16)center.x;
   231 					y -= (Sint16)center.y;
   232 					if ( x || y ) {
   233 						ClientToScreen(SDL_Window, &center);
   234 						SetCursorPos(center.x, center.y);
   235 						posted = SDL_PrivateMouseMotion(0, 1, x, y);
   236 					}
   237 				} else {
   238 					posted = SDL_PrivateMouseMotion(0, 0, x, y);
   239 				}
   240 			}
   241 		}
   242 		return(0);
   243 
   244 #ifdef WM_MOUSELEAVE
   245 		case WM_MOUSELEAVE: {
   246 
   247 			/* Mouse is handled by DirectInput when fullscreen */
   248 			if ( SDL_VideoSurface && ! DINPUT_FULLSCREEN() ) {
   249 				/* mouse has left the window */
   250 				/* or */
   251 				/* Elvis has left the building! */
   252 				in_window = FALSE;
   253 				posted = SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
   254 			}
   255 		}
   256 		return(0);
   257 #endif /* WM_MOUSELEAVE */
   258 
   259 		case WM_LBUTTONDOWN:
   260 		case WM_LBUTTONUP:
   261 		case WM_MBUTTONDOWN:
   262 		case WM_MBUTTONUP:
   263 		case WM_RBUTTONDOWN:
   264 		case WM_RBUTTONUP: {
   265 			/* Mouse is handled by DirectInput when fullscreen */
   266 			if ( SDL_VideoSurface && ! DINPUT_FULLSCREEN() ) {
   267 				Sint16 x, y;
   268 				Uint8 button, state;
   269 
   270 				/* DJM:
   271 				   We want the SDL window to take focus so that
   272 				   it acts like a normal windows "component"
   273 				   (e.g. gains keyboard focus on a mouse click).
   274 				 */
   275 				SetFocus(SDL_Window);
   276 
   277 				/* Figure out which button to use */
   278 				switch (msg) {
   279 					case WM_LBUTTONDOWN:
   280 						button = 1;
   281 						state = SDL_PRESSED;
   282 						break;
   283 					case WM_LBUTTONUP:
   284 						button = 1;
   285 						state = SDL_RELEASED;
   286 						break;
   287 					case WM_MBUTTONDOWN:
   288 						button = 2;
   289 						state = SDL_PRESSED;
   290 						break;
   291 					case WM_MBUTTONUP:
   292 						button = 2;
   293 						state = SDL_RELEASED;
   294 						break;
   295 					case WM_RBUTTONDOWN:
   296 						button = 3;
   297 						state = SDL_PRESSED;
   298 						break;
   299 					case WM_RBUTTONUP:
   300 						button = 3;
   301 						state = SDL_RELEASED;
   302 						break;
   303 					default:
   304 						/* Eh? Unknown button? */
   305 						return(0);
   306 				}
   307 				if ( state == SDL_PRESSED ) {
   308 					/* Grab mouse so we get up events */
   309 					if ( ++mouse_pressed > 0 ) {
   310 						SetCapture(hwnd);
   311 					}
   312 				} else {
   313 					/* Release mouse after all up events */
   314 					if ( --mouse_pressed <= 0 ) {
   315 						ReleaseCapture();
   316 						mouse_pressed = 0;
   317 					}
   318 				}
   319 				if ( mouse_relative ) {
   320 				/*	RJR: March 28, 2000
   321 					report internal mouse position if in relative mode */
   322 					x = 0; y = 0;
   323 				} else {
   324 					x = (Sint16)LOWORD(lParam);
   325 					y = (Sint16)HIWORD(lParam);
   326 				}
   327 				posted = SDL_PrivateMouseButton(
   328 							state, button, x, y);
   329 			}
   330 		}
   331 		return(0);
   332 
   333 
   334 #if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
   335 		case WM_MOUSEWHEEL: 
   336 			if ( SDL_VideoSurface && ! DINPUT_FULLSCREEN() ) {
   337 				int move = (short)HIWORD(wParam);
   338 				if ( move ) {
   339 					Uint8 button;
   340 					if ( move > 0 )
   341 						button = 4;
   342 					else
   343 						button = 5;
   344 					posted = SDL_PrivateMouseButton(
   345 						SDL_PRESSED, button, 0, 0);
   346 				}
   347 			}
   348 			return(0);
   349 #endif
   350 
   351 #ifdef WM_GETMINMAXINFO
   352 		/* This message is sent as a way for us to "check" the values
   353 		 * of a position change.  If we don't like it, we can adjust
   354 		 * the values before they are changed.
   355 		 */
   356 		case WM_GETMINMAXINFO: {
   357 			MINMAXINFO *info;
   358 			RECT        size;
   359 			int x, y;
   360 			int width;
   361 			int height;
   362 
   363 			/* We don't want to clobber an internal resize */
   364 			if ( SDL_resizing )
   365 				return(0);
   366 
   367 			/* We allow resizing with the SDL_RESIZABLE flag */
   368 			if ( SDL_PublicSurface &&
   369 				(SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
   370 				return(0);
   371 			}
   372 
   373 			/* Get the current position of our window */
   374 			GetWindowRect(SDL_Window, &size);
   375 			x = size.left;
   376 			y = size.top;
   377 
   378 			/* Calculate current width and height of our window */
   379 			size.top = 0;
   380 			size.left = 0;
   381 			if ( SDL_PublicSurface != NULL ) {
   382 				size.bottom = SDL_PublicSurface->h;
   383 				size.right = SDL_PublicSurface->w;
   384 			} else {
   385 				size.bottom = 0;
   386 				size.right = 0;
   387 			}
   388 			AdjustWindowRect(&size, GetWindowLong(hwnd, GWL_STYLE),
   389 									FALSE);
   390 			width = size.right - size.left;
   391 			height = size.bottom - size.top;
   392 
   393 			/* Fix our size to the current size */
   394 			info = (MINMAXINFO *)lParam;
   395 			info->ptMaxSize.x = width;
   396 			info->ptMaxSize.y = height;
   397 			info->ptMaxPosition.x = x;
   398 			info->ptMaxPosition.y = y;
   399 			info->ptMinTrackSize.x = width;
   400 			info->ptMinTrackSize.y = height;
   401 			info->ptMaxTrackSize.x = width;
   402 			info->ptMaxTrackSize.y = height;
   403 		}
   404 		return(0);
   405 #endif /* WM_GETMINMAXINFO */
   406 
   407 		case WM_MOVE: {
   408 			SDL_VideoDevice *this = current_video;
   409 
   410 			GetClientRect(SDL_Window, &SDL_bounds);
   411 			ClientToScreen(SDL_Window, (LPPOINT)&SDL_bounds);
   412 			ClientToScreen(SDL_Window, (LPPOINT)&SDL_bounds+1);
   413 			if ( this->input_grab != SDL_GRAB_OFF ) {
   414 				ClipCursor(&SDL_bounds);
   415 			}
   416 		}
   417 		break;
   418 	
   419 		case WM_SIZE: {
   420 			if ( SDL_PublicSurface &&
   421 				(SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
   422 				SDL_PrivateResize(LOWORD(lParam), HIWORD(lParam));
   423 			}
   424 			return(0);
   425 		}
   426 		break;
   427 
   428 		/* We need to set the cursor */
   429 		case WM_SETCURSOR: {
   430 			Uint16 hittest;
   431 
   432 			hittest = LOWORD(lParam);
   433 			if ( hittest == HTCLIENT ) {
   434 				SetCursor(SDL_hcursor);
   435 				return(TRUE);
   436 			}
   437 		}
   438 		break;
   439 
   440 		/* We are about to get palette focus! */
   441 		case WM_QUERYNEWPALETTE: {
   442 			WIN_RealizePalette(current_video);
   443 			return(TRUE);
   444 		}
   445 		break;
   446 
   447 		/* Another application changed the palette */
   448 		case WM_PALETTECHANGED: {
   449 			WIN_PaletteChanged(current_video, (HWND)wParam);
   450 		}
   451 		break;
   452 
   453 		/* We were occluded, refresh our display */
   454 		case WM_PAINT: {
   455 			HDC hdc;
   456 			PAINTSTRUCT ps;
   457 
   458 			hdc = BeginPaint(SDL_Window, &ps);
   459 			if ( current_video->screen &&
   460 			     !(current_video->screen->flags & SDL_OPENGL) ) {
   461 				WIN_WinPAINT(current_video, hdc);
   462 			}
   463 			EndPaint(SDL_Window, &ps);
   464 		}
   465 		return(0);
   466 
   467 		/* DJM: Send an expose event in this case */
   468 		case WM_ERASEBKGND: {
   469 			posted = SDL_PrivateExpose();
   470 		}
   471 		return(0);
   472 
   473 		case WM_CLOSE: {
   474 			if ( (posted = SDL_PrivateQuit()) )
   475 				PostQuitMessage(0);
   476 		}
   477 		return(0);
   478 
   479 		case WM_DESTROY: {
   480 			PostQuitMessage(0);
   481 		}
   482 		return(0);
   483 
   484 		default: {
   485 			/* Special handling by the video driver */
   486 			if (HandleMessage) {
   487 				return(HandleMessage(current_video,
   488 			                     hwnd, msg, wParam, lParam));
   489 			}
   490 		}
   491 		break;
   492 	}
   493 	return(DefWindowProc(hwnd, msg, wParam, lParam));
   494 }
   495 
   496 /* Allow the application handle to be stored and retrieved later */
   497 static void *SDL_handle = NULL;
   498 
   499 void SDL_SetModuleHandle(void *handle)
   500 {
   501 	SDL_handle = handle;
   502 }
   503 void *SDL_GetModuleHandle(void)
   504 {
   505 	void *handle;
   506 
   507 	if ( SDL_handle ) {
   508 		handle = SDL_handle;
   509 	} else {
   510 		/* Warning:
   511 		   If SDL is built as a DLL, this will return a handle to
   512 		   the DLL, not the application, and DirectInput may fail
   513 		   to initialize.
   514 		 */
   515 		handle = GetModuleHandle(NULL);
   516 	}
   517 	return(handle);
   518 }
   519 
   520 /* This allows the SDL_WINDOWID hack */
   521 const char *SDL_windowid = NULL;
   522 
   523 /* Register the class for this application -- exported for winmain.c */
   524 int SDL_RegisterApp(char *name, Uint32 style, void *hInst)
   525 {
   526 	static int initialized = 0;
   527 	WNDCLASS class;
   528 #ifdef WM_MOUSELEAVE
   529 	HMODULE handle;
   530 #endif
   531 
   532 	/* Only do this once... */
   533 	if ( initialized ) {
   534 		return(0);
   535 	}
   536 
   537 	/* This function needs to be passed the correct process handle
   538 	   by the application.
   539 	 */
   540 	if ( ! hInst ) {
   541 		hInst = SDL_GetModuleHandle();
   542 	}
   543 
   544 	/* Register the application class */
   545 	class.hCursor		= NULL;
   546 #ifdef _WIN32_WCE
   547     {
   548 	/* WinCE uses the UNICODE version */
   549 	int nLen = strlen(name)+1;
   550 	LPWSTR lpszW = alloca(nLen*2);
   551 	MultiByteToWideChar(CP_ACP, 0, name, -1, lpszW, nLen);
   552 	class.hIcon		= LoadImage(hInst, lpszW, IMAGE_ICON,
   553 	                                    0, 0, LR_DEFAULTCOLOR);
   554 	class.lpszMenuName	= NULL;
   555 	class.lpszClassName	= lpszW;
   556     }
   557 #else
   558 	class.hIcon		= LoadImage(hInst, name, IMAGE_ICON,
   559 	                                    0, 0, LR_DEFAULTCOLOR);
   560 	class.lpszMenuName	= "(none)";
   561 	class.lpszClassName	= name;
   562 #endif /* _WIN32_WCE */
   563 	class.hbrBackground	= NULL;
   564 	class.hInstance		= hInst;
   565 	class.style		= style;
   566 #ifdef HAVE_OPENGL
   567 	class.style		|= CS_OWNDC;
   568 #endif
   569 	class.lpfnWndProc	= WinMessage;
   570 	class.cbWndExtra	= 0;
   571 	class.cbClsExtra	= 0;
   572 	if ( ! RegisterClass(&class) ) {
   573 		SDL_SetError("Couldn't register application class");
   574 		return(-1);
   575 	}
   576 	SDL_Appname = name;
   577 	SDL_Instance = hInst;
   578 
   579 #ifdef WM_MOUSELEAVE
   580 	/* Get the version of TrackMouseEvent() we use */
   581 	_TrackMouseEvent = NULL;
   582 	handle = GetModuleHandle("USER32.DLL");
   583 	if ( handle ) {
   584 		_TrackMouseEvent = (BOOL (WINAPI *)(TRACKMOUSEEVENT *))GetProcAddress(handle, "TrackMouseEvent");
   585 	}
   586 	if ( _TrackMouseEvent == NULL ) {
   587 		_TrackMouseEvent = WIN_TrackMouseEvent;
   588 	}
   589 #endif /* WM_MOUSELEAVE */
   590 
   591 	/* Check for SDL_WINDOWID hack */
   592 	SDL_windowid = getenv("SDL_WINDOWID");
   593 
   594 	initialized = 1;
   595 	return(0);
   596 }
   597