// ****************************************************************** // * // * .,-::::: .,:: .::::::::. .,:: .: // * ,;;;'````' `;;;, .,;; ;;;'';;' `;;;, .,;; // * [[[ '[[,,[[' [[[__[[\. '[[,,[[' // * $$$ Y$$$P $$""""Y$$ Y$$$P // * `88bo,__,o, oP"``"Yo, _88o,,od8P oP"``"Yo, // * "YUMMMMMP",m" "Mm,""YUMMMP" ,m" "Mm, // * // * Cxbx->Win32->XBController.cpp // * // * This file is part of the Cxbx project. // * // * Cxbx and Cxbe are free software; you can redistribute them // * and/or modify them under the terms of the GNU General Public // * License as published by the Free Software Foundation; either // * version 2 of the license, or (at your option) any later version. // * // * This program 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 recieved a copy of the GNU General Public License // * along with this program; see the file COPYING. // * If not, write to the Free Software Foundation, Inc., // * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. // * // * (c) 2002-2003 Aaron Robinson // * // * All rights reserved // * // ****************************************************************** #include "XBController.h" #include #include "EmuDInput.h" // ****************************************************************** // * func: XBController::XBController // ****************************************************************** XBController::XBController() { m_CurrentState = XBCTRL_STATE_NONE; int v=0; for(v=0;v=0;v--) { // ****************************************************************** // * Poll the current device // ****************************************************************** { HRESULT hRet = m_InputDevice[v].m_Device->Poll(); if(FAILED(hRet)) { hRet = m_InputDevice[v].m_Device->Acquire(); while(hRet == DIERR_INPUTLOST) hRet = m_InputDevice[v].m_Device->Acquire(); } } DWORD dwHow = -1, dwFlags = m_InputDevice[v].m_Flags; // ****************************************************************** // * Detect Joystick Input // ****************************************************************** if(m_InputDevice[v].m_Flags & DEVICE_FLAG_JOYSTICK) { DIJOYSTATE JoyState; // ****************************************************************** // * Get Joystick State // ****************************************************************** { HRESULT hRet = m_InputDevice[v].m_Device->GetDeviceState(sizeof(DIJOYSTATE), &JoyState); if(FAILED(hRet)) continue; } dwFlags = DEVICE_FLAG_JOYSTICK; if(abs(JoyState.lX) > DETECT_SENSITIVITY_JOYSTICK) { dwHow = FIELD_OFFSET(DIJOYSTATE, lX); dwFlags |= (JoyState.lX > 0) ? (DEVICE_FLAG_AXIS | DEVICE_FLAG_POSITIVE) : (DEVICE_FLAG_AXIS | DEVICE_FLAG_NEGATIVE); } else if(abs(JoyState.lY) > DETECT_SENSITIVITY_JOYSTICK) { dwHow = FIELD_OFFSET(DIJOYSTATE, lY); dwFlags |= (JoyState.lY > 0) ? (DEVICE_FLAG_AXIS | DEVICE_FLAG_POSITIVE) : (DEVICE_FLAG_AXIS | DEVICE_FLAG_NEGATIVE); } else if(abs(JoyState.lZ) > DETECT_SENSITIVITY_JOYSTICK) { dwHow = FIELD_OFFSET(DIJOYSTATE, lZ); dwFlags |= (JoyState.lZ > 0) ? (DEVICE_FLAG_AXIS | DEVICE_FLAG_POSITIVE) : (DEVICE_FLAG_AXIS | DEVICE_FLAG_NEGATIVE); } else if(abs(JoyState.lRx) > DETECT_SENSITIVITY_JOYSTICK) { dwHow = FIELD_OFFSET(DIJOYSTATE, lRx); dwFlags |= (JoyState.lRx > 0) ? (DEVICE_FLAG_AXIS | DEVICE_FLAG_POSITIVE) : (DEVICE_FLAG_AXIS | DEVICE_FLAG_NEGATIVE); } else if(abs(JoyState.lRy) > DETECT_SENSITIVITY_JOYSTICK) { dwHow = FIELD_OFFSET(DIJOYSTATE, lRy); dwFlags |= (JoyState.lRy > 0) ? (DEVICE_FLAG_AXIS | DEVICE_FLAG_POSITIVE) : (DEVICE_FLAG_AXIS | DEVICE_FLAG_NEGATIVE); } else if(abs(JoyState.lRz) > DETECT_SENSITIVITY_JOYSTICK) { dwHow = FIELD_OFFSET(DIJOYSTATE, lRz); dwFlags |= (JoyState.lRz > 0) ? (DEVICE_FLAG_AXIS | DEVICE_FLAG_POSITIVE) : (DEVICE_FLAG_AXIS | DEVICE_FLAG_NEGATIVE); } else for(int b=0;b<2;b++) if(abs(JoyState.rglSlider[b]) > DETECT_SENSITIVITY_JOYSTICK) dwHow = FIELD_OFFSET(DIJOYSTATE, rglSlider[b]); else for(int b=0;b<4;b++) if(abs(JoyState.rgdwPOV[b]) > DETECT_SENSITIVITY_POV) dwHow = FIELD_OFFSET(DIJOYSTATE, rgdwPOV[b]); else for(int b=0;b<32;b++) { if(JoyState.rgbButtons[b] > DETECT_SENSITIVITY_BUTTON) { dwHow = FIELD_OFFSET(DIJOYSTATE, rgbButtons[b]); dwFlags |= DEVICE_FLAG_BUTTON; } } // ****************************************************************** // * Retrieve Object Info // ****************************************************************** if(dwHow != -1) { char *szDirection = (dwFlags & DEVICE_FLAG_AXIS) ? (dwFlags & DEVICE_FLAG_POSITIVE) ? "Positive " : "Negative " : ""; m_InputDevice[v].m_Device->GetDeviceInfo(&DeviceInstance); m_InputDevice[v].m_Device->GetObjectInfo(&ObjectInstance, dwHow, DIPH_BYOFFSET); Map(CurConfigObject, DeviceInstance.tszInstanceName, dwHow, dwFlags); printf("Cxbx: Detected %s %s on %s\n", szDirection, ObjectInstance.tszName, DeviceInstance.tszInstanceName, ObjectInstance.dwType); sprintf(szStatus, "Success: %s Mapped to '%s%s' on '%s'!", m_DeviceNameLookup[CurConfigObject], szDirection, ObjectInstance.tszName, DeviceInstance.tszInstanceName); return true; } } // ****************************************************************** // * Detect Keyboard Input // ****************************************************************** else if(m_InputDevice[v].m_Flags & DEVICE_FLAG_KEYBOARD) { BYTE KeyState[256]; m_InputDevice[v].m_Device->GetDeviceState(256, KeyState); dwFlags = DEVICE_FLAG_KEYBOARD; // ****************************************************************** // * Check for Mouse Key State Change // ****************************************************************** for(int r=0;r<256;r++) { if(KeyState[r] != 0) { dwHow = r; break; } } // ****************************************************************** // * Check for Success // ****************************************************************** if(dwHow != -1) { Map(CurConfigObject, "SysKeyboard", dwHow, dwFlags); printf("Cxbx: Detected Key %d on SysKeyboard\n", dwHow); sprintf(szStatus, "Success: %s Mapped to Key %d on SysKeyboard", m_DeviceNameLookup[CurConfigObject], dwHow); return true; } } // ****************************************************************** // * Detect Mouse Input // ****************************************************************** else if(m_InputDevice[v].m_Flags & DEVICE_FLAG_MOUSE) { DIMOUSESTATE2 MouseState; m_InputDevice[v].m_Device->GetDeviceState(sizeof(MouseState), &MouseState); dwFlags = DEVICE_FLAG_MOUSE; // ****************************************************************** // * Detect Button State Change // ****************************************************************** for(int r=0;r<8;r++) { // 0x80 is the mask for button push if(MouseState.rgbButtons[r] & 0x80) { dwHow = r; dwFlags |= DEVICE_FLAG_MOUSE_CLICK; break; } } // ****************************************************************** // * Check for Success // ****************************************************************** if(dwHow != -1) { Map(CurConfigObject, "SysMouse", dwHow, dwFlags); printf("Cxbx: Detected Button %d on SysMouse\n", dwHow); sprintf(szStatus, "Success: %s Mapped to Button %d on SysMouse", m_DeviceNameLookup[CurConfigObject], dwHow); return true; } // ****************************************************************** // * Check for Mouse Movement // ****************************************************************** else { LONG lAbsDeltaX=0, lAbsDeltaY=0, lAbsDeltaZ=0; LONG lDeltaX=0, lDeltaY=0, lDeltaZ=0; if(lPrevMouseX == -1 || lPrevMouseY == -1 || lPrevMouseZ == -1) lDeltaX = lDeltaY = lDeltaZ = 0; else { lDeltaX = MouseState.lX - lPrevMouseX; lDeltaY = MouseState.lY - lPrevMouseY; lDeltaZ = MouseState.lZ - lPrevMouseZ; lAbsDeltaX = abs(lDeltaX); lAbsDeltaY = abs(lDeltaY); lAbsDeltaZ = abs(lDeltaZ); } LONG lMax = (lAbsDeltaX > lAbsDeltaY) ? lAbsDeltaX : lAbsDeltaY; if(lAbsDeltaZ > lMax) lMax = lAbsDeltaZ; lPrevMouseX = MouseState.lX; lPrevMouseY = MouseState.lY; lPrevMouseZ = MouseState.lZ; if(lMax > DETECT_SENSITIVITY_MOUSE) { dwFlags |= DEVICE_FLAG_AXIS; if(lMax == lAbsDeltaX) { dwHow = DIMOFS_X; dwFlags |= (lDeltaX > 0) ? DEVICE_FLAG_POSITIVE : DEVICE_FLAG_NEGATIVE; } else if(lMax == lAbsDeltaY) { dwHow = DIMOFS_Y; dwFlags |= (lDeltaY > 0) ? DEVICE_FLAG_POSITIVE : DEVICE_FLAG_NEGATIVE; } else if(lMax == lAbsDeltaZ) { dwHow = DIMOFS_Z; dwFlags |= (lDeltaZ > 0) ? DEVICE_FLAG_POSITIVE : DEVICE_FLAG_NEGATIVE; } } // ****************************************************************** // * Check for Success // ****************************************************************** if(dwHow != -1) { char *szDirection = (dwFlags & DEVICE_FLAG_POSITIVE) ? "Positive" : "Negative"; char *szObjName = "Unknown"; ObjectInstance.dwSize = sizeof(ObjectInstance); if(m_InputDevice[v].m_Device->GetObjectInfo(&ObjectInstance, dwHow, DIPH_BYOFFSET) == DI_OK) szObjName = ObjectInstance.tszName; Map(CurConfigObject, "SysMouse", dwHow, dwFlags); printf("Cxbx: Detected Movement on the %s %s on SysMouse\n", szDirection, szObjName); sprintf(szStatus, "Success: %s Mapped to %s %s on SysMouse", m_DeviceNameLookup[CurConfigObject], szDirection, szObjName); return true; } } } } return false; } // ****************************************************************** // * func: XBController::ConfigEnd // ****************************************************************** void XBController::ConfigEnd() { if(m_CurrentState != XBCTRL_STATE_CONFIG) { SetError("Invalid State", false); return; } DInputCleanup(); m_CurrentState = XBCTRL_STATE_NONE; return; } // ****************************************************************** // * func: XBController::ListenBegin // ****************************************************************** void XBController::ListenBegin(HWND hwnd) { int v=0; if(m_CurrentState != XBCTRL_STATE_NONE) { SetError("Invalid State", false); return; } m_CurrentState = XBCTRL_STATE_LISTEN; DInputInit(hwnd); for(v=XBCTRL_MAX_DEVICES-1;v>=m_dwInputDeviceCount;v--) m_DeviceName[v][0] = '\0'; for(v=0;v= m_dwInputDeviceCount) { printf("Warning: Device Mapped to %s was not found!\n", m_DeviceNameLookup[v]); m_ObjectConfig[v].dwDevice = -1; } } return; } // ****************************************************************** // * func: XBController::ListenPoll // ****************************************************************** void XBController::ListenPoll(xapi::XINPUT_STATE *Controller) { LPDIRECTINPUTDEVICE8 pDevice=NULL; HRESULT hRet=0; DWORD dwFlags=0; // ****************************************************************** // * Default values necessary for axis // ****************************************************************** Controller->Gamepad.sThumbLX = 0; Controller->Gamepad.sThumbLY = 0; Controller->Gamepad.sThumbRX = 0; Controller->Gamepad.sThumbRY = 0; // ****************************************************************** // * Poll all devices // ****************************************************************** for(int v=0;vPoll(); if(FAILED(hRet)) { hRet = pDevice->Acquire(); while(hRet == DIERR_INPUTLOST) hRet = pDevice->Acquire(); } SHORT wValue = 0; // ****************************************************************** // * Interpret PC Joystick Input // ****************************************************************** if(dwFlags & DEVICE_FLAG_JOYSTICK) { DIJOYSTATE JoyState; if(pDevice->GetDeviceState(sizeof(JoyState), &JoyState) != DI_OK) continue; if(dwFlags & DEVICE_FLAG_AXIS) { LONG *pdwAxis = (LONG*)((uint32)&JoyState + dwInfo); wValue = (SHORT)(*pdwAxis); if(dwFlags & DEVICE_FLAG_NEGATIVE) { if(wValue < 0) wValue = abs(wValue+1); else wValue = 0; } else if(dwFlags & DEVICE_FLAG_POSITIVE) { if(wValue < 0) wValue = 0; } } else if(dwFlags & DEVICE_FLAG_BUTTON) { BYTE *pbButton = (BYTE*)((uint32)&JoyState + dwInfo); if(*pbButton & 0x80) wValue = 32767; else wValue = 0; } } // ****************************************************************** // * Interpret PC KeyBoard Input // ****************************************************************** else if(dwFlags & DEVICE_FLAG_KEYBOARD) { BYTE KeyboardState[256]; if(pDevice->GetDeviceState(sizeof(KeyboardState), &KeyboardState) != DI_OK) continue; BYTE bKey = KeyboardState[dwInfo]; if(bKey & 0x80) wValue = 32767; else wValue = 0; } // ****************************************************************** // * Map Xbox Joystick Input // ****************************************************************** if(v >= XBCTRL_OBJECT_LTHUMBPOSX && v <= XBCTRL_OBJECT_RTRIGGER) { switch(v) { case XBCTRL_OBJECT_LTHUMBPOSY: Controller->Gamepad.sThumbLY += wValue; break; case XBCTRL_OBJECT_LTHUMBNEGY: Controller->Gamepad.sThumbLY -= wValue; break; case XBCTRL_OBJECT_RTHUMBPOSY: Controller->Gamepad.sThumbRY += wValue; break; case XBCTRL_OBJECT_RTHUMBNEGY: Controller->Gamepad.sThumbRY -= wValue; break; case XBCTRL_OBJECT_LTHUMBPOSX: Controller->Gamepad.sThumbLX += wValue; break; case XBCTRL_OBJECT_LTHUMBNEGX: Controller->Gamepad.sThumbLX -= wValue; break; case XBCTRL_OBJECT_RTHUMBPOSX: Controller->Gamepad.sThumbRX += wValue; break; case XBCTRL_OBJECT_RTHUMBNEGX: Controller->Gamepad.sThumbRX -= wValue; break; case XBCTRL_OBJECT_A: Controller->Gamepad.bAnalogButtons[XINPUT_GAMEPAD_A] = (wValue / 128); break; case XBCTRL_OBJECT_B: Controller->Gamepad.bAnalogButtons[XINPUT_GAMEPAD_B] = (wValue / 128); break; case XBCTRL_OBJECT_X: Controller->Gamepad.bAnalogButtons[XINPUT_GAMEPAD_X] = (wValue / 128); break; case XBCTRL_OBJECT_Y: Controller->Gamepad.bAnalogButtons[XINPUT_GAMEPAD_Y] = (wValue / 128); break; case XBCTRL_OBJECT_WHITE: Controller->Gamepad.bAnalogButtons[XINPUT_GAMEPAD_WHITE] = (wValue / 128); break; case XBCTRL_OBJECT_BLACK: Controller->Gamepad.bAnalogButtons[XINPUT_GAMEPAD_BLACK] = (wValue / 128); break; case XBCTRL_OBJECT_LTRIGGER: Controller->Gamepad.bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER] = (wValue / 128); break; case XBCTRL_OBJECT_RTRIGGER: Controller->Gamepad.bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER] = (wValue / 128); break; } } } return; } // ****************************************************************** // * func: XBController::ListenEnd // ****************************************************************** void XBController::ListenEnd() { if(m_CurrentState != XBCTRL_STATE_LISTEN) { SetError("Invalid State", false); return; } DInputCleanup(); m_CurrentState = XBCTRL_STATE_NONE; return; } // ****************************************************************** // * func: XBController::DeviceIsUsed // ****************************************************************** bool XBController::DeviceIsUsed(const char *szDeviceName) { for(int v=0;vEnumDevices ( DI8DEVCLASS_GAMECTRL, WrapEnumGameCtrlCallback, this, DIEDFL_ATTACHEDONLY ); if(m_CurrentState == XBCTRL_STATE_CONFIG || DeviceIsUsed("SysKeyboard")) { hRet = m_pDirectInput8->CreateDevice(GUID_SysKeyboard, &m_InputDevice[m_dwInputDeviceCount].m_Device, NULL); if(!FAILED(hRet)) { m_InputDevice[m_dwInputDeviceCount].m_Flags = DEVICE_FLAG_KEYBOARD; m_InputDevice[m_dwInputDeviceCount++].m_Device->SetDataFormat(&c_dfDIKeyboard); } if(m_CurrentState == XBCTRL_STATE_LISTEN) ReorderObjects("SysKeyboard", m_dwInputDeviceCount - 1); } if(m_CurrentState == XBCTRL_STATE_CONFIG || DeviceIsUsed("SysMouse")) { hRet = m_pDirectInput8->CreateDevice(GUID_SysMouse, &m_InputDevice[m_dwInputDeviceCount].m_Device, NULL); if(!FAILED(hRet)) { m_InputDevice[m_dwInputDeviceCount].m_Flags = DEVICE_FLAG_MOUSE; m_InputDevice[m_dwInputDeviceCount++].m_Device->SetDataFormat(&c_dfDIMouse2); } if(m_CurrentState == XBCTRL_STATE_LISTEN) ReorderObjects("SysMouse", m_dwInputDeviceCount - 1); } } // ****************************************************************** // * Set cooperative level and acquire // ****************************************************************** { for(int v=m_dwInputDeviceCount-1;v>=0;v--) { m_InputDevice[v].m_Device->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND); m_InputDevice[v].m_Device->Acquire(); HRESULT hRet = m_InputDevice[v].m_Device->Poll(); if(FAILED(hRet)) { hRet = m_InputDevice[v].m_Device->Acquire(); while(hRet == DIERR_INPUTLOST) hRet = m_InputDevice[v].m_Device->Acquire(); if(hRet != DIERR_INPUTLOST) break; } } } // ****************************************************************** // * Enumerate Controller objects // ****************************************************************** for(m_dwCurObject=0;m_dwCurObjectEnumObjects(WrapEnumObjectsCallback, this, DIDFT_ALL); } // ****************************************************************** // * func: XBController::DInputCleanup // ****************************************************************** void XBController::DInputCleanup() { for(int v=m_dwInputDeviceCount-1;v>=0;v--) { m_InputDevice[v].m_Device->Unacquire(); m_InputDevice[v].m_Device->Release(); m_InputDevice[v].m_Device = 0; } m_dwInputDeviceCount = 0; if(m_pDirectInput8 != 0) { m_pDirectInput8->Release(); m_pDirectInput8 = 0; } return; } // ****************************************************************** // * func: XBController::Map // ****************************************************************** void XBController::Map(XBCtrlObject object, const char *szDeviceName, int dwInfo, int dwFlags) { // Initialize InputMapping instance m_ObjectConfig[object].dwDevice = Insert(szDeviceName); m_ObjectConfig[object].dwInfo = dwInfo; m_ObjectConfig[object].dwFlags = dwFlags; // Purse unused device slots for(int v=0;vtszInstanceName)) return DIENUM_CONTINUE; HRESULT hRet = m_pDirectInput8->CreateDevice(lpddi->guidInstance, &m_InputDevice[m_dwInputDeviceCount].m_Device, NULL); if(!FAILED(hRet)) { m_InputDevice[m_dwInputDeviceCount].m_Flags = DEVICE_FLAG_JOYSTICK; m_InputDevice[m_dwInputDeviceCount++].m_Device->SetDataFormat(&c_dfDIJoystick); if(m_CurrentState == XBCTRL_STATE_LISTEN) ReorderObjects(lpddi->tszInstanceName, m_dwInputDeviceCount - 1); } return DIENUM_CONTINUE; } // ****************************************************************** // * func: XBController::EnumObjectsCallback // ****************************************************************** BOOL XBController::EnumObjectsCallback(LPCDIDEVICEOBJECTINSTANCE lpddoi) { if(lpddoi->dwType & DIDFT_AXIS) { DIPROPRANGE diprg; diprg.diph.dwSize = sizeof(DIPROPRANGE); diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER); diprg.diph.dwHow = DIPH_BYID; diprg.diph.dwObj = lpddoi->dwType; diprg.lMin = 0 - 32768; diprg.lMax = 0 + 32767; HRESULT hRet = m_InputDevice[m_dwCurObject].m_Device->SetProperty(DIPROP_RANGE, &diprg.diph); if(FAILED(hRet)) return DIENUM_STOP; } else if(lpddoi->dwType & DIDFT_BUTTON) { DIPROPRANGE diprg; diprg.diph.dwSize = sizeof(DIPROPRANGE); diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER); diprg.diph.dwHow = DIPH_BYID; diprg.diph.dwObj = lpddoi->dwType; diprg.lMin = 0; diprg.lMax = 255; HRESULT hRet = m_InputDevice[m_dwCurObject].m_Device->SetProperty(DIPROP_RANGE, &diprg.diph); if(FAILED(hRet)) return DIENUM_STOP; } return DIENUM_CONTINUE; } // ****************************************************************** // * func: WrapEnumGameCtrlCallback // ****************************************************************** BOOL CALLBACK WrapEnumGameCtrlCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) { XBController *context = (XBController*)pvRef; return context->EnumGameCtrlCallback(lpddi); } // ****************************************************************** // * func: WrapEnumObjectsCallback // ****************************************************************** BOOL CALLBACK WrapEnumObjectsCallback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef) { XBController *context = (XBController*)pvRef; return context->EnumObjectsCallback(lpddoi); } // ****************************************************************** // * Input Device Name Lookup Table // ****************************************************************** const char *XBController::m_DeviceNameLookup[XBCTRL_OBJECT_COUNT] = { // ****************************************************************** // * Analog Axis // ****************************************************************** "LThumbPosX", "LThumbNegX", "LThumbPosY", "LThumbNegY", "RThumbPosX", "RThumbNegX", "RThumbPosY", "RThumbNegY", // ****************************************************************** // * Analog Buttons // ****************************************************************** "X", "Y", "A", "B", "White", "Black", "LTrigger", "RTrigger", // ****************************************************************** // * Digital Buttons // ****************************************************************** "DPadUp", "DPadDown", "DPadLeft", "DPadRight", "Back", "Start", "LThumb", "RThumb", };