summaryrefslogtreecommitdiff
path: root/Source/Core/InputCommon/ControllerInterface
diff options
context:
space:
mode:
authorJasper St. Pierre <jstpierre@mecheye.net>2013-12-07 15:14:29 -0500
committerJasper St. Pierre <jstpierre@mecheye.net>2013-12-31 14:03:19 -0500
commit34692ab826abc8f8faa61bdb2280b742424528f1 (patch)
treeb0eedc645badbea316ff59c125eaf808b28777ec /Source/Core/InputCommon/ControllerInterface
parent43e618682e8093602fc83dfa902ee7ae3fd3a4f6 (diff)
Remove unnecessary Src/ folders
Diffstat (limited to 'Source/Core/InputCommon/ControllerInterface')
-rw-r--r--Source/Core/InputCommon/ControllerInterface/Android/Android.cpp95
-rw-r--r--Source/Core/InputCommon/ControllerInterface/Android/Android.h71
-rw-r--r--Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp316
-rw-r--r--Source/Core/InputCommon/ControllerInterface/ControllerInterface.h131
-rw-r--r--Source/Core/InputCommon/ControllerInterface/DInput/DInput.cpp65
-rw-r--r--Source/Core/InputCommon/ControllerInterface/DInput/DInput.h31
-rw-r--r--Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp600
-rw-r--r--Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.h109
-rw-r--r--Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp311
-rw-r--r--Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.h113
-rw-r--r--Source/Core/InputCommon/ControllerInterface/DInput/NamedKeys.h144
-rw-r--r--Source/Core/InputCommon/ControllerInterface/Device.cpp193
-rw-r--r--Source/Core/InputCommon/ControllerInterface/Device.h171
-rw-r--r--Source/Core/InputCommon/ControllerInterface/ExpressionParser.cpp577
-rw-r--r--Source/Core/InputCommon/ControllerInterface/ExpressionParser.h69
-rw-r--r--Source/Core/InputCommon/ControllerInterface/OSX/OSX.h16
-rw-r--r--Source/Core/InputCommon/ControllerInterface/OSX/OSX.mm213
-rw-r--r--Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.h80
-rw-r--r--Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.mm274
-rw-r--r--Source/Core/InputCommon/ControllerInterface/OSX/OSXKeyboard.h71
-rw-r--r--Source/Core/InputCommon/ControllerInterface/OSX/OSXKeyboard.mm276
-rw-r--r--Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp406
-rw-r--r--Source/Core/InputCommon/ControllerInterface/SDL/SDL.h157
-rw-r--r--Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp251
-rw-r--r--Source/Core/InputCommon/ControllerInterface/XInput/XInput.h101
-rw-r--r--Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp378
-rw-r--r--Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h124
-rw-r--r--Source/Core/InputCommon/ControllerInterface/Xlib/Xlib.cpp162
-rw-r--r--Source/Core/InputCommon/ControllerInterface/Xlib/Xlib.h93
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 = &params; // 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 = &params; // 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 = &params; // 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(&params, 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