src/video/wincommon/SDL_sysevents.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 09 Aug 2001 14:04:46 +0000
changeset 149 0e66fd980014
parent 145 29a638dc26db
child 162 0a26c92c2385
permissions -rw-r--r--
Fixed compile errors and added call to SDL_SetModuleHandle() in WinMain()
     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] & 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 				Sint16 x, y;
   338 				Uint8 button = 0;
   339 				int move = (short)HIWORD(wParam);
   340 				if(move > 0)
   341 					button = 4;
   342 				else if(move < 0)
   343 					button = 5;
   344 				if(button)
   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 								SDL_PRESSED, button, x, y);
   356 				}
   357 			}
   358 			return(0);
   359 #endif
   360 
   361 #ifdef WM_GETMINMAXINFO
   362 		/* This message is sent as a way for us to "check" the values
   363 		 * of a position change.  If we don't like it, we can adjust
   364 		 * the values before they are changed.
   365 		 */
   366 		case WM_GETMINMAXINFO: {
   367 			MINMAXINFO *info;
   368 			RECT        size;
   369 			int x, y;
   370 			int width;
   371 			int height;
   372 
   373 			/* We don't want to clobber an internal resize */
   374 			if ( SDL_resizing )
   375 				return(0);
   376 
   377 			/* We allow resizing with the SDL_RESIZABLE flag */
   378 			if ( SDL_PublicSurface &&
   379 				(SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
   380 				return(0);
   381 			}
   382 
   383 			/* Get the current position of our window */
   384 			GetWindowRect(SDL_Window, &size);
   385 			x = size.left;
   386 			y = size.top;
   387 
   388 			/* Calculate current width and height of our window */
   389 			size.top = 0;
   390 			size.left = 0;
   391 			if ( SDL_PublicSurface != NULL ) {
   392 				size.bottom = SDL_PublicSurface->h;
   393 				size.right = SDL_PublicSurface->w;
   394 			} else {
   395 				size.bottom = 0;
   396 				size.right = 0;
   397 			}
   398 			AdjustWindowRect(&size, GetWindowLong(hwnd, GWL_STYLE),
   399 									FALSE);
   400 			width = size.right - size.left;
   401 			height = size.bottom - size.top;
   402 
   403 			/* Fix our size to the current size */
   404 			info = (MINMAXINFO *)lParam;
   405 			info->ptMaxSize.x = width;
   406 			info->ptMaxSize.y = height;
   407 			info->ptMaxPosition.x = x;
   408 			info->ptMaxPosition.y = y;
   409 			info->ptMinTrackSize.x = width;
   410 			info->ptMinTrackSize.y = height;
   411 			info->ptMaxTrackSize.x = width;
   412 			info->ptMaxTrackSize.y = height;
   413 		}
   414 		return(0);
   415 #endif /* WM_GETMINMAXINFO */
   416 
   417 		case WM_MOVE: {
   418 			SDL_VideoDevice *this = current_video;
   419 
   420 			GetClientRect(SDL_Window, &SDL_bounds);
   421 			ClientToScreen(SDL_Window, (LPPOINT)&SDL_bounds);
   422 			ClientToScreen(SDL_Window, (LPPOINT)&SDL_bounds+1);
   423 			if ( this->input_grab != SDL_GRAB_OFF ) {
   424 				ClipCursor(&SDL_bounds);
   425 			}
   426 		}
   427 		break;
   428 	
   429 		case WM_SIZE: {
   430 			if ( SDL_PublicSurface &&
   431 				(SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
   432 				SDL_PrivateResize(LOWORD(lParam), HIWORD(lParam));
   433 			}
   434 			return(0);
   435 		}
   436 		break;
   437 
   438 		/* We need to set the cursor */
   439 		case WM_SETCURSOR: {
   440 			Uint16 hittest;
   441 
   442 			hittest = LOWORD(lParam);
   443 			if ( hittest == HTCLIENT ) {
   444 				SetCursor(SDL_hcursor);
   445 				return(TRUE);
   446 			}
   447 		}
   448 		break;
   449 
   450 		/* We are about to get palette focus! */
   451 		case WM_QUERYNEWPALETTE: {
   452 			WIN_RealizePalette(current_video);
   453 			return(TRUE);
   454 		}
   455 		break;
   456 
   457 		/* Another application changed the palette */
   458 		case WM_PALETTECHANGED: {
   459 			WIN_PaletteChanged(current_video, (HWND)wParam);
   460 		}
   461 		break;
   462 
   463 		/* We were occluded, refresh our display */
   464 		case WM_PAINT: {
   465 			HDC hdc;
   466 			PAINTSTRUCT ps;
   467 
   468 			hdc = BeginPaint(SDL_Window, &ps);
   469 			if ( current_video->screen &&
   470 			     !(current_video->screen->flags & SDL_OPENGL) ) {
   471 				WIN_WinPAINT(current_video, hdc);
   472 			}
   473 			EndPaint(SDL_Window, &ps);
   474 		}
   475 		return(0);
   476 
   477 		/* DJM: Send an expose event in this case */
   478 		case WM_ERASEBKGND: {
   479 			posted = SDL_PrivateExpose();
   480 		}
   481 		return(0);
   482 
   483 		case WM_CLOSE: {
   484 			if ( (posted = SDL_PrivateQuit()) )
   485 				PostQuitMessage(0);
   486 		}
   487 		return(0);
   488 
   489 		case WM_DESTROY: {
   490 			PostQuitMessage(0);
   491 		}
   492 		return(0);
   493 
   494 		default: {
   495 			/* Special handling by the video driver */
   496 			if (HandleMessage) {
   497 				return(HandleMessage(current_video,
   498 			                     hwnd, msg, wParam, lParam));
   499 			}
   500 		}
   501 		break;
   502 	}
   503 	return(DefWindowProc(hwnd, msg, wParam, lParam));
   504 }
   505 
   506 /* Allow the application handle to be stored and retrieved later */
   507 static void *SDL_handle = NULL;
   508 
   509 void SDL_SetModuleHandle(void *handle)
   510 {
   511 	SDL_handle = handle;
   512 }
   513 void *SDL_GetModuleHandle(void)
   514 {
   515 	void *handle;
   516 
   517 	if ( SDL_handle ) {
   518 		handle = SDL_handle;
   519 	} else {
   520 		/* Warning:
   521 		   If SDL is built as a DLL, this will return a handle to
   522 		   the DLL, not the application, and DirectInput may fail
   523 		   to initialize.
   524 		 */
   525 		handle = GetModuleHandle(NULL);
   526 	}
   527 	return(handle);
   528 }
   529 
   530 /* This allows the SDL_WINDOWID hack */
   531 const char *SDL_windowid = NULL;
   532 
   533 /* Register the class for this application -- exported for winmain.c */
   534 int SDL_RegisterApp(char *name, Uint32 style, void *hInst)
   535 {
   536 	static int initialized = 0;
   537 	WNDCLASS class;
   538 #ifdef WM_MOUSELEAVE
   539 	HMODULE handle;
   540 #endif
   541 
   542 	/* Only do this once... */
   543 	if ( initialized ) {
   544 		return(0);
   545 	}
   546 
   547 	/* This function needs to be passed the correct process handle
   548 	   by the application.
   549 	 */
   550 	if ( ! hInst ) {
   551 		hInst = SDL_GetModuleHandle();
   552 	}
   553 
   554 	/* Register the application class */
   555 	class.hCursor		= NULL;
   556 #ifdef _WIN32_WCE
   557     {
   558 	/* WinCE uses the UNICODE version */
   559 	int nLen = strlen(name)+1;
   560 	LPWSTR lpszW = alloca(nLen*2);
   561 	MultiByteToWideChar(CP_ACP, 0, name, -1, lpszW, nLen);
   562 	class.hIcon		= LoadImage(hInst, lpszW, IMAGE_ICON,
   563 	                                    0, 0, LR_DEFAULTCOLOR);
   564 	class.lpszMenuName	= NULL;
   565 	class.lpszClassName	= lpszW;
   566     }
   567 #else
   568 	class.hIcon		= LoadImage(hInst, name, IMAGE_ICON,
   569 	                                    0, 0, LR_DEFAULTCOLOR);
   570 	class.lpszMenuName	= "(none)";
   571 	class.lpszClassName	= name;
   572 #endif /* _WIN32_WCE */
   573 	class.hbrBackground	= NULL;
   574 	class.hInstance		= hInst;
   575 	class.style		= style;
   576 #ifdef HAVE_OPENGL
   577 	class.style		|= CS_OWNDC;
   578 #endif
   579 	class.lpfnWndProc	= WinMessage;
   580 	class.cbWndExtra	= 0;
   581 	class.cbClsExtra	= 0;
   582 	if ( ! RegisterClass(&class) ) {
   583 		SDL_SetError("Couldn't register application class");
   584 		return(-1);
   585 	}
   586 	SDL_Appname = name;
   587 	SDL_Instance = hInst;
   588 
   589 #ifdef WM_MOUSELEAVE
   590 	/* Get the version of TrackMouseEvent() we use */
   591 	_TrackMouseEvent = NULL;
   592 	handle = GetModuleHandle("USER32.DLL");
   593 	if ( handle ) {
   594 		_TrackMouseEvent = (BOOL (WINAPI *)(TRACKMOUSEEVENT *))GetProcAddress(handle, "TrackMouseEvent");
   595 	}
   596 	if ( _TrackMouseEvent == NULL ) {
   597 		_TrackMouseEvent = WIN_TrackMouseEvent;
   598 	}
   599 #endif /* WM_MOUSELEAVE */
   600 
   601 	/* Check for SDL_WINDOWID hack */
   602 	SDL_windowid = getenv("SDL_WINDOWID");
   603 
   604 	initialized = 1;
   605 	return(0);
   606 }
   607