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