rpcs3/rpcs3/Input/evdev_joystick_handler.h

408 lines
16 KiB
C++

#pragma once
#ifdef HAVE_LIBEVDEV
#include "util/types.hpp"
#include "Utilities/File.h"
#include "Emu/Io/PadHandler.h"
#include <libevdev/libevdev.h>
#include <memory>
#include <unordered_map>
#include <vector>
#include <thread>
#include <ctime>
struct positive_axis : cfg::node
{
const std::string cfg_name = fs::get_config_dir() + "/evdev_positive_axis.yml";
cfg::_bool abs_x{ this, "ABS_X", false };
cfg::_bool abs_y{ this, "ABS_Y", false };
cfg::_bool abs_z{ this, "ABS_Z", false };
cfg::_bool abs_rx{ this, "ABS_RX", false };
cfg::_bool abs_ry{ this, "ABS_RY", false };
cfg::_bool abs_rz{ this, "ABS_RZ", false };
cfg::_bool abs_throttle{ this, "ABS_THROTTLE", false };
cfg::_bool abs_rudder{ this, "ABS_RUDDER", false };
cfg::_bool abs_wheel{ this, "ABS_WHEEL", false };
cfg::_bool abs_gas{ this, "ABS_GAS", false };
cfg::_bool abs_brake{ this, "ABS_BRAKE", false };
cfg::_bool abs_hat0x{ this, "ABS_HAT0X", false };
cfg::_bool abs_hat0y{ this, "ABS_HAT0Y", false };
cfg::_bool abs_hat1x{ this, "ABS_HAT1X", false };
cfg::_bool abs_hat1y{ this, "ABS_HAT1Y", false };
cfg::_bool abs_hat2x{ this, "ABS_HAT2X", false };
cfg::_bool abs_hat2y{ this, "ABS_HAT2Y", false };
cfg::_bool abs_hat3x{ this, "ABS_HAT3X", false };
cfg::_bool abs_hat3y{ this, "ABS_HAT3Y", false };
cfg::_bool abs_pressure{ this, "ABS_PRESSURE", false };
cfg::_bool abs_distance{ this, "ABS_DISTANCE", false };
cfg::_bool abs_tilt_x{ this, "ABS_TILT_X", false };
cfg::_bool abs_tilt_y{ this, "ABS_TILT_Y", false };
cfg::_bool abs_tool_width{ this, "ABS_TOOL_WIDTH", false };
cfg::_bool abs_volume{ this, "ABS_VOLUME", false };
cfg::_bool abs_misc{ this, "ABS_MISC", false };
cfg::_bool abs_mt_slot{ this, "ABS_MT_SLOT", false };
cfg::_bool abs_mt_touch_major{ this, "ABS_MT_TOUCH_MAJOR", false };
cfg::_bool abs_mt_touch_minor{ this, "ABS_MT_TOUCH_MINOR", false };
cfg::_bool abs_mt_width_major{ this, "ABS_MT_WIDTH_MAJOR", false };
cfg::_bool abs_mt_width_minor{ this, "ABS_MT_WIDTH_MINOR", false };
cfg::_bool abs_mt_orientation{ this, "ABS_MT_ORIENTATION", false };
cfg::_bool abs_mt_position_x{ this, "ABS_MT_POSITION_X", false };
cfg::_bool abs_mt_position_y{ this, "ABS_MT_POSITION_Y", false };
cfg::_bool abs_mt_tool_type{ this, "ABS_MT_TOOL_TYPE", false };
cfg::_bool abs_mt_blob_id{ this, "ABS_MT_BLOB_ID", false };
cfg::_bool abs_mt_tracking_id{ this, "ABS_MT_TRACKING_ID", false };
cfg::_bool abs_mt_pressure{ this, "ABS_MT_PRESSURE", false };
cfg::_bool abs_mt_distance{ this, "ABS_MT_DISTANCE", false };
cfg::_bool abs_mt_tool_x{ this, "ABS_MT_TOOL_X", false };
cfg::_bool abs_mt_tool_y{ this, "ABS_MT_TOOL_Y", false };
bool load()
{
if (fs::file cfg_file{ cfg_name, fs::read })
{
return from_string(cfg_file.to_string());
}
return false;
}
void save()
{
fs::file(cfg_name, fs::rewrite).write(to_string());
}
bool exist()
{
return fs::is_file(cfg_name);
}
};
class evdev_joystick_handler final : public PadHandlerBase
{
static constexpr u32 NO_BUTTON = u32{umax}; // Some event code that doesn't exist in evdev (code type is U16)
// Unique button names for the config files and our pad settings dialog
const std::unordered_map<u32, std::string> button_list =
{
{ NO_BUTTON , "" },
// Xbox One S Controller returns some buttons as key when connected through bluetooth
{ KEY_BACK , "Back Key" },
{ KEY_HOMEPAGE , "Homepage Key"},
// Wii/Wii U Controller drivers use the following few keys
{ KEY_UP , "Up Key" },
{ KEY_LEFT , "Left Key" },
{ KEY_RIGHT , "Right Key" },
{ KEY_DOWN , "Down Key" },
{ KEY_NEXT , "Next Key" },
{ KEY_PREVIOUS , "Previous Key"},
//{ BTN_MISC , "Misc" }, same as BTN_0
{ BTN_0 , "0" },
{ BTN_1 , "1" },
{ BTN_2 , "2" },
{ BTN_3 , "3" },
{ BTN_4 , "4" },
{ BTN_5 , "5" },
{ BTN_6 , "6" },
{ BTN_7 , "7" },
{ BTN_8 , "8" },
{ BTN_9 , "9" },
{ 0x10a , "0x10a" },
{ 0x10b , "0x10b" },
{ 0x10c , "0x10c" },
{ 0x10d , "0x10d" },
{ 0x10e , "0x10e" },
{ 0x10f , "0x10f" },
//{ BTN_MOUSE , "Mouse" }, same as BTN_LEFT
{ BTN_LEFT , "Left" },
{ BTN_RIGHT , "Right" },
{ BTN_MIDDLE , "Middle" },
{ BTN_SIDE , "Side" },
{ BTN_EXTRA , "Extra" },
{ BTN_FORWARD , "Forward" },
{ BTN_BACK , "Back" },
{ BTN_TASK , "Task" },
{ 0x118 , "0x118" },
{ 0x119 , "0x119" },
{ 0x11a , "0x11a" },
{ 0x11b , "0x11b" },
{ 0x11c , "0x11c" },
{ 0x11d , "0x11d" },
{ 0x11e , "0x11e" },
{ 0x11f , "0x11f" },
{ BTN_JOYSTICK , "Joystick" },
{ BTN_TRIGGER , "Trigger" },
{ BTN_THUMB , "Thumb" },
{ BTN_THUMB2 , "Thumb 2" },
{ BTN_TOP , "Top" },
{ BTN_TOP2 , "Top 2" },
{ BTN_PINKIE , "Pinkie" },
{ BTN_BASE , "Base" },
{ BTN_BASE2 , "Base 2" },
{ BTN_BASE3 , "Base 3" },
{ BTN_BASE4 , "Base 4" },
{ BTN_BASE5 , "Base 5" },
{ BTN_BASE6 , "Base 6" },
{ 0x12c , "0x12c" },
{ 0x12d , "0x12d" },
{ 0x12e , "0x12e" },
{ BTN_DEAD , "Dead" },
//{ BTN_GAMEPAD , "Gamepad" }, same as BTN_A
//{ BTN_SOUTH , "South" }, same as BTN_A
{ BTN_A , "A" },
//{ BTN_EAST , "South" }, same as BTN_B
{ BTN_B , "B" },
{ BTN_C , "C" },
//{ BTN_NORTH , "North" }, same as BTN_X
{ BTN_X , "X" },
//{ BTN_WEST , "West" }, same as BTN_Y
{ BTN_Y , "Y" },
{ BTN_Z , "Z" },
{ BTN_TL , "TL" },
{ BTN_TR , "TR" },
{ BTN_TL2 , "TL 2" },
{ BTN_TR2 , "TR 2" },
{ BTN_SELECT , "Select" },
{ BTN_START , "Start" },
{ BTN_MODE , "Mode" },
{ BTN_THUMBL , "Thumb L" },
{ BTN_THUMBR , "Thumb R" },
{ 0x13f , "0x13f" },
//{ BTN_DIGI , "Digi" }, same as BTN_TOOL_PEN
{ BTN_TOOL_PEN , "Pen" },
{ BTN_TOOL_RUBBER , "Rubber" },
{ BTN_TOOL_BRUSH , "Brush" },
{ BTN_TOOL_PENCIL , "Pencil" },
{ BTN_TOOL_AIRBRUSH , "Airbrush" },
{ BTN_TOOL_FINGER , "Finger" },
{ BTN_TOOL_MOUSE , "Mouse" },
{ BTN_TOOL_LENS , "Lense" },
{ BTN_TOOL_QUINTTAP , "Quinttap" },
{ 0x149 , "0x149" },
{ BTN_TOUCH , "Touch" },
{ BTN_STYLUS , "Stylus" },
{ BTN_STYLUS2 , "Stylus 2" },
{ BTN_TOOL_DOUBLETAP , "Doubletap" },
{ BTN_TOOL_TRIPLETAP , "Tripletap" },
{ BTN_TOOL_QUADTAP , "Quadtap" },
//{ BTN_WHEEL , "Wheel" }, same as BTN_GEAR_DOWN
{ BTN_GEAR_DOWN , "Gear Up" },
{ BTN_GEAR_UP , "Gear Down" },
{ BTN_DPAD_UP , "D-Pad Up" },
{ BTN_DPAD_DOWN , "D-Pad Down" },
{ BTN_DPAD_LEFT , "D-Pad Left" },
{ BTN_DPAD_RIGHT , "D-Pad Right" },
{ BTN_TRIGGER_HAPPY , "Happy" },
{ BTN_TRIGGER_HAPPY1 , "Happy 1" },
{ BTN_TRIGGER_HAPPY2 , "Happy 2" },
{ BTN_TRIGGER_HAPPY3 , "Happy 3" },
{ BTN_TRIGGER_HAPPY4 , "Happy 4" },
{ BTN_TRIGGER_HAPPY5 , "Happy 5" },
{ BTN_TRIGGER_HAPPY6 , "Happy 6" },
{ BTN_TRIGGER_HAPPY7 , "Happy 7" },
{ BTN_TRIGGER_HAPPY8 , "Happy 8" },
{ BTN_TRIGGER_HAPPY9 , "Happy 9" },
{ BTN_TRIGGER_HAPPY10 , "Happy 10" },
{ BTN_TRIGGER_HAPPY11 , "Happy 11" },
{ BTN_TRIGGER_HAPPY12 , "Happy 12" },
{ BTN_TRIGGER_HAPPY13 , "Happy 13" },
{ BTN_TRIGGER_HAPPY14 , "Happy 14" },
{ BTN_TRIGGER_HAPPY15 , "Happy 15" },
{ BTN_TRIGGER_HAPPY16 , "Happy 16" },
{ BTN_TRIGGER_HAPPY17 , "Happy 17" },
{ BTN_TRIGGER_HAPPY18 , "Happy 18" },
{ BTN_TRIGGER_HAPPY19 , "Happy 19" },
{ BTN_TRIGGER_HAPPY20 , "Happy 20" },
{ BTN_TRIGGER_HAPPY21 , "Happy 21" },
{ BTN_TRIGGER_HAPPY22 , "Happy 22" },
{ BTN_TRIGGER_HAPPY23 , "Happy 23" },
{ BTN_TRIGGER_HAPPY24 , "Happy 24" },
{ BTN_TRIGGER_HAPPY25 , "Happy 25" },
{ BTN_TRIGGER_HAPPY26 , "Happy 26" },
{ BTN_TRIGGER_HAPPY27 , "Happy 27" },
{ BTN_TRIGGER_HAPPY28 , "Happy 28" },
{ BTN_TRIGGER_HAPPY29 , "Happy 29" },
{ BTN_TRIGGER_HAPPY30 , "Happy 30" },
{ BTN_TRIGGER_HAPPY31 , "Happy 31" },
{ BTN_TRIGGER_HAPPY32 , "Happy 32" },
{ BTN_TRIGGER_HAPPY33 , "Happy 33" },
{ BTN_TRIGGER_HAPPY34 , "Happy 34" },
{ BTN_TRIGGER_HAPPY35 , "Happy 35" },
{ BTN_TRIGGER_HAPPY36 , "Happy 36" },
{ BTN_TRIGGER_HAPPY37 , "Happy 37" },
{ BTN_TRIGGER_HAPPY38 , "Happy 38" },
{ BTN_TRIGGER_HAPPY39 , "Happy 39" },
{ BTN_TRIGGER_HAPPY40 , "Happy 40" }
};
// Unique positive axis names for the config files and our pad settings dialog
const std::unordered_map<u32, std::string> axis_list =
{
{ ABS_X , "LX+" },
{ ABS_Y , "LY+" },
{ ABS_Z , "LZ+" },
{ ABS_RX , "RX+" },
{ ABS_RY , "RY+" },
{ ABS_RZ , "RZ+" },
{ ABS_THROTTLE , "Throttle+" },
{ ABS_RUDDER , "Rudder+" },
{ ABS_WHEEL , "Wheel+" },
{ ABS_GAS , "Gas+" },
{ ABS_BRAKE , "Brake+" },
{ ABS_HAT0X , "Hat0 X+" },
{ ABS_HAT0Y , "Hat0 Y+" },
{ ABS_HAT1X , "Hat1 X+" },
{ ABS_HAT1Y , "Hat1 Y+" },
{ ABS_HAT2X , "Hat2 X+" },
{ ABS_HAT2Y , "Hat2 Y+" },
{ ABS_HAT3X , "Hat3 X+" },
{ ABS_HAT3Y , "Hat3 Y+" },
{ ABS_PRESSURE , "Pressure+" },
{ ABS_DISTANCE , "Distance+" },
{ ABS_TILT_X , "Tilt X+" },
{ ABS_TILT_Y , "Tilt Y+" },
{ ABS_TOOL_WIDTH , "Width+" },
{ ABS_VOLUME , "Volume+" },
{ ABS_MISC , "Misc+" },
{ ABS_MT_SLOT , "Slot+" },
{ ABS_MT_TOUCH_MAJOR , "MT TMaj+" },
{ ABS_MT_TOUCH_MINOR , "MT TMin+" },
{ ABS_MT_WIDTH_MAJOR , "MT WMaj+" },
{ ABS_MT_WIDTH_MINOR , "MT WMin+" },
{ ABS_MT_ORIENTATION , "MT Orient+" },
{ ABS_MT_POSITION_X , "MT PosX+" },
{ ABS_MT_POSITION_Y , "MT PosY+" },
{ ABS_MT_TOOL_TYPE , "MT TType+" },
{ ABS_MT_BLOB_ID , "MT Blob ID+" },
{ ABS_MT_TRACKING_ID , "MT Track ID+" },
{ ABS_MT_PRESSURE , "MT Pressure+" },
{ ABS_MT_DISTANCE , "MT Distance+" },
{ ABS_MT_TOOL_X , "MT Tool X+" },
{ ABS_MT_TOOL_Y , "MT Tool Y+" },
};
// Unique negative axis names for the config files and our pad settings dialog
const std::unordered_map<u32, std::string> rev_axis_list =
{
{ ABS_X , "LX-" },
{ ABS_Y , "LY-" },
{ ABS_Z , "LZ-" },
{ ABS_RX , "RX-" },
{ ABS_RY , "RY-" },
{ ABS_RZ , "RZ-" },
{ ABS_THROTTLE , "Throttle-" },
{ ABS_RUDDER , "Rudder-" },
{ ABS_WHEEL , "Wheel-" },
{ ABS_GAS , "Gas-" },
{ ABS_BRAKE , "Brake-" },
{ ABS_HAT0X , "Hat0 X-" },
{ ABS_HAT0Y , "Hat0 Y-" },
{ ABS_HAT1X , "Hat1 X-" },
{ ABS_HAT1Y , "Hat1 Y-" },
{ ABS_HAT2X , "Hat2 X-" },
{ ABS_HAT2Y , "Hat2 Y-" },
{ ABS_HAT3X , "Hat3 X-" },
{ ABS_HAT3Y , "Hat3 Y-" },
{ ABS_PRESSURE , "Pressure-" },
{ ABS_DISTANCE , "Distance-" },
{ ABS_TILT_X , "Tilt X-" },
{ ABS_TILT_Y , "Tilt Y-" },
{ ABS_TOOL_WIDTH , "Width-" },
{ ABS_VOLUME , "Volume-" },
{ ABS_MISC , "Misc-" },
{ ABS_MT_SLOT , "Slot-" },
{ ABS_MT_TOUCH_MAJOR , "MT TMaj-" },
{ ABS_MT_TOUCH_MINOR , "MT TMin-" },
{ ABS_MT_WIDTH_MAJOR , "MT WMaj-" },
{ ABS_MT_WIDTH_MINOR , "MT WMin-" },
{ ABS_MT_ORIENTATION , "MT Orient-" },
{ ABS_MT_POSITION_X , "MT PosX-" },
{ ABS_MT_POSITION_Y , "MT PosY-" },
{ ABS_MT_TOOL_TYPE , "MT TType-" },
{ ABS_MT_BLOB_ID , "MT Blob ID-" },
{ ABS_MT_TRACKING_ID , "MT Track ID-" },
{ ABS_MT_PRESSURE , "MT Pressure-" },
{ ABS_MT_DISTANCE , "MT Distance-" },
{ ABS_MT_TOOL_X , "MT Tool X-" },
{ ABS_MT_TOOL_Y , "MT Tool Y-" },
};
struct EvdevButton
{
u32 code;
int dir;
int type;
};
struct EvdevDevice : public PadDevice
{
libevdev* device{ nullptr };
std::string path;
std::unordered_map<int, bool> axis_orientations; // value is true if key was found in rev_axis_list
s32 stick_val[4] = { 0, 0, 0, 0 };
u16 val_min[4] = { 0, 0, 0, 0 };
u16 val_max[4] = { 0, 0, 0, 0 };
EvdevButton trigger_left = { 0, 0, 0 };
EvdevButton trigger_right = { 0, 0, 0 };
std::vector<EvdevButton> axis_left = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } };
std::vector<EvdevButton> axis_right = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } };
int cur_dir = 0;
int cur_type = 0;
int effect_id = -1;
bool has_rumble = false;
u16 force_large = 0;
u16 force_small = 0;
clock_t last_vibration = 0;
};
public:
evdev_joystick_handler();
~evdev_joystick_handler();
void init_config(cfg_pad* cfg) override;
bool Init() override;
std::vector<std::string> ListDevices() override;
bool bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device, u8 player_id) override;
void Close();
void get_next_button_press(const std::string& padId, const pad_callback& callback, const pad_fail_callback& fail_callback, bool get_blacklist = false, const std::vector<std::string>& buttons = {}) override;
void SetPadData(const std::string& padId, u8 player_id, u32 largeMotor, u32 smallMotor, s32 r, s32 g, s32 b, bool battery_led, u32 battery_led_brightness) override;
private:
std::shared_ptr<EvdevDevice> get_evdev_device(const std::string& device);
std::string get_device_name(const libevdev* dev);
bool update_device(const std::shared_ptr<PadDevice>& device);
void update_devs();
int add_device(const std::string& device, const std::shared_ptr<Pad>& pad, bool in_settings = false);
u32 GetButtonInfo(const input_event& evt, const std::shared_ptr<EvdevDevice>& device, int& button_code);
std::unordered_map<u64, std::pair<u16, bool>> GetButtonValues(const std::shared_ptr<EvdevDevice>& device);
void SetRumble(EvdevDevice* device, u16 large, u16 small);
// Search axis_orientations map for the direction by index, returns -1 if not found, 0 for positive and 1 for negative
int FindAxisDirection(const std::unordered_map<int, bool>& map, int index);
positive_axis m_pos_axis_config;
std::vector<u32> m_positive_axis;
std::vector<std::string> m_blacklist;
std::unordered_map<std::string, int> m_settings_added;
std::shared_ptr<EvdevDevice> m_dev;
bool m_is_button_or_trigger;
bool m_is_negative;
bool m_is_init = false;
bool check_button(const EvdevButton& b, const u32 code);
bool check_buttons(const std::vector<EvdevButton>& b, const u32 code);
void handle_input_event(const input_event& evt, const std::shared_ptr<Pad>& pad);
protected:
PadHandlerBase::connection update_connection(const std::shared_ptr<PadDevice>& device) override;
void get_mapping(const std::shared_ptr<PadDevice>& device, const std::shared_ptr<Pad>& pad) override;
void apply_pad_data(const std::shared_ptr<PadDevice>& device, const std::shared_ptr<Pad>& pad) override;
bool get_is_left_trigger(u64 keyCode) override;
bool get_is_right_trigger(u64 keyCode) override;
bool get_is_left_stick(u64 keyCode) override;
bool get_is_right_stick(u64 keyCode) override;
};
#endif