/* SSNES - A frontend for libretro. * Copyright (C) 2010-2012 - Hans-Kristian Arntzen * * SSNES is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * SSNES 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along with SSNES. * If not, see . */ #include "ssnes_dinput.h" #include "SDL.h" #include "SDL_syswm.h" #include "../boolean.h" #include #include #include #include "../gfx/sdlwrap.h" void sdl_dinput_free(sdl_dinput_t *di) { if (di) { for (unsigned i = 0; i < MAX_PLAYERS; i++) { if (di->joypad[i]) { IDirectInputDevice8_Unacquire(di->joypad[i]); IDirectInputDevice8_Release(di->joypad[i]); } } if (di->ctx) IDirectInput8_Release(di->ctx); free(di); } } static BOOL CALLBACK enum_axes_cb(const DIDEVICEOBJECTINSTANCE *inst, void *p) { LPDIRECTINPUTDEVICE8 joypad = (LPDIRECTINPUTDEVICE8)p; DIPROPRANGE range; memset(&range, 0, sizeof(range)); range.diph.dwSize = sizeof(DIPROPRANGE); range.diph.dwHeaderSize = sizeof(DIPROPHEADER); range.diph.dwHow = DIPH_BYID; range.diph.dwObj = inst->dwType; range.lMin = -32768; range.lMax = 32767; IDirectInputDevice8_SetProperty(joypad, DIPROP_RANGE, &range.diph); return DIENUM_CONTINUE; } static BOOL CALLBACK enum_joypad_cb(const DIDEVICEINSTANCE *inst, void *p) { sdl_dinput_t *di = (sdl_dinput_t*)p; unsigned active = 0; unsigned n; for (n = 0; n < MAX_PLAYERS; n++) { if (!di->joypad[n] && g_settings.input.joypad_map[n] == (int)di->joypad_cnt) break; if (di->joypad[n]) active++; } if (active == MAX_PLAYERS) return DIENUM_STOP; di->joypad_cnt++; if (n == MAX_PLAYERS) return DIENUM_CONTINUE; #ifdef __cplusplus if (FAILED(IDirectInput8_CreateDevice(di->ctx, inst->guidInstance, &di->joypad[n], NULL))) #else if (FAILED(IDirectInput8_CreateDevice(di->ctx, &inst->guidInstance, &di->joypad[n], NULL))) #endif return DIENUM_CONTINUE; IDirectInputDevice8_SetDataFormat(di->joypad[n], &c_dfDIJoystick2); IDirectInputDevice8_SetCooperativeLevel(di->joypad[n], di->hWnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); IDirectInputDevice8_EnumObjects(di->joypad[n], enum_axes_cb, di->joypad[n], DIDFT_ABSAXIS); return DIENUM_CONTINUE; } sdl_dinput_t* sdl_dinput_init(void) { sdl_dinput_t *di = (sdl_dinput_t*)calloc(1, sizeof(*di)); if (!di) return NULL; CoInitialize(NULL); SDL_SysWMinfo info; SDL_VERSION(&info.version); if (!sdlwrap_get_wm_info(&info)) { SSNES_ERR("Failed to get SysWM info.\n"); goto error; } #if SDL_MODERN di->hWnd = info.info.win.window; #else di->hWnd = info.window; #endif #ifdef __cplusplus if (FAILED(DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&di->ctx, NULL))) #else if (FAILED(DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8, (void**)&di->ctx, NULL))) #endif { SSNES_ERR("Failed to init DirectInput.\n"); goto error; } SSNES_LOG("Enumerating DInput devices ...\n"); IDirectInput8_EnumDevices(di->ctx, DI8DEVCLASS_GAMECTRL, enum_joypad_cb, di, DIEDFL_ATTACHEDONLY); SSNES_LOG("Done enumerating DInput devices ...\n"); return di; error: sdl_dinput_free(di); return NULL; } static bool dinput_joykey_pressed(sdl_dinput_t *di, unsigned port_num, uint16_t joykey) { if (joykey == NO_BTN) return false; // Check hat. if (GET_HAT_DIR(joykey)) { unsigned hat = GET_HAT(joykey); unsigned elems = sizeof(di->joy_state[0].rgdwPOV) / sizeof(di->joy_state[0].rgdwPOV[0]); if (hat >= elems) return false; unsigned pov = di->joy_state[port_num].rgdwPOV[hat]; // Magic numbers I'm not sure where originate from. if (pov < 36000) { switch (GET_HAT_DIR(joykey)) { case HAT_UP_MASK: return (pov >= 31500) || (pov <= 4500); case HAT_RIGHT_MASK: return (pov >= 4500) && (pov <= 13500); case HAT_DOWN_MASK: return (pov >= 13500) && (pov <= 22500); case HAT_LEFT_MASK: return (pov >= 22500) && (pov <= 31500); } } return false; } else { unsigned elems = sizeof(di->joy_state[0].rgbButtons) / sizeof(di->joy_state[0].rgbButtons[0]); if (joykey < elems) return di->joy_state[port_num].rgbButtons[joykey]; } return false; } static bool dinput_joyaxis_pressed(sdl_dinput_t *di, unsigned port_num, uint32_t joyaxis) { if (joyaxis == AXIS_NONE) return false; int min = -32678 * g_settings.input.axis_threshold; int max = 32677 * g_settings.input.axis_threshold; switch (AXIS_NEG_GET(joyaxis)) { case 0: return di->joy_state[port_num].lX <= min; case 1: return di->joy_state[port_num].lY <= min; case 2: return di->joy_state[port_num].lZ <= min; case 3: return di->joy_state[port_num].lRx <= min; case 4: return di->joy_state[port_num].lRy <= min; case 5: return di->joy_state[port_num].lRz <= min; } switch (AXIS_POS_GET(joyaxis)) { case 0: return di->joy_state[port_num].lX >= max; case 1: return di->joy_state[port_num].lY >= max; case 2: return di->joy_state[port_num].lZ >= max; case 3: return di->joy_state[port_num].lRx >= max; case 4: return di->joy_state[port_num].lRy >= max; case 5: return di->joy_state[port_num].lRz >= max; } return false; } bool sdl_dinput_pressed(sdl_dinput_t *di, unsigned port_num, const struct snes_keybind *key) { if (di->joypad[port_num] == NULL) return false; if (dinput_joykey_pressed(di, port_num, key->joykey)) return true; if (dinput_joyaxis_pressed(di, port_num, key->joyaxis)) return true; return false; } void sdl_dinput_poll(sdl_dinput_t *di) { memset(di->joy_state, 0, sizeof(di->joy_state)); for (unsigned i = 0; i < MAX_PLAYERS; i++) { if (di->joypad[i]) { if (FAILED(IDirectInputDevice8_Poll(di->joypad[i]))) { IDirectInputDevice8_Acquire(di->joypad[i]); continue; } IDirectInputDevice8_GetDeviceState(di->joypad[i], sizeof(DIJOYSTATE2), &di->joy_state[i]); } } }