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@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 
GetTopLevelParent(HWND hWnd)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 */
SetDIerror(char * function,int code)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 
DX5_DInputInit(_THIS)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 */
DX5_DInputQuit(_THIS)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 */
handle_keyboard(const int numevents,DIDEVICEOBJECTDATA * keybuf)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 
post_mouse_motion(int relative,Sint16 x,Sint16 y)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 
handle_mouse(const int numevents,DIDEVICEOBJECTDATA * ptrbuf)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 */
DX5_HandleMessage(_THIS,HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)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 */
DX5_CheckInput(_THIS,int timeout,BOOL processInput)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 */
DX5_DInputReset(_THIS,int 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 
DX5_PumpEvents(_THIS)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 
DX5_InitOSKeymap(_THIS)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 
TranslateKey(UINT scancode,SDL_keysym * keysym,int pressed)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 
DX5_CreateWindow(_THIS)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 
DX5_DestroyWindow(_THIS)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