InputCommon: Detect when evdev exposes acceleration/gyroscope data.
This commit is contained in:
parent
d8c62b5965
commit
1180c231a6
|
@ -17,6 +17,7 @@ namespace MathUtil
|
||||||
{
|
{
|
||||||
constexpr double TAU = 6.2831853071795865;
|
constexpr double TAU = 6.2831853071795865;
|
||||||
constexpr double PI = TAU / 2;
|
constexpr double PI = TAU / 2;
|
||||||
|
constexpr double GRAVITY_ACCELERATION = 9.80665;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr auto Sign(const T& val) -> decltype((T{} < val) - (val < T{}))
|
constexpr auto Sign(const T& val) -> decltype((T{} < val) - (val < T{}))
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
|
#include "Common/MathUtil.h"
|
||||||
#include "Common/Matrix.h"
|
#include "Common/Matrix.h"
|
||||||
#include "Core/HW/WiimoteCommon/DataReport.h"
|
#include "Core/HW/WiimoteCommon/DataReport.h"
|
||||||
#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h"
|
#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h"
|
||||||
|
@ -18,7 +19,7 @@
|
||||||
|
|
||||||
namespace WiimoteEmu
|
namespace WiimoteEmu
|
||||||
{
|
{
|
||||||
constexpr double GRAVITY_ACCELERATION = 9.80665;
|
using MathUtil::GRAVITY_ACCELERATION;
|
||||||
|
|
||||||
struct PositionalState
|
struct PositionalState
|
||||||
{
|
{
|
||||||
|
|
|
@ -108,7 +108,7 @@ private:
|
||||||
int m_touch_y;
|
int m_touch_y;
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr double GRAVITY_ACCELERATION = 9.80665;
|
using MathUtil::GRAVITY_ACCELERATION;
|
||||||
static constexpr char DEFAULT_SERVER_ADDRESS[] = "127.0.0.1";
|
static constexpr char DEFAULT_SERVER_ADDRESS[] = "127.0.0.1";
|
||||||
static constexpr u16 DEFAULT_SERVER_PORT = 26760;
|
static constexpr u16 DEFAULT_SERVER_PORT = 26760;
|
||||||
static constexpr auto SERVER_REREGISTER_INTERVAL = std::chrono::seconds{1};
|
static constexpr auto SERVER_REREGISTER_INTERVAL = std::chrono::seconds{1};
|
||||||
|
|
|
@ -25,6 +25,121 @@
|
||||||
|
|
||||||
namespace ciface::evdev
|
namespace ciface::evdev
|
||||||
{
|
{
|
||||||
|
class Input : public Core::Device::Input
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Input(u16 code, libevdev* dev) : m_code(code), m_dev(dev) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const u16 m_code;
|
||||||
|
libevdev* const m_dev;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Button final : public Input
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Button(u8 index, u16 code, libevdev* dev) : Input(code, dev), m_index(index) {}
|
||||||
|
|
||||||
|
std::string GetName() const override
|
||||||
|
{
|
||||||
|
// Buttons below 0x100 are mostly keyboard keys, and the names make sense
|
||||||
|
if (m_code < 0x100)
|
||||||
|
{
|
||||||
|
const char* name = libevdev_event_code_get_name(EV_KEY, m_code);
|
||||||
|
if (name)
|
||||||
|
return std::string(StripSpaces(name));
|
||||||
|
}
|
||||||
|
// But controllers use codes above 0x100, and the standard label often doesn't match.
|
||||||
|
// We are better off with Button 0 and so on.
|
||||||
|
return "Button " + std::to_string(m_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlState GetState() const override
|
||||||
|
{
|
||||||
|
int value = 0;
|
||||||
|
libevdev_fetch_event_value(m_dev, EV_KEY, m_code, &value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const u8 m_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AnalogInput : public Input
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Input::Input;
|
||||||
|
|
||||||
|
ControlState GetState() const override
|
||||||
|
{
|
||||||
|
int value = 0;
|
||||||
|
libevdev_fetch_event_value(m_dev, EV_ABS, m_code, &value);
|
||||||
|
|
||||||
|
return ControlState(value - m_base) / m_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int m_range;
|
||||||
|
int m_base;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Axis final : public AnalogInput
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Axis(u8 index, u16 code, bool upper, libevdev* dev) : AnalogInput(code, dev), m_index(index)
|
||||||
|
{
|
||||||
|
const int min = libevdev_get_abs_minimum(m_dev, m_code);
|
||||||
|
const int max = libevdev_get_abs_maximum(m_dev, m_code);
|
||||||
|
|
||||||
|
m_base = (max + min) / 2;
|
||||||
|
m_range = (upper ? max : min) - m_base;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetName() const override
|
||||||
|
{
|
||||||
|
return "Axis " + std::to_string(m_index) + (m_range < 0 ? '-' : '+');
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const u8 m_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MotionDataInput final : public AnalogInput
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MotionDataInput(u16 code, ControlState resolution_scale, libevdev* dev) : AnalogInput(code, dev)
|
||||||
|
{
|
||||||
|
auto* const info = libevdev_get_abs_info(m_dev, m_code);
|
||||||
|
|
||||||
|
// The average of the minimum and maximum value. (neutral value)
|
||||||
|
m_base = (info->maximum + info->minimum) / 2;
|
||||||
|
|
||||||
|
m_range = info->resolution / resolution_scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetName() const override
|
||||||
|
{
|
||||||
|
// Unfortunately there doesn't seem to be a "standard" orientation
|
||||||
|
// so we can't use "Accel Up"-like names.
|
||||||
|
constexpr std::array<const char*, 6> motion_data_names = {{
|
||||||
|
"Accel X",
|
||||||
|
"Accel Y",
|
||||||
|
"Accel Z",
|
||||||
|
"Gyro X",
|
||||||
|
"Gyro Y",
|
||||||
|
"Gyro Z",
|
||||||
|
}};
|
||||||
|
|
||||||
|
// Our name array relies on sane axis codes from 0 to 5.
|
||||||
|
static_assert(ABS_X == 0, "evdev axis value sanity check");
|
||||||
|
static_assert(ABS_RX == 3, "evdev axis value sanity check");
|
||||||
|
|
||||||
|
return std::string(motion_data_names[m_code]) + (m_range < 0 ? '-' : '+');
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDetectable() override { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
static std::thread s_hotplug_thread;
|
static std::thread s_hotplug_thread;
|
||||||
static Common::Flag s_hotplug_thread_running;
|
static Common::Flag s_hotplug_thread_running;
|
||||||
static int s_wakeup_eventfd;
|
static int s_wakeup_eventfd;
|
||||||
|
@ -213,9 +328,43 @@ evdevDevice::evdevDevice(const std::string& devnode) : m_devfile(devnode)
|
||||||
AddInput(new Button(num_buttons++, key, m_dev));
|
AddInput(new Button(num_buttons++, key, m_dev));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int first_axis_code = 0;
|
||||||
|
|
||||||
|
int num_motion_axis = 0;
|
||||||
|
if (libevdev_has_property(m_dev, INPUT_PROP_ACCELEROMETER))
|
||||||
|
{
|
||||||
|
// If INPUT_PROP_ACCELEROMETER is set then X,Y,Z,RX,RY,RZ contain motion data.
|
||||||
|
|
||||||
|
auto add_motion_inputs = [&num_motion_axis, this](int first_code, double scale) {
|
||||||
|
for (int i = 0; i != 3; ++i)
|
||||||
|
{
|
||||||
|
const int code = first_code + i;
|
||||||
|
if (libevdev_has_event_code(m_dev, EV_ABS, code))
|
||||||
|
{
|
||||||
|
AddInput(new MotionDataInput(code, scale * -1, m_dev));
|
||||||
|
AddInput(new MotionDataInput(code, scale, m_dev));
|
||||||
|
|
||||||
|
++num_motion_axis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// evdev resolution is specified in "g"s and deg/s.
|
||||||
|
// Convert these to m/s/s and rad/s.
|
||||||
|
constexpr ControlState accel_scale = MathUtil::GRAVITY_ACCELERATION;
|
||||||
|
constexpr ControlState gyro_scale = MathUtil::TAU / 360;
|
||||||
|
|
||||||
|
add_motion_inputs(ABS_X, accel_scale);
|
||||||
|
add_motion_inputs(ABS_RX, gyro_scale);
|
||||||
|
|
||||||
|
// evdev says regular axes should not be mixed with motion data,
|
||||||
|
// but we'll keep looking for regular axes after RZ just in case.
|
||||||
|
first_axis_code = ABS_RZ + 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Absolute axis (thumbsticks)
|
// Absolute axis (thumbsticks)
|
||||||
int num_axis = 0;
|
int num_axis = 0;
|
||||||
for (int axis = 0; axis < 0x100; axis++)
|
for (int axis = first_axis_code; axis != ABS_CNT; ++axis)
|
||||||
{
|
{
|
||||||
if (libevdev_has_event_code(m_dev, EV_ABS, axis))
|
if (libevdev_has_event_code(m_dev, EV_ABS, axis))
|
||||||
{
|
{
|
||||||
|
@ -262,7 +411,7 @@ evdevDevice::evdevDevice(const std::string& devnode) : m_devfile(devnode)
|
||||||
// TODO: Add leds as output devices
|
// TODO: Add leds as output devices
|
||||||
|
|
||||||
// Was there some reasoning behind these numbers?
|
// Was there some reasoning behind these numbers?
|
||||||
m_interesting = num_axis >= 2 || num_buttons >= 8;
|
m_interesting = num_motion_axis != 0 || num_axis >= 2 || num_buttons >= 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
evdevDevice::~evdevDevice()
|
evdevDevice::~evdevDevice()
|
||||||
|
@ -306,50 +455,6 @@ bool evdevDevice::IsValid() const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string evdevDevice::Button::GetName() const
|
|
||||||
{
|
|
||||||
// Buttons below 0x100 are mostly keyboard keys, and the names make sense
|
|
||||||
if (m_code < 0x100)
|
|
||||||
{
|
|
||||||
const char* name = libevdev_event_code_get_name(EV_KEY, m_code);
|
|
||||||
if (name)
|
|
||||||
return std::string(StripSpaces(name));
|
|
||||||
}
|
|
||||||
// But controllers use codes above 0x100, and the standard label often doesn't match.
|
|
||||||
// We are better off with Button 0 and so on.
|
|
||||||
return "Button " + std::to_string(m_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
ControlState evdevDevice::Button::GetState() const
|
|
||||||
{
|
|
||||||
int value = 0;
|
|
||||||
libevdev_fetch_event_value(m_dev, EV_KEY, m_code, &value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
evdevDevice::Axis::Axis(u8 index, u16 code, bool upper, libevdev* dev)
|
|
||||||
: m_code(code), m_index(index), m_dev(dev)
|
|
||||||
{
|
|
||||||
const int min = libevdev_get_abs_minimum(m_dev, m_code);
|
|
||||||
const int max = libevdev_get_abs_maximum(m_dev, m_code);
|
|
||||||
|
|
||||||
m_base = (max + min) / 2;
|
|
||||||
m_range = (upper ? max : min) - m_base;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string evdevDevice::Axis::GetName() const
|
|
||||||
{
|
|
||||||
return "Axis " + std::to_string(m_index) + (m_range < 0 ? '-' : '+');
|
|
||||||
}
|
|
||||||
|
|
||||||
ControlState evdevDevice::Axis::GetState() const
|
|
||||||
{
|
|
||||||
int value = 0;
|
|
||||||
libevdev_fetch_event_value(m_dev, EV_ABS, m_code, &value);
|
|
||||||
|
|
||||||
return ControlState(value - m_base) / m_range;
|
|
||||||
}
|
|
||||||
|
|
||||||
evdevDevice::Effect::Effect(int fd) : m_fd(fd)
|
evdevDevice::Effect::Effect(int fd) : m_fd(fd)
|
||||||
{
|
{
|
||||||
m_effect.id = -1;
|
m_effect.id = -1;
|
||||||
|
|
|
@ -19,34 +19,6 @@ void Shutdown();
|
||||||
class evdevDevice : public Core::Device
|
class evdevDevice : public Core::Device
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
class Button : public Core::Device::Input
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::string GetName() const override;
|
|
||||||
Button(u8 index, u16 code, libevdev* dev) : m_index(index), m_code(code), m_dev(dev) {}
|
|
||||||
ControlState GetState() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const u8 m_index;
|
|
||||||
const u16 m_code;
|
|
||||||
libevdev* m_dev;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Axis : public Core::Device::Input
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::string GetName() const override;
|
|
||||||
Axis(u8 index, u16 code, bool upper, libevdev* dev);
|
|
||||||
ControlState GetState() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const u16 m_code;
|
|
||||||
const u8 m_index;
|
|
||||||
int m_range;
|
|
||||||
int m_base;
|
|
||||||
libevdev* m_dev;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Effect : public Core::Device::Output
|
class Effect : public Core::Device::Output
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
Loading…
Reference in New Issue