SDL-1.2.14
[sdl_omap.git] / src / video / windx5 / SDL_dx5events.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2009 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
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_FOREGROUND|DISCL_NONEXCLUSIVE),
153                 (DISCL_FOREGROUND|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_APPINPUTFOCUS|SDL_APPMOUSEFOCUS)) ==
301                 (SDL_APPINPUTFOCUS|SDL_APPMOUSEFOCUS) ) {
302                 posted = SDL_PrivateMouseMotion(
303                         0, relative, x, y);
304
305                 if ( !mouse_relative ) {
306                         /* As DirectInput reads raw device coordinates, it has no notion of
307                          * cursors or absolute position. We must assume responsibility for
308                          * keeping track of this. */
309                         int current_x, current_y;
310                         POINT cursor;
311                         RECT trap;
312                         RECT window;
313                         int at_edge;
314
315                         /* Get the current cursor position */
316                         SDL_GetMouseState(&current_x, &current_y);
317                         cursor.x = current_x;
318                         cursor.y = current_y;
319                         ClientToScreen(SDL_Window, &cursor);
320
321                         /* Construct a 1 pixel square RECT that is used to confine the cursor
322                          * pointer to a specific pixel using ClipCursor. This is used in
323                          * preference to SetCursorPos as it avoids the cursor jumping around as
324                          * both the OS and SDL attempt to move it simultaneously. */
325                         trap.left = cursor.x;
326                         trap.top = cursor.y;
327                         trap.right = cursor.x + 1;
328                         trap.bottom = cursor.y + 1;
329
330                         GetClientRect(SDL_Window, &window);
331                         window.right -= window.left; window.left = 0;
332                         window.bottom -= window.top; window.top = 0;
333
334                         /* As we're assuming control over the cursor, we need to know when to
335                          * relinquish control of it back to the operating system. This is when
336                          * the cursor reaches the edge of the window. */
337                         at_edge = (current_x == window.left) ||
338                                 (current_x == (window.right - 1)) ||
339                                 (current_y == window.top) ||
340                                 (current_y == (window.bottom - 1));
341
342                         if ( at_edge ) {
343                                 ClipCursor(NULL);
344                         } else {
345                                 ClipCursor(&trap);
346                         }
347                 } else {
348                         /* When in relative mode, warp the OS's idea of where the cursor is to
349                          * the center of the screen. This isn't really necessary as DirectInput
350                          * reads from the hardware itself, but in case things go wrong, the
351                          * cursor will be left in a sensible place. */
352                         POINT center;
353                         center.x = (SDL_VideoSurface->w/2);
354                         center.y = (SDL_VideoSurface->h/2);
355                         ClientToScreen(SDL_Window, &center);
356                         SetCursorPos(center.x, center.y);
357                 }
358         } else {
359                 /* No window or mouse focus, control is lost */
360                 mouse_lost = 1;
361                 ClipCursor(NULL);
362         }
363 }
364
365 static void handle_mouse(const int numevents, DIDEVICEOBJECTDATA *ptrbuf)
366 {
367         int i;
368         Sint16 xrel, yrel;
369         Uint8 state;
370         Uint8 button;
371         DWORD timestamp = 0;
372
373         /* Sanity check. Mailing list reports this being NULL unexpectedly. */
374         if (SDL_PublicSurface == NULL) {
375                 return;
376         }
377
378         /* If the mouse was lost, regain some sense of mouse state */
379         if ( mouse_lost && (SDL_GetAppState() & SDL_APPMOUSEFOCUS) ) {
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 /* The main Win32 event handler */
553 LRESULT DX5_HandleMessage(_THIS, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
554 {
555         switch (msg) {
556 #ifdef WM_ACTIVATEAPP
557                 case WM_ACTIVATEAPP: {
558                         int i, active;
559
560                         active = (wParam && (GetForegroundWindow() == hwnd));
561                         if ( active ) {
562                                 for ( i=0; SDL_DIdev[i]; ++i ) {
563                                         IDirectInputDevice2_Acquire(
564                                                                 SDL_DIdev[i]);
565                                 }
566                         } else {
567                                 for ( i=0; SDL_DIdev[i]; ++i ) {
568                                         IDirectInputDevice2_Unacquire(
569                                                                 SDL_DIdev[i]);
570                                 }
571                                 mouse_lost = 1;
572                         }
573                 }
574                 break;
575 #endif /* WM_ACTIVATEAPP */
576
577 #ifdef WM_DISPLAYCHANGE
578                 case WM_DISPLAYCHANGE: {
579                         WPARAM BitsPerPixel;
580                         WORD SizeX, SizeY;
581
582                         /* Ack!  The display changed size and/or depth! */
583                         SizeX = LOWORD(lParam);
584                         SizeY = HIWORD(lParam);
585                         BitsPerPixel = wParam;
586                         /* We cause this message when we go fullscreen */
587                 }
588                 break;
589 #endif /* WM_DISPLAYCHANGE */
590
591                 /* The keyboard is handled via DirectInput */
592                 case WM_SYSKEYUP:
593                 case WM_SYSKEYDOWN:
594                 case WM_KEYUP:
595                 case WM_KEYDOWN: {
596                         /* Ignore windows keyboard messages */;
597                 }
598                 return(0);
599
600 #if defined(SC_SCREENSAVE) || defined(SC_MONITORPOWER)
601                 /* Don't allow screen savers or monitor power downs.
602                    This is because they quietly clear DirectX surfaces.
603                    It would be better to allow the application to
604                    decide whether or not to blow these off, but the
605                    semantics of SDL_PrivateSysWMEvent() don't allow
606                    the application that choice.
607                  */
608                 case WM_SYSCOMMAND: {
609                         if ((wParam&0xFFF0)==SC_SCREENSAVE || 
610                             (wParam&0xFFF0)==SC_MONITORPOWER)
611                                 return(0);
612                 }
613                 /* Fall through to default processing */
614
615 #endif /* SC_SCREENSAVE || SC_MONITORPOWER */
616
617                 default: {
618                         /* Only post the event if we're watching for it */
619                         if ( SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE ) {
620                                 SDL_SysWMmsg wmmsg;
621
622                                 SDL_VERSION(&wmmsg.version);
623                                 wmmsg.hwnd = hwnd;
624                                 wmmsg.msg = msg;
625                                 wmmsg.wParam = wParam;
626                                 wmmsg.lParam = lParam;
627                                 posted = SDL_PrivateSysWMEvent(&wmmsg);
628
629                         /* DJM: If the user isn't watching for private
630                                 messages in her SDL event loop, then pass it
631                                 along to any win32 specific window proc.
632                          */
633                         } else if (userWindowProc) {
634                                 return CallWindowProc(userWindowProc, hwnd, msg, wParam, lParam);
635                         }
636                 }
637                 break;
638         }
639         return(DefWindowProc(hwnd, msg, wParam, lParam));
640 }
641
642 /* This function checks the windows message queue and DirectInput and returns
643    1 if there was input, 0 if there was no input, or -1 if the application has
644    posted a quit message.
645 */
646 static int DX5_CheckInput(_THIS, int timeout, BOOL processInput)
647 {
648         MSG msg;
649         int      i;
650         HRESULT  result;
651         DWORD    event;
652
653         /* Check the normal windows queue (highest preference) */
654         posted = 0;
655         while ( ! posted &&
656                 PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) ) {
657                 if ( GetMessage(&msg, NULL, 0, 0) > 0 ) {
658                         TranslateMessage(&msg);
659                         DispatchMessage(&msg);
660                 } else {
661                         return(-1);
662                 }
663         }
664         if ( posted ) {
665                 return(1);
666         }
667
668         /* Pump the DirectInput flow */
669         if ( SDL_GetAppState() & SDL_APPINPUTFOCUS ) {
670                 for ( i=0; i<MAX_INPUTS; ++i ) {
671                         if ( SDL_DIdev[i] != NULL ) {
672                                 result = IDirectInputDevice2_Poll(SDL_DIdev[i]);
673                                 if ( (result == DIERR_INPUTLOST) ||
674                                                 (result == DIERR_NOTACQUIRED) ) {
675                                         if ( SDL_strcmp(inputs[i].name, "mouse") == 0 ) {
676                                                 mouse_lost = 1;
677                                         }
678                                         IDirectInputDevice2_Acquire(SDL_DIdev[i]);
679                                         IDirectInputDevice2_Poll(SDL_DIdev[i]);
680                                 }
681                         }
682                 }
683         }
684
685         /* Wait for messages and input events */
686         event = MsgWaitForMultipleObjects(SDL_DIndev, SDL_DIevt, FALSE,
687                                                         timeout, QS_ALLEVENTS);
688         if ((event >= WAIT_OBJECT_0) && (event < (WAIT_OBJECT_0+SDL_DIndev))) {
689                 DWORD numevents;
690                 static DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE];
691
692                 event -= WAIT_OBJECT_0;
693                 numevents = INPUT_QSIZE;
694                 result = IDirectInputDevice2_GetDeviceData(
695                                 SDL_DIdev[event], sizeof(DIDEVICEOBJECTDATA),
696                                                         evtbuf, &numevents, 0);
697                 if ( (result == DIERR_INPUTLOST) ||
698                                         (result == DIERR_NOTACQUIRED) ) {
699                         if ( SDL_strcmp(inputs[event].name, "mouse") == 0 ) {
700                                 mouse_lost = 1;
701                         }
702                         IDirectInputDevice2_Acquire(SDL_DIdev[event]);
703                         result = IDirectInputDevice2_GetDeviceData(
704                                 SDL_DIdev[event], sizeof(DIDEVICEOBJECTDATA),
705                                                         evtbuf, &numevents, 0);
706                 }
707                 /* Handle the events */
708                 if ( result == DI_OK && processInput ) {
709                         /* Note: This can post multiple events to event queue
710                          */
711                         (*SDL_DIfun[event])((int)numevents, evtbuf);
712                         return(1);
713                 }
714         }
715         if ( event != WAIT_TIMEOUT ) {
716                 /* Maybe there was a windows message? */
717                 posted = 0;
718                 while ( ! posted &&
719                         PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) ) {
720                         if ( GetMessage(&msg, NULL, 0, 0) > 0 ) {
721                                 TranslateMessage(&msg);
722                                 DispatchMessage(&msg);
723                         } else {
724                                 return(-1);
725                         }
726                 }
727                 if ( posted ) {
728                         return(1);
729                 }
730         }
731         return(0);
732 }
733
734 /* Change cooperative level based on whether or not we are fullscreen */
735 void DX5_DInputReset(_THIS, int fullscreen)
736 {
737         DWORD level;
738         int i;
739         HRESULT result;
740         HWND topwnd;
741
742         for ( i=0; i<MAX_INPUTS; ++i ) {
743                 if ( SDL_DIdev[i] != NULL ) {
744                         if ( fullscreen ) {
745                                 level = inputs[i].raw_level;
746                         } else {
747                                 level = inputs[i].win_level;
748                         }
749                         IDirectInputDevice2_Unacquire(SDL_DIdev[i]);
750                         topwnd = GetTopLevelParent(SDL_Window);
751                         result = IDirectInputDevice2_SetCooperativeLevel(
752                                         SDL_DIdev[i], topwnd, level);
753                         IDirectInputDevice2_Acquire(SDL_DIdev[i]);
754                         if ( result != DI_OK ) {
755                                 SetDIerror(
756                         "DirectInputDevice::SetCooperativeLevel", result);
757                         }
758                 }
759         }
760         mouse_lost = 1;
761
762         /* Flush pending input */
763         DX5_CheckInput(this, 0, FALSE);
764 }
765
766 void DX5_PumpEvents(_THIS)
767 {
768         /* Wait for messages and DirectInput */
769         while ( DX5_CheckInput(this, 0, TRUE) > 0 ) {
770                 /* Loop and check again */;
771         }
772 }
773
774 void DX5_InitOSKeymap(_THIS)
775 {
776 #ifndef DIK_PAUSE
777 #define DIK_PAUSE       0xC5
778 #endif
779 #ifndef DIK_OEM_102
780 #define DIK_OEM_102     0x56    /* < > | on UK/Germany keyboards */
781 #endif
782         int i;
783
784         /* Map the DIK scancodes to SDL keysyms */
785         for ( i=0; i<SDL_arraysize(DIK_keymap); ++i )
786                 DIK_keymap[i] = 0;
787
788         /* Defined DIK_* constants */
789         DIK_keymap[DIK_ESCAPE] = SDLK_ESCAPE;
790         DIK_keymap[DIK_1] = SDLK_1;
791         DIK_keymap[DIK_2] = SDLK_2;
792         DIK_keymap[DIK_3] = SDLK_3;
793         DIK_keymap[DIK_4] = SDLK_4;
794         DIK_keymap[DIK_5] = SDLK_5;
795         DIK_keymap[DIK_6] = SDLK_6;
796         DIK_keymap[DIK_7] = SDLK_7;
797         DIK_keymap[DIK_8] = SDLK_8;
798         DIK_keymap[DIK_9] = SDLK_9;
799         DIK_keymap[DIK_0] = SDLK_0;
800         DIK_keymap[DIK_MINUS] = SDLK_MINUS;
801         DIK_keymap[DIK_EQUALS] = SDLK_EQUALS;
802         DIK_keymap[DIK_BACK] = SDLK_BACKSPACE;
803         DIK_keymap[DIK_TAB] = SDLK_TAB;
804         DIK_keymap[DIK_Q] = SDLK_q;
805         DIK_keymap[DIK_W] = SDLK_w;
806         DIK_keymap[DIK_E] = SDLK_e;
807         DIK_keymap[DIK_R] = SDLK_r;
808         DIK_keymap[DIK_T] = SDLK_t;
809         DIK_keymap[DIK_Y] = SDLK_y;
810         DIK_keymap[DIK_U] = SDLK_u;
811         DIK_keymap[DIK_I] = SDLK_i;
812         DIK_keymap[DIK_O] = SDLK_o;
813         DIK_keymap[DIK_P] = SDLK_p;
814         DIK_keymap[DIK_LBRACKET] = SDLK_LEFTBRACKET;
815         DIK_keymap[DIK_RBRACKET] = SDLK_RIGHTBRACKET;
816         DIK_keymap[DIK_RETURN] = SDLK_RETURN;
817         DIK_keymap[DIK_LCONTROL] = SDLK_LCTRL;
818         DIK_keymap[DIK_A] = SDLK_a;
819         DIK_keymap[DIK_S] = SDLK_s;
820         DIK_keymap[DIK_D] = SDLK_d;
821         DIK_keymap[DIK_F] = SDLK_f;
822         DIK_keymap[DIK_G] = SDLK_g;
823         DIK_keymap[DIK_H] = SDLK_h;
824         DIK_keymap[DIK_J] = SDLK_j;
825         DIK_keymap[DIK_K] = SDLK_k;
826         DIK_keymap[DIK_L] = SDLK_l;
827         DIK_keymap[DIK_SEMICOLON] = SDLK_SEMICOLON;
828         DIK_keymap[DIK_APOSTROPHE] = SDLK_QUOTE;
829         DIK_keymap[DIK_GRAVE] = SDLK_BACKQUOTE;
830         DIK_keymap[DIK_LSHIFT] = SDLK_LSHIFT;
831         DIK_keymap[DIK_BACKSLASH] = SDLK_BACKSLASH;
832         DIK_keymap[DIK_OEM_102] = SDLK_LESS;
833         DIK_keymap[DIK_Z] = SDLK_z;
834         DIK_keymap[DIK_X] = SDLK_x;
835         DIK_keymap[DIK_C] = SDLK_c;
836         DIK_keymap[DIK_V] = SDLK_v;
837         DIK_keymap[DIK_B] = SDLK_b;
838         DIK_keymap[DIK_N] = SDLK_n;
839         DIK_keymap[DIK_M] = SDLK_m;
840         DIK_keymap[DIK_COMMA] = SDLK_COMMA;
841         DIK_keymap[DIK_PERIOD] = SDLK_PERIOD;
842         DIK_keymap[DIK_SLASH] = SDLK_SLASH;
843         DIK_keymap[DIK_RSHIFT] = SDLK_RSHIFT;
844         DIK_keymap[DIK_MULTIPLY] = SDLK_KP_MULTIPLY;
845         DIK_keymap[DIK_LMENU] = SDLK_LALT;
846         DIK_keymap[DIK_SPACE] = SDLK_SPACE;
847         DIK_keymap[DIK_CAPITAL] = SDLK_CAPSLOCK;
848         DIK_keymap[DIK_F1] = SDLK_F1;
849         DIK_keymap[DIK_F2] = SDLK_F2;
850         DIK_keymap[DIK_F3] = SDLK_F3;
851         DIK_keymap[DIK_F4] = SDLK_F4;
852         DIK_keymap[DIK_F5] = SDLK_F5;
853         DIK_keymap[DIK_F6] = SDLK_F6;
854         DIK_keymap[DIK_F7] = SDLK_F7;
855         DIK_keymap[DIK_F8] = SDLK_F8;
856         DIK_keymap[DIK_F9] = SDLK_F9;
857         DIK_keymap[DIK_F10] = SDLK_F10;
858         DIK_keymap[DIK_NUMLOCK] = SDLK_NUMLOCK;
859         DIK_keymap[DIK_SCROLL] = SDLK_SCROLLOCK;
860         DIK_keymap[DIK_NUMPAD7] = SDLK_KP7;
861         DIK_keymap[DIK_NUMPAD8] = SDLK_KP8;
862         DIK_keymap[DIK_NUMPAD9] = SDLK_KP9;
863         DIK_keymap[DIK_SUBTRACT] = SDLK_KP_MINUS;
864         DIK_keymap[DIK_NUMPAD4] = SDLK_KP4;
865         DIK_keymap[DIK_NUMPAD5] = SDLK_KP5;
866         DIK_keymap[DIK_NUMPAD6] = SDLK_KP6;
867         DIK_keymap[DIK_ADD] = SDLK_KP_PLUS;
868         DIK_keymap[DIK_NUMPAD1] = SDLK_KP1;
869         DIK_keymap[DIK_NUMPAD2] = SDLK_KP2;
870         DIK_keymap[DIK_NUMPAD3] = SDLK_KP3;
871         DIK_keymap[DIK_NUMPAD0] = SDLK_KP0;
872         DIK_keymap[DIK_DECIMAL] = SDLK_KP_PERIOD;
873         DIK_keymap[DIK_F11] = SDLK_F11;
874         DIK_keymap[DIK_F12] = SDLK_F12;
875
876         DIK_keymap[DIK_F13] = SDLK_F13;
877         DIK_keymap[DIK_F14] = SDLK_F14;
878         DIK_keymap[DIK_F15] = SDLK_F15;
879
880         DIK_keymap[DIK_NUMPADEQUALS] = SDLK_KP_EQUALS;
881         DIK_keymap[DIK_NUMPADENTER] = SDLK_KP_ENTER;
882         DIK_keymap[DIK_RCONTROL] = SDLK_RCTRL;
883         DIK_keymap[DIK_DIVIDE] = SDLK_KP_DIVIDE;
884         DIK_keymap[DIK_SYSRQ] = SDLK_PRINT;
885         DIK_keymap[DIK_RMENU] = SDLK_RALT;
886         DIK_keymap[DIK_PAUSE] = SDLK_PAUSE;
887         DIK_keymap[DIK_HOME] = SDLK_HOME;
888         DIK_keymap[DIK_UP] = SDLK_UP;
889         DIK_keymap[DIK_PRIOR] = SDLK_PAGEUP;
890         DIK_keymap[DIK_LEFT] = SDLK_LEFT;
891         DIK_keymap[DIK_RIGHT] = SDLK_RIGHT;
892         DIK_keymap[DIK_END] = SDLK_END;
893         DIK_keymap[DIK_DOWN] = SDLK_DOWN;
894         DIK_keymap[DIK_NEXT] = SDLK_PAGEDOWN;
895         DIK_keymap[DIK_INSERT] = SDLK_INSERT;
896         DIK_keymap[DIK_DELETE] = SDLK_DELETE;
897         DIK_keymap[DIK_LWIN] = SDLK_LMETA;
898         DIK_keymap[DIK_RWIN] = SDLK_RMETA;
899         DIK_keymap[DIK_APPS] = SDLK_MENU;
900 }
901
902 static SDL_keysym *TranslateKey(UINT scancode, SDL_keysym *keysym, int pressed)
903 {
904         /* Set the keysym information */
905         keysym->scancode = (unsigned char)scancode;
906         keysym->sym = DIK_keymap[scancode];
907         keysym->mod = KMOD_NONE;
908         keysym->unicode = 0;
909         if ( pressed && SDL_TranslateUNICODE ) {
910                 UINT vkey;
911 #ifndef NO_GETKEYBOARDSTATE
912                 BYTE    keystate[256];
913                 Uint16  wchars[2];
914 #endif
915
916                 vkey = MapVirtualKey(scancode, 1);
917 #ifdef NO_GETKEYBOARDSTATE
918                 /* Uh oh, better hope the vkey is close enough.. */
919                 keysym->unicode = vkey;
920 #else
921                 GetKeyboardState(keystate);
922                 /* Numlock isn't taken into account in ToUnicode,
923                  * so we handle it as a special case here */
924                 if ((keystate[VK_NUMLOCK] & 1) && vkey >= VK_NUMPAD0 && vkey <= VK_NUMPAD9)
925                 {
926                         keysym->unicode = vkey - VK_NUMPAD0 + '0';
927                 }
928                 else if (SDL_ToUnicode(vkey, scancode, keystate, wchars, sizeof(wchars)/sizeof(wchars[0]), 0) > 0)
929                 {
930                         keysym->unicode = wchars[0];
931                 }
932 #endif /* NO_GETKEYBOARDSTATE */
933         }
934         return(keysym);
935 }
936
937 int DX5_CreateWindow(_THIS)
938 {
939         char *windowid = SDL_getenv("SDL_WINDOWID");
940         int i;
941
942         /* Clear out DirectInput variables in case we fail */
943         for ( i=0; i<MAX_INPUTS; ++i ) {
944                 SDL_DIdev[i] = NULL;
945                 SDL_DIevt[i] = NULL;
946                 SDL_DIfun[i] = NULL;
947         }
948
949         SDL_RegisterApp(NULL, 0, 0);
950
951         SDL_windowid = (windowid != NULL);
952         if ( SDL_windowid ) {
953                 SDL_Window = (HWND)SDL_strtoull(windowid, NULL, 0);
954                 if ( SDL_Window == NULL ) {
955                         SDL_SetError("Couldn't get user specified window");
956                         return(-1);
957                 }
958
959                 /* DJM: we want all event's for the user specified
960                         window to be handled by SDL.
961                  */
962                 userWindowProc = (WNDPROCTYPE)GetWindowLongPtr(SDL_Window, GWLP_WNDPROC);
963                 SetWindowLongPtr(SDL_Window, GWLP_WNDPROC, (LONG_PTR)WinMessage);
964         } else {
965                 SDL_Window = CreateWindow(SDL_Appname, SDL_Appname,
966                         (WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX),
967                         CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, NULL, NULL, SDL_Instance, NULL);
968                 if ( SDL_Window == NULL ) {
969                         SDL_SetError("Couldn't create window");
970                         return(-1);
971                 }
972                 ShowWindow(SDL_Window, SW_HIDE);
973         }
974
975         /* Initialize DirectInput */
976         if ( DX5_DInputInit(this) < 0 ) {
977                 return(-1);
978         }
979
980         /* JC 14 Mar 2006
981                 Flush the message loop or this can cause big problems later
982                 Especially if the user decides to use dialog boxes or assert()!
983         */
984         WIN_FlushMessageQueue();
985
986         /* Ready to roll */
987         return(0);
988 }
989
990 void DX5_DestroyWindow(_THIS)
991 {
992         /* Close down DirectInput */
993         DX5_DInputQuit(this);
994
995         /* Destroy our window */
996         if ( SDL_windowid ) {
997                 SetWindowLongPtr(SDL_Window, GWLP_WNDPROC, (LONG_PTR)userWindowProc);
998         } else {
999                 DestroyWindow(SDL_Window);
1000         }
1001         SDL_UnregisterApp();
1002
1003         /* JC 14 Mar 2006
1004                 Flush the message loop or this can cause big problems later
1005                 Especially if the user decides to use dialog boxes or assert()!
1006         */
1007         WIN_FlushMessageQueue();
1008 }