Home | History | Annotate | Download | only in windx5
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2012 Sam Lantinga
      4 
      5     This library is free software; you can redistribute it and/or
      6     modify it under the terms of the GNU Lesser General Public
      7     License as published by the Free Software Foundation; either
      8     version 2.1 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     Lesser General Public License for more details.
     14 
     15     You should have received a copy of the GNU Lesser General Public
     16     License along with this library; if not, write to the Free Software
     17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     18 
     19     Sam Lantinga
     20     slouken (at) libsdl.org
     21 */
     22 #include "SDL_config.h"
     23 
     24 /* CAUTION!!!!  If you modify this file, check ../windib/SDL_sysevents.c */
     25 
     26 #include "directx.h"
     27 
     28 #include "SDL_main.h"
     29 #include "SDL_events.h"
     30 #include "SDL_video.h"
     31 #include "SDL_syswm.h"
     32 #include "../../events/SDL_sysevents.h"
     33 #include "../../events/SDL_events_c.h"
     34 #include "../wincommon/SDL_lowvideo.h"
     35 #include "SDL_dx5video.h"
     36 
     37 #ifndef WM_APP
     38 #define WM_APP	0x8000
     39 #endif
     40 
     41 #ifdef _WIN32_WCE
     42 #define NO_GETKEYBOARDSTATE
     43 #endif
     44 
     45 /* The keyboard and mouse device input */
     46 #define MAX_INPUTS	2
     47 #define INPUT_QSIZE	512		/* Buffer up to 512 input messages */
     48 
     49 static LPDIRECTINPUT dinput = NULL;
     50 static LPDIRECTINPUTDEVICE2 SDL_DIdev[MAX_INPUTS];
     51 static HANDLE               SDL_DIevt[MAX_INPUTS];
     52 static void (*SDL_DIfun[MAX_INPUTS])(const int, DIDEVICEOBJECTDATA *);
     53 static int SDL_DIndev = 0;
     54 static int mouse_lost;
     55 static int mouse_pressed;
     56 static int mouse_buttons_swapped = 0;
     57 
     58 /* The translation table from a DirectInput scancode to an SDL keysym */
     59 static SDLKey DIK_keymap[256];
     60 static SDL_keysym *TranslateKey(UINT scancode, SDL_keysym *keysym, int pressed);
     61 
     62 /* DJM: If the user setup the window for us, we want to save his window proc,
     63    and give him a chance to handle some messages. */
     64 #ifdef STRICT
     65 #define WNDPROCTYPE	WNDPROC
     66 #else
     67 #define WNDPROCTYPE	FARPROC
     68 #endif
     69 static WNDPROCTYPE userWindowProc = NULL;
     70 
     71 static HWND GetTopLevelParent(HWND hWnd)
     72 {
     73     HWND hParentWnd;
     74     while (1)
     75     {
     76         hParentWnd = GetParent(hWnd);
     77         if (hParentWnd == NULL)
     78             break;
     79         hWnd = hParentWnd;
     80     }
     81     return hWnd;
     82 }
     83 
     84 /* Convert a DirectInput return code to a text message */
     85 static void SetDIerror(char *function, int code)
     86 {
     87 	static char *error;
     88 	static char  errbuf[1024];
     89 
     90 	errbuf[0] = 0;
     91 	switch (code) {
     92                 case DIERR_GENERIC:
     93                         error = "Undefined error!";
     94                         break;
     95 		case DIERR_OLDDIRECTINPUTVERSION:
     96 			error = "Your version of DirectInput needs upgrading";
     97 			break;
     98 		case DIERR_INVALIDPARAM:
     99                         error = "Invalid parameters";
    100                         break;
    101                 case DIERR_OUTOFMEMORY:
    102                         error = "Out of memory";
    103                         break;
    104 		case DIERR_DEVICENOTREG:
    105 			error = "Device not registered";
    106 			break;
    107 		case DIERR_NOINTERFACE:
    108 			error = "Interface not supported";
    109 			break;
    110 		case DIERR_NOTINITIALIZED:
    111 			error = "Device not initialized";
    112 			break;
    113 		default:
    114 			SDL_snprintf(errbuf, SDL_arraysize(errbuf),
    115 			         "%s: Unknown DirectInput error: 0x%x",
    116 								function, code);
    117 			break;
    118 	}
    119 	if ( ! errbuf[0] ) {
    120 		SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: %s", function, error);
    121 	}
    122 	SDL_SetError("%s", errbuf);
    123 	return;
    124 }
    125 
    126 /* Initialize DirectInput
    127    Note:  If NONEXCLUSIVE access is requested for the devices, normal
    128           windows input messages will continue to be generated for that
    129           input device, in addition to DirectInput messages.
    130  */
    131 static void handle_keyboard(const int numevents, DIDEVICEOBJECTDATA *events);
    132 static void handle_mouse(const int numevents, DIDEVICEOBJECTDATA *events);
    133 struct {
    134 	char *name;
    135 	REFGUID guid;
    136 	LPCDIDATAFORMAT format;
    137 	DWORD win_level;
    138 	DWORD raw_level;
    139 	void (*fun)(const int numevents, DIDEVICEOBJECTDATA *events);
    140 } inputs[] = {
    141 	{ "keyboard",
    142 		&GUID_SysKeyboard, &c_dfDIKeyboard,
    143 		(DISCL_FOREGROUND|DISCL_NONEXCLUSIVE),
    144 		(DISCL_FOREGROUND|DISCL_NONEXCLUSIVE), handle_keyboard },
    145 	{ "mouse",
    146 		&GUID_SysMouse,
    147 #if DIRECTINPUT_VERSION >= 0x700
    148 		&c_dfDIMouse2,
    149 #else
    150 		&c_dfDIMouse,
    151 #endif
    152 		(DISCL_BACKGROUND|DISCL_NONEXCLUSIVE),
    153 		(DISCL_BACKGROUND|DISCL_NONEXCLUSIVE), handle_mouse },
    154 	{ NULL, NULL, NULL, 0, 0, NULL }
    155 };
    156 
    157 static int DX5_DInputInit(_THIS)
    158 {
    159 	int         i;
    160 	LPDIRECTINPUTDEVICE device;
    161 	HRESULT     result;
    162 	DIPROPDWORD dipdw;
    163 	HWND        topwnd;
    164 
    165 	/* Create the DirectInput object */
    166 	result = DInputCreate(SDL_Instance, DIRECTINPUT_VERSION,
    167 							&dinput, NULL);
    168 	if ( result != DI_OK ) {
    169 		SetDIerror("DirectInputCreate", result);
    170 		return(-1);
    171 	}
    172 
    173 	/* Create all of our registered input devices */
    174 	SDL_DIndev = 0;
    175 	for ( i=0; inputs[i].name; ++i ) {
    176 		/* Create the DirectInput device */
    177 		result = IDirectInput_CreateDevice(dinput, inputs[i].guid,
    178 								&device, NULL);
    179 		if ( result != DI_OK ) {
    180 			SetDIerror("DirectInput::CreateDevice", result);
    181 			return(-1);
    182 		}
    183 		result = IDirectInputDevice_QueryInterface(device,
    184 			&IID_IDirectInputDevice2, (LPVOID *)&SDL_DIdev[i]);
    185 		IDirectInputDevice_Release(device);
    186 		if ( result != DI_OK ) {
    187 			SetDIerror("DirectInputDevice::QueryInterface", result);
    188 			return(-1);
    189 		}
    190 		topwnd =  GetTopLevelParent(SDL_Window);
    191 		result = IDirectInputDevice2_SetCooperativeLevel(SDL_DIdev[i],
    192 					topwnd, inputs[i].win_level);
    193 		if ( result != DI_OK ) {
    194 			SetDIerror("DirectInputDevice::SetCooperativeLevel",
    195 									result);
    196 			return(-1);
    197 		}
    198 		result = IDirectInputDevice2_SetDataFormat(SDL_DIdev[i],
    199 							inputs[i].format);
    200 		if ( result != DI_OK ) {
    201 			SetDIerror("DirectInputDevice::SetDataFormat", result);
    202 			return(-1);
    203 		}
    204 
    205 		/* Set buffered input -- we aren't polling */
    206 		SDL_memset(&dipdw, 0, sizeof(dipdw));
    207 		dipdw.diph.dwSize = sizeof(dipdw);
    208 		dipdw.diph.dwHeaderSize = sizeof(dipdw.diph);
    209 		dipdw.diph.dwObj = 0;
    210 		dipdw.diph.dwHow = DIPH_DEVICE;
    211 		dipdw.dwData = INPUT_QSIZE;
    212 		result = IDirectInputDevice2_SetProperty(SDL_DIdev[i],
    213 						DIPROP_BUFFERSIZE, &dipdw.diph);
    214 		if ( result != DI_OK ) {
    215 			SetDIerror("DirectInputDevice::SetProperty", result);
    216 			return(-1);
    217 		}
    218 
    219 		/* Create an event to be signaled when input is ready */
    220 		SDL_DIevt[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
    221 		if ( SDL_DIevt[i] == NULL ) {
    222 			SDL_SetError("Couldn't create DirectInput event");
    223 			return(-1);
    224 		}
    225 		result = IDirectInputDevice2_SetEventNotification(SDL_DIdev[i],
    226 								SDL_DIevt[i]);
    227 		if ( result != DI_OK ) {
    228 			SetDIerror("DirectInputDevice::SetEventNotification",
    229 									result);
    230 			return(-1);
    231 		}
    232 		SDL_DIfun[i] = inputs[i].fun;
    233 
    234 		/* Acquire the device for input */
    235 		IDirectInputDevice2_Acquire(SDL_DIdev[i]);
    236 
    237 		/* Increment the number of devices we have */
    238 		++SDL_DIndev;
    239 	}
    240 	mouse_pressed = 0;
    241 	mouse_buttons_swapped = GetSystemMetrics(SM_SWAPBUTTON);
    242 
    243 	/* DirectInput is ready! */
    244 	return(0);
    245 }
    246 
    247 /* Clean up DirectInput */
    248 static void DX5_DInputQuit(_THIS)
    249 {
    250 	int i;
    251 
    252 	if ( dinput != NULL ) {
    253 		/* Close and release all DirectInput devices */
    254 		for ( i=0; i<MAX_INPUTS; ++i ) {
    255 			if ( SDL_DIdev[i] != NULL ) {
    256 				IDirectInputDevice2_Unacquire(SDL_DIdev[i]);
    257 				IDirectInputDevice2_SetEventNotification(
    258 							SDL_DIdev[i], NULL);
    259 				if ( SDL_DIevt[i] != NULL ) {
    260 					CloseHandle(SDL_DIevt[i]);
    261 					SDL_DIevt[i] = NULL;
    262 				}
    263 				IDirectInputDevice2_Release(SDL_DIdev[i]);
    264 				SDL_DIdev[i] = NULL;
    265 			}
    266 		}
    267 		SDL_DIndev = 0;
    268 
    269 		/* Release DirectInput */
    270 		IDirectInput_Release(dinput);
    271 		dinput = NULL;
    272 	}
    273 }
    274 
    275 /* Flag to tell SDL whether or not we queued an event */
    276 static int posted = 0;
    277 
    278 /* Input event handler functions */
    279 static void handle_keyboard(const int numevents, DIDEVICEOBJECTDATA *keybuf)
    280 {
    281 	int i;
    282 	SDL_keysym keysym;
    283 
    284 	/* Translate keyboard messages */
    285 	for ( i=0; i<numevents; ++i ) {
    286 		if ( keybuf[i].dwData & 0x80 ) {
    287 			posted = SDL_PrivateKeyboard(SDL_PRESSED,
    288 				    TranslateKey(keybuf[i].dwOfs, &keysym, 1));
    289 		} else {
    290 			posted = SDL_PrivateKeyboard(SDL_RELEASED,
    291 				    TranslateKey(keybuf[i].dwOfs, &keysym, 0));
    292 		}
    293 	}
    294 }
    295 
    296 static void post_mouse_motion(int relative, Sint16 x, Sint16 y)
    297 {
    298 	extern int mouse_relative;
    299 
    300 	if ( SDL_GetAppState() & SDL_APPMOUSEFOCUS ) {
    301 		posted = SDL_PrivateMouseMotion(
    302 			0, relative, x, y);
    303 
    304 		if ( !mouse_relative ) {
    305 			/* As DirectInput reads raw device coordinates, it has no notion of
    306 			 * cursors or absolute position. We must assume responsibility for
    307 			 * keeping track of this. */
    308 			int current_x, current_y;
    309 			POINT cursor;
    310 			RECT trap;
    311 			RECT window;
    312 			int at_edge;
    313 
    314 			/* Get the current cursor position */
    315 			SDL_GetMouseState(&current_x, &current_y);
    316 			cursor.x = current_x;
    317 			cursor.y = current_y;
    318 			ClientToScreen(SDL_Window, &cursor);
    319 
    320 			/* Construct a 1 pixel square RECT that is used to confine the cursor
    321 			 * pointer to a specific pixel using ClipCursor. This is used in
    322 			 * preference to SetCursorPos as it avoids the cursor jumping around as
    323 			 * both the OS and SDL attempt to move it simultaneously. */
    324 			trap.left = cursor.x;
    325 			trap.top = cursor.y;
    326 			trap.right = cursor.x + 1;
    327 			trap.bottom = cursor.y + 1;
    328 
    329 			GetClientRect(SDL_Window, &window);
    330 			window.right -= window.left; window.left = 0;
    331 			window.bottom -= window.top; window.top = 0;
    332 
    333 			/* As we're assuming control over the cursor, we need to know when to
    334 			 * relinquish control of it back to the operating system. This is when
    335 			 * the cursor reaches the edge of the window. */
    336 			at_edge = (current_x == window.left) ||
    337 				(current_x == (window.right - 1)) ||
    338 				(current_y == window.top) ||
    339 				(current_y == (window.bottom - 1));
    340 
    341 			if ( at_edge ) {
    342 				ClipCursor(NULL);
    343 			} else {
    344 				ClipCursor(&trap);
    345 			}
    346 		} else {
    347 			/* When in relative mode, warp the OS's idea of where the cursor is to
    348 			 * the center of the screen. This isn't really necessary as DirectInput
    349 			 * reads from the hardware itself, but in case things go wrong, the
    350 			 * cursor will be left in a sensible place. */
    351 			POINT center;
    352 			center.x = (SDL_VideoSurface->w/2);
    353 			center.y = (SDL_VideoSurface->h/2);
    354 			ClientToScreen(SDL_Window, &center);
    355 			SetCursorPos(center.x, center.y);
    356 		}
    357 	}
    358 }
    359 
    360 static void handle_mouse(const int numevents, DIDEVICEOBJECTDATA *ptrbuf)
    361 {
    362 	int i;
    363 	Sint16 xrel, yrel;
    364 	Uint8 state;
    365 	Uint8 button;
    366 	DWORD timestamp = 0;
    367 
    368 	/* Sanity check. Mailing list reports this being NULL unexpectedly. */
    369 	if (SDL_PublicSurface == NULL) {
    370 		return;
    371 	}
    372 
    373 	/* If mouse focus has been lost, make sure we release the cursor. */
    374 	if ( !(SDL_GetAppState() & SDL_APPMOUSEFOCUS) ) {
    375 		mouse_lost = 1;
    376 		ClipCursor(NULL);
    377 	} else {
    378 		/* If the mouse was lost, regain some sense of mouse state */
    379 		if ( mouse_lost ) {
    380 			POINT mouse_pos;
    381 			Uint8 old_state;
    382 			Uint8 new_state;
    383 
    384 			/* Set ourselves up with the current cursor position */
    385 			GetCursorPos(&mouse_pos);
    386 			ScreenToClient(SDL_Window, &mouse_pos);
    387 			post_mouse_motion( 0, (Sint16)mouse_pos.x, (Sint16)mouse_pos.y);
    388 
    389 			/* Check for mouse button changes */
    390 			old_state = SDL_GetMouseState(NULL, NULL);
    391 			new_state = 0;
    392 			{ /* Get the new DirectInput button state for the mouse */
    393 	#if DIRECTINPUT_VERSION >= 0x700
    394 				DIMOUSESTATE2 distate;
    395 	#else
    396 				DIMOUSESTATE distate;
    397 	#endif
    398 				HRESULT result;
    399 
    400 				result=IDirectInputDevice2_GetDeviceState(SDL_DIdev[1],
    401 							sizeof(distate), &distate);
    402 				if ( result != DI_OK ) {
    403 					/* Try again next time */
    404 					SetDIerror(
    405 					"IDirectInputDevice2::GetDeviceState", result);
    406 					return;
    407 				}
    408 				for ( i=3; i>=0; --i ) {
    409 					if ( (distate.rgbButtons[i]&0x80) == 0x80 ) {
    410 						new_state |= 0x01;
    411 					}
    412 					new_state <<= 1;
    413 				}
    414 			}
    415 			for ( i=0; i<8; ++i ) {
    416 				if ( (old_state&0x01) != (new_state&0x01) ) {
    417 					button = (Uint8)(i+1);
    418 					/* Map DI button numbers to SDL */
    419 					switch ( button ) {
    420 						case 2: button = SDL_BUTTON_RIGHT; break;
    421 						case 3: button = SDL_BUTTON_MIDDLE; break;
    422 						case 4: button = SDL_BUTTON_X1; break;
    423 						case 5: button = SDL_BUTTON_X2; break;
    424 						default: break;
    425 					}
    426 					if ( new_state & 0x01 ) {
    427 						/* Grab mouse so we get mouse-up */
    428 						if ( ++mouse_pressed > 0 ) {
    429 							SetCapture(SDL_Window);
    430 						}
    431 						state = SDL_PRESSED;
    432 					} else {
    433 						/* Release mouse after all mouse-ups */
    434 						if ( --mouse_pressed <= 0 ) {
    435 							ReleaseCapture();
    436 							mouse_pressed = 0;
    437 						}
    438 						state = SDL_RELEASED;
    439 					}
    440 					if ( mouse_buttons_swapped ) {
    441 						if ( button == 1 ) button = 3;
    442 						else
    443 						if ( button == 3 ) button = 1;
    444 					}
    445 					posted = SDL_PrivateMouseButton(state, button,
    446 										0, 0);
    447 				}
    448 				old_state >>= 1;
    449 				new_state >>= 1;
    450 			}
    451 			mouse_lost = 0;
    452 			return;
    453 		}
    454 
    455 		/* Translate mouse messages */
    456 		xrel = 0;
    457 		yrel = 0;
    458 		for ( i=0; i<(int)numevents; ++i ) {
    459 			switch (ptrbuf[i].dwOfs) {
    460 				case DIMOFS_X:
    461 					if ( timestamp != ptrbuf[i].dwTimeStamp ) {
    462 						if ( xrel || yrel ) {
    463 							post_mouse_motion(1, xrel, yrel);
    464 							xrel = 0;
    465 							yrel = 0;
    466 						}
    467 						timestamp = ptrbuf[i].dwTimeStamp;
    468 					}
    469 					xrel += (Sint16)ptrbuf[i].dwData;
    470 					break;
    471 				case DIMOFS_Y:
    472 					if ( timestamp != ptrbuf[i].dwTimeStamp ) {
    473 						if ( xrel || yrel ) {
    474 							post_mouse_motion(1, xrel, yrel);
    475 							xrel = 0;
    476 							yrel = 0;
    477 						}
    478 						timestamp = ptrbuf[i].dwTimeStamp;
    479 					}
    480 					yrel += (Sint16)ptrbuf[i].dwData;
    481 					break;
    482 				case DIMOFS_Z:
    483 					if ( xrel || yrel ) {
    484 						post_mouse_motion(1, xrel, yrel);
    485 						xrel = 0;
    486 						yrel = 0;
    487 					}
    488 					timestamp = 0;
    489 					if((int)ptrbuf[i].dwData > 0)
    490 						button = SDL_BUTTON_WHEELUP;
    491 					else
    492 						button = SDL_BUTTON_WHEELDOWN;
    493 					posted = SDL_PrivateMouseButton(
    494 							SDL_PRESSED, button, 0, 0);
    495 					posted |= SDL_PrivateMouseButton(
    496 							SDL_RELEASED, button, 0, 0);
    497 					break;
    498 				case DIMOFS_BUTTON0:
    499 				case DIMOFS_BUTTON1:
    500 				case DIMOFS_BUTTON2:
    501 				case DIMOFS_BUTTON3:
    502 	#if DIRECTINPUT_VERSION >= 0x700
    503 				case DIMOFS_BUTTON4:
    504 				case DIMOFS_BUTTON5:
    505 				case DIMOFS_BUTTON6:
    506 				case DIMOFS_BUTTON7:
    507 	#endif
    508 					if ( xrel || yrel ) {
    509 						post_mouse_motion(1, xrel, yrel);
    510 						xrel = 0;
    511 						yrel = 0;
    512 					}
    513 					timestamp = 0;
    514 					button = (Uint8)(ptrbuf[i].dwOfs-DIMOFS_BUTTON0)+1;
    515 					/* Map DI button numbers to SDL */
    516 					switch ( button ) {
    517 						case 2: button = SDL_BUTTON_RIGHT; break;
    518 						case 3: button = SDL_BUTTON_MIDDLE; break;
    519 						case 4: button = SDL_BUTTON_X1; break;
    520 						case 5: button = SDL_BUTTON_X2; break;
    521 						default: break;
    522 					}
    523 					if ( ptrbuf[i].dwData & 0x80 ) {
    524 						/* Grab mouse so we get mouse-up */
    525 						if ( ++mouse_pressed > 0 ) {
    526 							SetCapture(SDL_Window);
    527 						}
    528 						state = SDL_PRESSED;
    529 					} else {
    530 						/* Release mouse after all mouse-ups */
    531 						if ( --mouse_pressed <= 0 ) {
    532 							ReleaseCapture();
    533 							mouse_pressed = 0;
    534 						}
    535 						state = SDL_RELEASED;
    536 					}
    537 					if ( mouse_buttons_swapped ) {
    538 						if ( button == 1 ) button = 3;
    539 						else
    540 						if ( button == 3 ) button = 1;
    541 					}
    542 					posted = SDL_PrivateMouseButton(state, button,
    543 										0, 0);
    544 					break;
    545 			}
    546 		}
    547 		if ( xrel || yrel ) {
    548 			post_mouse_motion(1, xrel, yrel);
    549 		}
    550 	}
    551 }
    552 
    553 /* The main Win32 event handler */
    554 LRESULT DX5_HandleMessage(_THIS, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    555 {
    556 	switch (msg) {
    557 #ifdef WM_ACTIVATEAPP
    558 		case WM_ACTIVATEAPP: {
    559 			int i, active;
    560 
    561 			active = (wParam && (GetForegroundWindow() == hwnd));
    562 			if ( active ) {
    563 				for ( i=0; i<MAX_INPUTS; ++i ) {
    564 					if (SDL_DIdev[i] != NULL)
    565 						IDirectInputDevice2_Acquire(
    566 								SDL_DIdev[i]);
    567 				}
    568 			} else {
    569 				for ( i=0; i<MAX_INPUTS; ++i ) {
    570 					if (SDL_DIdev[i] != NULL)
    571 						IDirectInputDevice2_Unacquire(
    572 								SDL_DIdev[i]);
    573 				}
    574 				mouse_lost = 1;
    575 			}
    576 		}
    577 		break;
    578 #endif /* WM_ACTIVATEAPP */
    579 
    580 #ifdef WM_DISPLAYCHANGE
    581 		case WM_DISPLAYCHANGE: {
    582 			WPARAM BitsPerPixel;
    583 			WORD SizeX, SizeY;
    584 
    585 			/* Ack!  The display changed size and/or depth! */
    586 			SizeX = LOWORD(lParam);
    587 			SizeY = HIWORD(lParam);
    588 			BitsPerPixel = wParam;
    589 			/* We cause this message when we go fullscreen */
    590 		}
    591 		break;
    592 #endif /* WM_DISPLAYCHANGE */
    593 
    594 		/* The keyboard is handled via DirectInput */
    595 		case WM_SYSKEYUP:
    596 		case WM_SYSKEYDOWN:
    597 		case WM_KEYUP:
    598 		case WM_KEYDOWN: {
    599 			/* Ignore windows keyboard messages */;
    600 		}
    601 		return(0);
    602 
    603 #if defined(SC_SCREENSAVE) || defined(SC_MONITORPOWER)
    604 		/* Don't allow screen savers or monitor power downs.
    605 		   This is because they quietly clear DirectX surfaces.
    606 		   It would be better to allow the application to
    607 		   decide whether or not to blow these off, but the
    608 		   semantics of SDL_PrivateSysWMEvent() don't allow
    609 		   the application that choice.
    610 		 */
    611 		case WM_SYSCOMMAND: {
    612 			if ((wParam&0xFFF0)==SC_SCREENSAVE ||
    613 			    (wParam&0xFFF0)==SC_MONITORPOWER)
    614 				return(0);
    615 		}
    616 		/* Fall through to default processing */
    617 
    618 #endif /* SC_SCREENSAVE || SC_MONITORPOWER */
    619 
    620 		default: {
    621 			/* Only post the event if we're watching for it */
    622 			if ( SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE ) {
    623 			        SDL_SysWMmsg wmmsg;
    624 
    625 				SDL_VERSION(&wmmsg.version);
    626 				wmmsg.hwnd = hwnd;
    627 				wmmsg.msg = msg;
    628 				wmmsg.wParam = wParam;
    629 				wmmsg.lParam = lParam;
    630 				posted = SDL_PrivateSysWMEvent(&wmmsg);
    631 
    632 			/* DJM: If the user isn't watching for private
    633 				messages in her SDL event loop, then pass it
    634 				along to any win32 specific window proc.
    635 			 */
    636 			} else if (userWindowProc) {
    637 				return CallWindowProc(userWindowProc, hwnd, msg, wParam, lParam);
    638 			}
    639 		}
    640 		break;
    641 	}
    642 	return(DefWindowProc(hwnd, msg, wParam, lParam));
    643 }
    644 
    645 /* This function checks the windows message queue and DirectInput and returns
    646    1 if there was input, 0 if there was no input, or -1 if the application has
    647    posted a quit message.
    648 */
    649 static int DX5_CheckInput(_THIS, int timeout, BOOL processInput)
    650 {
    651 	MSG msg;
    652 	int      i;
    653 	HRESULT  result;
    654 	DWORD    event;
    655 
    656 	/* Check the normal windows queue (highest preference) */
    657 	posted = 0;
    658 	while ( ! posted &&
    659 	        PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) ) {
    660 		if ( GetMessage(&msg, NULL, 0, 0) > 0 ) {
    661 			DispatchMessage(&msg);
    662 		} else {
    663 			return(-1);
    664 		}
    665 	}
    666 	if ( posted ) {
    667 		return(1);
    668 	}
    669 
    670 	/* Pump the DirectInput flow */
    671 	if ( SDL_GetAppState() & SDL_APPMOUSEFOCUS ) {
    672 		for ( i=0; i<MAX_INPUTS; ++i ) {
    673 			if ( SDL_DIdev[i] != NULL ) {
    674 				result = IDirectInputDevice2_Poll(SDL_DIdev[i]);
    675 				if ( (result == DIERR_INPUTLOST) ||
    676 						(result == DIERR_NOTACQUIRED) ) {
    677 					if ( SDL_strcmp(inputs[i].name, "mouse") == 0 ) {
    678 						mouse_lost = 1;
    679 					}
    680 					IDirectInputDevice2_Acquire(SDL_DIdev[i]);
    681 					IDirectInputDevice2_Poll(SDL_DIdev[i]);
    682 				}
    683 			}
    684 		}
    685 	}
    686 
    687 	/* Wait for messages and input events */
    688 	event = MsgWaitForMultipleObjects(SDL_DIndev, SDL_DIevt, FALSE,
    689 							timeout, QS_ALLEVENTS);
    690 	if ((event >= WAIT_OBJECT_0) && (event < (WAIT_OBJECT_0+SDL_DIndev))) {
    691 		DWORD numevents;
    692 		static DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE];
    693 
    694 		event -= WAIT_OBJECT_0;
    695 		numevents = INPUT_QSIZE;
    696 		result = IDirectInputDevice2_GetDeviceData(
    697 				SDL_DIdev[event], sizeof(DIDEVICEOBJECTDATA),
    698 							evtbuf, &numevents, 0);
    699 		if ( (result == DIERR_INPUTLOST) ||
    700 					(result == DIERR_NOTACQUIRED) ) {
    701 			if ( SDL_strcmp(inputs[event].name, "mouse") == 0 ) {
    702 				mouse_lost = 1;
    703 			}
    704 			IDirectInputDevice2_Acquire(SDL_DIdev[event]);
    705 			result = IDirectInputDevice2_GetDeviceData(
    706 				SDL_DIdev[event], sizeof(DIDEVICEOBJECTDATA),
    707 							evtbuf, &numevents, 0);
    708 		}
    709 		/* Handle the events */
    710 		if ( result == DI_OK && processInput ) {
    711 			/* Note: This can post multiple events to event queue
    712 			 */
    713 			(*SDL_DIfun[event])((int)numevents, evtbuf);
    714 			return(1);
    715 		}
    716 	}
    717 	if ( event != WAIT_TIMEOUT ) {
    718 		/* Maybe there was a windows message? */
    719 		if ( PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) ) {
    720 			if ( GetMessage(&msg, NULL, 0, 0) > 0 ) {
    721 				DispatchMessage(&msg);
    722 			} else {
    723 				return(-1);
    724 			}
    725 			return(1);
    726 		}
    727 	}
    728 	return(0);
    729 }
    730 
    731 /* Change cooperative level based on whether or not we are fullscreen */
    732 void DX5_DInputReset(_THIS, int fullscreen)
    733 {
    734 	DWORD level;
    735 	int i;
    736 	HRESULT result;
    737 	HWND topwnd;
    738 
    739 	for ( i=0; i<MAX_INPUTS; ++i ) {
    740 		if ( SDL_DIdev[i] != NULL ) {
    741 			if ( fullscreen ) {
    742 				level = inputs[i].raw_level;
    743 			} else {
    744 				level = inputs[i].win_level;
    745 			}
    746 			IDirectInputDevice2_Unacquire(SDL_DIdev[i]);
    747 			topwnd = GetTopLevelParent(SDL_Window);
    748 			result = IDirectInputDevice2_SetCooperativeLevel(
    749 					SDL_DIdev[i], topwnd, level);
    750 			IDirectInputDevice2_Acquire(SDL_DIdev[i]);
    751 			if ( result != DI_OK ) {
    752 				SetDIerror(
    753 			"DirectInputDevice::SetCooperativeLevel", result);
    754 			}
    755 		}
    756 	}
    757 	mouse_lost = 1;
    758 
    759 	/* Flush pending input */
    760 	DX5_CheckInput(this, 0, FALSE);
    761 }
    762 
    763 void DX5_PumpEvents(_THIS)
    764 {
    765 	/* Wait for messages and DirectInput */
    766 	while ( DX5_CheckInput(this, 0, TRUE) > 0 ) {
    767 		/* Loop and check again */;
    768 	}
    769 }
    770 
    771 void DX5_InitOSKeymap(_THIS)
    772 {
    773 #ifndef DIK_PAUSE
    774 #define DIK_PAUSE	0xC5
    775 #endif
    776 #ifndef DIK_OEM_102
    777 #define DIK_OEM_102	0x56	/* < > | on UK/Germany keyboards */
    778 #endif
    779 	int i;
    780 
    781 	/* Map the DIK scancodes to SDL keysyms */
    782 	for ( i=0; i<SDL_arraysize(DIK_keymap); ++i )
    783 		DIK_keymap[i] = 0;
    784 
    785 	/* Defined DIK_* constants */
    786 	DIK_keymap[DIK_ESCAPE] = SDLK_ESCAPE;
    787 	DIK_keymap[DIK_1] = SDLK_1;
    788 	DIK_keymap[DIK_2] = SDLK_2;
    789 	DIK_keymap[DIK_3] = SDLK_3;
    790 	DIK_keymap[DIK_4] = SDLK_4;
    791 	DIK_keymap[DIK_5] = SDLK_5;
    792 	DIK_keymap[DIK_6] = SDLK_6;
    793 	DIK_keymap[DIK_7] = SDLK_7;
    794 	DIK_keymap[DIK_8] = SDLK_8;
    795 	DIK_keymap[DIK_9] = SDLK_9;
    796 	DIK_keymap[DIK_0] = SDLK_0;
    797 	DIK_keymap[DIK_MINUS] = SDLK_MINUS;
    798 	DIK_keymap[DIK_EQUALS] = SDLK_EQUALS;
    799 	DIK_keymap[DIK_BACK] = SDLK_BACKSPACE;
    800 	DIK_keymap[DIK_TAB] = SDLK_TAB;
    801 	DIK_keymap[DIK_Q] = SDLK_q;
    802 	DIK_keymap[DIK_W] = SDLK_w;
    803 	DIK_keymap[DIK_E] = SDLK_e;
    804 	DIK_keymap[DIK_R] = SDLK_r;
    805 	DIK_keymap[DIK_T] = SDLK_t;
    806 	DIK_keymap[DIK_Y] = SDLK_y;
    807 	DIK_keymap[DIK_U] = SDLK_u;
    808 	DIK_keymap[DIK_I] = SDLK_i;
    809 	DIK_keymap[DIK_O] = SDLK_o;
    810 	DIK_keymap[DIK_P] = SDLK_p;
    811 	DIK_keymap[DIK_LBRACKET] = SDLK_LEFTBRACKET;
    812 	DIK_keymap[DIK_RBRACKET] = SDLK_RIGHTBRACKET;
    813 	DIK_keymap[DIK_RETURN] = SDLK_RETURN;
    814 	DIK_keymap[DIK_LCONTROL] = SDLK_LCTRL;
    815 	DIK_keymap[DIK_A] = SDLK_a;
    816 	DIK_keymap[DIK_S] = SDLK_s;
    817 	DIK_keymap[DIK_D] = SDLK_d;
    818 	DIK_keymap[DIK_F] = SDLK_f;
    819 	DIK_keymap[DIK_G] = SDLK_g;
    820 	DIK_keymap[DIK_H] = SDLK_h;
    821 	DIK_keymap[DIK_J] = SDLK_j;
    822 	DIK_keymap[DIK_K] = SDLK_k;
    823 	DIK_keymap[DIK_L] = SDLK_l;
    824 	DIK_keymap[DIK_SEMICOLON] = SDLK_SEMICOLON;
    825 	DIK_keymap[DIK_APOSTROPHE] = SDLK_QUOTE;
    826 	DIK_keymap[DIK_GRAVE] = SDLK_BACKQUOTE;
    827 	DIK_keymap[DIK_LSHIFT] = SDLK_LSHIFT;
    828 	DIK_keymap[DIK_BACKSLASH] = SDLK_BACKSLASH;
    829 	DIK_keymap[DIK_OEM_102] = SDLK_LESS;
    830 	DIK_keymap[DIK_Z] = SDLK_z;
    831 	DIK_keymap[DIK_X] = SDLK_x;
    832 	DIK_keymap[DIK_C] = SDLK_c;
    833 	DIK_keymap[DIK_V] = SDLK_v;
    834 	DIK_keymap[DIK_B] = SDLK_b;
    835 	DIK_keymap[DIK_N] = SDLK_n;
    836 	DIK_keymap[DIK_M] = SDLK_m;
    837 	DIK_keymap[DIK_COMMA] = SDLK_COMMA;
    838 	DIK_keymap[DIK_PERIOD] = SDLK_PERIOD;
    839 	DIK_keymap[DIK_SLASH] = SDLK_SLASH;
    840 	DIK_keymap[DIK_RSHIFT] = SDLK_RSHIFT;
    841 	DIK_keymap[DIK_MULTIPLY] = SDLK_KP_MULTIPLY;
    842 	DIK_keymap[DIK_LMENU] = SDLK_LALT;
    843 	DIK_keymap[DIK_SPACE] = SDLK_SPACE;
    844 	DIK_keymap[DIK_CAPITAL] = SDLK_CAPSLOCK;
    845 	DIK_keymap[DIK_F1] = SDLK_F1;
    846 	DIK_keymap[DIK_F2] = SDLK_F2;
    847 	DIK_keymap[DIK_F3] = SDLK_F3;
    848 	DIK_keymap[DIK_F4] = SDLK_F4;
    849 	DIK_keymap[DIK_F5] = SDLK_F5;
    850 	DIK_keymap[DIK_F6] = SDLK_F6;
    851 	DIK_keymap[DIK_F7] = SDLK_F7;
    852 	DIK_keymap[DIK_F8] = SDLK_F8;
    853 	DIK_keymap[DIK_F9] = SDLK_F9;
    854 	DIK_keymap[DIK_F10] = SDLK_F10;
    855 	DIK_keymap[DIK_NUMLOCK] = SDLK_NUMLOCK;
    856 	DIK_keymap[DIK_SCROLL] = SDLK_SCROLLOCK;
    857 	DIK_keymap[DIK_NUMPAD7] = SDLK_KP7;
    858 	DIK_keymap[DIK_NUMPAD8] = SDLK_KP8;
    859 	DIK_keymap[DIK_NUMPAD9] = SDLK_KP9;
    860 	DIK_keymap[DIK_SUBTRACT] = SDLK_KP_MINUS;
    861 	DIK_keymap[DIK_NUMPAD4] = SDLK_KP4;
    862 	DIK_keymap[DIK_NUMPAD5] = SDLK_KP5;
    863 	DIK_keymap[DIK_NUMPAD6] = SDLK_KP6;
    864 	DIK_keymap[DIK_ADD] = SDLK_KP_PLUS;
    865 	DIK_keymap[DIK_NUMPAD1] = SDLK_KP1;
    866 	DIK_keymap[DIK_NUMPAD2] = SDLK_KP2;
    867 	DIK_keymap[DIK_NUMPAD3] = SDLK_KP3;
    868 	DIK_keymap[DIK_NUMPAD0] = SDLK_KP0;
    869 	DIK_keymap[DIK_DECIMAL] = SDLK_KP_PERIOD;
    870 	DIK_keymap[DIK_F11] = SDLK_F11;
    871 	DIK_keymap[DIK_F12] = SDLK_F12;
    872 
    873 	DIK_keymap[DIK_F13] = SDLK_F13;
    874 	DIK_keymap[DIK_F14] = SDLK_F14;
    875 	DIK_keymap[DIK_F15] = SDLK_F15;
    876 
    877 	DIK_keymap[DIK_NUMPADEQUALS] = SDLK_KP_EQUALS;
    878 	DIK_keymap[DIK_NUMPADENTER] = SDLK_KP_ENTER;
    879 	DIK_keymap[DIK_RCONTROL] = SDLK_RCTRL;
    880 	DIK_keymap[DIK_DIVIDE] = SDLK_KP_DIVIDE;
    881 	DIK_keymap[DIK_SYSRQ] = SDLK_PRINT;
    882 	DIK_keymap[DIK_RMENU] = SDLK_RALT;
    883 	DIK_keymap[DIK_PAUSE] = SDLK_PAUSE;
    884 	DIK_keymap[DIK_HOME] = SDLK_HOME;
    885 	DIK_keymap[DIK_UP] = SDLK_UP;
    886 	DIK_keymap[DIK_PRIOR] = SDLK_PAGEUP;
    887 	DIK_keymap[DIK_LEFT] = SDLK_LEFT;
    888 	DIK_keymap[DIK_RIGHT] = SDLK_RIGHT;
    889 	DIK_keymap[DIK_END] = SDLK_END;
    890 	DIK_keymap[DIK_DOWN] = SDLK_DOWN;
    891 	DIK_keymap[DIK_NEXT] = SDLK_PAGEDOWN;
    892 	DIK_keymap[DIK_INSERT] = SDLK_INSERT;
    893 	DIK_keymap[DIK_DELETE] = SDLK_DELETE;
    894 	DIK_keymap[DIK_LWIN] = SDLK_LMETA;
    895 	DIK_keymap[DIK_RWIN] = SDLK_RMETA;
    896 	DIK_keymap[DIK_APPS] = SDLK_MENU;
    897 }
    898 
    899 static SDL_keysym *TranslateKey(UINT scancode, SDL_keysym *keysym, int pressed)
    900 {
    901 	/* Set the keysym information */
    902 	keysym->scancode = (unsigned char)scancode;
    903 	keysym->sym = DIK_keymap[scancode];
    904 	keysym->mod = KMOD_NONE;
    905 	keysym->unicode = 0;
    906 	if ( pressed && SDL_TranslateUNICODE ) {
    907 		UINT vkey;
    908 #ifndef NO_GETKEYBOARDSTATE
    909 		BYTE	keystate[256];
    910 		Uint16	wchars[2];
    911 #endif
    912 
    913 		vkey = MapVirtualKey(scancode, 1);
    914 #ifdef NO_GETKEYBOARDSTATE
    915 		/* Uh oh, better hope the vkey is close enough.. */
    916 		keysym->unicode = vkey;
    917 #else
    918 		GetKeyboardState(keystate);
    919 		/* Numlock isn't taken into account in ToUnicode,
    920 		 * so we handle it as a special case here */
    921 		if ((keystate[VK_NUMLOCK] & 1) && vkey >= VK_NUMPAD0 && vkey <= VK_NUMPAD9)
    922 		{
    923 			keysym->unicode = vkey - VK_NUMPAD0 + '0';
    924 		}
    925 		else if (SDL_ToUnicode(vkey, scancode, keystate, wchars, sizeof(wchars)/sizeof(wchars[0]), 0) > 0)
    926 		{
    927 			keysym->unicode = wchars[0];
    928 		}
    929 #endif /* NO_GETKEYBOARDSTATE */
    930 	}
    931 	return(keysym);
    932 }
    933 
    934 int DX5_CreateWindow(_THIS)
    935 {
    936 	char *windowid = SDL_getenv("SDL_WINDOWID");
    937 	int i;
    938 
    939 	/* Clear out DirectInput variables in case we fail */
    940 	for ( i=0; i<MAX_INPUTS; ++i ) {
    941 		SDL_DIdev[i] = NULL;
    942 		SDL_DIevt[i] = NULL;
    943 		SDL_DIfun[i] = NULL;
    944 	}
    945 
    946 	SDL_RegisterApp(NULL, 0, 0);
    947 
    948 	SDL_windowid = (windowid != NULL);
    949 	if ( SDL_windowid ) {
    950 		SDL_Window = (HWND)((size_t)SDL_strtoull(windowid, NULL, 0));
    951 		if ( SDL_Window == NULL ) {
    952 			SDL_SetError("Couldn't get user specified window");
    953 			return(-1);
    954 		}
    955 
    956 		/* DJM: we want all event's for the user specified
    957 			window to be handled by SDL.
    958 		 */
    959 		userWindowProc = (WNDPROCTYPE)GetWindowLongPtr(SDL_Window, GWLP_WNDPROC);
    960 		SetWindowLongPtr(SDL_Window, GWLP_WNDPROC, (LONG_PTR)WinMessage);
    961 	} else {
    962 		SDL_Window = CreateWindow(SDL_Appname, SDL_Appname,
    963                         (WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX),
    964                         CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, NULL, NULL, SDL_Instance, NULL);
    965 		if ( SDL_Window == NULL ) {
    966 			SDL_SetError("Couldn't create window");
    967 			return(-1);
    968 		}
    969 		ShowWindow(SDL_Window, SW_HIDE);
    970 	}
    971 
    972 	/* Initialize DirectInput */
    973 	if ( DX5_DInputInit(this) < 0 ) {
    974 		return(-1);
    975 	}
    976 
    977 	/* JC 14 Mar 2006
    978 		Flush the message loop or this can cause big problems later
    979 		Especially if the user decides to use dialog boxes or assert()!
    980 	*/
    981 	WIN_FlushMessageQueue();
    982 
    983 	/* Ready to roll */
    984 	return(0);
    985 }
    986 
    987 void DX5_DestroyWindow(_THIS)
    988 {
    989 	/* Close down DirectInput */
    990 	DX5_DInputQuit(this);
    991 
    992 	/* Destroy our window */
    993 	if ( SDL_windowid ) {
    994 		SetWindowLongPtr(SDL_Window, GWLP_WNDPROC, (LONG_PTR)userWindowProc);
    995 	} else {
    996 		DestroyWindow(SDL_Window);
    997 	}
    998 	SDL_UnregisterApp();
    999 
   1000 	/* JC 14 Mar 2006
   1001 		Flush the message loop or this can cause big problems later
   1002 		Especially if the user decides to use dialog boxes or assert()!
   1003 	*/
   1004 	WIN_FlushMessageQueue();
   1005 }
   1006