src/video/wincommon/SDL_sysevents.c
author Sam Lantinga <slouken@lokigames.com>
Sat, 16 Jun 2001 01:01:42 +0000
changeset 61 994ed1d668e7
parent 36 13ee9f4834ea
child 145 29a638dc26db
permissions -rw-r--r--
Mouse wheel sends mouse button (4/5) events 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 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 && ! DINPUT_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 && ! DINPUT_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 && ! DINPUT_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 
   325 #if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
   326 		case WM_MOUSEWHEEL: 
   327 			if ( SDL_VideoSurface && ! DINPUT_FULLSCREEN() ) {
   328 				Sint16 x, y;
   329 				Uint8 button = 0;
   330 				int move = (short)HIWORD(wParam);
   331 				if(move > 0)
   332 					button = 4;
   333 				else if(move < 0)
   334 					button = 5;
   335 				if(button)
   336 				{
   337 					if ( mouse_relative ) {
   338 					/*	RJR: March 28, 2000
   339 						report internal mouse position if in relative mode */
   340 						x = 0; y = 0;
   341 					} else {
   342 						x = (Sint16)LOWORD(lParam);
   343 						y = (Sint16)HIWORD(lParam);
   344 					}
   345 					posted = SDL_PrivateMouseButton(
   346 								SDL_PRESSED, button, x, y);
   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 		case WM_ERASEBKGND: {
   469 			/* Just do nothing */ ;
   470 		}
   471 		return(1);
   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 /* This allows the SDL_WINDOWID hack */
   497 const char *SDL_windowid = NULL;
   498 
   499 /* Register the class for this application -- exported for winmain.c */
   500 int SDL_RegisterApp(char *name, Uint32 style, void *hInst)
   501 {
   502 	static int initialized = 0;
   503 	WNDCLASS class;
   504 #ifdef WM_MOUSELEAVE
   505 	HMODULE handle;
   506 #endif
   507 
   508 	/* Only do this once... */
   509 	if ( initialized ) {
   510 		return(0);
   511 	}
   512 
   513 	/* This function needs to be passed the correct process handle
   514 	   by the application.  The following call just returns a handle
   515 	   to the SDL DLL, which is useless for our purposes and causes
   516 	   DirectInput to fail to initialize.
   517 	 */
   518 	if ( ! hInst ) {
   519 		hInst = GetModuleHandle(NULL);
   520 	}
   521 
   522 	/* Register the application class */
   523 	class.hCursor		= NULL;
   524 #ifdef _WIN32_WCE
   525     {
   526 	/* WinCE uses the UNICODE version */
   527 	int nLen = strlen(name)+1;
   528 	LPWSTR lpszW = alloca(nLen*2);
   529 	MultiByteToWideChar(CP_ACP, 0, name, -1, lpszW, nLen);
   530 	class.hIcon		= LoadImage(hInst, lpszW, IMAGE_ICON,
   531 	                                    0, 0, LR_DEFAULTCOLOR);
   532 	class.lpszMenuName	= NULL;
   533 	class.lpszClassName	= lpszW;
   534     }
   535 #else
   536 	class.hIcon		= LoadImage(hInst, name, IMAGE_ICON,
   537 	                                    0, 0, LR_DEFAULTCOLOR);
   538 	class.lpszMenuName	= "(none)";
   539 	class.lpszClassName	= name;
   540 #endif /* _WIN32_WCE */
   541 	class.hbrBackground	= NULL;
   542 	class.hInstance		= hInst ? hInst : GetModuleHandle(0);
   543 	class.style		= style;
   544 #ifdef HAVE_OPENGL
   545 	class.style		|= CS_OWNDC;
   546 #endif
   547 	class.lpfnWndProc	= WinMessage;
   548 	class.cbWndExtra	= 0;
   549 	class.cbClsExtra	= 0;
   550 	if ( ! RegisterClass(&class) ) {
   551 		SDL_SetError("Couldn't register application class");
   552 		return(-1);
   553 	}
   554 	SDL_Appname = name;
   555 	SDL_Instance = hInst;
   556 
   557 #ifdef WM_MOUSELEAVE
   558 	/* Get the version of TrackMouseEvent() we use */
   559 	_TrackMouseEvent = NULL;
   560 	handle = GetModuleHandle("USER32.DLL");
   561 	if ( handle ) {
   562 		_TrackMouseEvent = (BOOL (WINAPI *)(TRACKMOUSEEVENT *))GetProcAddress(handle, "TrackMouseEvent");
   563 	}
   564 	if ( _TrackMouseEvent == NULL ) {
   565 		_TrackMouseEvent = WIN_TrackMouseEvent;
   566 	}
   567 #endif /* WM_MOUSELEAVE */
   568 
   569 	/* Check for SDL_WINDOWID hack */
   570 	SDL_windowid = getenv("SDL_WINDOWID");
   571 
   572 	initialized = 1;
   573 	return(0);
   574 }
   575