/* SDL - Simple DirectMedia Layer Copyright (C) 1997-2012 Sam Lantinga This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Sam Lantinga slouken@libsdl.org */ #include "SDL_config.h" /* CAUTION!!!! If you modify this file, check ../windib/SDL_sysevents.c */ #include "directx.h" #include "SDL_main.h" #include "SDL_events.h" #include "SDL_video.h" #include "SDL_syswm.h" #include "../../events/SDL_sysevents.h" #include "../../events/SDL_events_c.h" #include "../wincommon/SDL_lowvideo.h" #include "SDL_dx5video.h" #ifndef WM_APP #define WM_APP 0x8000 #endif #ifdef _WIN32_WCE #define NO_GETKEYBOARDSTATE #endif /* The keyboard and mouse device input */ #define MAX_INPUTS 2 #define INPUT_QSIZE 512 /* Buffer up to 512 input messages */ static LPDIRECTINPUT dinput = NULL; static LPDIRECTINPUTDEVICE2 SDL_DIdev[MAX_INPUTS]; static HANDLE SDL_DIevt[MAX_INPUTS]; static void (*SDL_DIfun[MAX_INPUTS])(const int, DIDEVICEOBJECTDATA *); static int SDL_DIndev = 0; static int mouse_lost; static int mouse_pressed; static int mouse_buttons_swapped = 0; /* The translation table from a DirectInput scancode to an SDL keysym */ static SDLKey DIK_keymap[256]; static SDL_keysym *TranslateKey(UINT scancode, SDL_keysym *keysym, int pressed); /* DJM: If the user setup the window for us, we want to save his window proc, and give him a chance to handle some messages. */ #ifdef STRICT #define WNDPROCTYPE WNDPROC #else #define WNDPROCTYPE FARPROC #endif static WNDPROCTYPE userWindowProc = NULL; static HWND GetTopLevelParent(HWND hWnd) { HWND hParentWnd; while (1) { hParentWnd = GetParent(hWnd); if (hParentWnd == NULL) break; hWnd = hParentWnd; } return hWnd; } /* Convert a DirectInput return code to a text message */ static void SetDIerror(char *function, int code) { static char *error; static char errbuf[1024]; errbuf[0] = 0; switch (code) { case DIERR_GENERIC: error = "Undefined error!"; break; case DIERR_OLDDIRECTINPUTVERSION: error = "Your version of DirectInput needs upgrading"; break; case DIERR_INVALIDPARAM: error = "Invalid parameters"; break; case DIERR_OUTOFMEMORY: error = "Out of memory"; break; case DIERR_DEVICENOTREG: error = "Device not registered"; break; case DIERR_NOINTERFACE: error = "Interface not supported"; break; case DIERR_NOTINITIALIZED: error = "Device not initialized"; break; default: SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: Unknown DirectInput error: 0x%x", function, code); break; } if ( ! errbuf[0] ) { SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: %s", function, error); } SDL_SetError("%s", errbuf); return; } /* Initialize DirectInput Note: If NONEXCLUSIVE access is requested for the devices, normal windows input messages will continue to be generated for that input device, in addition to DirectInput messages. */ static void handle_keyboard(const int numevents, DIDEVICEOBJECTDATA *events); static void handle_mouse(const int numevents, DIDEVICEOBJECTDATA *events); struct { char *name; REFGUID guid; LPCDIDATAFORMAT format; DWORD win_level; DWORD raw_level; void (*fun)(const int numevents, DIDEVICEOBJECTDATA *events); } inputs[] = { { "keyboard", &GUID_SysKeyboard, &c_dfDIKeyboard, (DISCL_FOREGROUND|DISCL_NONEXCLUSIVE), (DISCL_FOREGROUND|DISCL_NONEXCLUSIVE), handle_keyboard }, { "mouse", &GUID_SysMouse, #if DIRECTINPUT_VERSION >= 0x700 &c_dfDIMouse2, #else &c_dfDIMouse, #endif (DISCL_BACKGROUND|DISCL_NONEXCLUSIVE), (DISCL_BACKGROUND|DISCL_NONEXCLUSIVE), handle_mouse }, { NULL, NULL, NULL, 0, 0, NULL } }; static int DX5_DInputInit(_THIS) { int i; LPDIRECTINPUTDEVICE device; HRESULT result; DIPROPDWORD dipdw; HWND topwnd; /* Create the DirectInput object */ result = DInputCreate(SDL_Instance, DIRECTINPUT_VERSION, &dinput, NULL); if ( result != DI_OK ) { SetDIerror("DirectInputCreate", result); return(-1); } /* Create all of our registered input devices */ SDL_DIndev = 0; for ( i=0; inputs[i].name; ++i ) { /* Create the DirectInput device */ result = IDirectInput_CreateDevice(dinput, inputs[i].guid, &device, NULL); if ( result != DI_OK ) { SetDIerror("DirectInput::CreateDevice", result); return(-1); } result = IDirectInputDevice_QueryInterface(device, &IID_IDirectInputDevice2, (LPVOID *)&SDL_DIdev[i]); IDirectInputDevice_Release(device); if ( result != DI_OK ) { SetDIerror("DirectInputDevice::QueryInterface", result); return(-1); } topwnd = GetTopLevelParent(SDL_Window); result = IDirectInputDevice2_SetCooperativeLevel(SDL_DIdev[i], topwnd, inputs[i].win_level); if ( result != DI_OK ) { SetDIerror("DirectInputDevice::SetCooperativeLevel", result); return(-1); } result = IDirectInputDevice2_SetDataFormat(SDL_DIdev[i], inputs[i].format); if ( result != DI_OK ) { SetDIerror("DirectInputDevice::SetDataFormat", result); return(-1); } /* Set buffered input -- we aren't polling */ SDL_memset(&dipdw, 0, sizeof(dipdw)); dipdw.diph.dwSize = sizeof(dipdw); dipdw.diph.dwHeaderSize = sizeof(dipdw.diph); dipdw.diph.dwObj = 0; dipdw.diph.dwHow = DIPH_DEVICE; dipdw.dwData = INPUT_QSIZE; result = IDirectInputDevice2_SetProperty(SDL_DIdev[i], DIPROP_BUFFERSIZE, &dipdw.diph); if ( result != DI_OK ) { SetDIerror("DirectInputDevice::SetProperty", result); return(-1); } /* Create an event to be signaled when input is ready */ SDL_DIevt[i] = CreateEvent(NULL, FALSE, FALSE, NULL); if ( SDL_DIevt[i] == NULL ) { SDL_SetError("Couldn't create DirectInput event"); return(-1); } result = IDirectInputDevice2_SetEventNotification(SDL_DIdev[i], SDL_DIevt[i]); if ( result != DI_OK ) { SetDIerror("DirectInputDevice::SetEventNotification", result); return(-1); } SDL_DIfun[i] = inputs[i].fun; /* Acquire the device for input */ IDirectInputDevice2_Acquire(SDL_DIdev[i]); /* Increment the number of devices we have */ ++SDL_DIndev; } mouse_pressed = 0; mouse_buttons_swapped = GetSystemMetrics(SM_SWAPBUTTON); /* DirectInput is ready! */ return(0); } /* Clean up DirectInput */ static void DX5_DInputQuit(_THIS) { int i; if ( dinput != NULL ) { /* Close and release all DirectInput devices */ for ( i=0; iw/2); center.y = (SDL_VideoSurface->h/2); ClientToScreen(SDL_Window, ¢er); SetCursorPos(center.x, center.y); } } } static void handle_mouse(const int numevents, DIDEVICEOBJECTDATA *ptrbuf) { int i; Sint16 xrel, yrel; Uint8 state; Uint8 button; DWORD timestamp = 0; /* Sanity check. Mailing list reports this being NULL unexpectedly. */ if (SDL_PublicSurface == NULL) { return; } /* If mouse focus has been lost, make sure we release the cursor. */ if ( !(SDL_GetAppState() & SDL_APPMOUSEFOCUS) ) { mouse_lost = 1; ClipCursor(NULL); } else { /* If the mouse was lost, regain some sense of mouse state */ if ( mouse_lost ) { POINT mouse_pos; Uint8 old_state; Uint8 new_state; /* Set ourselves up with the current cursor position */ GetCursorPos(&mouse_pos); ScreenToClient(SDL_Window, &mouse_pos); post_mouse_motion( 0, (Sint16)mouse_pos.x, (Sint16)mouse_pos.y); /* Check for mouse button changes */ old_state = SDL_GetMouseState(NULL, NULL); new_state = 0; { /* Get the new DirectInput button state for the mouse */ #if DIRECTINPUT_VERSION >= 0x700 DIMOUSESTATE2 distate; #else DIMOUSESTATE distate; #endif HRESULT result; result=IDirectInputDevice2_GetDeviceState(SDL_DIdev[1], sizeof(distate), &distate); if ( result != DI_OK ) { /* Try again next time */ SetDIerror( "IDirectInputDevice2::GetDeviceState", result); return; } for ( i=3; i>=0; --i ) { if ( (distate.rgbButtons[i]&0x80) == 0x80 ) { new_state |= 0x01; } new_state <<= 1; } } for ( i=0; i<8; ++i ) { if ( (old_state&0x01) != (new_state&0x01) ) { button = (Uint8)(i+1); /* Map DI button numbers to SDL */ switch ( button ) { case 2: button = SDL_BUTTON_RIGHT; break; case 3: button = SDL_BUTTON_MIDDLE; break; case 4: button = SDL_BUTTON_X1; break; case 5: button = SDL_BUTTON_X2; break; default: break; } if ( new_state & 0x01 ) { /* Grab mouse so we get mouse-up */ if ( ++mouse_pressed > 0 ) { SetCapture(SDL_Window); } state = SDL_PRESSED; } else { /* Release mouse after all mouse-ups */ if ( --mouse_pressed <= 0 ) { ReleaseCapture(); mouse_pressed = 0; } state = SDL_RELEASED; } if ( mouse_buttons_swapped ) { if ( button == 1 ) button = 3; else if ( button == 3 ) button = 1; } posted = SDL_PrivateMouseButton(state, button, 0, 0); } old_state >>= 1; new_state >>= 1; } mouse_lost = 0; return; } /* Translate mouse messages */ xrel = 0; yrel = 0; for ( i=0; i<(int)numevents; ++i ) { switch (ptrbuf[i].dwOfs) { case DIMOFS_X: if ( timestamp != ptrbuf[i].dwTimeStamp ) { if ( xrel || yrel ) { post_mouse_motion(1, xrel, yrel); xrel = 0; yrel = 0; } timestamp = ptrbuf[i].dwTimeStamp; } xrel += (Sint16)ptrbuf[i].dwData; break; case DIMOFS_Y: if ( timestamp != ptrbuf[i].dwTimeStamp ) { if ( xrel || yrel ) { post_mouse_motion(1, xrel, yrel); xrel = 0; yrel = 0; } timestamp = ptrbuf[i].dwTimeStamp; } yrel += (Sint16)ptrbuf[i].dwData; break; case DIMOFS_Z: if ( xrel || yrel ) { post_mouse_motion(1, xrel, yrel); xrel = 0; yrel = 0; } timestamp = 0; if((int)ptrbuf[i].dwData > 0) button = SDL_BUTTON_WHEELUP; else button = SDL_BUTTON_WHEELDOWN; posted = SDL_PrivateMouseButton( SDL_PRESSED, button, 0, 0); posted |= SDL_PrivateMouseButton( SDL_RELEASED, button, 0, 0); break; case DIMOFS_BUTTON0: case DIMOFS_BUTTON1: case DIMOFS_BUTTON2: case DIMOFS_BUTTON3: #if DIRECTINPUT_VERSION >= 0x700 case DIMOFS_BUTTON4: case DIMOFS_BUTTON5: case DIMOFS_BUTTON6: case DIMOFS_BUTTON7: #endif if ( xrel || yrel ) { post_mouse_motion(1, xrel, yrel); xrel = 0; yrel = 0; } timestamp = 0; button = (Uint8)(ptrbuf[i].dwOfs-DIMOFS_BUTTON0)+1; /* Map DI button numbers to SDL */ switch ( button ) { case 2: button = SDL_BUTTON_RIGHT; break; case 3: button = SDL_BUTTON_MIDDLE; break; case 4: button = SDL_BUTTON_X1; break; case 5: button = SDL_BUTTON_X2; break; default: break; } if ( ptrbuf[i].dwData & 0x80 ) { /* Grab mouse so we get mouse-up */ if ( ++mouse_pressed > 0 ) { SetCapture(SDL_Window); } state = SDL_PRESSED; } else { /* Release mouse after all mouse-ups */ if ( --mouse_pressed <= 0 ) { ReleaseCapture(); mouse_pressed = 0; } state = SDL_RELEASED; } if ( mouse_buttons_swapped ) { if ( button == 1 ) button = 3; else if ( button == 3 ) button = 1; } posted = SDL_PrivateMouseButton(state, button, 0, 0); break; } } if ( xrel || yrel ) { post_mouse_motion(1, xrel, yrel); } } } /* The main Win32 event handler */ LRESULT DX5_HandleMessage(_THIS, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { #ifdef WM_ACTIVATEAPP case WM_ACTIVATEAPP: { int i, active; active = (wParam && (GetForegroundWindow() == hwnd)); if ( active ) { for ( i=0; i 0 ) { DispatchMessage(&msg); } else { return(-1); } } if ( posted ) { return(1); } /* Pump the DirectInput flow */ if ( SDL_GetAppState() & SDL_APPMOUSEFOCUS ) { for ( i=0; i= WAIT_OBJECT_0) && (event < (WAIT_OBJECT_0+SDL_DIndev))) { DWORD numevents; static DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE]; event -= WAIT_OBJECT_0; numevents = INPUT_QSIZE; result = IDirectInputDevice2_GetDeviceData( SDL_DIdev[event], sizeof(DIDEVICEOBJECTDATA), evtbuf, &numevents, 0); if ( (result == DIERR_INPUTLOST) || (result == DIERR_NOTACQUIRED) ) { if ( SDL_strcmp(inputs[event].name, "mouse") == 0 ) { mouse_lost = 1; } IDirectInputDevice2_Acquire(SDL_DIdev[event]); result = IDirectInputDevice2_GetDeviceData( SDL_DIdev[event], sizeof(DIDEVICEOBJECTDATA), evtbuf, &numevents, 0); } /* Handle the events */ if ( result == DI_OK && processInput ) { /* Note: This can post multiple events to event queue */ (*SDL_DIfun[event])((int)numevents, evtbuf); return(1); } } if ( event != WAIT_TIMEOUT ) { /* Maybe there was a windows message? */ if ( PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) ) { if ( GetMessage(&msg, NULL, 0, 0) > 0 ) { DispatchMessage(&msg); } else { return(-1); } return(1); } } return(0); } /* Change cooperative level based on whether or not we are fullscreen */ void DX5_DInputReset(_THIS, int fullscreen) { DWORD level; int i; HRESULT result; HWND topwnd; for ( i=0; i 0 ) { /* Loop and check again */; } } void DX5_InitOSKeymap(_THIS) { #ifndef DIK_PAUSE #define DIK_PAUSE 0xC5 #endif #ifndef DIK_OEM_102 #define DIK_OEM_102 0x56 /* < > | on UK/Germany keyboards */ #endif int i; /* Map the DIK scancodes to SDL keysyms */ for ( i=0; iscancode = (unsigned char)scancode; keysym->sym = DIK_keymap[scancode]; keysym->mod = KMOD_NONE; keysym->unicode = 0; if ( pressed && SDL_TranslateUNICODE ) { UINT vkey; #ifndef NO_GETKEYBOARDSTATE BYTE keystate[256]; Uint16 wchars[2]; #endif vkey = MapVirtualKey(scancode, 1); #ifdef NO_GETKEYBOARDSTATE /* Uh oh, better hope the vkey is close enough.. */ keysym->unicode = vkey; #else GetKeyboardState(keystate); /* Numlock isn't taken into account in ToUnicode, * so we handle it as a special case here */ if ((keystate[VK_NUMLOCK] & 1) && vkey >= VK_NUMPAD0 && vkey <= VK_NUMPAD9) { keysym->unicode = vkey - VK_NUMPAD0 + '0'; } else if (SDL_ToUnicode(vkey, scancode, keystate, wchars, sizeof(wchars)/sizeof(wchars[0]), 0) > 0) { keysym->unicode = wchars[0]; } #endif /* NO_GETKEYBOARDSTATE */ } return(keysym); } int DX5_CreateWindow(_THIS) { char *windowid = SDL_getenv("SDL_WINDOWID"); int i; /* Clear out DirectInput variables in case we fail */ for ( i=0; i