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