mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-21 03:55:32 +00:00
Linux evdev joystick support (#2885)
* Linux evdev joystick support (#2678) * Cleanup libevdev configure code * evdev fixes * Evdev joystick additions/fixes * Error message tweak * Fix evdev multiple joysticks (thanks @hcorion!) * Change by-id to by-path in evdev
This commit is contained in:
parent
08f0047529
commit
dbd69536ed
6 changed files with 516 additions and 0 deletions
|
@ -158,6 +158,17 @@ elseif(WIN32)
|
|||
set(PLATFORM_ARCH "Windows/x86_64")
|
||||
else()
|
||||
set(PLATFORM_ARCH "linux/x86_64")
|
||||
option(USE_LIBEVDEV "libevdev-based joystick support" ON)
|
||||
endif()
|
||||
|
||||
if(USE_LIBEVDEV)
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(LIBEVDEV libevdev)
|
||||
if(LIBEVDEV_FOUND)
|
||||
add_definitions(-DHAVE_LIBEVDEV)
|
||||
include_directories(SYSTEM ${LIBEVDEV_INCLUDE_DIRS})
|
||||
list(APPEND ADDITIONAL_LIBS ${LIBEVDEV_LDFLAGS})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Select the version of libpng to use, default is builtin
|
||||
|
|
|
@ -74,6 +74,9 @@ void fmt_class_string<pad_handler>::format(std::string& out, u64 arg)
|
|||
#endif
|
||||
#ifdef _WIN32
|
||||
case pad_handler::mm: return "MMJoystick";
|
||||
#endif
|
||||
#ifdef HAVE_LIBEVDEV
|
||||
case pad_handler::evdev: return "Evdev";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -68,6 +68,9 @@ enum class pad_handler
|
|||
#ifdef _WIN32
|
||||
mm,
|
||||
#endif
|
||||
#ifdef HAVE_LIBEVDEV
|
||||
evdev,
|
||||
#endif
|
||||
};
|
||||
|
||||
enum class video_renderer
|
||||
|
|
403
rpcs3/evdev_joystick_handler.cpp
Normal file
403
rpcs3/evdev_joystick_handler.cpp
Normal file
|
@ -0,0 +1,403 @@
|
|||
#ifdef HAVE_LIBEVDEV
|
||||
|
||||
#include "evdev_joystick_handler.h"
|
||||
#include "Utilities/Thread.h"
|
||||
#include "Utilities/Log.h"
|
||||
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
evdev_joystick_config g_evdev_joystick_config;
|
||||
|
||||
namespace
|
||||
{
|
||||
const u32 THREAD_SLEEP_USEC = 100;
|
||||
const u32 THREAD_SLEEP_INACTIVE_USEC = 1000000;
|
||||
const u32 READ_TIMEOUT = 10;
|
||||
const u32 THREAD_TIMEOUT_USEC = 1000000;
|
||||
|
||||
const std::string EVENT_JOYSTICK = "event-joystick";
|
||||
}
|
||||
|
||||
evdev_joystick_handler::evdev_joystick_handler() {}
|
||||
|
||||
evdev_joystick_handler::~evdev_joystick_handler() { Close(); }
|
||||
|
||||
void evdev_joystick_handler::Init(const u32 max_connect)
|
||||
{
|
||||
std::memset(&m_info, 0, sizeof m_info);
|
||||
|
||||
g_evdev_joystick_config.load();
|
||||
|
||||
needscale = static_cast<bool>(g_evdev_joystick_config.needscale);
|
||||
axistrigger = static_cast<bool>(g_evdev_joystick_config.axistrigger);
|
||||
|
||||
revaxis.emplace_back(g_evdev_joystick_config.lxreverse);
|
||||
revaxis.emplace_back(g_evdev_joystick_config.lyreverse);
|
||||
revaxis.emplace_back(g_evdev_joystick_config.rxreverse);
|
||||
revaxis.emplace_back(g_evdev_joystick_config.ryreverse);
|
||||
|
||||
fs::dir devdir{"/dev/input/by-path"};
|
||||
fs::dir_entry et;
|
||||
|
||||
while (devdir.read(et))
|
||||
{
|
||||
// Does the entry name end with event-joystick?
|
||||
if (et.name.size() > EVENT_JOYSTICK.size() &&
|
||||
et.name.compare(et.name.size() - EVENT_JOYSTICK.size(),
|
||||
EVENT_JOYSTICK.size(), EVENT_JOYSTICK) == 0)
|
||||
{
|
||||
joy_paths.emplace_back(fmt::format("/dev/input/by-path/%s", et.name));
|
||||
}
|
||||
}
|
||||
|
||||
m_info.max_connect = std::min(max_connect, static_cast<u32>(joy_paths.size()));
|
||||
|
||||
for (u32 i = 0; i < m_info.max_connect; ++i)
|
||||
{
|
||||
joy_devs.push_back(nullptr);
|
||||
joy_axis_maps.emplace_back(ABS_RZ - ABS_X, -1);
|
||||
joy_button_maps.emplace_back(KEY_MAX - BTN_JOYSTICK, -1);
|
||||
joy_hat_ids.emplace_back(-1);
|
||||
m_pads.emplace_back(
|
||||
CELL_PAD_STATUS_DISCONNECTED,
|
||||
CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF,
|
||||
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE,
|
||||
CELL_PAD_DEV_TYPE_STANDARD
|
||||
);
|
||||
auto& pad = m_pads.back();
|
||||
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.triangle, CELL_PAD_CTRL_TRIANGLE);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.circle, CELL_PAD_CTRL_CIRCLE);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.cross, CELL_PAD_CTRL_CROSS);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.square, CELL_PAD_CTRL_SQUARE);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.l2, CELL_PAD_CTRL_L2);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.r2, CELL_PAD_CTRL_R2);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.l1, CELL_PAD_CTRL_L1);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_evdev_joystick_config.r1, CELL_PAD_CTRL_R1);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.start, CELL_PAD_CTRL_START);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.select, CELL_PAD_CTRL_SELECT);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.l3, CELL_PAD_CTRL_L3);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.r3, CELL_PAD_CTRL_R3);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, 0x100/*CELL_PAD_CTRL_PS*/);// TODO: PS button support
|
||||
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.up, CELL_PAD_CTRL_UP);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.down, CELL_PAD_CTRL_DOWN);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.left, CELL_PAD_CTRL_LEFT);
|
||||
pad.m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_evdev_joystick_config.right, CELL_PAD_CTRL_RIGHT);
|
||||
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X+g_evdev_joystick_config.lxstick, 0, 0);
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X+g_evdev_joystick_config.lystick, 0, 0);
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X+g_evdev_joystick_config.rxstick, 0, 0);
|
||||
pad.m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X+g_evdev_joystick_config.rystick, 0, 0);
|
||||
}
|
||||
|
||||
update_devs();
|
||||
active.store(true);
|
||||
joy_thread.reset(new std::thread(std::bind(&evdev_joystick_handler::thread_func, this)));
|
||||
}
|
||||
|
||||
void evdev_joystick_handler::update_devs()
|
||||
{
|
||||
int connected=0;
|
||||
|
||||
for (u32 i = 0; i < m_info.max_connect; ++i)
|
||||
if (try_open_dev(i)) ++connected;
|
||||
|
||||
m_info.now_connect = connected;
|
||||
}
|
||||
|
||||
bool evdev_joystick_handler::try_open_dev(u32 index)
|
||||
{
|
||||
libevdev*& dev = joy_devs[index];
|
||||
bool was_connected = dev != nullptr;
|
||||
|
||||
if (index >= joy_paths.size()) return false;
|
||||
const auto& path = joy_paths[index];
|
||||
|
||||
if (access(path.c_str(), R_OK) == -1)
|
||||
{
|
||||
if (was_connected)
|
||||
{
|
||||
// It was disconnected.
|
||||
m_pads[index].m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
|
||||
int fd = libevdev_get_fd(dev);
|
||||
libevdev_free(dev);
|
||||
close(fd);
|
||||
dev = nullptr;
|
||||
}
|
||||
m_pads[index].m_port_status &= ~CELL_PAD_STATUS_CONNECTED;
|
||||
LOG_ERROR(GENERAL, "Joystick %s is not present or accessible [previous status: %d]", path.c_str(),
|
||||
was_connected ? 1 : 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (was_connected) return true; // It's already been connected, and the js is still present.
|
||||
int fd = open(path.c_str(), O_RDONLY | O_NONBLOCK);
|
||||
|
||||
if (fd == -1)
|
||||
{
|
||||
int err = errno;
|
||||
LOG_ERROR(GENERAL, "Failed to open joystick #%d: %s [errno %d]", index, strerror(err), err);
|
||||
return false;
|
||||
}
|
||||
|
||||
int ret = libevdev_new_from_fd(fd, &dev);
|
||||
if (ret < 0)
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Failed to initialize libevdev for joystick #%d: %s [errno %d]", index, strerror(-ret), -ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_NOTICE(GENERAL, "Opened joystick #%d '%s' at %s (fd %d)", index, libevdev_get_name(dev), path, fd);
|
||||
|
||||
if (!was_connected)
|
||||
// Connection status changed from disconnected to connected.
|
||||
m_pads[index].m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
m_pads[index].m_port_status |= CELL_PAD_STATUS_CONNECTED;
|
||||
|
||||
int buttons=0;
|
||||
for (int i=BTN_JOYSTICK; i<KEY_MAX; i++)
|
||||
if (libevdev_has_event_code(dev, EV_KEY, i))
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "Joystick #%d has button %d as %d", index, i, buttons);
|
||||
joy_button_maps[index][i - BTN_MISC] = buttons++;
|
||||
}
|
||||
|
||||
int axes=0;
|
||||
for (int i=ABS_X; i<=ABS_RZ; i++)
|
||||
{
|
||||
// Skip ABS_Z and ABS_RZ on controllers where it's used for the triggers.
|
||||
if (axistrigger && (i == ABS_Z || i == ABS_RZ)) continue;
|
||||
|
||||
if (libevdev_has_event_code(dev, EV_ABS, i))
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "Joystick #%d has axis %d as %d", index, i, axes);
|
||||
joy_axis_maps[index][i - ABS_X] = axes++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=ABS_HAT0X; i<=ABS_HAT3Y; i+=2)
|
||||
if (libevdev_has_event_code(dev, EV_ABS, i) ||
|
||||
libevdev_has_event_code(dev, EV_ABS, i+1))
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "Joystick #%d has hat %d", index, i);
|
||||
joy_hat_ids[index] = i - ABS_HAT0X;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void evdev_joystick_handler::Close()
|
||||
{
|
||||
if (active.load())
|
||||
{
|
||||
active.store(false);
|
||||
if (!dead.load())
|
||||
{
|
||||
usleep(THREAD_TIMEOUT_USEC);
|
||||
if (!dead.load())
|
||||
LOG_ERROR(GENERAL, "EvdevJoystick thread could not stop within %d microseconds", THREAD_TIMEOUT_USEC);
|
||||
}
|
||||
}
|
||||
|
||||
joy_thread->detach();
|
||||
|
||||
for (auto& dev : joy_devs)
|
||||
{
|
||||
if (dev != nullptr)
|
||||
{
|
||||
int fd = libevdev_get_fd(dev);
|
||||
libevdev_free(dev);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void evdev_joystick_handler::thread_func()
|
||||
{
|
||||
while (active)
|
||||
{
|
||||
update_devs();
|
||||
|
||||
for (int i=0; i<joy_devs.size(); i++)
|
||||
{
|
||||
auto& pad = m_pads[i];
|
||||
auto& dev = joy_devs[i];
|
||||
if (dev == nullptr) continue;
|
||||
|
||||
// Try to query the latest event from the joystick.
|
||||
input_event evt;
|
||||
int ret = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &evt);
|
||||
|
||||
// Grab any pending sync event.
|
||||
if (ret == LIBEVDEV_READ_STATUS_SYNC)
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "Captured sync event");
|
||||
ret = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL | LIBEVDEV_READ_FLAG_SYNC, &evt);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
// -EAGAIN signifies no available events, not an actual *error*.
|
||||
if (ret != -EAGAIN)
|
||||
LOG_ERROR(GENERAL, "Failed to read latest event from joystick #%d: %s [errno %d]", i, strerror(-ret), -ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (evt.type)
|
||||
{
|
||||
case EV_SYN:
|
||||
LOG_NOTICE(GENERAL, "Joystick #%d sent EV_SYN", i);
|
||||
break;
|
||||
case EV_MSC:
|
||||
LOG_NOTICE(GENERAL, "Joystick #%d sent EV_MSC", i);
|
||||
break;
|
||||
case EV_KEY:
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "Joystick #%d EV_KEY: %d %d", i, evt.code, evt.value);
|
||||
if (evt.code < BTN_MISC)
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "Joystick #%d sent non-button key event %d", i, evt.code);
|
||||
break;
|
||||
}
|
||||
|
||||
int button_code = joy_button_maps[i][evt.code - BTN_MISC];
|
||||
if (button_code == -1)
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick #%d sent invalid button code %d", i, evt.code);
|
||||
}
|
||||
|
||||
auto which_button = std::find_if(
|
||||
pad.m_buttons.begin(), pad.m_buttons.end(),
|
||||
[&](const Button& bt) { return bt.m_keyCode == button_code; });
|
||||
if (which_button == pad.m_buttons.end())
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick #%d sent button event for unmapped button %d", i, evt.code);
|
||||
break;
|
||||
}
|
||||
|
||||
which_button->m_pressed = evt.value;
|
||||
which_button->m_value = evt.value ? 255 : 0;
|
||||
break;
|
||||
}
|
||||
case EV_ABS:
|
||||
LOG_NOTICE(GENERAL, "Joystick #%d EV_ABS: %d %d", i, evt.code, evt.value);
|
||||
|
||||
if (evt.code >= ABS_HAT0X && evt.code <= ABS_HAT3Y) {
|
||||
int hat = evt.code - ABS_HAT0X;
|
||||
if (hat != joy_hat_ids[i] && hat-1 != joy_hat_ids[i])
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick #%d sent HAT event for invalid hat %d (expected %d)", i, hat, joy_hat_ids[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
int source_axis = hat == joy_hat_ids[i] ? EVDEV_DPAD_HAT_AXIS_X : EVDEV_DPAD_HAT_AXIS_Y;
|
||||
|
||||
for (Button& bt : pad.m_buttons)
|
||||
{
|
||||
if (bt.m_keyCode != source_axis) continue;
|
||||
|
||||
if (evt.value == 0)
|
||||
{
|
||||
bt.m_pressed = false;
|
||||
bt.m_value = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int code = -1;
|
||||
if (source_axis == EVDEV_DPAD_HAT_AXIS_X)
|
||||
{
|
||||
code = evt.value > 0 ? CELL_PAD_CTRL_RIGHT : CELL_PAD_CTRL_LEFT;
|
||||
}
|
||||
else
|
||||
{
|
||||
code = evt.value > 0 ? CELL_PAD_CTRL_DOWN : CELL_PAD_CTRL_UP;
|
||||
}
|
||||
|
||||
if (bt.m_outKeyCode == code)
|
||||
{
|
||||
bt.m_pressed = true;
|
||||
bt.m_value = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (axistrigger && (evt.code == ABS_Z || evt.code == ABS_RZ))
|
||||
{
|
||||
// For Xbox 360 controllers, a third axis represent the left/right triggers.
|
||||
int which_trigger=0;
|
||||
|
||||
if (evt.code == ABS_Z)
|
||||
{
|
||||
which_trigger = CELL_PAD_CTRL_L2;
|
||||
}
|
||||
else if (evt.code == ABS_RZ)
|
||||
{
|
||||
which_trigger = CELL_PAD_CTRL_R2;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick #%d sent invalid event code %d for 3rd axis", i, evt.code);
|
||||
break;
|
||||
}
|
||||
|
||||
auto which_button = std::find_if(
|
||||
pad.m_buttons.begin(), pad.m_buttons.end(),
|
||||
[&](const Button& bt) { return bt.m_outKeyCode == which_trigger; });
|
||||
if (which_button == pad.m_buttons.end())
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick #%d's pad has no trigger %d", i, which_trigger);
|
||||
break;
|
||||
}
|
||||
which_button->m_pressed = evt.value == 255;
|
||||
which_button->m_value = evt.value;
|
||||
}
|
||||
else if (evt.code >= ABS_X && evt.code <= ABS_RZ)
|
||||
{
|
||||
int axis = joy_axis_maps[i][evt.code - ABS_X];
|
||||
|
||||
if (axis > pad.m_sticks.size())
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Joystick #%d sent axis event for invalid axis %d", i, axis);
|
||||
break;
|
||||
}
|
||||
|
||||
auto& stick = pad.m_sticks[axis];
|
||||
|
||||
int value = evt.value;
|
||||
|
||||
if (needscale)
|
||||
{
|
||||
// Scale from the -32768...32768 range to the 0...256 range.
|
||||
value = (value / 256) + 128;
|
||||
}
|
||||
|
||||
if (revaxis[axis])
|
||||
{
|
||||
value = 256 - value;
|
||||
}
|
||||
|
||||
stick.m_value = value;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(GENERAL, "Unknown joystick #%d event %d", i, evt.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int to_sleep = m_info.now_connect > 0 ? THREAD_SLEEP_USEC : THREAD_SLEEP_INACTIVE_USEC;
|
||||
usleep(to_sleep);
|
||||
}
|
||||
|
||||
dead = true;
|
||||
}
|
||||
|
||||
#endif
|
90
rpcs3/evdev_joystick_handler.h
Normal file
90
rpcs3/evdev_joystick_handler.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
#pragma once
|
||||
|
||||
#include "Utilities/types.h"
|
||||
#include "Utilities/Config.h"
|
||||
#include "Utilities/File.h"
|
||||
#include "Emu/Io/PadHandler.h"
|
||||
#include <libevdev/libevdev.h>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
|
||||
|
||||
enum { EVDEV_DPAD_HAT_AXIS_X = -1, EVDEV_DPAD_HAT_AXIS_Y = -2 };
|
||||
|
||||
struct evdev_joystick_config final : cfg::node
|
||||
{
|
||||
const std::string cfg_name = fs::get_config_dir() + "/config_linuxjoystick.yml";
|
||||
|
||||
cfg::int32 select{this, "Select", 6};
|
||||
cfg::int32 start{this, "Start", 7};
|
||||
cfg::int32 cross{this, "Cross", 0};
|
||||
cfg::int32 circle{this, "Circle", 1};
|
||||
cfg::int32 square{this, "Square", 2};
|
||||
cfg::int32 triangle{this, "Triangle", 3};
|
||||
|
||||
cfg::int32 r1{this, "R1", 5};
|
||||
cfg::int32 r2{this, "R2", 11};
|
||||
cfg::int32 r3{this, "R3", 10};
|
||||
cfg::int32 l1{this, "L1", 4};
|
||||
cfg::int32 l2{this, "L2", 12};
|
||||
cfg::int32 l3{this, "L3", 9};
|
||||
|
||||
cfg::int32 up{this, "Up", EVDEV_DPAD_HAT_AXIS_Y};
|
||||
cfg::int32 down{this, "Down", EVDEV_DPAD_HAT_AXIS_Y};
|
||||
cfg::int32 left{this, "Left", EVDEV_DPAD_HAT_AXIS_X};
|
||||
cfg::int32 right{this, "Right", EVDEV_DPAD_HAT_AXIS_X};
|
||||
|
||||
cfg::int32 rxstick{this, "Right stick X axis", 0};
|
||||
cfg::int32 rystick{this, "Right stick Y axis", 1};
|
||||
cfg::int32 lxstick{this, "Left stick X axis", 2};
|
||||
cfg::int32 lystick{this, "Left stick Y axis", 3};
|
||||
|
||||
cfg::_bool rxreverse{this, "Reverse right stick X axis", false};
|
||||
cfg::_bool ryreverse{this, "Reverse right stick Y axis", false};
|
||||
cfg::_bool lxreverse{this, "Reverse left stick X axis", false};
|
||||
cfg::_bool lyreverse{this, "Reverse left stick Y axis", false};
|
||||
|
||||
cfg::_bool needscale{this, "Axis scaling", true};
|
||||
cfg::_bool axistrigger{this, "Z axis triggers", true};
|
||||
|
||||
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());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class evdev_joystick_handler final : public PadHandlerBase
|
||||
{
|
||||
public:
|
||||
evdev_joystick_handler();
|
||||
~evdev_joystick_handler();
|
||||
|
||||
void Init(const u32 max_connect) override;
|
||||
void Close();
|
||||
|
||||
private:
|
||||
void update_devs();
|
||||
bool try_open_dev(u32 index);
|
||||
void thread_func();
|
||||
|
||||
std::unique_ptr<std::thread> joy_thread;
|
||||
mutable atomic_t<bool> active{false}, dead{false};
|
||||
std::vector<std::string> joy_paths;
|
||||
std::vector<libevdev*> joy_devs;
|
||||
std::vector<std::vector<int>> joy_button_maps;
|
||||
std::vector<std::vector<int>> joy_axis_maps;
|
||||
std::vector<int> joy_hat_ids;
|
||||
bool needscale, axistrigger;
|
||||
std::vector<bool> revaxis;
|
||||
};
|
|
@ -27,6 +27,9 @@
|
|||
#ifdef _WIN32
|
||||
#include "mm_joystick_handler.h"
|
||||
#endif
|
||||
#ifdef HAVE_LIBEVDEV
|
||||
#include "evdev_joystick_handler.h"
|
||||
#endif
|
||||
|
||||
|
||||
#include "Emu/RSX/Null/NullGSRender.h"
|
||||
|
@ -137,6 +140,9 @@ void rpcs3_app::InitializeCallbacks()
|
|||
#endif
|
||||
#ifdef _WIN32
|
||||
case pad_handler::mm: return std::make_shared<mm_joystick_handler>();
|
||||
#endif
|
||||
#ifdef HAVE_LIBEVDEV
|
||||
case pad_handler::evdev: return std::make_shared<evdev_joystick_handler>();
|
||||
#endif
|
||||
default: fmt::throw_exception("Invalid pad handler: %s", type);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue