InputCommon: Detect when evdev exposes acceleration/gyroscope data.

This commit is contained in:
Jordan Woyak 2019-11-08 18:23:22 -06:00
parent d8c62b5965
commit 1180c231a6
5 changed files with 155 additions and 76 deletions

View file

@ -17,6 +17,7 @@ namespace MathUtil
{
constexpr double TAU = 6.2831853071795865;
constexpr double PI = TAU / 2;
constexpr double GRAVITY_ACCELERATION = 9.80665;
template <typename T>
constexpr auto Sign(const T& val) -> decltype((T{} < val) - (val < T{}))

View file

@ -6,6 +6,7 @@
#include <array>
#include "Common/MathUtil.h"
#include "Common/Matrix.h"
#include "Core/HW/WiimoteCommon/DataReport.h"
#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h"
@ -18,7 +19,7 @@
namespace WiimoteEmu
{
constexpr double GRAVITY_ACCELERATION = 9.80665;
using MathUtil::GRAVITY_ACCELERATION;
struct PositionalState
{

View file

@ -108,7 +108,7 @@ private:
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 u16 DEFAULT_SERVER_PORT = 26760;
static constexpr auto SERVER_REREGISTER_INTERVAL = std::chrono::seconds{1};

View file

@ -25,6 +25,121 @@
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 Common::Flag s_hotplug_thread_running;
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));
}
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)
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))
{
@ -262,7 +411,7 @@ evdevDevice::evdevDevice(const std::string& devnode) : m_devfile(devnode)
// TODO: Add leds as output devices
// 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()
@ -306,50 +455,6 @@ bool evdevDevice::IsValid() const
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)
{
m_effect.id = -1;

View file

@ -19,34 +19,6 @@ void Shutdown();
class evdevDevice : public Core::Device
{
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
{
public: