diff options
| author | Jasper St. Pierre <jstpierre@mecheye.net> | 2013-12-07 15:14:29 -0500 |
|---|---|---|
| committer | Jasper St. Pierre <jstpierre@mecheye.net> | 2013-12-31 14:03:19 -0500 |
| commit | 34692ab826abc8f8faa61bdb2280b742424528f1 (patch) | |
| tree | b0eedc645badbea316ff59c125eaf808b28777ec /Source/Core/InputCommon/ControllerInterface | |
| parent | 43e618682e8093602fc83dfa902ee7ae3fd3a4f6 (diff) | |
Remove unnecessary Src/ folders
Diffstat (limited to 'Source/Core/InputCommon/ControllerInterface')
29 files changed, 5598 insertions, 0 deletions
diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp new file mode 100644 index 0000000000..5213b196d1 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp @@ -0,0 +1,95 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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 Foundation, version 2.0. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Android.h" + +namespace ciface +{ + +namespace Android +{ + +void Init( std::vector<Core::Device*>& devices ) +{ + devices.push_back(new Touchscreen(0)); + devices.push_back(new Touchscreen(1)); + devices.push_back(new Touchscreen(2)); + devices.push_back(new Touchscreen(3)); +} + +// Touchscreens and stuff +std::string Touchscreen::GetName() const +{ + return "Touchscreen"; +} + +std::string Touchscreen::GetSource() const +{ + return "Android"; +} + +int Touchscreen::GetId() const +{ + return 0; +} +Touchscreen::Touchscreen(int padID) + : _padID(padID) +{ + AddInput(new Button(_padID, ButtonManager::BUTTON_A)); + AddInput(new Button(_padID, ButtonManager::BUTTON_B)); + AddInput(new Button(_padID, ButtonManager::BUTTON_START)); + AddInput(new Button(_padID, ButtonManager::BUTTON_X)); + AddInput(new Button(_padID, ButtonManager::BUTTON_Y)); + AddInput(new Button(_padID, ButtonManager::BUTTON_Z)); + AddInput(new Button(_padID, ButtonManager::BUTTON_UP)); + AddInput(new Button(_padID, ButtonManager::BUTTON_DOWN)); + AddInput(new Button(_padID, ButtonManager::BUTTON_LEFT)); + AddInput(new Button(_padID, ButtonManager::BUTTON_RIGHT)); + AddAnalogInputs(new Axis(_padID, ButtonManager::STICK_MAIN_LEFT, -1.0f), new Axis(_padID, ButtonManager::STICK_MAIN_RIGHT)); + AddAnalogInputs(new Axis(_padID, ButtonManager::STICK_MAIN_UP, -1.0f), new Axis(_padID, ButtonManager::STICK_MAIN_DOWN)); + AddAnalogInputs(new Axis(_padID, ButtonManager::STICK_C_UP, -1.0f), new Axis(_padID, ButtonManager::STICK_C_DOWN)); + AddAnalogInputs(new Axis(_padID, ButtonManager::STICK_C_LEFT, -1.0f), new Axis(_padID, ButtonManager::STICK_C_RIGHT)); + AddAnalogInputs(new Axis(_padID, ButtonManager::TRIGGER_L), new Axis(_padID, ButtonManager::TRIGGER_L)); + AddAnalogInputs(new Axis(_padID, ButtonManager::TRIGGER_R), new Axis(_padID, ButtonManager::TRIGGER_R)); +} +// Buttons and stuff + +std::string Touchscreen::Button::GetName() const +{ + std::ostringstream ss; + ss << "Button " << (int)_index; + return ss.str(); +} + +ControlState Touchscreen::Button::GetState() const +{ + return ButtonManager::GetButtonPressed(_padID, _index); +} +std::string Touchscreen::Axis::GetName() const +{ + std::ostringstream ss; + ss << "Axis " << (int)_index; + return ss.str(); +} + +ControlState Touchscreen::Axis::GetState() const +{ + return ButtonManager::GetAxisValue(_padID, _index) * _neg; +} + +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.h b/Source/Core/InputCommon/ControllerInterface/Android/Android.h new file mode 100644 index 0000000000..93e528f013 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.h @@ -0,0 +1,71 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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 Foundation, version 2.0. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#ifndef _CIFACE_ANDROID_H_ +#define _CIFACE_ANDROID_H_ + +#include "../Device.h" +#include "Android/ButtonManager.h" + +namespace ciface +{ +namespace Android +{ + +void Init( std::vector<Core::Device*>& devices ); +class Touchscreen : public Core::Device +{ +private: + class Button : public Input + { + public: + std::string GetName() const; + Button(int padID, ButtonManager::ButtonType index) : _padID(padID), _index(index) {} + ControlState GetState() const; + private: + const int _padID; + const ButtonManager::ButtonType _index; + }; + class Axis : public Input + { + public: + std::string GetName() const; + Axis(int padID, ButtonManager::ButtonType index, float neg = 1.0f) : _padID(padID), _index(index), _neg(neg) {} + ControlState GetState() const; + private: + const int _padID; + const ButtonManager::ButtonType _index; + const float _neg; + }; + +public: + bool UpdateInput() { return true; } + bool UpdateOutput() { return true; } + + Touchscreen(int padID); + ~Touchscreen() {} + + std::string GetName() const; + int GetId() const; + std::string GetSource() const; +private: + const int _padID; +}; + +} +} + +#endif diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp new file mode 100644 index 0000000000..7287c380d5 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp @@ -0,0 +1,316 @@ +#include "ControllerInterface.h" + +#ifdef CIFACE_USE_XINPUT + #include "XInput/XInput.h" +#endif +#ifdef CIFACE_USE_DINPUT + #include "DInput/DInput.h" +#endif +#ifdef CIFACE_USE_XLIB + #include "Xlib/Xlib.h" + #ifdef CIFACE_USE_X11_XINPUT2 + #include "Xlib/XInput2.h" + #endif +#endif +#ifdef CIFACE_USE_OSX + #include "OSX/OSX.h" +#endif +#ifdef CIFACE_USE_SDL + #include "SDL/SDL.h" +#endif +#ifdef CIFACE_USE_ANDROID + #include "Android/Android.h" +#endif + +#include "Thread.h" + +using namespace ciface::ExpressionParser; + +namespace +{ +const float INPUT_DETECT_THRESHOLD = 0.55f; +} + +ControllerInterface g_controller_interface; + +// +// Init +// +// detect devices and inputs outputs / will make refresh function later +// +void ControllerInterface::Initialize() +{ + if (m_is_init) + return; + +#ifdef CIFACE_USE_DINPUT + ciface::DInput::Init(m_devices, (HWND)m_hwnd); +#endif +#ifdef CIFACE_USE_XINPUT + ciface::XInput::Init(m_devices); +#endif +#ifdef CIFACE_USE_XLIB + ciface::Xlib::Init(m_devices, m_hwnd); + #ifdef CIFACE_USE_X11_XINPUT2 + ciface::XInput2::Init(m_devices, m_hwnd); + #endif +#endif +#ifdef CIFACE_USE_OSX + ciface::OSX::Init(m_devices, m_hwnd); +#endif +#ifdef CIFACE_USE_SDL + ciface::SDL::Init(m_devices); +#endif +#ifdef CIFACE_USE_ANDROID + ciface::Android::Init(m_devices); +#endif + + m_is_init = true; +} + +// +// DeInit +// +// remove all devices/ call library cleanup functions +// +void ControllerInterface::Shutdown() +{ + if (false == m_is_init) + return; + + std::vector<Device*>::const_iterator + d = m_devices.begin(), + de = m_devices.end(); + for ( ;d != de; ++d ) + { + std::vector<Device::Output*>::const_iterator + o = (*d)->Outputs().begin(), + oe = (*d)->Outputs().end(); + // set outputs to ZERO before destroying device + for ( ;o!=oe; ++o) + (*o)->SetState(0); + // update output + (*d)->UpdateOutput(); + + //delete device + delete *d; + } + + m_devices.clear(); + +#ifdef CIFACE_USE_XINPUT + ciface::XInput::DeInit(); +#endif +#ifdef CIFACE_USE_DINPUT + // nothing needed +#endif +#ifdef CIFACE_USE_XLIB + // nothing needed +#endif +#ifdef CIFACE_USE_OSX + ciface::OSX::DeInit(); +#endif +#ifdef CIFACE_USE_SDL + // TODO: there seems to be some sort of memory leak with SDL, quit isn't freeing everything up + SDL_Quit(); +#endif +#ifdef CIFACE_USE_ANDROID + // nothing needed +#endif + + m_is_init = false; +} + +// +// SetHwnd +// +// sets the hwnd used for some crap when initializing, use before calling Init +// +void ControllerInterface::SetHwnd( void* const hwnd ) +{ + m_hwnd = hwnd; +} + +// +// UpdateInput +// +// update input for all devices, return true if all devices returned successful +// +bool ControllerInterface::UpdateInput(const bool force) +{ + std::unique_lock<std::recursive_mutex> lk(update_lock, std::defer_lock); + + if (force) + lk.lock(); + else if (!lk.try_lock()) + return false; + + size_t ok_count = 0; + + std::vector<Device*>::const_iterator + d = m_devices.begin(), + e = m_devices.end(); + for ( ;d != e; ++d ) + { + if ((*d)->UpdateInput()) + ++ok_count; + //else + // disabled. it might be causing problems + //(*d)->ClearInputState(); + } + + return (m_devices.size() == ok_count); +} + +// +// UpdateOutput +// +// update output for all devices, return true if all devices returned successful +// +bool ControllerInterface::UpdateOutput(const bool force) +{ + std::unique_lock<std::recursive_mutex> lk(update_lock, std::defer_lock); + + if (force) + lk.lock(); + else if (!lk.try_lock()) + return false; + + size_t ok_count = 0; + + for (auto d = m_devices.cbegin(); d != m_devices.cend(); ++d) + { + if ((*d)->UpdateOutput()) + ++ok_count; + } + + return (m_devices.size() == ok_count); +} + +// +// InputReference :: State +// +// get the state of an input reference +// override function for ControlReference::State ... +// +ControlState ControllerInterface::InputReference::State( const ControlState ignore ) +{ + if (parsed_expression) + return parsed_expression->GetValue() * range; + else + return 0.0f; +} + +// +// OutputReference :: State +// +// set the state of all binded outputs +// overrides ControlReference::State .. combined them so i could make the gui simple / inputs == same as outputs one list +// i was lazy and it works so watever +// +ControlState ControllerInterface::OutputReference::State(const ControlState state) +{ + if (parsed_expression) + parsed_expression->SetValue(state); + return 0.0f; +} + +// +// UpdateReference +// +// updates a controlreference's binded devices/controls +// need to call this to re-parse a control reference's expression after changing it +// +void ControllerInterface::UpdateReference(ControllerInterface::ControlReference* ref + , const DeviceQualifier& default_device) const +{ + delete ref->parsed_expression; + ref->parsed_expression = NULL; + + ControlFinder finder(*this, default_device, ref->is_input); + ref->parse_error = ParseExpression(ref->expression, finder, &ref->parsed_expression); +} + +// +// InputReference :: Detect +// +// wait for input on all binded devices +// supports not detecting inputs that were held down at the time of Detect start, +// which is useful for those crazy flightsticks that have certain buttons that are always held down +// or some crazy axes or something +// upon input, return pointer to detected Control +// else return NULL +// +Device::Control* ControllerInterface::InputReference::Detect(const unsigned int ms, Device* const device) +{ + unsigned int time = 0; + std::vector<bool> states(device->Inputs().size()); + + if (device->Inputs().size() == 0) + return NULL; + + // get starting state of all inputs, + // so we can ignore those that were activated at time of Detect start + std::vector<Device::Input*>::const_iterator + i = device->Inputs().begin(), + e = device->Inputs().end(); + for (std::vector<bool>::iterator state = states.begin(); i != e; ++i) + *state++ = ((*i)->GetState() > (1 - INPUT_DETECT_THRESHOLD)); + + while (time < ms) + { + device->UpdateInput(); + i = device->Inputs().begin(); + for (std::vector<bool>::iterator state = states.begin(); i != e; ++i,++state) + { + // detected an input + if ((*i)->IsDetectable() && (*i)->GetState() > INPUT_DETECT_THRESHOLD) + { + // input was released at some point during Detect call + // return the detected input + if (false == *state) + return *i; + } + else if ((*i)->GetState() < (1 - INPUT_DETECT_THRESHOLD)) + { + *state = false; + } + } + Common::SleepCurrentThread(10); time += 10; + } + + // no input was detected + return NULL; +} + +// +// OutputReference :: Detect +// +// Totally different from the inputReference detect / I have them combined so it was simpler to make the GUI. +// The GUI doesn't know the difference between an input and an output / it's odd but I was lazy and it was easy +// +// set all binded outputs to <range> power for x milliseconds return false +// +Device::Control* ControllerInterface::OutputReference::Detect(const unsigned int ms, Device* const device) +{ + // ignore device + + // don't hang if we don't even have any controls mapped + if (BoundCount() > 0) + { + State(1); + unsigned int slept = 0; + + // this loop is to make stuff like flashing keyboard LEDs work + while (ms > (slept += 10)) + { + // TODO: improve this to update more than just the default device's output + device->UpdateOutput(); + Common::SleepCurrentThread(10); + } + + State(0); + device->UpdateOutput(); + } + return NULL; +} diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h new file mode 100644 index 0000000000..4efb8941b7 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h @@ -0,0 +1,131 @@ +#ifndef _DEVICEINTERFACE_H_ +#define _DEVICEINTERFACE_H_ + +#include <vector> +#include <string> +#include <sstream> +#include <map> +#include <algorithm> + +#include "Common.h" +#include "Thread.h" +#include "ExpressionParser.h" +#include "Device.h" + +// enable disable sources +#ifdef _WIN32 + #define CIFACE_USE_XINPUT + #define CIFACE_USE_DINPUT + #define CIFACE_USE_SDL +#endif +#if defined(HAVE_X11) && HAVE_X11 + #define CIFACE_USE_XLIB + #define CIFACE_USE_SDL + #if defined(HAVE_X11_XINPUT2) && HAVE_X11_XINPUT2 + #define CIFACE_USE_X11_XINPUT2 + #endif +#endif +#if defined(__APPLE__) + #define CIFACE_USE_OSX +#endif +#ifdef ANDROID + #define CIFACE_USE_ANDROID +#endif + +using namespace ciface::Core; + +// +// ControllerInterface +// +// some crazy shit I made to control different device inputs and outputs +// from lots of different sources, hopefully more easily +// +class ControllerInterface : public DeviceContainer +{ +public: + + // + // ControlReference + // + // these are what you create to actually use the inputs, InputReference or OutputReference + // + // after being bound to devices and controls with ControllerInterface::UpdateReference, + // each one can link to multiple devices and controls + // when you change a ControlReference's expression, + // you must use ControllerInterface::UpdateReference on it to rebind controls + // + class ControlReference + { + friend class ControllerInterface; + public: + virtual ControlState State(const ControlState state = 0) = 0; + virtual Device::Control* Detect(const unsigned int ms, Device* const device) = 0; + + ControlState range; + std::string expression; + const bool is_input; + ciface::ExpressionParser::ExpressionParseStatus parse_error; + + virtual ~ControlReference() { + delete parsed_expression; + } + + int BoundCount() { + if (parsed_expression) + return parsed_expression->num_controls; + else + return 0; + } + + protected: + ControlReference(const bool _is_input) : range(1), is_input(_is_input), parsed_expression(NULL) {} + ciface::ExpressionParser::Expression *parsed_expression; + }; + + // + // InputReference + // + // control reference for inputs + // + class InputReference : public ControlReference + { + public: + InputReference() : ControlReference(true) {} + ControlState State(const ControlState state); + Device::Control* Detect(const unsigned int ms, Device* const device); + }; + + // + // OutputReference + // + // control reference for outputs + // + class OutputReference : public ControlReference + { + public: + OutputReference() : ControlReference(false) {} + ControlState State(const ControlState state); + Device::Control* Detect(const unsigned int ms, Device* const device); + }; + + ControllerInterface() : m_is_init(false), m_hwnd(NULL) {} + + void SetHwnd(void* const hwnd); + void Initialize(); + void Shutdown(); + bool IsInit() const { return m_is_init; } + + void UpdateReference(ControlReference* control, const DeviceQualifier& default_device) const; + bool UpdateInput(const bool force = false); + bool UpdateOutput(const bool force = false); + + std::recursive_mutex update_lock; + +private: + bool m_is_init; + void* m_hwnd; +}; + +extern ControllerInterface g_controller_interface; + +#endif diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInput.cpp b/Source/Core/InputCommon/ControllerInterface/DInput/DInput.cpp new file mode 100644 index 0000000000..6b7b492ea9 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInput.cpp @@ -0,0 +1,65 @@ + +#include "DInput.h" + +#include "StringUtil.h" + +#include "DInputJoystick.h" +#include "DInputKeyboardMouse.h" + +#pragma comment(lib, "Dinput8.lib") +#pragma comment(lib, "dxguid.lib") + +namespace ciface +{ +namespace DInput +{ + +//BOOL CALLBACK DIEnumEffectsCallback(LPCDIEFFECTINFO pdei, LPVOID pvRef) +//{ +// ((std::list<DIEFFECTINFO>*)pvRef)->push_back(*pdei); +// return DIENUM_CONTINUE; +//} + +BOOL CALLBACK DIEnumDeviceObjectsCallback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef) +{ + ((std::list<DIDEVICEOBJECTINSTANCE>*)pvRef)->push_back(*lpddoi); + return DIENUM_CONTINUE; +} + +BOOL CALLBACK DIEnumDevicesCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) +{ + ((std::list<DIDEVICEINSTANCE>*)pvRef)->push_back(*lpddi); + return DIENUM_CONTINUE; +} + +std::string GetDeviceName(const LPDIRECTINPUTDEVICE8 device) +{ + DIPROPSTRING str = {}; + str.diph.dwSize = sizeof(str); + str.diph.dwHeaderSize = sizeof(str.diph); + str.diph.dwHow = DIPH_DEVICE; + + std::string result; + if (SUCCEEDED(device->GetProperty(DIPROP_PRODUCTNAME, &str.diph))) + { + result = StripSpaces(UTF16ToUTF8(str.wsz)); + } + + return result; +} + +void Init(std::vector<Core::Device*>& devices, HWND hwnd) +{ + IDirectInput8* idi8; + if (FAILED(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (LPVOID*)&idi8, NULL))) + return; + + InitKeyboardMouse(idi8, devices, hwnd); + InitJoystick(idi8, devices, hwnd); + + idi8->Release(); + +} + +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInput.h b/Source/Core/InputCommon/ControllerInterface/DInput/DInput.h new file mode 100644 index 0000000000..86452c23ab --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInput.h @@ -0,0 +1,31 @@ +#ifndef _CIFACE_DINPUT_H_ +#define _CIFACE_DINPUT_H_ + +#include "../Device.h" + +#define DINPUT_SOURCE_NAME "DInput" + +#define DIRECTINPUT_VERSION 0x0800 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include <Windows.h> +#include <dinput.h> + +#include <list> + +namespace ciface +{ +namespace DInput +{ + +//BOOL CALLBACK DIEnumEffectsCallback(LPCDIEFFECTINFO pdei, LPVOID pvRef); +BOOL CALLBACK DIEnumDeviceObjectsCallback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef); +BOOL CALLBACK DIEnumDevicesCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef); +std::string GetDeviceName(const LPDIRECTINPUTDEVICE8 device); + +void Init(std::vector<Core::Device*>& devices, HWND hwnd); + +} +} + +#endif diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp new file mode 100644 index 0000000000..7211559e93 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp @@ -0,0 +1,600 @@ + +#include "DInputJoystick.h" +#include "DInput.h" + +#include <map> +#include <sstream> +#include <algorithm> + +#include <wbemidl.h> +#include <oleauto.h> + +namespace ciface +{ +namespace DInput +{ + +// template instantiation +template class Joystick::Force<DICONSTANTFORCE>; +template class Joystick::Force<DIRAMPFORCE>; +template class Joystick::Force<DIPERIODIC>; + +static const struct +{ + GUID guid; + const char* name; +} force_type_names[] = +{ + {GUID_ConstantForce, "Constant"}, // DICONSTANTFORCE + {GUID_RampForce, "Ramp"}, // DIRAMPFORCE + {GUID_Square, "Square"}, // DIPERIODIC ... + {GUID_Sine, "Sine"}, + {GUID_Triangle, "Triangle"}, + {GUID_SawtoothUp, "Sawtooth Up"}, + {GUID_SawtoothDown, "Sawtooth Down"}, + //{GUID_Spring, "Spring"}, // DICUSTOMFORCE ... < I think + //{GUID_Damper, "Damper"}, + //{GUID_Inertia, "Inertia"}, + //{GUID_Friction, "Friction"}, +}; + +#define DATA_BUFFER_SIZE 32 + +//----------------------------------------------------------------------------- +// Modified some MSDN code to get all the XInput device GUID.Data1 values in a vector, +// faster than checking all the devices for each DirectInput device, like MSDN says to do +//----------------------------------------------------------------------------- +void GetXInputGUIDS( std::vector<DWORD>& guids ) +{ + +#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } + + IWbemLocator* pIWbemLocator = NULL; + IEnumWbemClassObject* pEnumDevices = NULL; + IWbemClassObject* pDevices[20] = {0}; + IWbemServices* pIWbemServices = NULL; + BSTR bstrNamespace = NULL; + BSTR bstrDeviceID = NULL; + BSTR bstrClassName = NULL; + DWORD uReturned = 0; + VARIANT var; + HRESULT hr; + + // CoInit if needed + hr = CoInitialize(NULL); + bool bCleanupCOM = SUCCEEDED(hr); + + // Create WMI + hr = CoCreateInstance( __uuidof(WbemLocator), + NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IWbemLocator), + (LPVOID*) &pIWbemLocator); + if( FAILED(hr) || pIWbemLocator == NULL ) + goto LCleanup; + + bstrNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );if( bstrNamespace == NULL ) goto LCleanup; + bstrClassName = SysAllocString( L"Win32_PNPEntity" ); if( bstrClassName == NULL ) goto LCleanup; + bstrDeviceID = SysAllocString( L"DeviceID" ); if( bstrDeviceID == NULL ) goto LCleanup; + + // Connect to WMI + hr = pIWbemLocator->ConnectServer( bstrNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pIWbemServices ); + if( FAILED(hr) || pIWbemServices == NULL ) + goto LCleanup; + + // Switch security level to IMPERSONATE. + CoSetProxyBlanket( pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, + RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE ); + + hr = pIWbemServices->CreateInstanceEnum( bstrClassName, 0, NULL, &pEnumDevices ); + if( FAILED(hr) || pEnumDevices == NULL ) + goto LCleanup; + + // Loop over all devices + while( true ) + { + // Get 20 at a time + hr = pEnumDevices->Next( 10000, 20, pDevices, &uReturned ); + if( FAILED(hr) || uReturned == 0 ) + break; + + for( UINT iDevice=0; iDevice<uReturned; ++iDevice ) + { + // For each device, get its device ID + hr = pDevices[iDevice]->Get( bstrDeviceID, 0L, &var, NULL, NULL ); + if( SUCCEEDED( hr ) && var.vt == VT_BSTR && var.bstrVal != NULL ) + { + // Check if the device ID contains "IG_". If it does, then it's an XInput device + // This information can not be found from DirectInput + if( wcsstr( var.bstrVal, L"IG_" ) ) + { + // If it does, then get the VID/PID from var.bstrVal + DWORD dwPid = 0, dwVid = 0; + WCHAR* strVid = wcsstr( var.bstrVal, L"VID_" ); + if( strVid && swscanf( strVid, L"VID_%4X", &dwVid ) != 1 ) + dwVid = 0; + WCHAR* strPid = wcsstr( var.bstrVal, L"PID_" ); + if( strPid && swscanf( strPid, L"PID_%4X", &dwPid ) != 1 ) + dwPid = 0; + + // Compare the VID/PID to the DInput device + DWORD dwVidPid = MAKELONG( dwVid, dwPid ); + guids.push_back( dwVidPid ); + //bIsXinputDevice = true; + } + } + SAFE_RELEASE( pDevices[iDevice] ); + } + } + +LCleanup: + if(bstrNamespace) + SysFreeString(bstrNamespace); + if(bstrDeviceID) + SysFreeString(bstrDeviceID); + if(bstrClassName) + SysFreeString(bstrClassName); + for( UINT iDevice=0; iDevice<20; iDevice++ ) + SAFE_RELEASE( pDevices[iDevice] ); + SAFE_RELEASE( pEnumDevices ); + SAFE_RELEASE( pIWbemLocator ); + SAFE_RELEASE( pIWbemServices ); + + if( bCleanupCOM ) + CoUninitialize(); +} + +void InitJoystick(IDirectInput8* const idi8, std::vector<Core::Device*>& devices, HWND hwnd) +{ + std::list<DIDEVICEINSTANCE> joysticks; + idi8->EnumDevices( DI8DEVCLASS_GAMECTRL, DIEnumDevicesCallback, (LPVOID)&joysticks, DIEDFL_ATTACHEDONLY ); + + // this is used to number the joysticks + // multiple joysticks with the same name shall get unique ids starting at 0 + std::map< std::basic_string<TCHAR>, int> name_counts; + + std::vector<DWORD> xinput_guids; + GetXInputGUIDS( xinput_guids ); + + std::list<DIDEVICEINSTANCE>::iterator + i = joysticks.begin(), + e = joysticks.end(); + for ( ; i!=e; ++i ) + { + // skip XInput Devices + if ( std::find( xinput_guids.begin(), xinput_guids.end(), i->guidProduct.Data1 ) != xinput_guids.end() ) + continue; + + LPDIRECTINPUTDEVICE8 js_device; + if (SUCCEEDED(idi8->CreateDevice(i->guidInstance, &js_device, NULL))) + { + if (SUCCEEDED(js_device->SetDataFormat(&c_dfDIJoystick))) + { + if (FAILED(js_device->SetCooperativeLevel(GetAncestor(hwnd, GA_ROOT), DISCL_BACKGROUND | DISCL_EXCLUSIVE))) + { + //PanicAlert("SetCooperativeLevel(DISCL_EXCLUSIVE) failed!"); + // fall back to non-exclusive mode, with no rumble + if (FAILED(js_device->SetCooperativeLevel(NULL, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE))) + { + //PanicAlert("SetCooperativeLevel failed!"); + js_device->Release(); + continue; + } + } + + Joystick* js = new Joystick(/*&*i, */js_device, name_counts[i->tszInstanceName]++); + // only add if it has some inputs/outputs + if (js->Inputs().size() || js->Outputs().size()) + devices.push_back(js); + else + delete js; + } + else + { + //PanicAlert("SetDataFormat failed!"); + js_device->Release(); + } + } + } +} + +Joystick::Joystick( /*const LPCDIDEVICEINSTANCE lpddi, */const LPDIRECTINPUTDEVICE8 device, const unsigned int index ) + : m_device(device) + , m_index(index) + //, m_name(TStringToString(lpddi->tszInstanceName)) +{ + // seems this needs to be done before GetCapabilities + // polled or buffered data + DIPROPDWORD dipdw; + dipdw.diph.dwSize = sizeof(DIPROPDWORD); + dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = DATA_BUFFER_SIZE; + // set the buffer size, + // if we can't set the property, we can't use buffered data + m_buffered = SUCCEEDED(m_device->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph)); + + // seems this needs to be done after SetProperty of buffer size + m_device->Acquire(); + + // get joystick caps + DIDEVCAPS js_caps; + js_caps.dwSize = sizeof(js_caps); + if (FAILED(m_device->GetCapabilities(&js_caps))) + return; + + // max of 32 buttons and 4 hats / the limit of the data format I am using + js_caps.dwButtons = std::min((DWORD)32, js_caps.dwButtons); + js_caps.dwPOVs = std::min((DWORD)4, js_caps.dwPOVs); + + //m_must_poll = (js_caps.dwFlags & DIDC_POLLEDDATAFORMAT) != 0; + + // buttons + for (u8 i = 0; i != js_caps.dwButtons; ++i) + AddInput(new Button(i, m_state_in.rgbButtons[i])); + + // hats + for (u8 i = 0; i != js_caps.dwPOVs; ++i) + { + // each hat gets 4 input instances associated with it, (up down left right) + for (u8 d = 0; d != 4; ++d) + AddInput(new Hat(i, m_state_in.rgdwPOV[i], d)); + } + + // get up to 6 axes and 2 sliders + DIPROPRANGE range; + range.diph.dwSize = sizeof(range); + range.diph.dwHeaderSize = sizeof(range.diph); + range.diph.dwHow = DIPH_BYOFFSET; + // screw EnumObjects, just go through all the axis offsets and try to GetProperty + // this should be more foolproof, less code, and probably faster + for (unsigned int offset = 0; offset < DIJOFS_BUTTON(0) / sizeof(LONG); ++offset) + { + range.diph.dwObj = offset * sizeof(LONG); + // try to set some nice power of 2 values (128) to match the GameCube controls + range.lMin = -(1 << 7); + range.lMax = (1 << 7); + m_device->SetProperty(DIPROP_RANGE, &range.diph); + // but I guess not all devices support setting range + // so I getproperty right afterward incase it didn't set. + // This also checks that the axis is present + if (SUCCEEDED(m_device->GetProperty(DIPROP_RANGE, &range.diph))) + { + const LONG base = (range.lMin + range.lMax) / 2; + const LONG& ax = (&m_state_in.lX)[offset]; + + // each axis gets a negative and a positive input instance associated with it + AddAnalogInputs(new Axis(offset, ax, base, range.lMin-base), + new Axis(offset, ax, base, range.lMax-base)); + } + } + + // TODO: check for DIDC_FORCEFEEDBACK in devcaps? + + // get supported ff effects + std::list<DIDEVICEOBJECTINSTANCE> objects; + m_device->EnumObjects(DIEnumDeviceObjectsCallback, (LPVOID)&objects, DIDFT_AXIS); + // got some ff axes or something + if ( objects.size() ) + { + // temporary + DWORD rgdwAxes[2] = {DIJOFS_X, DIJOFS_Y}; + LONG rglDirection[2] = {-200, 0}; + + DIEFFECT eff; + ZeroMemory(&eff, sizeof(eff)); + eff.dwSize = sizeof(DIEFFECT); + eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; + eff.dwDuration = INFINITE; // (4 * DI_SECONDS) + eff.dwSamplePeriod = 0; + eff.dwGain = DI_FFNOMINALMAX; + eff.dwTriggerButton = DIEB_NOTRIGGER; + eff.dwTriggerRepeatInterval = 0; + eff.cAxes = std::min((DWORD)1, (DWORD)objects.size()); + eff.rgdwAxes = rgdwAxes; + eff.rglDirection = rglDirection; + + // DIPERIODIC is the largest, so we'll use that + DIPERIODIC f; + eff.lpvTypeSpecificParams = &f; + ZeroMemory(&f, sizeof(f)); + + // doesn't seem needed + //DIENVELOPE env; + //eff.lpEnvelope = &env; + //ZeroMemory(&env, sizeof(env)); + //env.dwSize = sizeof(env); + + for (unsigned int f = 0; f < sizeof(force_type_names)/sizeof(*force_type_names); ++f) + { + // ugly if ladder + if (0 == f) + { + DICONSTANTFORCE diCF = {-10000}; + diCF.lMagnitude = DI_FFNOMINALMAX; + eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + eff.lpvTypeSpecificParams = &diCF; + } + else if (1 == f) + { + eff.cbTypeSpecificParams = sizeof(DIRAMPFORCE); + } + else + { + eff.cbTypeSpecificParams = sizeof(DIPERIODIC); + } + + LPDIRECTINPUTEFFECT pEffect; + if (SUCCEEDED(m_device->CreateEffect(force_type_names[f].guid, &eff, &pEffect, NULL))) + { + m_state_out.push_back(EffectState(pEffect)); + + // ugly if ladder again :/ + if (0 == f) + AddOutput(new ForceConstant(f, m_state_out.back())); + else if (1 == f) + AddOutput(new ForceRamp(f, m_state_out.back())); + else + AddOutput(new ForcePeriodic(f, m_state_out.back())); + } + } + } + + // disable autocentering + if (Outputs().size()) + { + DIPROPDWORD dipdw; + dipdw.diph.dwSize = sizeof( DIPROPDWORD ); + dipdw.diph.dwHeaderSize = sizeof( DIPROPHEADER ); + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = DIPROPAUTOCENTER_OFF; + m_device->SetProperty( DIPROP_AUTOCENTER, &dipdw.diph ); + } + + ClearInputState(); +} + +Joystick::~Joystick() +{ + // release the ff effect iface's + std::list<EffectState>::iterator + i = m_state_out.begin(), + e = m_state_out.end(); + for (; i!=e; ++i) + { + i->iface->Stop(); + i->iface->Unload(); + i->iface->Release(); + } + + m_device->Unacquire(); + m_device->Release(); +} + +void Joystick::ClearInputState() +{ + ZeroMemory(&m_state_in, sizeof(m_state_in)); + // set hats to center + memset( m_state_in.rgdwPOV, 0xFF, sizeof(m_state_in.rgdwPOV) ); +} + +std::string Joystick::GetName() const +{ + return GetDeviceName(m_device); +} + +int Joystick::GetId() const +{ + return m_index; +} + +std::string Joystick::GetSource() const +{ + return DINPUT_SOURCE_NAME; +} + +// update IO + +bool Joystick::UpdateInput() +{ + HRESULT hr = 0; + + // just always poll, + // MSDN says if this isn't needed it doesn't do anything + m_device->Poll(); + + if (m_buffered) + { + DIDEVICEOBJECTDATA evtbuf[DATA_BUFFER_SIZE]; + DWORD numevents = DATA_BUFFER_SIZE; + hr = m_device->GetDeviceData(sizeof(*evtbuf), evtbuf, &numevents, 0); + + if (SUCCEEDED(hr)) + { + for (LPDIDEVICEOBJECTDATA evt = evtbuf; evt != (evtbuf + numevents); ++evt) + { + // all the buttons are at the end of the data format + // they are bytes rather than longs + if (evt->dwOfs < DIJOFS_BUTTON(0)) + *(DWORD*)(((BYTE*)&m_state_in) + evt->dwOfs) = evt->dwData; + else + ((BYTE*)&m_state_in)[evt->dwOfs] = (BYTE)evt->dwData; + } + + // seems like this needs to be done maybe... + if (DI_BUFFEROVERFLOW == hr) + hr = m_device->GetDeviceState(sizeof(m_state_in), &m_state_in); + } + } + else + { + hr = m_device->GetDeviceState(sizeof(m_state_in), &m_state_in); + } + + // try reacquire if input lost + if (DIERR_INPUTLOST == hr || DIERR_NOTACQUIRED == hr) + hr = m_device->Acquire(); + + return SUCCEEDED(hr); +} + +bool Joystick::UpdateOutput() +{ + size_t ok_count = 0; + + DIEFFECT eff; + ZeroMemory(&eff, sizeof(eff)); + eff.dwSize = sizeof(DIEFFECT); + eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; + + std::list<EffectState>::iterator + i = m_state_out.begin(), + e = m_state_out.end(); + for (; i!=e; ++i) + { + if (i->params) + { + if (i->size) + { + eff.cbTypeSpecificParams = i->size; + eff.lpvTypeSpecificParams = i->params; + // set params and start effect + ok_count += SUCCEEDED(i->iface->SetParameters(&eff, DIEP_TYPESPECIFICPARAMS | DIEP_START)); + } + else + { + ok_count += SUCCEEDED(i->iface->Stop()); + } + + i->params = NULL; + } + else + { + ++ok_count; + } + } + + return (m_state_out.size() == ok_count); +} + +// get name + +std::string Joystick::Button::GetName() const +{ + std::ostringstream ss; + ss << "Button " << (int)m_index; + return ss.str(); +} + +std::string Joystick::Axis::GetName() const +{ + std::ostringstream ss; + // axis + if (m_index < 6) + { + ss << "Axis " << (char)('X' + (m_index % 3)); + if (m_index > 2) + ss << 'r'; + } + // slider + else + { + ss << "Slider " << (int)(m_index - 6); + } + + ss << (m_range < 0 ? '-' : '+'); + return ss.str(); +} + +std::string Joystick::Hat::GetName() const +{ + static char tmpstr[] = "Hat . ."; + tmpstr[4] = (char)('0' + m_index); + tmpstr[6] = "NESW"[m_direction]; + return tmpstr; +} + +template <typename P> +std::string Joystick::Force<P>::GetName() const +{ + return force_type_names[m_index].name; +} + +// get / set state + +ControlState Joystick::Axis::GetState() const +{ + return std::max(0.0f, ControlState(m_axis - m_base) / m_range); +} + +ControlState Joystick::Button::GetState() const +{ + return ControlState(m_button > 0); +} + +ControlState Joystick::Hat::GetState() const +{ + // can this func be simplified ? + // hat centered code from MSDN + if (0xFFFF == LOWORD(m_hat)) + return 0; + return (abs((int)(m_hat / 4500 - m_direction * 2 + 8) % 8 - 4) > 2); +} + +void Joystick::ForceConstant::SetState(const ControlState state) +{ + const LONG new_val = LONG(10000 * state); + + LONG &val = params.lMagnitude; + if (val != new_val) + { + val = new_val; + m_state.params = ¶ms; // tells UpdateOutput the state has changed + + // tells UpdateOutput to either start or stop the force + m_state.size = new_val ? sizeof(params) : 0; + } +} + +void Joystick::ForceRamp::SetState(const ControlState state) +{ + const LONG new_val = LONG(10000 * state); + + if (params.lStart != new_val) + { + params.lStart = params.lEnd = new_val; + m_state.params = ¶ms; // tells UpdateOutput the state has changed + + // tells UpdateOutput to either start or stop the force + m_state.size = new_val ? sizeof(params) : 0; + } +} + +void Joystick::ForcePeriodic::SetState(const ControlState state) +{ + const LONG new_val = LONG(10000 * state); + + DWORD &val = params.dwMagnitude; + if (val != new_val) + { + val = new_val; + //params.dwPeriod = 0;//DWORD(0.05 * DI_SECONDS); // zero is working fine for me + + m_state.params = ¶ms; // tells UpdateOutput the state has changed + + // tells UpdateOutput to either start or stop the force + m_state.size = new_val ? sizeof(params) : 0; + } +} + +template <typename P> +Joystick::Force<P>::Force(u8 index, EffectState& state) + : m_index(index), m_state(state) +{ + ZeroMemory(¶ms, sizeof(params)); +} + +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.h b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.h new file mode 100644 index 0000000000..20b38c8a69 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.h @@ -0,0 +1,109 @@ +#ifndef _CIFACE_DINPUT_JOYSTICK_H_ +#define _CIFACE_DINPUT_JOYSTICK_H_ + +#include "../Device.h" + +#define DIRECTINPUT_VERSION 0x0800 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include <Windows.h> +#include <dinput.h> + +#include <list> + +namespace ciface +{ +namespace DInput +{ + +void InitJoystick(IDirectInput8* const idi8, std::vector<Core::Device*>& devices, HWND hwnd); + +class Joystick : public Core::Device +{ +private: + struct EffectState + { + EffectState(LPDIRECTINPUTEFFECT eff) : iface(eff), params(NULL), size(0) {} + + LPDIRECTINPUTEFFECT iface; + void* params; // null when force hasn't changed + u8 size; // zero when force should stop + }; + + class Button : public Input + { + public: + std::string GetName() const; + Button(u8 index, const BYTE& button) : m_index(index), m_button(button) {} + ControlState GetState() const; + private: + const BYTE& m_button; + const u8 m_index; + }; + + class Axis : public Input + { + public: + std::string GetName() const; + Axis(u8 index, const LONG& axis, LONG base, LONG range) : m_index(index), m_axis(axis), m_base(base), m_range(range) {} + ControlState GetState() const; + private: + const LONG& m_axis; + const LONG m_base, m_range; + const u8 m_index; + }; + + class Hat : public Input + { + public: + std::string GetName() const; + Hat(u8 index, const DWORD& hat, u8 direction) : m_index(index), m_hat(hat), m_direction(direction) {} + ControlState GetState() const; + private: + const DWORD& m_hat; + const u8 m_index, m_direction; + }; + + template <typename P> + class Force : public Output + { + public: + std::string GetName() const; + Force(u8 index, EffectState& state); + void SetState(ControlState state); + private: + EffectState& m_state; + P params; + const u8 m_index; + }; + typedef Force<DICONSTANTFORCE> ForceConstant; + typedef Force<DIRAMPFORCE> ForceRamp; + typedef Force<DIPERIODIC> ForcePeriodic; + +public: + bool UpdateInput(); + bool UpdateOutput(); + + void ClearInputState(); + + Joystick(const LPDIRECTINPUTDEVICE8 device, const unsigned int index); + ~Joystick(); + + std::string GetName() const; + int GetId() const; + std::string GetSource() const; + +private: + const LPDIRECTINPUTDEVICE8 m_device; + const unsigned int m_index; + + DIJOYSTATE m_state_in; + std::list<EffectState> m_state_out; + + bool m_buffered; +}; + +} +} + +#endif diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp new file mode 100644 index 0000000000..850ff27281 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp @@ -0,0 +1,311 @@ + +#include "DInputKeyboardMouse.h" +#include "DInput.h" + + // (lower would be more sensitive) user can lower sensitivity by setting range + // seems decent here ( at 8 ), I don't think anyone would need more sensitive than this + // and user can lower it much farther than they would want to with the range +#define MOUSE_AXIS_SENSITIVITY 8 + + // if input hasn't been received for this many ms, mouse input will be skipped + // otherwise it is just some crazy value +#define DROP_INPUT_TIME 250 + +namespace ciface +{ +namespace DInput +{ + +static const struct +{ + const BYTE code; + const char* const name; +} named_keys[] = +{ +#include "NamedKeys.h" +}; + +static const struct +{ + const BYTE code; + const char* const name; +} named_lights[] = +{ + { VK_NUMLOCK, "NUM LOCK" }, + { VK_CAPITAL, "CAPS LOCK" }, + { VK_SCROLL, "SCROLL LOCK" } +}; + +// lil silly +static HWND hwnd; + +void InitKeyboardMouse(IDirectInput8* const idi8, std::vector<Core::Device*>& devices, HWND _hwnd) +{ + hwnd = _hwnd; + + // mouse and keyboard are a combined device, to allow shift+click and stuff + // if that's dumb, I will make a VirtualDevice class that just uses ranges of inputs/outputs from other devices + // so there can be a separated Keyboard and mouse, as well as combined KeyboardMouse + + LPDIRECTINPUTDEVICE8 kb_device = NULL; + LPDIRECTINPUTDEVICE8 mo_device = NULL; + + if (SUCCEEDED(idi8->CreateDevice( GUID_SysKeyboard, &kb_device, NULL))) + { + if (SUCCEEDED(kb_device->SetDataFormat(&c_dfDIKeyboard))) + { + if (SUCCEEDED(kb_device->SetCooperativeLevel(NULL, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE))) + { + if (SUCCEEDED(idi8->CreateDevice( GUID_SysMouse, &mo_device, NULL ))) + { + if (SUCCEEDED(mo_device->SetDataFormat(&c_dfDIMouse2))) + { + if (SUCCEEDED(mo_device->SetCooperativeLevel(NULL, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE))) + { + devices.push_back(new KeyboardMouse(kb_device, mo_device)); + return; + } + } + } + } + } + } + + if (kb_device) + kb_device->Release(); + if (mo_device) + mo_device->Release(); +} + +KeyboardMouse::~KeyboardMouse() +{ + // kb + m_kb_device->Unacquire(); + m_kb_device->Release(); + // mouse + m_mo_device->Unacquire(); + m_mo_device->Release(); +} + +KeyboardMouse::KeyboardMouse(const LPDIRECTINPUTDEVICE8 kb_device, const LPDIRECTINPUTDEVICE8 mo_device) + : m_kb_device(kb_device) + , m_mo_device(mo_device) +{ + m_kb_device->Acquire(); + m_mo_device->Acquire(); + + m_last_update = GetTickCount(); + + ZeroMemory(&m_state_in, sizeof(m_state_in)); + ZeroMemory(m_state_out, sizeof(m_state_out)); + ZeroMemory(&m_current_state_out, sizeof(m_current_state_out)); + + // KEYBOARD + // add keys + for (u8 i = 0; i < sizeof(named_keys)/sizeof(*named_keys); ++i) + AddInput(new Key(i, m_state_in.keyboard[named_keys[i].code])); + // add lights + for (u8 i = 0; i < sizeof(named_lights)/sizeof(*named_lights); ++i) + AddOutput(new Light(i)); + + // MOUSE + // get caps + DIDEVCAPS mouse_caps; + ZeroMemory( &mouse_caps, sizeof(mouse_caps) ); + mouse_caps.dwSize = sizeof(mouse_caps); + m_mo_device->GetCapabilities(&mouse_caps); + // mouse buttons + for (u8 i = 0; i < mouse_caps.dwButtons; ++i) + AddInput(new Button(i, m_state_in.mouse.rgbButtons[i])); + // mouse axes + for (unsigned int i = 0; i < mouse_caps.dwAxes; ++i) + { + const LONG& ax = (&m_state_in.mouse.lX)[i]; + + // each axis gets a negative and a positive input instance associated with it + AddInput(new Axis(i, ax, (2==i) ? -1 : -MOUSE_AXIS_SENSITIVITY)); + AddInput(new Axis(i, ax, -(2==i) ? 1 : MOUSE_AXIS_SENSITIVITY)); + } + // cursor, with a hax for-loop + for (unsigned int i=0; i<4; ++i) + AddInput(new Cursor(!!(i&2), (&m_state_in.cursor.x)[i/2], !!(i&1))); +} + +void GetMousePos(float* const x, float* const y) +{ + unsigned int win_width = 2, win_height = 2; + POINT point = { 1, 1 }; + GetCursorPos(&point); + // Get the cursor position relative to the upper left corner of the rendering window + ScreenToClient(hwnd, &point); + + // Get the size of the rendering window. (In my case Rect.top and Rect.left was zero.) + RECT rect; + GetClientRect(hwnd, &rect); + // Width and height is the size of the rendering window + win_width = rect.right - rect.left; + win_height = rect.bottom - rect.top; + + // Return the mouse position as a range from -1 to 1 + *x = (float)point.x / (float)win_width * 2 - 1; + *y = (float)point.y / (float)win_height * 2 - 1; +} + +bool KeyboardMouse::UpdateInput() +{ + DIMOUSESTATE2 tmp_mouse; + + // if mouse position hasn't been updated in a short while, skip a dev state + DWORD cur_time = GetTickCount(); + if (cur_time - m_last_update > DROP_INPUT_TIME) + { + // set axes to zero + ZeroMemory(&m_state_in.mouse, sizeof(m_state_in.mouse)); + // skip this input state + m_mo_device->GetDeviceState(sizeof(tmp_mouse), &tmp_mouse); + } + + m_last_update = cur_time; + + HRESULT kb_hr = m_kb_device->GetDeviceState(sizeof(m_state_in.keyboard), &m_state_in.keyboard); + HRESULT mo_hr = m_mo_device->GetDeviceState(sizeof(tmp_mouse), &tmp_mouse); + + if (DIERR_INPUTLOST == kb_hr || DIERR_NOTACQUIRED == kb_hr) + m_kb_device->Acquire(); + + if (DIERR_INPUTLOST == mo_hr || DIERR_NOTACQUIRED == mo_hr) + m_mo_device->Acquire(); + + if (SUCCEEDED(kb_hr) && SUCCEEDED(mo_hr)) + { + // need to smooth out the axes, otherwise it doesn't work for shit + for (unsigned int i = 0; i < 3; ++i) + ((&m_state_in.mouse.lX)[i] += (&tmp_mouse.lX)[i]) /= 2; + + // copy over the buttons + memcpy(m_state_in.mouse.rgbButtons, tmp_mouse.rgbButtons, sizeof(m_state_in.mouse.rgbButtons)); + + // update mouse cursor + GetMousePos(&m_state_in.cursor.x, &m_state_in.cursor.y); + + return true; + } + + return false; +} + +bool KeyboardMouse::UpdateOutput() +{ + class KInput : public INPUT + { + public: + KInput( const unsigned char key, const bool up = false ) + { + memset( this, 0, sizeof(*this) ); + type = INPUT_KEYBOARD; + ki.wVk = key; + + if (up) + ki.dwFlags = KEYEVENTF_KEYUP; + } + }; + + std::vector< KInput > kbinputs; + for (unsigned int i = 0; i < sizeof(m_state_out)/sizeof(*m_state_out); ++i) + { + bool want_on = false; + if (m_state_out[i]) + want_on = m_state_out[i] > GetTickCount() % 255 ; // light should flash when output is 0.5 + + // lights are set to their original state when output is zero + if (want_on ^ m_current_state_out[i]) + { + kbinputs.push_back(KInput(named_lights[i].code)); // press + kbinputs.push_back(KInput(named_lights[i].code, true)); // release + + m_current_state_out[i] ^= 1; + } + } + + if (kbinputs.size()) + return ( kbinputs.size() == SendInput( (UINT)kbinputs.size(), &kbinputs[0], sizeof( kbinputs[0] ) ) ); + else + return true; +} + +std::string KeyboardMouse::GetName() const +{ + return "Keyboard Mouse"; +} + +int KeyboardMouse::GetId() const +{ + // should this be -1, idk + return 0; +} + +std::string KeyboardMouse::GetSource() const +{ + return DINPUT_SOURCE_NAME; +} + +// names +std::string KeyboardMouse::Key::GetName() const +{ + return named_keys[m_index].name; +} + +std::string KeyboardMouse::Button::GetName() const +{ + return std::string("Click ") + char('0' + m_index); +} + +std::string KeyboardMouse::Axis::GetName() const +{ + static char tmpstr[] = "Axis .."; + tmpstr[5] = (char)('X' + m_index); + tmpstr[6] = (m_range<0 ? '-' : '+'); + return tmpstr; +} + +std::string KeyboardMouse::Cursor::GetName() const +{ + static char tmpstr[] = "Cursor .."; + tmpstr[7] = (char)('X' + m_index); + tmpstr[8] = (m_positive ? '+' : '-'); + return tmpstr; +} + +std::string KeyboardMouse::Light::GetName() const +{ + return named_lights[m_index].name; +} + +// get/set state +ControlState KeyboardMouse::Key::GetState() const +{ + return (m_key != 0); +} + +ControlState KeyboardMouse::Button::GetState() const +{ + return (m_button != 0); +} + +ControlState KeyboardMouse::Axis::GetState() const +{ + return std::max(0.0f, ControlState(m_axis) / m_range); +} + +ControlState KeyboardMouse::Cursor::GetState() const +{ + return std::max(0.0f, ControlState(m_axis) / (m_positive ? 1.0f : -1.0f)); +} + +void KeyboardMouse::Light::SetState(const ControlState state) +{ + //state_out[m_index] = (unsigned char)(state * 255); +} + +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.h b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.h new file mode 100644 index 0000000000..73398ca1b1 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.h @@ -0,0 +1,113 @@ +#ifndef _CIFACE_DINPUT_KBM_H_ +#define _CIFACE_DINPUT_KBM_H_ + +#include "../Device.h" + +#define DIRECTINPUT_VERSION 0x0800 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include <Windows.h> +#include <dinput.h> + +namespace ciface +{ +namespace DInput +{ + +void InitKeyboardMouse(IDirectInput8* const idi8, std::vector<Core::Device*>& devices, HWND _hwnd); + +class KeyboardMouse : public Core::Device +{ +private: + struct State + { + BYTE keyboard[256]; + DIMOUSESTATE2 mouse; + struct + { + float x, y; + } cursor; + }; + + class Key : public Input + { + public: + std::string GetName() const; + Key(u8 index, const BYTE& key) : m_index(index), m_key(key) {} + ControlState GetState() const; + private: + const BYTE& m_key; + const u8 m_index; + }; + + class Button : public Input + { + public: + std::string GetName() const; + Button(u8 index, const BYTE& button) : m_index(index), m_button(button) {} + ControlState GetState() const; + private: + const BYTE& m_button; + const u8 m_index; + }; + + class Axis : public Input + { + public: + std::string GetName() const; + Axis(u8 index, const LONG& axis, LONG range) : m_index(index), m_axis(axis), m_range(range) {} + ControlState GetState() const; + private: + const LONG& m_axis; + const LONG m_range; + const u8 m_index; + }; + + class Cursor : public Input + { + public: + std::string GetName() const; + bool IsDetectable() { return false; } + Cursor(u8 index, const float& axis, const bool positive) : m_index(index), m_axis(axis), m_positive(positive) {} + ControlState GetState() const; + private: + const float& m_axis; + const u8 m_index; + const bool m_positive; + }; + + class Light : public Output + { + public: + std::string GetName() const; + Light(u8 index) : m_index(index) {} + void SetState(ControlState state); + private: + const u8 m_index; + }; + +public: + bool UpdateInput(); + bool UpdateOutput(); + + KeyboardMouse(const LPDIRECTINPUTDEVICE8 kb_device, const LPDIRECTINPUTDEVICE8 mo_device); + ~KeyboardMouse(); + + std::string GetName() const; + int GetId() const; + std::string GetSource() const; + +private: + const LPDIRECTINPUTDEVICE8 m_kb_device; + const LPDIRECTINPUTDEVICE8 m_mo_device; + + DWORD m_last_update; + State m_state_in; + unsigned char m_state_out[3]; // NUM CAPS SCROLL + bool m_current_state_out[3]; // NUM CAPS SCROLL +}; + +} +} + +#endif diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/NamedKeys.h b/Source/Core/InputCommon/ControllerInterface/DInput/NamedKeys.h new file mode 100644 index 0000000000..43e090dbeb --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/DInput/NamedKeys.h @@ -0,0 +1,144 @@ +{ DIK_A, "A" }, +{ DIK_B, "B" }, +{ DIK_C, "C" }, +{ DIK_D, "D" }, +{ DIK_E, "E" }, +{ DIK_F, "F" }, +{ DIK_G, "G" }, +{ DIK_H, "H" }, +{ DIK_I, "I" }, +{ DIK_J, "J" }, +{ DIK_K, "K" }, +{ DIK_L, "L" }, +{ DIK_M, "M" }, +{ DIK_N, "N" }, +{ DIK_O, "O" }, +{ DIK_P, "P" }, +{ DIK_Q, "Q" }, +{ DIK_R, "R" }, +{ DIK_S, "S" }, +{ DIK_T, "T" }, +{ DIK_U, "U" }, +{ DIK_V, "V" }, +{ DIK_W, "W" }, +{ DIK_X, "X" }, +{ DIK_Y, "Y" }, +{ DIK_Z, "Z" }, +{ DIK_0, "0" }, +{ DIK_1, "1" }, +{ DIK_2, "2" }, +{ DIK_3, "3" }, +{ DIK_4, "4" }, +{ DIK_5, "5" }, +{ DIK_6, "6" }, +{ DIK_7, "7" }, +{ DIK_8, "8" }, +{ DIK_9, "9" }, +{ DIK_UP, "UP" }, +{ DIK_DOWN, "DOWN" }, +{ DIK_LEFT, "LEFT" }, +{ DIK_RIGHT, "RIGHT" }, +{ DIK_ABNT_C1, "ABNT_C1" }, +{ DIK_ABNT_C2, "ABNT_C2" }, +{ DIK_ADD, "ADD" }, +{ DIK_APOSTROPHE, "APOSTROPHE" }, +{ DIK_APPS, "APPS" }, +{ DIK_AT, "AT" }, +{ DIK_AX, "AX" }, +{ DIK_BACK, "BACK" }, +{ DIK_BACKSLASH, "BACKSLASH" }, +{ DIK_CALCULATOR, "CALCULATOR" }, +{ DIK_CAPITAL, "CAPITAL" }, +{ DIK_COLON, "COLON" }, +{ DIK_COMMA, "COMMA" }, +{ DIK_CONVERT, "CONVERT" }, +{ DIK_DECIMAL, "DECIMAL" }, +{ DIK_DELETE, "DELETE" }, +{ DIK_DIVIDE, "DIVIDE" }, +{ DIK_EQUALS, "EQUALS" }, +{ DIK_ESCAPE, "ESCAPE" }, +{ DIK_F1, "F1" }, +{ DIK_F2, "F2" }, +{ DIK_F3, "F3" }, +{ DIK_F4, "F4" }, +{ DIK_F5, "F5" }, +{ DIK_F6, "F6" }, +{ DIK_F7, "F7" }, +{ DIK_F8, "F8" }, +{ DIK_F9, "F9" }, +{ DIK_F10, "F10" }, +{ DIK_F11, "F11" }, +{ DIK_F12, "F12" }, +{ DIK_F13, "F13" }, +{ DIK_F14, "F14" }, +{ DIK_F15, "F15" }, +{ DIK_GRAVE, "GRAVE" }, +{ DIK_HOME, "HOME" }, +{ DIK_END, "END" }, +{ DIK_INSERT, "INSERT" }, +{ DIK_KANA, "KANA" }, +{ DIK_KANJI, "KANJI" }, +{ DIK_MAIL, "MAIL" }, +{ DIK_MEDIASELECT, "MEDIASELECT" }, +{ DIK_MEDIASTOP, "MEDIASTOP" }, +{ DIK_MINUS, "MINUS" }, +{ DIK_MULTIPLY, "MULTIPLY" }, +{ DIK_MUTE, "MUTE" }, +{ DIK_MYCOMPUTER, "MYCOMPUTER" }, +{ DIK_NEXTTRACK, "NEXTTRACK" }, +{ DIK_NOCONVERT, "NOCONVERT" }, +{ DIK_NUMLOCK, "NUMLOCK" }, +{ DIK_NUMPAD0, "NUMPAD0" }, +{ DIK_NUMPAD1, "NUMPAD1" }, +{ DIK_NUMPAD2, "NUMPAD2" }, +{ DIK_NUMPAD3, "NUMPAD3" }, +{ DIK_NUMPAD4, "NUMPAD4" }, +{ DIK_NUMPAD5, "NUMPAD5" }, +{ DIK_NUMPAD6, "NUMPAD6" }, +{ DIK_NUMPAD7, "NUMPAD7" }, +{ DIK_NUMPAD8, "NUMPAD8" }, +{ DIK_NUMPAD9, "NUMPAD9" }, +{ DIK_NUMPADCOMMA, "NUMPADCOMMA" }, +{ DIK_NUMPADENTER, "NUMPADENTER" }, +{ DIK_NUMPADEQUALS, "NUMPADEQUALS" }, +{ DIK_OEM_102, "OEM_102" }, +{ DIK_PAUSE, "PAUSE" }, +{ DIK_PERIOD, "PERIOD" }, +{ DIK_PLAYPAUSE, "PLAYPAUSE" }, +{ DIK_POWER, "POWER" }, +{ DIK_PREVTRACK, "PREVTRACK" }, +{ DIK_PRIOR, "PRIOR" }, +{ DIK_NEXT, "NEXT" }, +{ DIK_RETURN, "RETURN" }, +{ DIK_LBRACKET, "LBRACKET" }, +{ DIK_RBRACKET, "RBRACKET" }, +{ DIK_LCONTROL, "LCONTROL" }, +{ DIK_RCONTROL, "RCONTROL" }, +{ DIK_LMENU, "LMENU" }, +{ DIK_RMENU, "RMENU" }, +{ DIK_LSHIFT, "LSHIFT" }, +{ DIK_RSHIFT, "RSHIFT" }, +{ DIK_LWIN, "LWIN" }, +{ DIK_RWIN, "RWIN" }, +{ DIK_SCROLL, "SCROLL" }, +{ DIK_SEMICOLON, "SEMICOLON" }, +{ DIK_SLASH, "SLASH" }, +{ DIK_SLEEP, "SLEEP" }, +{ DIK_SPACE, "SPACE" }, +{ DIK_STOP, "STOP" }, +{ DIK_SUBTRACT, "SUBTRACT" }, +{ DIK_SYSRQ, "SYSRQ" }, +{ DIK_TAB, "TAB" }, +{ DIK_UNDERLINE, "UNDERLINE" }, +{ DIK_UNLABELED, "UNLABELED" }, +{ DIK_VOLUMEDOWN, "VOLUMEDOWN" }, +{ DIK_VOLUMEUP, "VOLUMEUP" }, +{ DIK_WAKE, "WAKE" }, +{ DIK_WEBBACK, "WEBBACK" }, +{ DIK_WEBFAVORITES, "WEBFAVORITES" }, +{ DIK_WEBFORWARD, "WEBFORWARD" }, +{ DIK_WEBHOME, "WEBHOME" }, +{ DIK_WEBREFRESH, "WEBREFRESH" }, +{ DIK_WEBSEARCH, "WEBSEARCH" }, +{ DIK_WEBSTOP, "WEBSTOP" }, +{ DIK_YEN, "YEN" }, diff --git a/Source/Core/InputCommon/ControllerInterface/Device.cpp b/Source/Core/InputCommon/ControllerInterface/Device.cpp new file mode 100644 index 0000000000..965ecfc817 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/Device.cpp @@ -0,0 +1,193 @@ + +#include "Device.h" + +#include <string> +#include <sstream> + +namespace ciface +{ +namespace Core +{ + +// +// Device :: ~Device +// +// Destructor, delete all inputs/outputs on device destruction +// +Device::~Device() +{ + { + // delete inputs + std::vector<Device::Input*>::iterator + i = m_inputs.begin(), + e = m_inputs.end(); + for ( ;i!=e; ++i) + delete *i; + } + + { + // delete outputs + std::vector<Device::Output*>::iterator + o = m_outputs.begin(), + e = m_outputs.end(); + for ( ;o!=e; ++o) + delete *o; + } +} + +void Device::AddInput(Device::Input* const i) +{ + m_inputs.push_back(i); +} + +void Device::AddOutput(Device::Output* const o) +{ + m_outputs.push_back(o); +} + +Device::Input* Device::FindInput(const std::string &name) const +{ + std::vector<Input*>::const_iterator + it = m_inputs.begin(), + itend = m_inputs.end(); + for (; it != itend; ++it) + if ((*it)->GetName() == name) + return *it; + + return NULL; +} + +Device::Output* Device::FindOutput(const std::string &name) const +{ + std::vector<Output*>::const_iterator + it = m_outputs.begin(), + itend = m_outputs.end(); + for (; it != itend; ++it) + if ((*it)->GetName() == name) + return *it; + + return NULL; +} + +// +// Device :: ClearInputState +// +// Device classes should override this function +// ControllerInterface will call this when the device returns failure during UpdateInput +// used to try to set all buttons and axes to their default state when user unplugs a gamepad during play +// buttons/axes that were held down at the time of unplugging should be seen as not pressed after unplugging +// +void Device::ClearInputState() +{ + // this is going to be called for every UpdateInput call that fails + // kinda slow but, w/e, should only happen when user unplugs a device while playing +} + +// +// DeviceQualifier :: ToString +// +// get string from a device qualifier / serialize +// +std::string DeviceQualifier::ToString() const +{ + if (source.empty() && (cid < 0) && name.empty()) + return ""; + std::ostringstream ss; + ss << source << '/'; + if ( cid > -1 ) + ss << cid; + ss << '/' << name; + return ss.str(); +} + +// +// DeviceQualifier :: FromString +// +// set a device qualifier from a string / unserialize +// +void DeviceQualifier::FromString(const std::string& str) +{ + std::istringstream ss(str); + + std::getline(ss, source = "", '/'); + + // silly + std::getline(ss, name, '/'); + std::istringstream(name) >> (cid = -1); + + std::getline(ss, name = ""); +} + +// +// DeviceQualifier :: FromDevice +// +// set a device qualifier from a device +// +void DeviceQualifier::FromDevice(const Device* const dev) +{ + name = dev->GetName(); + cid = dev->GetId(); + source= dev->GetSource(); +} + +bool DeviceQualifier::operator==(const Device* const dev) const +{ + if (dev->GetId() == cid) + if (dev->GetName() == name) + if (dev->GetSource() == source) + return true; + return false; +} + +bool DeviceQualifier::operator==(const DeviceQualifier& devq) const +{ + if (cid == devq.cid) + if (name == devq.name) + if (source == devq.source) + return true; + + return false; +} + +Device* DeviceContainer::FindDevice(const DeviceQualifier& devq) const +{ + std::vector<Device*>::const_iterator + di = m_devices.begin(), + de = m_devices.end(); + for (; di!=de; ++di) + if (devq == *di) + return *di; + + return NULL; +} + +Device::Input* DeviceContainer::FindInput(const std::string& name, const Device* def_dev) const +{ + if (def_dev) + { + Device::Input* const inp = def_dev->FindInput(name); + if (inp) + return inp; + } + + std::vector<Device*>::const_iterator + di = m_devices.begin(), + de = m_devices.end(); + for (; di != de; ++di) + { + Device::Input* const i = (*di)->FindInput(name); + + if (i) + return i; + } + + return NULL; +} + +Device::Output* DeviceContainer::FindOutput(const std::string& name, const Device* def_dev) const +{ + return def_dev->FindOutput(name); +} + +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/Device.h b/Source/Core/InputCommon/ControllerInterface/Device.h new file mode 100644 index 0000000000..a7c26b867d --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/Device.h @@ -0,0 +1,171 @@ + +#ifndef _DEVICE_H_ +#define _DEVICE_H_ + +#include <string> +#include <vector> + +#include "Common.h" + +// idk in case I wanted to change it to double or something, idk what's best +typedef float ControlState; + +namespace ciface +{ +namespace Core +{ + +// Forward declarations +class DeviceQualifier; + +// +// Device +// +// a device class +// +class Device +{ +public: + class Input; + class Output; + + // + // Control + // + // control includes inputs and outputs + // + class Control // input or output + { + public: + virtual std::string GetName() const = 0; + virtual ~Control() {} + + virtual Input* ToInput() { return NULL; } + virtual Output* ToOutput() { return NULL; } + }; + + // + // Input + // + // an input on a device + // + class Input : public Control + { + public: + // things like absolute axes/ absolute mouse position will override this + virtual bool IsDetectable() { return true; } + + virtual ControlState GetState() const = 0; + + Input* ToInput() { return this; } + }; + + // + // Output + // + // an output on a device + // + class Output : public Control + { + public: + virtual ~Output() {} + + virtual void SetState(ControlState state) = 0; + + Output* ToOutput() { return this; } + }; + + virtual ~Device(); + + virtual std::string GetName() const = 0; + virtual int GetId() const = 0; + virtual std::string GetSource() const = 0; + virtual bool UpdateInput() = 0; + virtual bool UpdateOutput() = 0; + + virtual void ClearInputState(); + + const std::vector<Input*>& Inputs() const { return m_inputs; } + const std::vector<Output*>& Outputs() const { return m_outputs; } + + Input* FindInput(const std::string& name) const; + Output* FindOutput(const std::string& name) const; + +protected: + void AddInput(Input* const i); + void AddOutput(Output* const o); + + class FullAnalogSurface : public Input + { + public: + FullAnalogSurface(Input* low, Input* high) + : m_low(*low), m_high(*high) + {} + + ControlState GetState() const + { + return (1 + m_high.GetState() - m_low.GetState()) / 2; + } + + std::string GetName() const + { + return m_low.GetName() + *m_high.GetName().rbegin(); + } + + private: + Input& m_low; + Input& m_high; + }; + + void AddAnalogInputs(Input* low, Input* high) + { + AddInput(low); + AddInput(high); + AddInput(new FullAnalogSurface(low, high)); + AddInput(new FullAnalogSurface(high, low)); + } + +private: + std::vector<Input*> m_inputs; + std::vector<Output*> m_outputs; +}; + +// +// DeviceQualifier +// +// device qualifier used to match devices +// currently has ( source, id, name ) properties which match a device +// +class DeviceQualifier +{ +public: + DeviceQualifier() : cid(-1) {} + DeviceQualifier(const std::string& _source, const int _id, const std::string& _name) + : source(_source), cid(_id), name(_name) {} + void FromDevice(const Device* const dev); + void FromString(const std::string& str); + std::string ToString() const; + bool operator==(const DeviceQualifier& devq) const; + bool operator==(const Device* const dev) const; + + std::string source; + int cid; + std::string name; +}; + +class DeviceContainer +{ +public: + Device::Input* FindInput(const std::string& name, const Device* def_dev) const; + Device::Output* FindOutput(const std::string& name, const Device* def_dev) const; + + const std::vector<Device*>& Devices() const { return m_devices; } + Device* FindDevice(const DeviceQualifier& devq) const; +protected: + std::vector<Device*> m_devices; +}; + +} +} + +#endif diff --git a/Source/Core/InputCommon/ControllerInterface/ExpressionParser.cpp b/Source/Core/InputCommon/ControllerInterface/ExpressionParser.cpp new file mode 100644 index 0000000000..b313559907 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/ExpressionParser.cpp @@ -0,0 +1,577 @@ + +#include "ExpressionParser.h" + +#include <cassert> +#include <iostream> +#include <string> +#include <map> +#include <vector> + +using namespace ciface::Core; + +namespace ciface +{ +namespace ExpressionParser +{ + +enum TokenType +{ + TOK_DISCARD, + TOK_INVALID, + TOK_EOF, + TOK_LPAREN, + TOK_RPAREN, + TOK_AND, + TOK_OR, + TOK_NOT, + TOK_ADD, + TOK_CONTROL, +}; + +inline std::string OpName(TokenType op) +{ + switch (op) + { + case TOK_AND: + return "And"; + case TOK_OR: + return "Or"; + case TOK_NOT: + return "Not"; + case TOK_ADD: + return "Add"; + default: + assert(false); + return ""; + } +} + +class Token +{ +public: + TokenType type; + ControlQualifier qualifier; + + Token(TokenType type_) : type(type_) {} + Token(TokenType type_, ControlQualifier qualifier_) : type(type_), qualifier(qualifier_) {} + + operator std::string() + { + switch (type) + { + case TOK_INVALID: + return "Invalid"; + case TOK_DISCARD: + return "Discard"; + case TOK_EOF: + return "EOF"; + case TOK_LPAREN: + return "("; + case TOK_RPAREN: + return ")"; + case TOK_AND: + return "&"; + case TOK_OR: + return "|"; + case TOK_NOT: + return "!"; + case TOK_ADD: + return "+"; + case TOK_CONTROL: + return "Device(" + (std::string)qualifier + ")"; + } + } +}; + +class Lexer { +public: + std::string expr; + std::string::iterator it; + + Lexer(std::string expr_) : expr(expr_) + { + it = expr.begin(); + } + + bool FetchBacktickString(std::string &value, char otherDelim = 0) + { + value = ""; + while (it != expr.end()) + { + char c = *it; + it++; + if (c == '`') + return false; + if (c > 0 && c == otherDelim) + return true; + value += c; + } + return false; + } + + Token GetFullyQualifiedControl() + { + ControlQualifier qualifier; + std::string value; + + if (FetchBacktickString(value, ':')) + { + // Found colon, this is the device name + qualifier.has_device = true; + qualifier.device_qualifier.FromString(value); + FetchBacktickString(value); + } + + qualifier.control_name = value; + + return Token(TOK_CONTROL, qualifier); + } + + Token GetBarewordsControl(char c) + { + std::string name; + name += c; + + while (it != expr.end()) { + c = *it; + if (!isalpha(c)) + break; + name += c; + it++; + } + + ControlQualifier qualifier; + qualifier.control_name = name; + return Token(TOK_CONTROL, qualifier); + } + + Token NextToken() + { + if (it == expr.end()) + return Token(TOK_EOF); + + char c = *it++; + switch (c) + { + case ' ': + case '\t': + case '\n': + case '\r': + return Token(TOK_DISCARD); + case '(': + return Token(TOK_LPAREN); + case ')': + return Token(TOK_RPAREN); + case '&': + return Token(TOK_AND); + case '|': + return Token(TOK_OR); + case '!': + return Token(TOK_NOT); + case '+': + return Token(TOK_ADD); + case '`': + return GetFullyQualifiedControl(); + default: + if (isalpha(c)) + return GetBarewordsControl(c); + else + return Token(TOK_INVALID); + } + } + + ExpressionParseStatus Tokenize(std::vector<Token> &tokens) + { + while (true) + { + Token tok = NextToken(); + + if (tok.type == TOK_DISCARD) + continue; + + if (tok.type == TOK_INVALID) + { + tokens.clear(); + return EXPRESSION_PARSE_SYNTAX_ERROR; + } + + tokens.push_back(tok); + + if (tok.type == TOK_EOF) + break; + } + return EXPRESSION_PARSE_SUCCESS; + } +}; + +class ExpressionNode +{ +public: + virtual ~ExpressionNode() {} + virtual ControlState GetValue() { return 0; } + virtual void SetValue(ControlState state) {} + virtual int CountNumControls() { return 0; } + virtual operator std::string() { return ""; } +}; + +class ControlExpression : public ExpressionNode +{ +public: + ControlQualifier qualifier; + Device::Control *control; + + ControlExpression(ControlQualifier qualifier_, Device::Control *control_) : qualifier(qualifier_), control(control_) {} + + virtual ControlState GetValue() override + { + return control->ToInput()->GetState(); + } + + virtual void SetValue(ControlState value) override + { + control->ToOutput()->SetState(value); + } + + virtual int CountNumControls() override + { + return 1; + } + + virtual operator std::string() override + { + return "`" + (std::string)qualifier + "`"; + } +}; + +class BinaryExpression : public ExpressionNode +{ +public: + TokenType op; + ExpressionNode *lhs; + ExpressionNode *rhs; + + BinaryExpression(TokenType op_, ExpressionNode *lhs_, ExpressionNode *rhs_) : op(op_), lhs(lhs_), rhs(rhs_) {} + virtual ~BinaryExpression() + { + delete lhs; + delete rhs; + } + + virtual ControlState GetValue() override + { + ControlState lhsValue = lhs->GetValue(); + ControlState rhsValue = rhs->GetValue(); + switch (op) + { + case TOK_AND: + return std::min(lhsValue, rhsValue); + case TOK_OR: + return std::max(lhsValue, rhsValue); + case TOK_ADD: + return std::min(lhsValue + rhsValue, 1.0f); + default: + assert(false); + return 0; + } + } + + virtual void SetValue(ControlState value) override + { + // Don't do anything special with the op we have. + // Treat "A & B" the same as "A | B". + lhs->SetValue(value); + rhs->SetValue(value); + } + + virtual int CountNumControls() override + { + return lhs->CountNumControls() + rhs->CountNumControls(); + } + + virtual operator std::string() override + { + return OpName(op) + "(" + (std::string)(*lhs) + ", " + (std::string)(*rhs) + ")"; + } +}; + +class UnaryExpression : public ExpressionNode +{ +public: + TokenType op; + ExpressionNode *inner; + + UnaryExpression(TokenType op_, ExpressionNode *inner_) : op(op_), inner(inner_) {} + virtual ~UnaryExpression() + { + delete inner; + } + + virtual ControlState GetValue() override + { + ControlState value = inner->GetValue(); + switch (op) + { + case TOK_NOT: + return 1.0f - value; + default: + assert(false); + return 0; + } + } + + virtual void SetValue(ControlState value) override + { + switch (op) + { + case TOK_NOT: + inner->SetValue(1.0f - value); + default: + assert(false); + } + } + + virtual int CountNumControls() override + { + return inner->CountNumControls(); + } + + virtual operator std::string() override + { + return OpName(op) + "(" + (std::string)(*inner) + ")"; + } +}; + +Device *ControlFinder::FindDevice(ControlQualifier qualifier) +{ + if (qualifier.has_device) + return container.FindDevice(qualifier.device_qualifier); + else + return container.FindDevice(default_device); +} + +Device::Control *ControlFinder::FindControl(ControlQualifier qualifier) +{ + Device *device = FindDevice(qualifier); + if (!device) + return NULL; + + if (is_input) + return device->FindInput(qualifier.control_name); + else + return device->FindOutput(qualifier.control_name); +} + +class Parser +{ +public: + + Parser(std::vector<Token> tokens_, ControlFinder &finder_) : tokens(tokens_), finder(finder_) + { + m_it = tokens.begin(); + } + + ExpressionParseStatus Parse(Expression **expr_out) + { + ExpressionNode *node; + ExpressionParseStatus status = Toplevel(&node); + if (status != EXPRESSION_PARSE_SUCCESS) + return status; + + *expr_out = new Expression(node); + return EXPRESSION_PARSE_SUCCESS; + } + +private: + std::vector<Token> tokens; + std::vector<Token>::iterator m_it; + ControlFinder &finder; + + Token Chew() + { + return *m_it++; + } + + Token Peek() + { + return *m_it; + } + + bool Expects(TokenType type) + { + Token tok = Chew(); + return tok.type == type; + } + + ExpressionParseStatus Atom(ExpressionNode **expr_out) + { + Token tok = Chew(); + switch (tok.type) + { + case TOK_CONTROL: + { + Device::Control *control = finder.FindControl(tok.qualifier); + if (control == NULL) + return EXPRESSION_PARSE_NO_DEVICE; + + *expr_out = new ControlExpression(tok.qualifier, control); + return EXPRESSION_PARSE_SUCCESS; + } + case TOK_LPAREN: + return Paren(expr_out); + default: + return EXPRESSION_PARSE_SYNTAX_ERROR; + } + } + + bool IsUnaryExpression(TokenType type) + { + switch (type) + { + case TOK_NOT: + return true; + default: + return false; + } + } + + ExpressionParseStatus Unary(ExpressionNode **expr_out) + { + ExpressionParseStatus status; + + if (IsUnaryExpression(Peek().type)) + { + Token tok = Chew(); + ExpressionNode *atom_expr; + if ((status = Atom(&atom_expr)) != EXPRESSION_PARSE_SUCCESS) + return status; + *expr_out = new UnaryExpression(tok.type, atom_expr); + return EXPRESSION_PARSE_SUCCESS; + } + + return Atom(expr_out); + } + + bool IsBinaryToken(TokenType type) + { + switch (type) + { + case TOK_AND: + case TOK_OR: + case TOK_ADD: + return true; + default: + return false; + } + } + + ExpressionParseStatus Binary(ExpressionNode **expr_out) + { + ExpressionParseStatus status; + + if ((status = Unary(expr_out)) != EXPRESSION_PARSE_SUCCESS) + return status; + + while (IsBinaryToken(Peek().type)) + { + Token tok = Chew(); + ExpressionNode *unary_expr; + if ((status = Unary(&unary_expr)) != EXPRESSION_PARSE_SUCCESS) + { + delete *expr_out; + return status; + } + + *expr_out = new BinaryExpression(tok.type, *expr_out, unary_expr); + } + + return EXPRESSION_PARSE_SUCCESS; + } + + ExpressionParseStatus Paren(ExpressionNode **expr_out) + { + ExpressionParseStatus status; + + // lparen already chewed + if ((status = Toplevel(expr_out)) != EXPRESSION_PARSE_SUCCESS) + return status; + + if (!Expects(TOK_RPAREN)) + { + delete *expr_out; + return EXPRESSION_PARSE_SYNTAX_ERROR; + } + + return EXPRESSION_PARSE_SUCCESS; + } + + ExpressionParseStatus Toplevel(ExpressionNode **expr_out) + { + return Binary(expr_out); + } +}; + +ControlState Expression::GetValue() +{ + return node->GetValue(); +} + +void Expression::SetValue(ControlState value) +{ + node->SetValue(value); +} + +Expression::Expression(ExpressionNode *node_) +{ + node = node_; + num_controls = node->CountNumControls(); +} + +Expression::~Expression() +{ + delete node; +} + +ExpressionParseStatus ParseExpressionInner(std::string str, ControlFinder &finder, Expression **expr_out) +{ + ExpressionParseStatus status; + Expression *expr; + *expr_out = NULL; + + if (str == "") + return EXPRESSION_PARSE_SUCCESS; + + Lexer l(str); + std::vector<Token> tokens; + status = l.Tokenize(tokens); + if (status != EXPRESSION_PARSE_SUCCESS) + return status; + + Parser p(tokens, finder); + status = p.Parse(&expr); + if (status != EXPRESSION_PARSE_SUCCESS) + return status; + + *expr_out = expr; + return EXPRESSION_PARSE_SUCCESS; +} + +ExpressionParseStatus ParseExpression(std::string str, ControlFinder &finder, Expression **expr_out) +{ + // Add compatibility with old simple expressions, which are simple + // barewords control names. + + ControlQualifier qualifier; + qualifier.control_name = str; + qualifier.has_device = false; + + Device::Control *control = finder.FindControl(qualifier); + if (control) { + *expr_out = new Expression(new ControlExpression(qualifier, control)); + return EXPRESSION_PARSE_SUCCESS; + } + + return ParseExpressionInner(str, finder, expr_out); +} + +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/ExpressionParser.h b/Source/Core/InputCommon/ControllerInterface/ExpressionParser.h new file mode 100644 index 0000000000..70cc51373b --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/ExpressionParser.h @@ -0,0 +1,69 @@ + +#ifndef _EXPRESSIONPARSER_H_ +#define _EXPRESSIONPARSER_H_ + +#include <string> +#include "Device.h" + +namespace ciface +{ +namespace ExpressionParser +{ + +class ControlQualifier +{ +public: + bool has_device; + Core::DeviceQualifier device_qualifier; + std::string control_name; + + ControlQualifier() : has_device(false) {} + + operator std::string() + { + if (has_device) + return device_qualifier.ToString() + ":" + control_name; + else + return control_name; + } +}; + +class ControlFinder +{ +public: + ControlFinder(const Core::DeviceContainer &container_, const Core::DeviceQualifier &default_, const bool is_input_) : container(container_), default_device(default_), is_input(is_input_) {} + Core::Device::Control *FindControl(ControlQualifier qualifier); + +private: + Core::Device *FindDevice(ControlQualifier qualifier); + const Core::DeviceContainer &container; + const Core::DeviceQualifier &default_device; + bool is_input; +}; + +class ExpressionNode; +class Expression +{ +public: + Expression() : node(NULL) {} + Expression(ExpressionNode *node); + ~Expression(); + ControlState GetValue(); + void SetValue (ControlState state); + int num_controls; + ExpressionNode *node; +}; + +enum ExpressionParseStatus +{ + EXPRESSION_PARSE_SUCCESS = 0, + EXPRESSION_PARSE_SYNTAX_ERROR, + EXPRESSION_PARSE_NO_DEVICE, +}; + +ExpressionParseStatus ParseExpression(std::string expr, ControlFinder &finder, Expression **expr_out); + +} +} + +#endif diff --git a/Source/Core/InputCommon/ControllerInterface/OSX/OSX.h b/Source/Core/InputCommon/ControllerInterface/OSX/OSX.h new file mode 100644 index 0000000000..a922e3f2dd --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/OSX/OSX.h @@ -0,0 +1,16 @@ +#pragma once + +#include "../Device.h" + +namespace ciface +{ +namespace OSX +{ + +void Init(std::vector<Core::Device*>& devices, void *window); +void DeInit(); + +void DeviceElementDebugPrint(const void *, void *); + +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/OSX/OSX.mm b/Source/Core/InputCommon/ControllerInterface/OSX/OSX.mm new file mode 100644 index 0000000000..8a859f46c8 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/OSX/OSX.mm @@ -0,0 +1,213 @@ +#include <Foundation/Foundation.h> +#include <IOKit/hid/IOHIDLib.h> +#include <Cocoa/Cocoa.h> + +#include "OSX.h" +#include "OSXKeyboard.h" +#include "OSXJoystick.h" + +#include <map> + +namespace ciface +{ +namespace OSX +{ + + +static IOHIDManagerRef HIDManager = NULL; +static CFStringRef OurRunLoop = CFSTR("DolphinOSXInput"); +static std::map<std::string, int> kbd_name_counts, joy_name_counts; + +void DeviceElementDebugPrint(const void *value, void *context) +{ + IOHIDElementRef e = (IOHIDElementRef)value; + bool recurse = false; + if (context) + recurse = *(bool*)context; + + std::string type = ""; + switch (IOHIDElementGetType(e)) { + case kIOHIDElementTypeInput_Axis: + type = "axis"; + break; + case kIOHIDElementTypeInput_Button: + type = "button"; + break; + case kIOHIDElementTypeInput_Misc: + type = "misc"; + break; + case kIOHIDElementTypeInput_ScanCodes: + type = "scancodes"; + break; + case kIOHIDElementTypeOutput: + type = "output"; + break; + case kIOHIDElementTypeFeature: + type = "feature"; + break; + case kIOHIDElementTypeCollection: + type = "collection"; + break; + } + + std::string c_type = ""; + if (type == "collection") + { + switch (IOHIDElementGetCollectionType(e)) { + case kIOHIDElementCollectionTypePhysical: + c_type = "physical"; + break; + case kIOHIDElementCollectionTypeApplication: + c_type = "application"; + break; + case kIOHIDElementCollectionTypeLogical: + c_type = "logical"; + break; + case kIOHIDElementCollectionTypeReport: + c_type = "report"; + break; + case kIOHIDElementCollectionTypeNamedArray: + c_type = "namedArray"; + break; + case kIOHIDElementCollectionTypeUsageSwitch: + c_type = "usageSwitch"; + break; + case kIOHIDElementCollectionTypeUsageModifier: + c_type = "usageModifier"; + break; + } + } + + c_type.append(" "); + NSLog(@"%s%s%spage: 0x%x usage: 0x%x name: %@ " + "lmin: %ld lmax: %ld pmin: %ld pmax: %ld", + type.c_str(), + type == "collection" ? ":" : "", + type == "collection" ? c_type.c_str() : " ", + IOHIDElementGetUsagePage(e), + IOHIDElementGetUsage(e), + IOHIDElementGetName(e), // usually just NULL + IOHIDElementGetLogicalMin(e), + IOHIDElementGetLogicalMax(e), + IOHIDElementGetPhysicalMin(e), + IOHIDElementGetPhysicalMax(e)); + + if ((type == "collection") && recurse) + { + CFArrayRef elements = IOHIDElementGetChildren(e); + CFRange range = {0, CFArrayGetCount(elements)}; + // this leaks...but it's just debug code, right? :D + CFArrayApplyFunction(elements, range, + DeviceElementDebugPrint, NULL); + } +} + +void DeviceDebugPrint(IOHIDDeviceRef device) +{ +#if 0 +#define shortlog(x) NSLog(@"%s: %@", \ + x, IOHIDDeviceGetProperty(device, CFSTR(x))); + NSLog(@"-------------------------"); + NSLog(@"Got Device: %@", + IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey))); + shortlog(kIOHIDTransportKey) + shortlog(kIOHIDVendorIDKey) + shortlog(kIOHIDVendorIDSourceKey) + shortlog(kIOHIDProductIDKey) + shortlog(kIOHIDVersionNumberKey) + shortlog(kIOHIDManufacturerKey) + shortlog(kIOHIDProductKey) + shortlog(kIOHIDSerialNumberKey) + shortlog(kIOHIDCountryCodeKey) + shortlog(kIOHIDLocationIDKey) + shortlog(kIOHIDDeviceUsageKey) + shortlog(kIOHIDDeviceUsagePageKey) + shortlog(kIOHIDDeviceUsagePairsKey) + shortlog(kIOHIDPrimaryUsageKey) + shortlog(kIOHIDPrimaryUsagePageKey) + shortlog(kIOHIDMaxInputReportSizeKey) + shortlog(kIOHIDMaxOutputReportSizeKey) + shortlog(kIOHIDMaxFeatureReportSizeKey) + shortlog(kIOHIDReportIntervalKey) + shortlog(kIOHIDReportDescriptorKey) +#endif +} + +static void *g_window; + +static void DeviceMatching_callback(void* inContext, + IOReturn inResult, + void *inSender, + IOHIDDeviceRef inIOHIDDeviceRef) +{ + NSString *pName = (NSString *) + IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDProductKey)); + std::string name = (pName != NULL) ? [pName UTF8String] : "Unknown device"; + + DeviceDebugPrint(inIOHIDDeviceRef); + + std::vector<Core::Device*> *devices = + (std::vector<Core::Device*> *)inContext; + + // Add to the devices vector if it's of a type we want + if (IOHIDDeviceConformsTo(inIOHIDDeviceRef, + kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard)) + devices->push_back(new Keyboard(inIOHIDDeviceRef, + name, kbd_name_counts[name]++, g_window)); +#if 0 + else if (IOHIDDeviceConformsTo(inIOHIDDeviceRef, + kHIDPage_GenericDesktop, kHIDUsage_GD_Mouse)) + devices->push_back(new Mouse(inIOHIDDeviceRef, + name, mouse_name_counts[name]++)); +#endif + else + devices->push_back(new Joystick(inIOHIDDeviceRef, + name, joy_name_counts[name]++)); +} + +void Init(std::vector<Core::Device*>& devices, void *window) +{ + HIDManager = IOHIDManagerCreate(kCFAllocatorDefault, + kIOHIDOptionsTypeNone); + if (!HIDManager) + NSLog(@"Failed to create HID Manager reference"); + + g_window = window; + + IOHIDManagerSetDeviceMatching(HIDManager, NULL); + + // Callbacks for acquisition or loss of a matching device + IOHIDManagerRegisterDeviceMatchingCallback(HIDManager, + DeviceMatching_callback, (void *)&devices); + + // Match devices that are plugged in right now + IOHIDManagerScheduleWithRunLoop(HIDManager, + CFRunLoopGetCurrent(), OurRunLoop); + if (IOHIDManagerOpen(HIDManager, kIOHIDOptionsTypeNone) != + kIOReturnSuccess) + NSLog(@"Failed to open HID Manager"); + + kbd_name_counts.clear(); + joy_name_counts.clear(); + + // Wait while current devices are initialized + while (CFRunLoopRunInMode(OurRunLoop, 0, TRUE) == + kCFRunLoopRunHandledSource) {}; + + // Things should be configured now + // Disable hotplugging and other scheduling + IOHIDManagerRegisterDeviceMatchingCallback(HIDManager, NULL, NULL); + IOHIDManagerUnscheduleFromRunLoop(HIDManager, + CFRunLoopGetCurrent(), OurRunLoop); +} + +void DeInit() +{ + // This closes all devices as well + IOHIDManagerClose(HIDManager, kIOHIDOptionsTypeNone); + CFRelease(HIDManager); +} + + +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.h b/Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.h new file mode 100644 index 0000000000..ebe908522b --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.h @@ -0,0 +1,80 @@ +#include <IOKit/hid/IOHIDLib.h> + +#include "../Device.h" + +namespace ciface +{ +namespace OSX +{ + +class Joystick : public Core::Device +{ +private: + class Button : public Input + { + public: + std::string GetName() const; + Button(IOHIDElementRef element, IOHIDDeviceRef device) + : m_element(element), m_device(device) {} + ControlState GetState() const; + private: + const IOHIDElementRef m_element; + const IOHIDDeviceRef m_device; + }; + + class Axis : public Input + { + public: + enum direction { + positive = 0, + negative + }; + std::string GetName() const; + Axis(IOHIDElementRef element, IOHIDDeviceRef device, direction dir); + ControlState GetState() const; + private: + const IOHIDElementRef m_element; + const IOHIDDeviceRef m_device; + std::string m_name; + const direction m_direction; + float m_neutral; + float m_scale; + }; + + class Hat : public Input + { + public: + enum direction { + up = 0, + right, + down, + left + }; + std::string GetName() const; + Hat(IOHIDElementRef element, IOHIDDeviceRef device, direction dir); + ControlState GetState() const; + private: + const IOHIDElementRef m_element; + const IOHIDDeviceRef m_device; + const char* m_name; + const direction m_direction; + }; + +public: + bool UpdateInput(); + bool UpdateOutput(); + + Joystick(IOHIDDeviceRef device, std::string name, int index); + + std::string GetName() const; + std::string GetSource() const; + int GetId() const; + +private: + const IOHIDDeviceRef m_device; + const std::string m_device_name; + const int m_index; +}; + +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.mm b/Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.mm new file mode 100644 index 0000000000..ebd713d142 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.mm @@ -0,0 +1,274 @@ +#include <Foundation/Foundation.h> +#include <IOKit/hid/IOHIDLib.h> + +#include "OSXJoystick.h" + +#include <sstream> + +namespace ciface +{ +namespace OSX +{ + + +Joystick::Joystick(IOHIDDeviceRef device, std::string name, int index) + : m_device(device) + , m_device_name(name) + , m_index(index) +{ + // Buttons + NSDictionary *buttonDict = + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInteger: kIOHIDElementTypeInput_Button], + @kIOHIDElementTypeKey, + [NSNumber numberWithInteger: kHIDPage_Button], + @kIOHIDElementUsagePageKey, + nil]; + + CFArrayRef buttons = IOHIDDeviceCopyMatchingElements(m_device, + (CFDictionaryRef)buttonDict, kIOHIDOptionsTypeNone); + + if (buttons) + { + for (int i = 0; i < CFArrayGetCount(buttons); i++) + { + IOHIDElementRef e = + (IOHIDElementRef)CFArrayGetValueAtIndex(buttons, i); + //DeviceElementDebugPrint(e, NULL); + + AddInput(new Button(e, m_device)); + } + CFRelease(buttons); + } + + // Axes + NSDictionary *axisDict = + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInteger: kIOHIDElementTypeInput_Misc], + @kIOHIDElementTypeKey, + nil]; + + CFArrayRef axes = IOHIDDeviceCopyMatchingElements(m_device, + (CFDictionaryRef)axisDict, kIOHIDOptionsTypeNone); + + if (axes) + { + for (int i = 0; i < CFArrayGetCount(axes); i++) + { + IOHIDElementRef e = + (IOHIDElementRef)CFArrayGetValueAtIndex(axes, i); + //DeviceElementDebugPrint(e, NULL); + + if (IOHIDElementGetUsage(e) == kHIDUsage_GD_Hatswitch) { + AddInput(new Hat(e, m_device, Hat::up)); + AddInput(new Hat(e, m_device, Hat::right)); + AddInput(new Hat(e, m_device, Hat::down)); + AddInput(new Hat(e, m_device, Hat::left)); + } else { + AddAnalogInputs(new Axis(e, m_device, Axis::negative), + new Axis(e, m_device, Axis::positive)); + } + } + CFRelease(axes); + } +} + +bool Joystick::UpdateInput() +{ + return true; +} + +bool Joystick::UpdateOutput() +{ + return true; +} + +std::string Joystick::GetName() const +{ + return m_device_name; +} + +std::string Joystick::GetSource() const +{ + return "Input"; +} + +int Joystick::GetId() const +{ + return m_index; +} + +ControlState Joystick::Button::GetState() const +{ + IOHIDValueRef value; + if (IOHIDDeviceGetValue(m_device, m_element, &value) == kIOReturnSuccess) + return IOHIDValueGetIntegerValue(value); + else + return 0; +} + +std::string Joystick::Button::GetName() const +{ + std::ostringstream s; + s << IOHIDElementGetUsage(m_element); + return std::string("Button ") + s.str(); +} + +Joystick::Axis::Axis(IOHIDElementRef element, IOHIDDeviceRef device, direction dir) + : m_element(element) + , m_device(device) + , m_direction(dir) +{ + // Need to parse the element a bit first + std::string description("unk"); + + int const usage = IOHIDElementGetUsage(m_element); + switch (usage) + { + case kHIDUsage_GD_X: + description = "X"; + break; + case kHIDUsage_GD_Y: + description = "Y"; + break; + case kHIDUsage_GD_Z: + description = "Z"; + break; + case kHIDUsage_GD_Rx: + description = "Rx"; + break; + case kHIDUsage_GD_Ry: + description = "Ry"; + break; + case kHIDUsage_GD_Rz: + description = "Rz"; + break; + case kHIDUsage_GD_Wheel: + description = "Wheel"; + break; + case kHIDUsage_Csmr_ACPan: + description = "Pan"; + break; + default: + { + std::ostringstream s; + s << usage; + description = s.str(); + break; + } + } + + m_name = std::string("Axis ") + description; + m_name.append((m_direction == positive) ? "+" : "-"); + + m_neutral = (IOHIDElementGetLogicalMax(m_element) + + IOHIDElementGetLogicalMin(m_element)) / 2.; + m_scale = 1 / fabs(IOHIDElementGetLogicalMax(m_element) - m_neutral); +} + +ControlState Joystick::Axis::GetState() const +{ + IOHIDValueRef value; + + if (IOHIDDeviceGetValue(m_device, m_element, &value) == kIOReturnSuccess) + { + // IOHIDValueGetIntegerValue() crashes when trying + // to convert unusually large element values. + if (IOHIDValueGetLength(value) > 2) + return 0; + + float position = IOHIDValueGetIntegerValue(value); + + if (m_direction == positive && position > m_neutral) + return (position - m_neutral) * m_scale; + if (m_direction == negative && position < m_neutral) + return (m_neutral - position) * m_scale; + } + + return 0; +} + +std::string Joystick::Axis::GetName() const +{ + return m_name; +} + +Joystick::Hat::Hat(IOHIDElementRef element, IOHIDDeviceRef device, direction dir) + : m_element(element) + , m_device(device) + , m_direction(dir) +{ + switch (dir) { + case up: + m_name = "Up"; + break; + case right: + m_name = "Right"; + break; + case down: + m_name = "Down"; + break; + case left: + m_name = "Left"; + break; + default: + m_name = "unk"; + } +} + +ControlState Joystick::Hat::GetState() const +{ + IOHIDValueRef value; + int position; + + if (IOHIDDeviceGetValue(m_device, m_element, &value) == kIOReturnSuccess) + { + position = IOHIDValueGetIntegerValue(value); + + switch (position) { + case 0: + if (m_direction == up) + return 1; + break; + case 1: + if (m_direction == up || m_direction == right) + return 1; + break; + case 2: + if (m_direction == right) + return 1; + break; + case 3: + if (m_direction == right || m_direction == down) + return 1; + break; + case 4: + if (m_direction == down) + return 1; + break; + case 5: + if (m_direction == down || m_direction == left) + return 1; + break; + case 6: + if (m_direction == left) + return 1; + break; + case 7: + if (m_direction == left || m_direction == up) + return 1; + break; + }; + } + + return 0; +} + +std::string Joystick::Hat::GetName() const +{ + return m_name; +} + + +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/OSX/OSXKeyboard.h b/Source/Core/InputCommon/ControllerInterface/OSX/OSXKeyboard.h new file mode 100644 index 0000000000..e5c2c1b3d4 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/OSX/OSXKeyboard.h @@ -0,0 +1,71 @@ +#include <IOKit/hid/IOHIDLib.h> + +#include "../Device.h" + +namespace ciface +{ +namespace OSX +{ + +class Keyboard : public Core::Device +{ +private: + class Key : public Input + { + public: + std::string GetName() const; + Key(IOHIDElementRef element, IOHIDDeviceRef device); + ControlState GetState() const; + private: + const IOHIDElementRef m_element; + const IOHIDDeviceRef m_device; + std::string m_name; + }; + class Cursor : public Input + { + public: + std::string GetName() const; + bool IsDetectable() { return false; } + Cursor(u8 index, const float& axis, const bool positive) : m_axis(axis), m_index(index), m_positive(positive) {} + ControlState GetState() const; + private: + const float& m_axis; + const u8 m_index; + const bool m_positive; + }; + class Button : public Input + { + public: + std::string GetName() const; + Button(u8 index, const unsigned char& button) : m_button(button), m_index(index) {} + ControlState GetState() const; + private: + const unsigned char& m_button; + const u8 m_index; + }; + +public: + bool UpdateInput(); + bool UpdateOutput(); + + Keyboard(IOHIDDeviceRef device, std::string name, int index, void *window); + + std::string GetName() const; + std::string GetSource() const; + int GetId() const; + +private: + struct + { + float x, y; + } m_cursor; + + const IOHIDDeviceRef m_device; + const std::string m_device_name; + int m_index; + uint32_t m_windowid; + unsigned char m_mousebuttons[3]; +}; + +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/OSX/OSXKeyboard.mm b/Source/Core/InputCommon/ControllerInterface/OSX/OSXKeyboard.mm new file mode 100644 index 0000000000..93de15daa4 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/OSX/OSXKeyboard.mm @@ -0,0 +1,276 @@ +#include <Foundation/Foundation.h> +#include <IOKit/hid/IOHIDLib.h> +#include <Cocoa/Cocoa.h> +#include <wx/wx.h> // wxWidgets + +#include "OSXKeyboard.h" + +#include <sstream> + +namespace ciface +{ +namespace OSX +{ + +Keyboard::Keyboard(IOHIDDeviceRef device, std::string name, int index, void *window) + : m_device(device) + , m_device_name(name) + , m_index(index) +{ + // This class should only recieve Keyboard or Keypad devices + // Now, filter on just the buttons we can handle sanely + NSDictionary *matchingElements = + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInteger: kIOHIDElementTypeInput_Button], + @kIOHIDElementTypeKey, + [NSNumber numberWithInteger: 0], @kIOHIDElementMinKey, + [NSNumber numberWithInteger: 1], @kIOHIDElementMaxKey, + nil]; + + CFArrayRef elements = IOHIDDeviceCopyMatchingElements(m_device, + (CFDictionaryRef)matchingElements, kIOHIDOptionsTypeNone); + + if (elements) + { + for (int i = 0; i < CFArrayGetCount(elements); i++) + { + IOHIDElementRef e = + (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i); + //DeviceElementDebugPrint(e, NULL); + + AddInput(new Key(e, m_device)); + } + CFRelease(elements); + } + + m_windowid = [[(NSView *)(((wxWindow *)window)->GetHandle()) window] windowNumber]; + + // cursor, with a hax for-loop + for (unsigned int i=0; i<4; ++i) + AddInput(new Cursor(!!(i&2), (&m_cursor.x)[i/2], !!(i&1))); + + for (u8 i = 0; i < sizeof(m_mousebuttons) / sizeof(m_mousebuttons[0]); ++i) + AddInput(new Button(i, m_mousebuttons[i])); +} + +bool Keyboard::UpdateInput() +{ + CGRect bounds = CGRectZero; + uint32_t windowid[1] = { m_windowid }; + CFArrayRef windowArray = CFArrayCreate(NULL, (const void **) windowid, 1, NULL); + CFArrayRef windowDescriptions = CGWindowListCreateDescriptionFromArray(windowArray); + CFDictionaryRef windowDescription = (CFDictionaryRef) CFArrayGetValueAtIndex((CFArrayRef) windowDescriptions, 0); + + if (CFDictionaryContainsKey(windowDescription, kCGWindowBounds)) + { + CFDictionaryRef boundsDictionary = (CFDictionaryRef) CFDictionaryGetValue(windowDescription, kCGWindowBounds); + + if (boundsDictionary != NULL) + CGRectMakeWithDictionaryRepresentation(boundsDictionary, &bounds); + } + + CFRelease(windowDescriptions); + CFRelease(windowArray); + + CGEventRef event = CGEventCreate(nil); + CGPoint loc = CGEventGetLocation(event); + CFRelease(event); + + loc.x -= bounds.origin.x; + loc.y -= bounds.origin.y; + m_cursor.x = loc.x / bounds.size.width * 2 - 1.0; + m_cursor.y = loc.y / bounds.size.height * 2 - 1.0; + + m_mousebuttons[0] = CGEventSourceButtonState(kCGEventSourceStateHIDSystemState, kCGMouseButtonLeft); + m_mousebuttons[1] = CGEventSourceButtonState(kCGEventSourceStateHIDSystemState, kCGMouseButtonRight); + m_mousebuttons[2] = CGEventSourceButtonState(kCGEventSourceStateHIDSystemState, kCGMouseButtonCenter); + + return true; +} + +bool Keyboard::UpdateOutput() +{ + return true; +} + +std::string Keyboard::GetName() const +{ + return m_device_name; +} + +std::string Keyboard::GetSource() const +{ + return "Keyboard"; +} + +int Keyboard::GetId() const +{ + return m_index; +} + +Keyboard::Key::Key(IOHIDElementRef element, IOHIDDeviceRef device) + : m_element(element) + , m_device(device) +{ + static const struct PrettyKeys { + const uint32_t code; + const char *const name; + } named_keys[] = { + { kHIDUsage_KeyboardA, "A" }, + { kHIDUsage_KeyboardB, "B" }, + { kHIDUsage_KeyboardC, "C" }, + { kHIDUsage_KeyboardD, "D" }, + { kHIDUsage_KeyboardE, "E" }, + { kHIDUsage_KeyboardF, "F" }, + { kHIDUsage_KeyboardG, "G" }, + { kHIDUsage_KeyboardH, "H" }, + { kHIDUsage_KeyboardI, "I" }, + { kHIDUsage_KeyboardJ, "J" }, + { kHIDUsage_KeyboardK, "K" }, + { kHIDUsage_KeyboardL, "L" }, + { kHIDUsage_KeyboardM, "M" }, + { kHIDUsage_KeyboardN, "N" }, + { kHIDUsage_KeyboardO, "O" }, + { kHIDUsage_KeyboardP, "P" }, + { kHIDUsage_KeyboardQ, "Q" }, + { kHIDUsage_KeyboardR, "R" }, + { kHIDUsage_KeyboardS, "S" }, + { kHIDUsage_KeyboardT, "T" }, + { kHIDUsage_KeyboardU, "U" }, + { kHIDUsage_KeyboardV, "V" }, + { kHIDUsage_KeyboardW, "W" }, + { kHIDUsage_KeyboardX, "X" }, + { kHIDUsage_KeyboardY, "Y" }, + { kHIDUsage_KeyboardZ, "Z" }, + { kHIDUsage_Keyboard1, "1" }, + { kHIDUsage_Keyboard2, "2" }, + { kHIDUsage_Keyboard3, "3" }, + { kHIDUsage_Keyboard4, "4" }, + { kHIDUsage_Keyboard5, "5" }, + { kHIDUsage_Keyboard6, "6" }, + { kHIDUsage_Keyboard7, "7" }, + { kHIDUsage_Keyboard8, "8" }, + { kHIDUsage_Keyboard9, "9" }, + { kHIDUsage_Keyboard0, "0" }, + { kHIDUsage_KeyboardReturnOrEnter, "Return" }, + { kHIDUsage_KeyboardEscape, "Escape" }, + { kHIDUsage_KeyboardDeleteOrBackspace, "Backspace" }, + { kHIDUsage_KeyboardTab, "Tab" }, + { kHIDUsage_KeyboardSpacebar, "Space" }, + { kHIDUsage_KeyboardHyphen, "-" }, + { kHIDUsage_KeyboardEqualSign, "=" }, + { kHIDUsage_KeyboardOpenBracket, "[" }, + { kHIDUsage_KeyboardCloseBracket, "]" }, + { kHIDUsage_KeyboardBackslash, "\\" }, + { kHIDUsage_KeyboardSemicolon, ";" }, + { kHIDUsage_KeyboardQuote, "'" }, + { kHIDUsage_KeyboardGraveAccentAndTilde, "Tilde" }, + { kHIDUsage_KeyboardComma, "," }, + { kHIDUsage_KeyboardPeriod, "." }, + { kHIDUsage_KeyboardSlash, "/" }, + { kHIDUsage_KeyboardCapsLock, "Caps Lock" }, + { kHIDUsage_KeyboardF1, "F1" }, + { kHIDUsage_KeyboardF2, "F2" }, + { kHIDUsage_KeyboardF3, "F3" }, + { kHIDUsage_KeyboardF4, "F4" }, + { kHIDUsage_KeyboardF5, "F5" }, + { kHIDUsage_KeyboardF6, "F6" }, + { kHIDUsage_KeyboardF7, "F7" }, + { kHIDUsage_KeyboardF8, "F8" }, + { kHIDUsage_KeyboardF9, "F9" }, + { kHIDUsage_KeyboardF10, "F10" }, + { kHIDUsage_KeyboardF11, "F11" }, + { kHIDUsage_KeyboardF12, "F12" }, + { kHIDUsage_KeyboardInsert, "Insert" }, + { kHIDUsage_KeyboardHome, "Home" }, + { kHIDUsage_KeyboardPageUp, "Page Up" }, + { kHIDUsage_KeyboardDeleteForward, "Delete" }, + { kHIDUsage_KeyboardEnd, "End" }, + { kHIDUsage_KeyboardPageDown, "Page Down" }, + { kHIDUsage_KeyboardRightArrow, "Right Arrow" }, + { kHIDUsage_KeyboardLeftArrow, "Left Arrow" }, + { kHIDUsage_KeyboardDownArrow, "Down Arrow" }, + { kHIDUsage_KeyboardUpArrow, "Up Arrow" }, + { kHIDUsage_KeypadSlash, "Keypad /" }, + { kHIDUsage_KeypadAsterisk, "Keypad *" }, + { kHIDUsage_KeypadHyphen, "Keypad -" }, + { kHIDUsage_KeypadPlus, "Keypad +" }, + { kHIDUsage_KeypadEnter, "Keypad Enter" }, + { kHIDUsage_Keypad1, "Keypad 1" }, + { kHIDUsage_Keypad2, "Keypad 2" }, + { kHIDUsage_Keypad3, "Keypad 3" }, + { kHIDUsage_Keypad4, "Keypad 4" }, + { kHIDUsage_Keypad5, "Keypad 5" }, + { kHIDUsage_Keypad6, "Keypad 6" }, + { kHIDUsage_Keypad7, "Keypad 7" }, + { kHIDUsage_Keypad8, "Keypad 8" }, + { kHIDUsage_Keypad9, "Keypad 9" }, + { kHIDUsage_Keypad0, "Keypad 0" }, + { kHIDUsage_KeypadPeriod, "Keypad ." }, + { kHIDUsage_KeyboardNonUSBackslash, "Paragraph" }, + { kHIDUsage_KeypadEqualSign, "Keypad =" }, + { kHIDUsage_KeypadComma, "Keypad ," }, + { kHIDUsage_KeyboardLeftControl, "Left Control" }, + { kHIDUsage_KeyboardLeftShift, "Left Shift" }, + { kHIDUsage_KeyboardLeftAlt, "Left Alt" }, + { kHIDUsage_KeyboardLeftGUI, "Left Command" }, + { kHIDUsage_KeyboardRightControl, "Right Control" }, + { kHIDUsage_KeyboardRightShift, "Right Shift" }, + { kHIDUsage_KeyboardRightAlt, "Right Alt" }, + { kHIDUsage_KeyboardRightGUI, "Right Command" }, + { 184, "Eject" }, + }; + + const uint32_t keycode = IOHIDElementGetUsage(m_element); + for (auto & named_key : named_keys) + if (named_key.code == keycode) { + m_name = named_key.name; + return; + } + + std::stringstream ss; + ss << "Key " << keycode; + m_name = ss.str(); +} + +ControlState Keyboard::Key::GetState() const +{ + IOHIDValueRef value; + + if (IOHIDDeviceGetValue(m_device, m_element, &value) == kIOReturnSuccess) + return IOHIDValueGetIntegerValue(value); + else + return 0; +} + +ControlState Keyboard::Cursor::GetState() const +{ + return std::max(0.0f, ControlState(m_axis) / (m_positive ? 1.0f : -1.0f)); +} + +ControlState Keyboard::Button::GetState() const +{ + return (m_button != 0); +} + +std::string Keyboard::Cursor::GetName() const +{ + static char tmpstr[] = "Cursor .."; + tmpstr[7] = (char)('X' + m_index); + tmpstr[8] = (m_positive ? '+' : '-'); + return tmpstr; +} + +std::string Keyboard::Button::GetName() const +{ + return std::string("Click ") + char('0' + m_index); +} + +std::string Keyboard::Key::GetName() const +{ + return m_name; +} + + +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp new file mode 100644 index 0000000000..24e0d0de58 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp @@ -0,0 +1,406 @@ + +#include "SDL.h" +#include <StringUtil.h> + +#include <map> +#include <sstream> +#include <algorithm> + +#ifdef _WIN32 +#pragma comment(lib, "SDL2.lib") +#endif + +namespace ciface +{ +namespace SDL +{ + +std::string GetJoystickName(int index) +{ +#if SDL_VERSION_ATLEAST(2, 0, 0) + return SDL_JoystickNameForIndex(index); +#else + return SDL_JoystickName(index); +#endif +} + +void Init( std::vector<Core::Device*>& devices ) +{ + // this is used to number the joysticks + // multiple joysticks with the same name shall get unique ids starting at 0 + std::map<std::string, int> name_counts; + + if (SDL_Init( SDL_INIT_FLAGS ) >= 0) + { + // joysticks + for(int i = 0; i < SDL_NumJoysticks(); ++i) + { + SDL_Joystick* dev = SDL_JoystickOpen(i); + if (dev) + { + Joystick* js = new Joystick(dev, i, name_counts[GetJoystickName(i)]++); + // only add if it has some inputs/outputs + if (js->Inputs().size() || js->Outputs().size()) + devices.push_back( js ); + else + delete js; + } + } + } +} + +Joystick::Joystick(SDL_Joystick* const joystick, const int sdl_index, const unsigned int index) + : m_joystick(joystick) + , m_sdl_index(sdl_index) + , m_index(index) +{ + // really bad HACKS: + // to not use SDL for an XInput device + // too many people on the forums pick the SDL device and ask: + // "why don't my 360 gamepad triggers/rumble work correctly" +#ifdef _WIN32 + // checking the name is probably good (and hacky) enough + // but I'll double check with the num of buttons/axes + std::string lcasename = GetName(); + std::transform(lcasename.begin(), lcasename.end(), lcasename.begin(), tolower); + + if ((std::string::npos != lcasename.find("xbox 360")) + && (10 == SDL_JoystickNumButtons(joystick)) + && (5 == SDL_JoystickNumAxes(joystick)) + && (1 == SDL_JoystickNumHats(joystick)) + && (0 == SDL_JoystickNumBalls(joystick)) + ) + { + // this device won't be used + return; + } +#endif + + // get buttons + for (u8 i = 0; i != SDL_JoystickNumButtons(m_joystick); ++i) + AddInput(new Button(i, m_joystick)); + + // get hats + for (u8 i = 0; i != SDL_JoystickNumHats(m_joystick); ++i) + { + // each hat gets 4 input instances associated with it, (up down left right) + for (u8 d = 0; d != 4; ++d) + AddInput(new Hat(i, m_joystick, d)); + } + + // get axes + for (u8 i = 0; i != SDL_JoystickNumAxes(m_joystick); ++i) + { + // each axis gets a negative and a positive input instance associated with it + AddAnalogInputs(new Axis(i, m_joystick, -32768), + new Axis(i, m_joystick, 32767)); + } + +#ifdef USE_SDL_HAPTIC + // try to get supported ff effects + m_haptic = SDL_HapticOpenFromJoystick( m_joystick ); + if (m_haptic) + { + //SDL_HapticSetGain( m_haptic, 1000 ); + //SDL_HapticSetAutocenter( m_haptic, 0 ); + + const unsigned int supported_effects = SDL_HapticQuery( m_haptic ); + + // constant effect + if (supported_effects & SDL_HAPTIC_CONSTANT) + { + m_state_out.push_back(EffectIDState()); + AddOutput(new ConstantEffect(m_state_out.back())); + } + + // ramp effect + if (supported_effects & SDL_HAPTIC_RAMP) + { + m_state_out.push_back(EffectIDState()); + AddOutput(new RampEffect(m_state_out.back())); + } + + // sine effect + if (supported_effects & SDL_HAPTIC_SINE) + { + m_state_out.push_back(EffectIDState()); + AddOutput(new SineEffect(m_state_out.back())); + } + +#ifdef SDL_HAPTIC_SQUARE + // square effect + if (supported_effects & SDL_HAPTIC_SQUARE) + { + m_state_out.push_back(EffectIDState()); + AddOutput(new SquareEffect(m_state_out.back())); + } +#endif // defined(SDL_HAPTIC_SQUARE) + + // triangle effect + if (supported_effects & SDL_HAPTIC_TRIANGLE) + { + m_state_out.push_back(EffectIDState()); + AddOutput(new TriangleEffect(m_state_out.back())); + } + } +#endif + +} + +Joystick::~Joystick() +{ +#ifdef USE_SDL_HAPTIC + if (m_haptic) + { + // stop/destroy all effects + SDL_HapticStopAll(m_haptic); + for (auto &i : m_state_out) + { + if (i.id != -1) + { + SDL_HapticDestroyEffect(m_haptic, i.id); + } + } + // close haptic first + SDL_HapticClose(m_haptic); + } +#endif + + // close joystick + SDL_JoystickClose(m_joystick); +} + +#ifdef USE_SDL_HAPTIC +std::string Joystick::ConstantEffect::GetName() const +{ + return "Constant"; +} + +std::string Joystick::RampEffect::GetName() const +{ + return "Ramp"; +} + +std::string Joystick::SineEffect::GetName() const +{ + return "Sine"; +} + +#ifdef SDL_HAPTIC_SQUARE +std::string Joystick::SquareEffect::GetName() const +{ + return "Square"; +} +#endif // defined(SDL_HAPTIC_SQUARE) + +std::string Joystick::TriangleEffect::GetName() const +{ + return "Triangle"; +} + +void Joystick::ConstantEffect::SetState(ControlState state) +{ + if (state) + { + m_effect.effect.type = SDL_HAPTIC_CONSTANT; + m_effect.effect.constant.length = SDL_HAPTIC_INFINITY; + } + else + { + m_effect.effect.type = 0; + } + + const Sint16 old = m_effect.effect.constant.level; + m_effect.effect.constant.level = (Sint16)(state * 0x7FFF); + if (old != m_effect.effect.constant.level) + m_effect.changed = true; +} + +void Joystick::RampEffect::SetState(ControlState state) +{ + if (state) + { + m_effect.effect.type = SDL_HAPTIC_RAMP; + m_effect.effect.ramp.length = SDL_HAPTIC_INFINITY; + } + else + { + m_effect.effect.type = 0; + } + + const Sint16 old = m_effect.effect.ramp.start; + m_effect.effect.ramp.start = (Sint16)(state * 0x7FFF); + if (old != m_effect.effect.ramp.start) + m_effect.changed = true; +} + +void Joystick::SineEffect::SetState(ControlState state) +{ + if (state) + { + m_effect.effect.type = SDL_HAPTIC_SINE; + m_effect.effect.periodic.length = 250; + } + else + { + m_effect.effect.type = 0; + } + + const Sint16 old = m_effect.effect.periodic.magnitude; + m_effect.effect.periodic.period = 5; + m_effect.effect.periodic.magnitude = (Sint16)(state * 0x5000); + m_effect.effect.periodic.attack_length = 0; + m_effect.effect.periodic.fade_length = 500; + + if (old != m_effect.effect.periodic.magnitude) + m_effect.changed = true; +} + +#ifdef SDL_HAPTIC_SQUARE +void Joystick::SquareEffect::SetState(ControlState state) +{ + if (state) + { + m_effect.effect.type = SDL_HAPTIC_SQUARE; + m_effect.effect.periodic.length = 250; + } + else + { + m_effect.effect.type = 0; + } + + const Sint16 old = m_effect.effect.periodic.magnitude; + m_effect.effect.periodic.period = 5; + m_effect.effect.periodic.magnitude = state * 0x5000; + m_effect.effect.periodic.attack_length = 0; + m_effect.effect.periodic.fade_length = 100; + + if (old != m_effect.effect.periodic.magnitude) + m_effect.changed = true; +} +#endif // defined(SDL_HAPTIC_SQUARE) + +void Joystick::TriangleEffect::SetState(ControlState state) +{ + if (state) + { + m_effect.effect.type = SDL_HAPTIC_TRIANGLE; + m_effect.effect.periodic.length = 250; + } + else + { + m_effect.effect.type = 0; + } + + const Sint16 old = m_effect.effect.periodic.magnitude; + m_effect.effect.periodic.period = 5; + m_effect.effect.periodic.magnitude = (Sint16)(state * 0x5000); + m_effect.effect.periodic.attack_length = 0; + m_effect.effect.periodic.fade_length = 100; + + if (old != m_effect.effect.periodic.magnitude) + m_effect.changed = true; +} +#endif + +bool Joystick::UpdateInput() +{ + // each joystick is doin this, o well + SDL_JoystickUpdate(); + + return true; +} + +bool Joystick::UpdateOutput() +{ +#ifdef USE_SDL_HAPTIC + for (auto &i : m_state_out) + { + if (i.changed) // if SetState was called on this output + { + if (-1 == i.id) // effect isn't currently uploaded + { + if (i.effect.type) // if outputstate is >0 this would be true + { + if ((i.id = SDL_HapticNewEffect(m_haptic, &i.effect)) > -1) // upload the effect + { + SDL_HapticRunEffect(m_haptic, i.id, 1); // run the effect + } + } + } + else // effect is already uploaded + { + if (i.effect.type) // if ouputstate >0 + { + SDL_HapticUpdateEffect(m_haptic, i.id, &i.effect); // update the effect + } + else + { + SDL_HapticStopEffect(m_haptic, i.id); // else, stop and remove the effect + SDL_HapticDestroyEffect(m_haptic, i.id); + i.id = -1; // mark it as not uploaded + } + } + + i.changed = false; + } + } +#endif + return true; +} + +std::string Joystick::GetName() const +{ + return StripSpaces(GetJoystickName(m_sdl_index)); +} + +std::string Joystick::GetSource() const +{ + return "SDL"; +} + +int Joystick::GetId() const +{ + return m_index; +} + +std::string Joystick::Button::GetName() const +{ + std::ostringstream ss; + ss << "Button " << (int)m_index; + return ss.str(); +} + +std::string Joystick::Axis::GetName() const +{ + std::ostringstream ss; + ss << "Axis " << (int)m_index << (m_range<0 ? '-' : '+'); + return ss.str(); +} + +std::string Joystick::Hat::GetName() const +{ + static char tmpstr[] = "Hat . ."; + // I don't think more than 10 hats are supported + tmpstr[4] = (char)('0' + m_index); + tmpstr[6] = "NESW"[m_direction]; + return tmpstr; +} + +ControlState Joystick::Button::GetState() const +{ + return SDL_JoystickGetButton(m_js, m_index); +} + +ControlState Joystick::Axis::GetState() const +{ + return std::max(0.0f, ControlState(SDL_JoystickGetAxis(m_js, m_index)) / m_range); +} + +ControlState Joystick::Hat::GetState() const +{ + return (SDL_JoystickGetHat(m_js, m_index) & (1 << m_direction)) > 0; +} + +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.h b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.h new file mode 100644 index 0000000000..f1714b253e --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.h @@ -0,0 +1,157 @@ +#ifndef _CIFACE_SDL_H_ +#define _CIFACE_SDL_H_ + +#include "../Device.h" + +#include <list> + +#include <SDL.h> + +#if SDL_VERSION_ATLEAST(1, 3, 0) + #define USE_SDL_HAPTIC +#endif + +#ifdef USE_SDL_HAPTIC + #include <SDL_haptic.h> + #define SDL_INIT_FLAGS SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC +#else + #define SDL_INIT_FLAGS SDL_INIT_JOYSTICK +#endif + +namespace ciface +{ +namespace SDL +{ + +void Init( std::vector<Core::Device*>& devices ); + +class Joystick : public Core::Device +{ +private: + +#ifdef USE_SDL_HAPTIC + struct EffectIDState + { + EffectIDState() : effect(SDL_HapticEffect()), id(-1), changed(false) {} + + SDL_HapticEffect effect; + int id; + bool changed; + }; +#endif + + class Button : public Core::Device::Input + { + public: + std::string GetName() const override; + Button(u8 index, SDL_Joystick* js) : m_js(js), m_index(index) {} + ControlState GetState() const override; + private: + SDL_Joystick* const m_js; + const u8 m_index; + }; + + class Axis : public Core::Device::Input + { + public: + std::string GetName() const override; + Axis(u8 index, SDL_Joystick* js, Sint16 range) : m_js(js), m_range(range), m_index(index) {} + ControlState GetState() const override; + private: + SDL_Joystick* const m_js; + const Sint16 m_range; + const u8 m_index; + }; + + class Hat : public Input + { + public: + std::string GetName() const override; + Hat(u8 index, SDL_Joystick* js, u8 direction) : m_js(js), m_direction(direction), m_index(index) {} + ControlState GetState() const override; + private: + SDL_Joystick* const m_js; + const u8 m_direction; + const u8 m_index; + }; + +#ifdef USE_SDL_HAPTIC + class ConstantEffect : public Output + { + public: + std::string GetName() const; + ConstantEffect(EffectIDState& effect) : m_effect(effect) {} + void SetState(ControlState state); + private: + EffectIDState& m_effect; + }; + + class RampEffect : public Output + { + public: + std::string GetName() const; + RampEffect(EffectIDState& effect) : m_effect(effect) {} + void SetState(ControlState state); + private: + EffectIDState& m_effect; + }; + + class SineEffect : public Output + { + public: + std::string GetName() const; + SineEffect(EffectIDState& effect) : m_effect(effect) {} + void SetState(ControlState state); + private: + EffectIDState& m_effect; + }; + +#ifdef SDL_HAPTIC_SQUARE + class SquareEffect : public Output + { + public: + std::string GetName() const; + SquareEffect(EffectIDState& effect) : m_effect(effect) {} + void SetState(ControlState state); + private: + EffectIDState& m_effect; + }; +#endif // defined(SDL_HAPTIC_SQUARE) + + class TriangleEffect : public Output + { + public: + std::string GetName() const; + TriangleEffect(EffectIDState& effect) : m_effect(effect) {} + void SetState(ControlState state); + private: + EffectIDState& m_effect; + }; +#endif + +public: + bool UpdateInput() override; + bool UpdateOutput() override; + + Joystick(SDL_Joystick* const joystick, const int sdl_index, const unsigned int index); + ~Joystick(); + + std::string GetName() const override; + int GetId() const override; + std::string GetSource() const override; + +private: + SDL_Joystick* const m_joystick; + const int m_sdl_index; + const unsigned int m_index; + +#ifdef USE_SDL_HAPTIC + std::list<EffectIDState> m_state_out; + SDL_Haptic* m_haptic; +#endif +}; + +} +} + +#endif diff --git a/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp new file mode 100644 index 0000000000..0aa639aae8 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp @@ -0,0 +1,251 @@ + +#include "XInput.h" + +namespace ciface +{ +namespace XInput +{ + +static const struct +{ + const char* const name; + const WORD bitmask; +} named_buttons[] = +{ + { "Button A", XINPUT_GAMEPAD_A }, + { "Button B", XINPUT_GAMEPAD_B }, + { "Button X", XINPUT_GAMEPAD_X }, + { "Button Y", XINPUT_GAMEPAD_Y }, + { "Pad N", XINPUT_GAMEPAD_DPAD_UP }, + { "Pad S", XINPUT_GAMEPAD_DPAD_DOWN }, + { "Pad W", XINPUT_GAMEPAD_DPAD_LEFT }, + { "Pad E", XINPUT_GAMEPAD_DPAD_RIGHT }, + { "Start", XINPUT_GAMEPAD_START }, + { "Back", XINPUT_GAMEPAD_BACK }, + { "Shoulder L", XINPUT_GAMEPAD_LEFT_SHOULDER }, + { "Shoulder R", XINPUT_GAMEPAD_RIGHT_SHOULDER }, + { "Thumb L", XINPUT_GAMEPAD_LEFT_THUMB }, + { "Thumb R", XINPUT_GAMEPAD_RIGHT_THUMB } +}; + +static const char* const named_triggers[] = +{ + "Trigger L", + "Trigger R" +}; + +static const char* const named_axes[] = +{ + "Left X", + "Left Y", + "Right X", + "Right Y" +}; + +static const char* const named_motors[] = +{ + "Motor L", + "Motor R" +}; + +static HMODULE hXInput = nullptr; + +typedef decltype(&XInputGetCapabilities) XInputGetCapabilities_t; +typedef decltype(&XInputSetState) XInputSetState_t; +typedef decltype(&XInputGetState) XInputGetState_t; + +static XInputGetCapabilities_t PXInputGetCapabilities = nullptr; +static XInputSetState_t PXInputSetState = nullptr; +static XInputGetState_t PXInputGetState = nullptr; + +void Init(std::vector<Core::Device*>& devices) +{ + if (!hXInput) + { + // Try for the most recent version we were compiled against (will only work if running on Win8+) + hXInput = ::LoadLibrary(XINPUT_DLL); + if (!hXInput) + { + // Drop back to DXSDK June 2010 version. Requires DX June 2010 redist. + hXInput = ::LoadLibrary(TEXT("xinput1_3.dll")); + if (!hXInput) + { + return; + } + } + + PXInputGetCapabilities = (XInputGetCapabilities_t)::GetProcAddress(hXInput, "XInputGetCapabilities"); + PXInputSetState = (XInputSetState_t)::GetProcAddress(hXInput, "XInputSetState"); + PXInputGetState = (XInputGetState_t)::GetProcAddress(hXInput, "XInputGetState"); + if (!PXInputGetCapabilities || + !PXInputSetState || + !PXInputGetState) + { + ::FreeLibrary(hXInput); + hXInput = nullptr; + return; + } + } + + XINPUT_CAPABILITIES caps; + for (int i = 0; i != 4; ++i) + if (ERROR_SUCCESS == PXInputGetCapabilities(i, 0, &caps)) + devices.push_back(new Device(caps, i)); +} + +void DeInit() +{ + if (hXInput) + { + ::FreeLibrary(hXInput); + hXInput = nullptr; + } +} + +Device::Device(const XINPUT_CAPABILITIES& caps, u8 index) + : m_index(index), m_subtype(caps.SubType) +{ + ZeroMemory(&m_state_out, sizeof(m_state_out)); + ZeroMemory(&m_current_state_out, sizeof(m_current_state_out)); + + // XInputGetCaps seems to always claim all capabilities are supported + // but I will leave all this stuff in, incase m$ fixes xinput up a bit + + // get supported buttons + for (int i = 0; i != sizeof(named_buttons)/sizeof(*named_buttons); ++i) + { + if (named_buttons[i].bitmask & caps.Gamepad.wButtons) + AddInput(new Button(i, m_state_in.Gamepad.wButtons)); + } + + // get supported triggers + for (int i = 0; i != sizeof(named_triggers)/sizeof(*named_triggers); ++i) + { + //BYTE val = (&caps.Gamepad.bLeftTrigger)[i]; // should be max value / MSDN lies + if ((&caps.Gamepad.bLeftTrigger)[i]) + AddInput(new Trigger(i, (&m_state_in.Gamepad.bLeftTrigger)[i], 255 )); + } + + // get supported axes + for (int i = 0; i != sizeof(named_axes)/sizeof(*named_axes); ++i) + { + //SHORT val = (&caps.Gamepad.sThumbLX)[i]; // xinput doesn't give the range / MSDN is a liar + if ((&caps.Gamepad.sThumbLX)[i]) + { + const SHORT& ax = (&m_state_in.Gamepad.sThumbLX)[i]; + + // each axis gets a negative and a positive input instance associated with it + AddInput(new Axis(i, ax, -32768)); + AddInput(new Axis(i, ax, 32767)); + } + } + + // get supported motors + for (int i = 0; i != sizeof(named_motors)/sizeof(*named_motors); ++i) + { + //WORD val = (&caps.Vibration.wLeftMotorSpeed)[i]; // should be max value / nope, more lies + if ((&caps.Vibration.wLeftMotorSpeed)[i]) + AddOutput(new Motor(i, (&m_state_out.wLeftMotorSpeed)[i], 65535)); + } + + ClearInputState(); +} + +void Device::ClearInputState() +{ + ZeroMemory(&m_state_in, sizeof(m_state_in)); +} + +std::string Device::GetName() const +{ + switch (m_subtype) + { + case XINPUT_DEVSUBTYPE_GAMEPAD: return "Gamepad"; break; + case XINPUT_DEVSUBTYPE_WHEEL: return "Wheel"; break; + case XINPUT_DEVSUBTYPE_ARCADE_STICK: return "Arcade Stick"; break; + case XINPUT_DEVSUBTYPE_FLIGHT_STICK: return "Flight Stick"; break; + case XINPUT_DEVSUBTYPE_DANCE_PAD: return "Dance Pad"; break; + case XINPUT_DEVSUBTYPE_GUITAR: return "Guitar"; break; + case XINPUT_DEVSUBTYPE_DRUM_KIT: return "Drum Kit"; break; + default: return "Device"; break; + } +} + +int Device::GetId() const +{ + return m_index; +} + +std::string Device::GetSource() const +{ + return "XInput"; +} + +// Update I/O + +bool Device::UpdateInput() +{ + return (ERROR_SUCCESS == PXInputGetState(m_index, &m_state_in)); +} + +bool Device::UpdateOutput() +{ + // this if statement is to make rumble work better when multiple ControllerInterfaces are using the device + // only calls XInputSetState if the state changed + if (memcmp(&m_state_out, &m_current_state_out, sizeof(m_state_out))) + { + m_current_state_out = m_state_out; + return (ERROR_SUCCESS == PXInputSetState(m_index, &m_state_out)); + } + else + { + return true; + } +} + +// GET name/source/id + +std::string Device::Button::GetName() const +{ + return named_buttons[m_index].name; +} + +std::string Device::Axis::GetName() const +{ + return std::string(named_axes[m_index]) + (m_range<0 ? '-' : '+'); +} + +std::string Device::Trigger::GetName() const +{ + return named_triggers[m_index]; +} + +std::string Device::Motor::GetName() const +{ + return named_motors[m_index]; +} + +// GET / SET STATES + +ControlState Device::Button::GetState() const +{ + return (m_buttons & named_buttons[m_index].bitmask) > 0; +} + +ControlState Device::Trigger::GetState() const +{ + return ControlState(m_trigger) / m_range; +} + +ControlState Device::Axis::GetState() const +{ + return std::max( 0.0f, ControlState(m_axis) / m_range ); +} + +void Device::Motor::SetState(ControlState state) +{ + m_motor = (WORD)(state * m_range); +} + +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h new file mode 100644 index 0000000000..17e49e5e2b --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h @@ -0,0 +1,101 @@ +// XInput suffers a similar issue as XAudio2. Since Win8, it is part of the OS. +// However, unlike XAudio2 they have not made the API incompatible - so we just +// compile against the latest version and fall back to dynamically loading the +// old DLL. + +#ifndef _CIFACE_XINPUT_H_ +#define _CIFACE_XINPUT_H_ + +#include "../Device.h" + +#define NOMINMAX +#include <Windows.h> +#include <XInput.h> + +#ifndef XINPUT_DEVSUBTYPE_FLIGHT_STICK +#error You are building this module against the wrong version of DirectX. You probably need to remove DXSDK_DIR from your include path and/or _WIN32_WINNT is wrong. +#endif + +namespace ciface +{ +namespace XInput +{ + +void Init(std::vector<Core::Device*>& devices); +void DeInit(); + +class Device : public Core::Device +{ +private: + class Button : public Core::Device::Input + { + public: + std::string GetName() const; + Button(u8 index, const WORD& buttons) : m_index(index), m_buttons(buttons) {} + ControlState GetState() const; + private: + const WORD& m_buttons; + u8 m_index; + }; + + class Axis : public Core::Device::Input + { + public: + std::string GetName() const; + Axis(u8 index, const SHORT& axis, SHORT range) : m_index(index), m_axis(axis), m_range(range) {} + ControlState GetState() const; + private: + const SHORT& m_axis; + const SHORT m_range; + const u8 m_index; + }; + + class Trigger : public Core::Device::Input + { + public: + std::string GetName() const; + Trigger(u8 index, const BYTE& trigger, BYTE range) : m_index(index), m_trigger(trigger), m_range(range) {} + ControlState GetState() const; + private: + const BYTE& m_trigger; + const BYTE m_range; + const u8 m_index; + }; + + class Motor : public Core::Device::Output + { + public: + std::string GetName() const; + Motor(u8 index, WORD& motor, WORD range) : m_index(index), m_motor(motor), m_range(range) {} + void SetState(ControlState state); + private: + WORD& m_motor; + const WORD m_range; + const u8 m_index; + }; + +public: + bool UpdateInput(); + bool UpdateOutput(); + + void ClearInputState(); + + Device(const XINPUT_CAPABILITIES& capabilities, u8 index); + + std::string GetName() const; + int GetId() const; + std::string GetSource() const; + +private: + XINPUT_STATE m_state_in; + XINPUT_VIBRATION m_state_out, m_current_state_out; + const BYTE m_subtype; + const u8 m_index; +}; + + +} +} + + +#endif diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp new file mode 100644 index 0000000000..23cadd3ee6 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp @@ -0,0 +1,378 @@ +// Copyright 2013 Max Eliaser +// Licensed under the GNU General Public License, version 2 or higher. +// Refer to the license.txt file included. + + +#include "XInput2.h" +#include <X11/XKBlib.h> +#include <cmath> + +// This is an input plugin using the XInput 2.0 extension to the X11 protocol, +// loosely based on the old XLib plugin. (Has nothing to do with the XInput +// API on Windows.) + +// This plugin creates one KeyboardMouse object for each master pointer/ +// keyboard pair. Each KeyboardMouse object exports four types of controls: +// * Mouse button controls: hardcoded at five of them, but could be made to +// support infinitely many mouse buttons in theory; XInput2 has no limit. +// * Mouse cursor controls: one for each cardinal direction. Calculated by +// comparing the absolute position of the mouse pointer on screen to the +// center of the emulator window. +// * Mouse axis controls: one for each cardinal direction. Calculated using +// a running average of relative mouse motion on each axis. +// * Key controls: these correspond to a limited subset of the keyboard +// keys. + + +// Mouse axis control tuning. Unlike absolute mouse position, relative mouse +// motion data needs to be tweaked and smoothed out a bit to be usable. + +// Mouse axis control output is simply divided by this number. In practice, +// that just means you can use a smaller "dead zone" if you bind axis controls +// to a joystick. No real need to make this customizable. +#define MOUSE_AXIS_SENSITIVITY 8.0f + +// The mouse axis controls use a weighted running average. Each frame, the new +// value is the average of the old value and the amount of relative mouse +// motion during that frame. The old value is weighted by a ratio of +// MOUSE_AXIS_SMOOTHING:1 compared to the new value. Increasing +// MOUSE_AXIS_SMOOTHING makes the controls smoother, decreasing it makes them +// more responsive. This might be useful as a user-customizable option. +#define MOUSE_AXIS_SMOOTHING 1.5f + +namespace ciface +{ +namespace XInput2 +{ + +// This function will add zero or more KeyboardMouse objects to devices. +void Init(std::vector<Core::Device*>& devices, void* const hwnd) +{ + Display* dpy; + + dpy = XOpenDisplay(NULL); + + // xi_opcode is important; it will be used to identify XInput events by + // the polling loop in UpdateInput. + int xi_opcode, event, error; + + // verify that the XInput extension is available + if (!XQueryExtension(dpy, "XInputExtension", &xi_opcode, &event, &error)) + return; + + // verify that the XInput extension is at at least version 2.0 + int major = 2, minor = 0; + + if (XIQueryVersion(dpy, &major, &minor) != Success) + return; + + // register all master devices with Dolphin + + XIDeviceInfo* all_masters; + XIDeviceInfo* current_master; + int num_masters; + + all_masters = XIQueryDevice(dpy, XIAllMasterDevices, &num_masters); + + for (int i = 0; i < num_masters; i++) + { + current_master = &all_masters[i]; + if (current_master->use == XIMasterPointer) + // Since current_master is a master pointer, its attachment must + // be a master keyboard. + devices.push_back(new KeyboardMouse((Window)hwnd, xi_opcode, current_master->deviceid, current_master->attachment)); + } + + XCloseDisplay(dpy); + + XIFreeDeviceInfo(all_masters); +} + +// Apply the event mask to the device and all its slaves. Only used in the +// constructor. Remember, each KeyboardMouse has its own copy of the event +// stream, which is how multiple event masks can "coexist." +void KeyboardMouse::SelectEventsForDevice(Window window, XIEventMask *mask, int deviceid) +{ + // Set the event mask for the master device. + + mask->deviceid = deviceid; + XISelectEvents(m_display, window, mask, 1); + + // Query all the master device's slaves and set the same event mask for + // those too. There are two reasons we want to do this. For mouse devices, + // we want the raw motion events, and only slaves (i.e. physical hardware + // devices) emit those. For keyboard devices, selecting slaves avoids + // dealing with key focus. + + XIDeviceInfo* all_slaves; + XIDeviceInfo* current_slave; + int num_slaves; + + all_slaves = XIQueryDevice(m_display, XIAllDevices, &num_slaves); + + for (int i = 0; i < num_slaves; i++) + { + current_slave = &all_slaves[i]; + if ((current_slave->use != XISlavePointer && current_slave->use != XISlaveKeyboard) || current_slave->attachment != deviceid) + continue; + mask->deviceid = current_slave->deviceid; + XISelectEvents(m_display, window, mask, 1); + } + + XIFreeDeviceInfo(all_slaves); +} + +KeyboardMouse::KeyboardMouse(Window window, int opcode, int pointer, int keyboard) + : m_window(window), xi_opcode(opcode), pointer_deviceid(pointer), keyboard_deviceid(keyboard) +{ + memset(&m_state, 0, sizeof(m_state)); + + // The cool thing about each KeyboardMouse object having its own Display + // is that each one gets its own separate copy of the X11 event stream, + // which it can individually filter to get just the events it's interested + // in. So be aware that each KeyboardMouse object actually has its own X11 + // "context." + m_display = XOpenDisplay(NULL); + + int min_keycode, max_keycode; + XDisplayKeycodes(m_display, &min_keycode, &max_keycode); + + int unused; // should always be 1 + XIDeviceInfo* pointer_device = XIQueryDevice(m_display, pointer_deviceid, &unused); + name = std::string(pointer_device->name); + XIFreeDeviceInfo(pointer_device); + + XIEventMask mask; + unsigned char mask_buf[(XI_LASTEVENT + 7)/8]; + + mask.mask_len = sizeof(mask_buf); + mask.mask = mask_buf; + memset(mask_buf, 0, sizeof(mask_buf)); + + XISetMask(mask_buf, XI_ButtonPress); + XISetMask(mask_buf, XI_ButtonRelease); + XISetMask(mask_buf, XI_RawMotion); + XISetMask(mask_buf, XI_KeyPress); + XISetMask(mask_buf, XI_KeyRelease); + + SelectEventsForDevice(DefaultRootWindow(m_display), &mask, pointer_deviceid); + SelectEventsForDevice(DefaultRootWindow(m_display), &mask, keyboard_deviceid); + + // Keyboard Keys + for (int i = min_keycode; i <= max_keycode; ++i) + { + Key* temp_key = new Key(m_display, i, m_state.keyboard); + if (temp_key->m_keyname.length()) + AddInput(temp_key); + else + delete temp_key; + } + + // Mouse Buttons + for (int i = 0; i < 5; i++) + AddInput(new Button(i, m_state.buttons)); + + // Mouse Cursor, X-/+ and Y-/+ + for (int i = 0; i != 4; ++i) + AddInput(new Cursor(!!(i & 2), !!(i & 1), (&m_state.cursor.x)[!!(i & 2)])); + + // Mouse Axis, X-/+ and Y-/+ + for (int i = 0; i != 4; ++i) + AddInput(new Axis(!!(i & 2), !!(i & 1), (&m_state.axis.x)[!!(i & 2)])); +} + +KeyboardMouse::~KeyboardMouse() +{ + XCloseDisplay(m_display); +} + +// Update the mouse cursor controls +void KeyboardMouse::UpdateCursor() +{ + double root_x, root_y, win_x, win_y; + Window root, child; + + // unused-- we're not interested in button presses here, as those are + // updated using events + XIButtonState button_state; + XIModifierState mods; + XIGroupState group; + + XIQueryPointer(m_display, pointer_deviceid, m_window, &root, &child, &root_x, &root_y, &win_x, &win_y, &button_state, &mods, &group); + + free (button_state.mask); + + XWindowAttributes win_attribs; + XGetWindowAttributes(m_display, m_window, &win_attribs); + + // the mouse position as a range from -1 to 1 + m_state.cursor.x = win_x / (float)win_attribs.width * 2 - 1; + m_state.cursor.y = win_y / (float)win_attribs.height * 2 - 1; +} + +bool KeyboardMouse::UpdateInput() +{ + XFlush(m_display); + + // Get the absolute position of the mouse pointer + UpdateCursor(); + + // for the axis controls + float delta_x = 0.0f, delta_y = 0.0f; + double delta_delta; + + // Iterate through the event queue - update the axis controls, mouse + // button controls, and keyboard controls. + XEvent event; + while (XPending(m_display)) + { + XNextEvent(m_display, &event); + + if (event.xcookie.type != GenericEvent) + continue; + if (event.xcookie.extension != xi_opcode) + continue; + if (!XGetEventData(m_display, &event.xcookie)) + continue; + + // only one of these will get used + XIDeviceEvent* dev_event = (XIDeviceEvent*)event.xcookie.data; + XIRawEvent* raw_event = (XIRawEvent*)event.xcookie.data; + + switch (event.xcookie.evtype) + { + case XI_ButtonPress: + m_state.buttons |= 1<<(dev_event->detail-1); + break; + case XI_ButtonRelease: + m_state.buttons &= ~(1<<(dev_event->detail-1)); + break; + case XI_KeyPress: + m_state.keyboard[dev_event->detail / 8] |= 1<<(dev_event->detail % 8); + break; + case XI_KeyRelease: + m_state.keyboard[dev_event->detail / 8] &= ~(1<<(dev_event->detail % 8)); + break; + case XI_RawMotion: + // always safe because there is always at least one byte in + // raw_event->valuators.mask, and if a bit is set in the mask, + // then the value in raw_values is also available. + if (XIMaskIsSet(raw_event->valuators.mask, 0)) + { + delta_delta = raw_event->raw_values[0]; + // test for inf and nan + if (delta_delta == delta_delta && 1+delta_delta != delta_delta) + delta_x += delta_delta; + } + if (XIMaskIsSet(raw_event->valuators.mask, 1)) + { + delta_delta = raw_event->raw_values[1]; + // test for inf and nan + if (delta_delta == delta_delta && 1+delta_delta != delta_delta) + delta_y += delta_delta; + } + break; + } + + XFreeEventData(m_display, &event.xcookie); + } + + // apply axis smoothing + m_state.axis.x *= MOUSE_AXIS_SMOOTHING; + m_state.axis.x += delta_x; + m_state.axis.x /= MOUSE_AXIS_SMOOTHING+1.0f; + m_state.axis.y *= MOUSE_AXIS_SMOOTHING; + m_state.axis.y += delta_y; + m_state.axis.y /= MOUSE_AXIS_SMOOTHING+1.0f; + + return true; +} + +bool KeyboardMouse::UpdateOutput() +{ + return true; +} + +std::string KeyboardMouse::GetName() const +{ + // This is the name string we got from the X server for this master + // pointer/keyboard pair. + return name; +} + +std::string KeyboardMouse::GetSource() const +{ + return "XInput2"; +} + +int KeyboardMouse::GetId() const +{ + return -1; +} + +KeyboardMouse::Key::Key(Display* const display, KeyCode keycode, const char* keyboard) + : m_display(display), m_keyboard(keyboard), m_keycode(keycode) +{ + int i = 0; + KeySym keysym = 0; + do + { + keysym = XkbKeycodeToKeysym(m_display, keycode, i, 0); + i++; + } + while (keysym == NoSymbol && i < 8); + + // Convert to upper case for the keyname + if (keysym >= 97 && keysym <= 122) + keysym -= 32; + + // 0x0110ffff is the top of the unicode character range according + // to keysymdef.h although it is probably more than we need. + if (keysym == NoSymbol || keysym > 0x0110ffff || + XKeysymToString(keysym) == NULL) + m_keyname = std::string(); + else + m_keyname = std::string(XKeysymToString(keysym)); +} + +ControlState KeyboardMouse::Key::GetState() const +{ + return (m_keyboard[m_keycode / 8] & (1 << (m_keycode % 8))) != 0; +} + +KeyboardMouse::Button::Button(unsigned int index, unsigned int& buttons) + : m_buttons(buttons), m_index(index) +{ + // this will be a problem if we remove the hardcoded five-button limit + name = std::string("Click ") + (char)('1' + m_index); +} + +ControlState KeyboardMouse::Button::GetState() const +{ + return ((m_buttons & (1 << m_index)) != 0); +} + +KeyboardMouse::Cursor::Cursor(u8 index, bool positive, const float& cursor) + : m_cursor(cursor), m_index(index), m_positive(positive) +{ + name = std::string("Cursor ") + (char)('X' + m_index) + (m_positive ? '+' : '-'); +} + +ControlState KeyboardMouse::Cursor::GetState() const +{ + return std::max(0.0f, m_cursor / (m_positive ? 1.0f : -1.0f)); +} + +KeyboardMouse::Axis::Axis(u8 index, bool positive, const float& axis) + : m_axis(axis), m_index(index), m_positive(positive) +{ + name = std::string("Axis ") + (char)('X' + m_index) + (m_positive ? '+' : '-'); +} + +ControlState KeyboardMouse::Axis::GetState() const +{ + return std::max(0.0f, m_axis / (m_positive ? MOUSE_AXIS_SENSITIVITY : -MOUSE_AXIS_SENSITIVITY)); +} + +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h new file mode 100644 index 0000000000..dfb8fa285d --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h @@ -0,0 +1,124 @@ +// Copyright 2013 Max Eliaser +// Licensed under the GNU General Public License, version 2 or higher. +// Refer to the license.txt file included. + +// See XInput2.cpp for extensive documentation. + +#ifndef _CIFACE_X11_XINPUT2_H_ +#define _CIFACE_X11_XINPUT2_H_ + +#include "../Device.h" + +extern "C" { +#include <X11/Xlib.h> +#include <X11/extensions/XInput2.h> +#include <X11/keysym.h> +} + +namespace ciface +{ +namespace XInput2 +{ + +void Init(std::vector<Core::Device*>& devices, void* const hwnd); + +class KeyboardMouse : public Core::Device +{ + +private: + struct State + { + char keyboard[32]; + unsigned int buttons; + struct + { + float x, y; + } cursor, axis; + }; + + class Key : public Input + { + friend class KeyboardMouse; + public: + std::string GetName() const { return m_keyname; } + Key(Display* display, KeyCode keycode, const char* keyboard); + ControlState GetState() const; + + private: + std::string m_keyname; + Display* const m_display; + const char* const m_keyboard; + const KeyCode m_keycode; + }; + + class Button : public Input + { + public: + std::string GetName() const { return name; } + Button(unsigned int index, unsigned int& buttons); + ControlState GetState() const; + + private: + const unsigned int& m_buttons; + const unsigned int m_index; + std::string name; + }; + + class Cursor : public Input + { + public: + std::string GetName() const { return name; } + bool IsDetectable() { return false; } + Cursor(u8 index, bool positive, const float& cursor); + ControlState GetState() const; + + private: + const float& m_cursor; + const u8 m_index; + const bool m_positive; + std::string name; + }; + + class Axis : public Input + { + public: + std::string GetName() const { return name; } + bool IsDetectable() { return false; } + Axis(u8 index, bool positive, const float& axis); + ControlState GetState() const; + + private: + const float& m_axis; + const u8 m_index; + const bool m_positive; + std::string name; + }; + +private: + void SelectEventsForDevice(Window window, XIEventMask *mask, int deviceid); + void UpdateCursor(); + +public: + bool UpdateInput(); + bool UpdateOutput(); + + KeyboardMouse(Window window, int opcode, int pointer_deviceid, int keyboard_deviceid); + ~KeyboardMouse(); + + std::string GetName() const; + std::string GetSource() const; + int GetId() const; + +private: + Window m_window; + Display* m_display; + State m_state; + int xi_opcode; + const int pointer_deviceid, keyboard_deviceid; + std::string name; +}; + +} +} + +#endif diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/Xlib.cpp b/Source/Core/InputCommon/ControllerInterface/Xlib/Xlib.cpp new file mode 100644 index 0000000000..e507b88c9f --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/Xlib.cpp @@ -0,0 +1,162 @@ +#include "Xlib.h" + +#include <X11/XKBlib.h> + +namespace ciface +{ +namespace Xlib +{ + +void Init(std::vector<Core::Device*>& devices, void* const hwnd) +{ + devices.push_back(new KeyboardMouse((Window)hwnd)); +} + +KeyboardMouse::KeyboardMouse(Window window) : m_window(window) +{ + memset(&m_state, 0, sizeof(m_state)); + + m_display = XOpenDisplay(NULL); + + int min_keycode, max_keycode; + XDisplayKeycodes(m_display, &min_keycode, &max_keycode); + + // Keyboard Keys + for (int i = min_keycode; i <= max_keycode; ++i) + { + Key *temp_key = new Key(m_display, i, m_state.keyboard); + if (temp_key->m_keyname.length()) + AddInput(temp_key); + else + delete temp_key; + } + + // Mouse Buttons + AddInput(new Button(Button1Mask, m_state.buttons)); + AddInput(new Button(Button2Mask, m_state.buttons)); + AddInput(new Button(Button3Mask, m_state.buttons)); + AddInput(new Button(Button4Mask, m_state.buttons)); + AddInput(new Button(Button5Mask, m_state.buttons)); + + // Mouse Cursor, X-/+ and Y-/+ + for (int i = 0; i != 4; ++i) + AddInput(new Cursor(!!(i & 2), !!(i & 1), (&m_state.cursor.x)[!!(i & 2)])); +} + +KeyboardMouse::~KeyboardMouse() +{ + XCloseDisplay(m_display); +} + +bool KeyboardMouse::UpdateInput() +{ + XQueryKeymap(m_display, m_state.keyboard); + + int root_x, root_y, win_x, win_y; + Window root, child; + XQueryPointer(m_display, m_window, &root, &child, &root_x, &root_y, &win_x, &win_y, &m_state.buttons); + + // update mouse cursor + XWindowAttributes win_attribs; + XGetWindowAttributes(m_display, m_window, &win_attribs); + + // the mouse position as a range from -1 to 1 + m_state.cursor.x = (float)win_x / (float)win_attribs.width * 2 - 1; + m_state.cursor.y = (float)win_y / (float)win_attribs.height * 2 - 1; + + return true; +} + +bool KeyboardMouse::UpdateOutput() +{ + return true; +} + + +std::string KeyboardMouse::GetName() const +{ + return "Keyboard Mouse"; +} + +std::string KeyboardMouse::GetSource() const +{ + return "Xlib"; +} + +int KeyboardMouse::GetId() const +{ + return 0; +} + +KeyboardMouse::Key::Key(Display* const display, KeyCode keycode, const char* keyboard) + : m_display(display), m_keyboard(keyboard), m_keycode(keycode) +{ + int i = 0; + KeySym keysym = 0; + do + { + keysym = XkbKeycodeToKeysym(m_display, keycode, i, 0); + i++; + } + while (keysym == NoSymbol && i < 8); + + // Convert to upper case for the keyname + if (keysym >= 97 && keysym <= 122) + keysym -= 32; + + // 0x0110ffff is the top of the unicode character range according + // to keysymdef.h although it is probably more than we need. + if (keysym == NoSymbol || keysym > 0x0110ffff || + XKeysymToString(keysym) == NULL) + m_keyname = std::string(); + else + m_keyname = std::string(XKeysymToString(keysym)); +} + +ControlState KeyboardMouse::Key::GetState() const +{ + return (m_keyboard[m_keycode / 8] & (1 << (m_keycode % 8))) != 0; +} + +ControlState KeyboardMouse::Button::GetState() const +{ + return ((m_buttons & m_index) != 0); +} + +ControlState KeyboardMouse::Cursor::GetState() const +{ + return std::max(0.0f, m_cursor / (m_positive ? 1.0f : -1.0f)); +} + +std::string KeyboardMouse::Key::GetName() const +{ + return m_keyname; +} + +std::string KeyboardMouse::Cursor::GetName() const +{ + static char tmpstr[] = "Cursor .."; + tmpstr[7] = (char)('X' + m_index); + tmpstr[8] = (m_positive ? '+' : '-'); + return tmpstr; +} + +std::string KeyboardMouse::Button::GetName() const +{ + char button = '0'; + switch (m_index) + { + case Button1Mask: button = '1'; break; + case Button2Mask: button = '2'; break; + case Button3Mask: button = '3'; break; + case Button4Mask: button = '4'; break; + case Button5Mask: button = '5'; break; + } + + static char tmpstr[] = "Click ."; + tmpstr[6] = button; + return tmpstr; +} + +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/Xlib.h b/Source/Core/InputCommon/ControllerInterface/Xlib/Xlib.h new file mode 100644 index 0000000000..35b6100081 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/Xlib.h @@ -0,0 +1,93 @@ +#ifndef _CIFACE_XLIB_H_ +#define _CIFACE_XLIB_H_ + +#include "../Device.h" + +#include <X11/Xlib.h> +#include <X11/keysym.h> + +namespace ciface +{ +namespace Xlib +{ + +void Init(std::vector<Core::Device*>& devices, void* const hwnd); + +class KeyboardMouse : public Core::Device +{ + +private: + struct State + { + char keyboard[32]; + unsigned int buttons; + struct + { + float x, y; + } cursor; + }; + + class Key : public Input + { + friend class KeyboardMouse; + public: + std::string GetName() const; + Key(Display* display, KeyCode keycode, const char* keyboard); + ControlState GetState() const; + + private: + std::string m_keyname; + Display* const m_display; + const char* const m_keyboard; + const KeyCode m_keycode; + }; + + class Button : public Input + { + public: + std::string GetName() const; + Button(unsigned int index, unsigned int& buttons) + : m_buttons(buttons), m_index(index) {} + ControlState GetState() const; + + private: + const unsigned int& m_buttons; + const unsigned int m_index; + }; + + class Cursor : public Input + { + public: + std::string GetName() const; + bool IsDetectable() { return false; } + Cursor(u8 index, bool positive, const float& cursor) + : m_cursor(cursor), m_index(index), m_positive(positive) {} + ControlState GetState() const; + + private: + const float& m_cursor; + const u8 m_index; + const bool m_positive; + }; + +public: + bool UpdateInput(); + bool UpdateOutput(); + + KeyboardMouse(Window window); + ~KeyboardMouse(); + + std::string GetName() const; + std::string GetSource() const; + int GetId() const; + +private: + Window m_window; + Display* m_display; + State m_state; +}; + +} +} + +#endif |
