#pragma once #include "Utilities/types.h" #include "Utilities/Config.h" #include "Utilities/File.h" #include "Emu/Io/PadHandler.h" #include #include #include #include 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 { // Unique button names for the config files and our pad settings dialog const std::unordered_map button_list = { //{ 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" }, //{ 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" }, { 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" }, { 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" }, //{ 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" }, { 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 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 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 { libevdev* device{ nullptr }; pad_config* config{ nullptr }; std::string path; std::shared_ptr pad; std::unordered_map 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 axis_left = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }; std::vector 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; }; const int BUTTON_COUNT = 17; public: evdev_joystick_handler(); ~evdev_joystick_handler(); void init_config(pad_config* cfg, const std::string& name) override; bool Init() override; std::vector ListDevices() override; bool bindPadToDevice(std::shared_ptr pad, const std::string& device) override; void ThreadProc() override; void Close(); void GetNextButtonPress(const std::string& padId, const std::function& callback, bool get_blacklist = false, std::vector buttons = {}) override; void TestVibration(const std::string& padId, u32 largeMotor, u32 smallMotor) override; private: void TranslateButtonPress(u64 keyCode, bool& pressed, u16& value, bool ignore_threshold = false) override; EvdevDevice* get_device(const std::string& device); bool update_device(EvdevDevice& device); void update_devs(); int add_device(const std::string& device, bool in_settings = false); int GetButtonInfo(const input_event& evt, const EvdevDevice& device, int& button_code); std::unordered_map> GetButtonValues(const 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& map, int index); positive_axis m_pos_axis_config; std::vector m_positive_axis; std::vector blacklist; std::vector devices; int m_pad_index = -1; EvdevDevice m_dev; bool m_is_button_or_trigger; bool m_is_negative; bool m_is_init = false; };