diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index 6af39c6e15..bb224f60e6 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -11,9 +11,13 @@ #include "Emu/IdManager.h" #include "Emu/RSX/Overlays/overlay_cursor.h" #include "Input/pad_thread.h" + +#ifdef HAVE_LIBEVDEV #include "Input/evdev_gun_handler.h" +#endif #include // for fmod +#include LOG_CHANNEL(cellGem); @@ -89,6 +93,46 @@ void fmt_class_string::format(std::string& out, u // * HLE helper structs * // ********************** +#ifdef HAVE_LIBEVDEV +struct gun_handler +{ +public: + gun_handler() = default; + + static constexpr auto thread_name = "Evdev Gun Thread"sv; + + evdev_gun_handler handler{}; + atomic_t num_devices{0}; + + void operator()() + { + if (g_cfg.io.move != move_handler::gun) + { + return; + } + + while (thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped()) + { + const bool is_active = !Emu.IsPaused() && handler.is_init(); + + if (is_active) + { + for (u32 i = 0; i < num_devices; i++) + { + std::scoped_lock lock(handler.mutex); + handler.poll(i); + } + } + + thread_ctrl::wait_for(is_active ? 1000 : 10000); + } + } +}; + +using gun_thread = named_thread; + +#endif + struct gem_config_data { public: @@ -215,13 +259,16 @@ public: connected_controllers = 1; break; } +#ifdef HAVE_LIBEVDEV case move_handler::gun: { -#ifdef HAVE_LIBEVDEV - connected_controllers = evdev_gun_handler::getInstance()->getNumGuns(); -#endif + gun_thread& gun = g_fxo->get(); + std::scoped_lock lock(gun.handler.mutex); + connected_controllers = gun.handler.init() ? gun.handler.get_num_guns() : 0; + gun.num_devices = connected_controllers; break; } +#endif case move_handler::null: default: break; @@ -654,9 +701,10 @@ static inline void ds3_get_stick_values(const std::shared_ptr& pad, s32& x_ } } -static void ds3_pos_to_gem_image_state(const u32 port_no, const gem_config::gem_controller& controller, vm::ptr& gem_image_state) +template +static void ds3_pos_to_gem_state(const u32 port_no, const gem_config::gem_controller& controller, T& gem_state) { - if (!is_input_allowed()) + if (!gem_state || !is_input_allowed()) { return; } @@ -674,30 +722,14 @@ static void ds3_pos_to_gem_image_state(const u32 port_no, const gem_config::gem_ s32 ds3_pos_x, ds3_pos_y; ds3_get_stick_values(pad, ds3_pos_x, ds3_pos_y); - pos_to_gem_image_state(port_no, controller, gem_image_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y); -} - -static void ds3_pos_to_gem_state(const u32 port_no, const gem_config::gem_controller& controller, vm::ptr& gem_state) -{ - if (!is_input_allowed()) + if constexpr (std::is_same>::value) { - return; + pos_to_gem_state(port_no, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y); } - - std::lock_guard lock(pad::g_pad_mutex); - - const auto handler = pad::get_current_handler(); - const auto& pad = ::at32(handler->GetPads(), port_no); - - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + else if constexpr (std::is_same>::value) { - return; + pos_to_gem_image_state(port_no, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y); } - - s32 ds3_pos_x, ds3_pos_y; - ds3_get_stick_values(pad, ds3_pos_x, ds3_pos_y); - - pos_to_gem_state(port_no, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y); } /** @@ -811,31 +843,8 @@ static bool mouse_input_to_pad(const u32 mouse_no, be_t& digital_buttons, b return true; } -static void mouse_pos_to_gem_image_state(const u32 mouse_no, const gem_config::gem_controller& controller, vm::ptr& gem_image_state) -{ - if (!gem_image_state || !is_input_allowed()) - { - return; - } - - auto& handler = g_fxo->get(); - - std::scoped_lock lock(handler.mutex); - - // Make sure that the mouse handler is initialized - handler.Init(std::min(g_fxo->get().attribute.max_connect, CELL_GEM_MAX_NUM)); - - if (mouse_no >= handler.GetMice().size()) - { - return; - } - - const auto& mouse = ::at32(handler.GetMice(), mouse_no); - - pos_to_gem_image_state(mouse_no, controller, gem_image_state, mouse.x_pos, mouse.y_pos, mouse.x_max, mouse.y_max); -} - -static void mouse_pos_to_gem_state(const u32 mouse_no, const gem_config::gem_controller& controller, vm::ptr& gem_state) +template +static void mouse_pos_to_gem_state(const u32 mouse_no, const gem_config::gem_controller& controller, T& gem_state) { if (!gem_state || !is_input_allowed()) { @@ -856,65 +865,82 @@ static void mouse_pos_to_gem_state(const u32 mouse_no, const gem_config::gem_con const auto& mouse = ::at32(handler.GetMice(), mouse_no); - pos_to_gem_state(mouse_no, controller, gem_state, mouse.x_pos, mouse.y_pos, mouse.x_max, mouse.y_max); + if constexpr (std::is_same>::value) + { + pos_to_gem_state(mouse_no, controller, gem_state, mouse.x_pos, mouse.y_pos, mouse.x_max, mouse.y_max); + } + else if constexpr (std::is_same>::value) + { + pos_to_gem_image_state(mouse_no, controller, gem_state, mouse.x_pos, mouse.y_pos, mouse.x_max, mouse.y_max); + } } +#ifdef HAVE_LIBEVDEV static bool gun_input_to_pad(const u32 gem_no, be_t& digital_buttons, be_t& analog_t) { - digital_buttons = 0; - analog_t = 0; + digital_buttons = 0; + analog_t = 0; - if (!is_input_allowed()) { - return false; - } + if (!is_input_allowed()) + return false; - evdev_gun_handler *gh = evdev_gun_handler::getInstance(); - gh->pool(); + gun_thread& gun = g_fxo->get(); + std::scoped_lock lock(gun.handler.mutex); - digital_buttons = 0; + if (gun.handler.get_button(gem_no, gun_button::btn_left) == 1) + digital_buttons |= CELL_GEM_CTRL_T; - if(gh->getButton(gem_no, EVDEV_GUN_BUTTON_LEFT) == 1) - digital_buttons |= CELL_GEM_CTRL_T; - - if(gh->getButton(gem_no, EVDEV_GUN_BUTTON_RIGHT) == 1) - digital_buttons |= CELL_GEM_CTRL_MOVE; + if (gun.handler.get_button(gem_no, gun_button::btn_right) == 1) + digital_buttons |= CELL_GEM_CTRL_MOVE; - if(gh->getButton(gem_no, EVDEV_GUN_BUTTON_MIDDLE) == 1) - digital_buttons |= CELL_GEM_CTRL_SELECT; + if (gun.handler.get_button(gem_no, gun_button::btn_middle) == 1) + digital_buttons |= CELL_GEM_CTRL_SELECT; - if(gh->getButton(gem_no, EVDEV_GUN_BUTTON_BTN1) == 1) - digital_buttons |= CELL_GEM_CTRL_START; + if (gun.handler.get_button(gem_no, gun_button::btn_1) == 1) + digital_buttons |= CELL_GEM_CTRL_START; - if(gh->getButton(gem_no, EVDEV_GUN_BUTTON_BTN2) == 1) - digital_buttons |= CELL_GEM_CTRL_CROSS; + if (gun.handler.get_button(gem_no, gun_button::btn_2) == 1) + digital_buttons |= CELL_GEM_CTRL_CROSS; - if(gh->getButton(gem_no, EVDEV_GUN_BUTTON_BTN3) == 1) - digital_buttons |= CELL_GEM_CTRL_CIRCLE; + if (gun.handler.get_button(gem_no, gun_button::btn_3) == 1) + digital_buttons |= CELL_GEM_CTRL_CIRCLE; - if(gh->getButton(gem_no, EVDEV_GUN_BUTTON_BTN4) == 1) - digital_buttons |= CELL_GEM_CTRL_SQUARE; + if (gun.handler.get_button(gem_no, gun_button::btn_4) == 1) + digital_buttons |= CELL_GEM_CTRL_SQUARE; - if(gh->getButton(gem_no, EVDEV_GUN_BUTTON_BTN5) == 1) - digital_buttons |= CELL_GEM_CTRL_TRIANGLE; + if (gun.handler.get_button(gem_no, gun_button::btn_5) == 1) + digital_buttons |= CELL_GEM_CTRL_TRIANGLE; - //analog_t = (mouse_data.buttons & CELL_MOUSE_BUTTON_1) ? 0xFFFF : 0; - return true; + return true; } -static void gun_pos_to_gem_state(const u32 gem_no, const gem_config::gem_controller& controller, vm::ptr& gem_state) +template +static void gun_pos_to_gem_state(const u32 gem_no, const gem_config::gem_controller& controller, T& gem_state) { - if (!gem_state || !is_input_allowed()) { - return; - } + if (!gem_state || !is_input_allowed()) + return; - evdev_gun_handler *gh = evdev_gun_handler::getInstance(); - int x_pos = gh->getAxisX(gem_no); - int y_pos = gh->getAxisY(gem_no); - int x_max = gh->getAxisXMax(gem_no); - int y_max = gh->getAxisYMax(gem_no); + int x_pos, y_pos, x_max, y_max; + { + gun_thread& gun = g_fxo->get(); + std::scoped_lock lock(gun.handler.mutex); - pos_to_gem_state(gem_no, controller, gem_state, x_pos, y_pos, x_max, y_max); + x_pos = gun.handler.get_axis_x(gem_no); + y_pos = gun.handler.get_axis_y(gem_no); + x_max = gun.handler.get_axis_x_max(gem_no); + y_max = gun.handler.get_axis_y_max(gem_no); + } + + if constexpr (std::is_same>::value) + { + pos_to_gem_state(gem_no, controller, gem_state, x_pos, y_pos, x_max, y_max); + } + else if constexpr (std::is_same>::value) + { + pos_to_gem_image_state(gem_no, controller, gem_state, x_pos, y_pos, x_max, y_max); + } } +#endif // ********************* // * cellGem functions * @@ -1297,7 +1323,7 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr gem_imag *gem_image_state = {}; - if (g_cfg.io.move == move_handler::fake || g_cfg.io.move == move_handler::mouse || g_cfg.io.move == move_handler::gun) + if (g_cfg.io.move != move_handler::null) { auto& shared_data = g_fxo->get(); gem_image_state->frame_timestamp = shared_data.frame_timestamp.load(); @@ -1307,19 +1333,21 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr gem_imag gem_image_state->visible = gem.is_controller_ready(gem_num); gem_image_state->r_valid = true; - if (g_cfg.io.move == move_handler::fake) - { - ds3_pos_to_gem_image_state(gem_num, gem.controllers[gem_num], gem_image_state); - } - else if (g_cfg.io.move == move_handler::mouse) - { - mouse_pos_to_gem_image_state(gem_num, gem.controllers[gem_num], gem_image_state); - } - else if (g_cfg.io.move == move_handler::gun) + switch (g_cfg.io.move) { + case move_handler::fake: + ds3_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_image_state); + break; + case move_handler::mouse: + mouse_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_image_state); + break; #ifdef HAVE_LIBEVDEV + case move_handler::gun: gun_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_image_state); + break; #endif + case move_handler::null: + fmt::throw_exception("Unreachable"); } } @@ -1351,7 +1379,7 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v *inertial_state = {}; - if (g_cfg.io.move == move_handler::fake || g_cfg.io.move == move_handler::mouse || g_cfg.io.move == move_handler::gun) + if (g_cfg.io.move != move_handler::null) { ds3_input_to_ext(gem_num, gem.controllers[gem_num], inertial_state->ext); @@ -1359,19 +1387,21 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v inertial_state->counter = gem.inertial_counter++; inertial_state->accelerometer[0] = 10; // Current gravity in m/s² - if (g_cfg.io.move == move_handler::fake) + switch (g_cfg.io.move) { + case move_handler::fake: ds3_input_to_pad(gem_num, inertial_state->pad.digitalbuttons, inertial_state->pad.analog_T); - } - else if (g_cfg.io.move == move_handler::mouse) - { + break; + case move_handler::mouse: mouse_input_to_pad(gem_num, inertial_state->pad.digitalbuttons, inertial_state->pad.analog_T); - } - else if (g_cfg.io.move == move_handler::gun) - { + break; #ifdef HAVE_LIBEVDEV + case move_handler::gun: gun_input_to_pad(gem_num, inertial_state->pad.digitalbuttons, inertial_state->pad.analog_T); + break; #endif + case move_handler::null: + fmt::throw_exception("Unreachable"); } } @@ -1519,7 +1549,7 @@ error_code cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptrext); @@ -1533,22 +1563,24 @@ error_code cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptrcamera_pitch_angle = 0.f; gem_state->quat[3] = 1.f; - if (g_cfg.io.move == move_handler::fake) + switch (g_cfg.io.move) { + case move_handler::fake: ds3_input_to_pad(gem_num, gem_state->pad.digitalbuttons, gem_state->pad.analog_T); ds3_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_state); - } - else if (g_cfg.io.move == move_handler::mouse) - { + break; + case move_handler::mouse: mouse_input_to_pad(gem_num, gem_state->pad.digitalbuttons, gem_state->pad.analog_T); mouse_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_state); - } - else if (g_cfg.io.move == move_handler::gun) - { + break; #ifdef HAVE_LIBEVDEV + case move_handler::gun: gun_input_to_pad(gem_num, gem_state->pad.digitalbuttons, gem_state->pad.analog_T); gun_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_state); + break; #endif + case move_handler::null: + fmt::throw_exception("Unreachable"); } } diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index d2090f3ac9..9a88105dd8 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -9,7 +9,6 @@ #include "Emu/perf_monitor.hpp" #include "Emu/vfs_config.h" #include "Emu/IPC_config.h" -#include "Input/evdev_gun_handler.h" #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/PPUThread.h" @@ -2738,9 +2737,6 @@ void Emulator::CleanUp() { // Deinitialize object manager to prevent any hanging objects at program exit g_fxo->clear(); - - // deinitialize gun - evdev_gun_handler::shutdown(); } std::string Emulator::GetFormattedTitle(double fps) const diff --git a/rpcs3/Emu/system_config_types.cpp b/rpcs3/Emu/system_config_types.cpp index cc0fb3f434..4d087a02f9 100644 --- a/rpcs3/Emu/system_config_types.cpp +++ b/rpcs3/Emu/system_config_types.cpp @@ -412,7 +412,9 @@ void fmt_class_string::format(std::string& out, u64 arg) case move_handler::null: return "Null"; case move_handler::fake: return "Fake"; case move_handler::mouse: return "Mouse"; +#ifdef HAVE_LIBEVDEV case move_handler::gun: return "Gun"; +#endif } return unknown; diff --git a/rpcs3/Emu/system_config_types.h b/rpcs3/Emu/system_config_types.h index 4df742ffe9..0f93970524 100644 --- a/rpcs3/Emu/system_config_types.h +++ b/rpcs3/Emu/system_config_types.h @@ -128,7 +128,9 @@ enum class move_handler null, fake, mouse, +#ifdef HAVE_LIBEVDEV gun +#endif }; enum class buzz_handler diff --git a/rpcs3/Input/evdev_gun_handler.cpp b/rpcs3/Input/evdev_gun_handler.cpp index e36d9de7ed..55d128cf14 100644 --- a/rpcs3/Input/evdev_gun_handler.cpp +++ b/rpcs3/Input/evdev_gun_handler.cpp @@ -3,74 +3,69 @@ #ifdef HAVE_LIBEVDEV +#include "stdafx.h" #include "evdev_gun_handler.h" +#include "util/logs.hpp" + #include #include #include +#include LOG_CHANNEL(evdev_log, "evdev"); -evdev_gun_handler* evdev_gun_handler_instance = nullptr; +constexpr usz max_devices = 8; -evdev_gun_handler* evdev_gun_handler::getInstance() +const std::map button_map { - if (evdev_gun_handler_instance != nullptr) - return evdev_gun_handler_instance; - evdev_gun_handler_instance = new evdev_gun_handler(); - evdev_gun_handler_instance->init(); - return evdev_gun_handler_instance; -} + {gun_button::btn_left, BTN_LEFT}, + {gun_button::btn_right, BTN_RIGHT}, + {gun_button::btn_middle, BTN_MIDDLE}, + {gun_button::btn_1, BTN_1}, + {gun_button::btn_2, BTN_2}, + {gun_button::btn_3, BTN_3}, + {gun_button::btn_4, BTN_4}, + {gun_button::btn_5, BTN_5} +}; struct event_udev_entry { - const char* devnode; - struct udev_list_entry* item; + const char* devnode = nullptr; + struct udev_list_entry* item = nullptr; }; -int event_isNumber(const char* s) +bool event_is_number(const char* s) { - size_t n; + if (!s) + return false; - if (strlen(s) == 0) - { - return 0; - } + const usz len = strlen(s); + if (len == 0) + return false; - for (n = 0; n < strlen(s); n++) + for (usz n = 0; n < len; n++) { - if (!(s[n] == '0' || s[n] == '1' || s[n] == '2' || s[n] == '3' || s[n] == '4' || - s[n] == '5' || s[n] == '6' || s[n] == '7' || s[n] == '8' || s[n] == '9')) - return 0; + if (s[n] < '0' || s[n] > '9') + return false; } - return 1; + return true; } // compare /dev/input/eventX and /dev/input/eventY where X and Y are numbers int event_strcmp_events(const char* x, const char* y) { - // find a common string - int n, common, is_number; - int a, b; - - n = 0; - while (x[n] == y[n] && x[n] != '\0' && y[n] != '\0') + int n = 0; + while (x && y && x[n] == y[n] && x[n] != '\0' && y[n] != '\0') { n++; } - common = n; // check if remaining string is a number - is_number = 1; - if (event_isNumber(x + common) == 0) - is_number = 0; - if (event_isNumber(y + common) == 0) - is_number = 0; - - if (is_number == 1) + if (event_is_number(x + n) && event_is_number(y + n)) { - a = atoi(x + common); - b = atoi(y + common); + const int a = atoi(x + n); + const int b = atoi(y + n); if (a == b) return 0; @@ -78,134 +73,100 @@ int event_strcmp_events(const char* x, const char* y) return -1; return 1; } - else - { - return strcmp(x, y); - } -} -/* Used for sorting devnodes to appear in the correct order */ -static int sort_devnodes(const void* a, const void* b) -{ - const struct event_udev_entry* aa = static_cast(a); - const struct event_udev_entry* bb = static_cast(b); - return event_strcmp_events(aa->devnode, bb->devnode); + return strcmp(x, y); } evdev_gun_handler::evdev_gun_handler() { - m_is_init = false; - m_ndevices = 0; } evdev_gun_handler::~evdev_gun_handler() { - for (int i = 0; i < m_ndevices; i++) + for (evdev_gun& gun : m_devices) { - close(m_devices[i]); + close(gun.fd); } if (m_udev != nullptr) udev_unref(m_udev); - m_ndevices = 0; - m_is_init = false; evdev_log.notice("Lightgun: Shutdown udev initialization"); } -int evdev_gun_handler::getButton(int gunno, int button) +int evdev_gun_handler::get_button(u32 gunno, gun_button button) const { - return m_devices_buttons[gunno][button]; -} + const auto& buttons = ::at32(m_devices, gunno).buttons; -int evdev_gun_handler::getAxisX(int gunno) -{ - return m_devices_axis[gunno][0][EVDEV_GUN_AXIS_VALS_CURRENT] - m_devices_axis[gunno][0][EVDEV_GUN_AXIS_VALS_MIN]; -} - -int evdev_gun_handler::getAxisY(int gunno) -{ - return m_devices_axis[gunno][1][EVDEV_GUN_AXIS_VALS_CURRENT] - m_devices_axis[gunno][1][EVDEV_GUN_AXIS_VALS_MIN]; -} - -int evdev_gun_handler::getAxisXMax(int gunno) -{ - return m_devices_axis[gunno][0][EVDEV_GUN_AXIS_VALS_MAX] - m_devices_axis[gunno][0][EVDEV_GUN_AXIS_VALS_MIN]; -} - -int evdev_gun_handler::getAxisYMax(int gunno) -{ - return m_devices_axis[gunno][1][EVDEV_GUN_AXIS_VALS_MAX] - m_devices_axis[gunno][1][EVDEV_GUN_AXIS_VALS_MIN]; -} - -void evdev_gun_handler::pool() -{ - struct input_event input_events[32]; - int j, len; - - for (int i = 0; i < m_ndevices; i++) + if (const auto it = buttons.find(::at32(button_map, button)); it != buttons.end()) { - while ((len = read(m_devices[i], input_events, sizeof(input_events))) > 0) + return it->second; + } + + return 0; +} + +int evdev_gun_handler::get_axis_x(u32 gunno) const +{ + const evdev_axis& axis = ::at32(::at32(m_devices, gunno).axis, ABS_X); + return axis.value - axis.min; +} + +int evdev_gun_handler::get_axis_y(u32 gunno) const +{ + const evdev_axis& axis = ::at32(::at32(m_devices, gunno).axis, ABS_Y); + return axis.value - axis.min; +} + +int evdev_gun_handler::get_axis_x_max(u32 gunno) const +{ + const evdev_axis& axis = ::at32(::at32(m_devices, gunno).axis, ABS_X); + return axis.max - axis.min; +} + +int evdev_gun_handler::get_axis_y_max(u32 gunno) const +{ + const evdev_axis& axis = ::at32(::at32(m_devices, gunno).axis, ABS_Y); + return axis.max - axis.min; +} + +void evdev_gun_handler::poll(u32 index) +{ + if (!m_is_init || index >= m_devices.size()) + return; + + std::array input_events; + evdev_gun& gun = ::at32(m_devices, index); + + if (usz len = read(gun.fd, input_events.data(), input_events.size() * sizeof(input_event)); len > 0) + { + len /= sizeof(input_event); + + for (usz i = 0; i < std::min(len, input_events.size()); i++) { - len /= sizeof(*input_events); - for (j = 0; j < len; j++) + const input_event& evt = input_events[i]; + + switch (evt.type) { - if (input_events[j].type == EV_KEY) - { - switch (input_events[j].code) - { - case BTN_LEFT: - m_devices_buttons[i][EVDEV_GUN_BUTTON_LEFT] = input_events[j].value; - break; - case BTN_RIGHT: - m_devices_buttons[i][EVDEV_GUN_BUTTON_RIGHT] = input_events[j].value; - break; - case BTN_MIDDLE: - m_devices_buttons[i][EVDEV_GUN_BUTTON_MIDDLE] = input_events[j].value; - break; - case BTN_1: - m_devices_buttons[i][EVDEV_GUN_BUTTON_BTN1] = input_events[j].value; - break; - case BTN_2: - m_devices_buttons[i][EVDEV_GUN_BUTTON_BTN2] = input_events[j].value; - break; - case BTN_3: - m_devices_buttons[i][EVDEV_GUN_BUTTON_BTN3] = input_events[j].value; - break; - case BTN_4: - m_devices_buttons[i][EVDEV_GUN_BUTTON_BTN4] = input_events[j].value; - break; - case BTN_5: - m_devices_buttons[i][EVDEV_GUN_BUTTON_BTN5] = input_events[j].value; - break; - } - } - else if (input_events[j].type == EV_ABS) - { - if (input_events[j].code == ABS_X) - { - m_devices_axis[i][0][EVDEV_GUN_AXIS_VALS_CURRENT] = input_events[j].value; - } - else if (input_events[j].code == ABS_Y) - { - m_devices_axis[i][1][EVDEV_GUN_AXIS_VALS_CURRENT] = input_events[j].value; - } - } + case EV_KEY: + gun.buttons[evt.code] = evt.value; + break; + case EV_ABS: + gun.axis[evt.code].value = evt.value; + break; + default: + break; } } } } -int evdev_gun_handler::getNumGuns() +bool evdev_gun_handler::is_init() const { - return m_ndevices; + return m_is_init; } -void evdev_gun_handler::shutdown() +u32 evdev_gun_handler::get_num_guns() const { - if (evdev_gun_handler_instance != nullptr) - { - delete evdev_gun_handler_instance; - evdev_gun_handler_instance = nullptr; - } + return ::narrow(m_devices.size()); } bool evdev_gun_handler::init() @@ -213,39 +174,38 @@ bool evdev_gun_handler::init() if (m_is_init) return true; - struct udev_enumerate* enumerate; - struct udev_list_entry* devs = nullptr; - struct udev_list_entry* item = nullptr; - unsigned sorted_count = 0; - struct event_udev_entry sorted[8]; // max devices - unsigned int i; - evdev_log.notice("Lightgun: Begin udev initialization"); + m_devices.clear(); + m_udev = udev_new(); if (m_udev == nullptr) + { + evdev_log.error("Lightgun: Failed udev initialization"); return false; + } - enumerate = udev_enumerate_new(m_udev); - - if (enumerate != nullptr) + if (udev_enumerate* enumerate = udev_enumerate_new(m_udev)) { udev_enumerate_add_match_property(enumerate, "ID_INPUT_MOUSE", "1"); udev_enumerate_add_match_subsystem(enumerate, "input"); udev_enumerate_scan_devices(enumerate); - devs = udev_enumerate_get_list_entry(enumerate); + udev_list_entry* devs = udev_enumerate_get_list_entry(enumerate); - for (item = devs; item; item = udev_list_entry_get_next(item)) + std::vector sorted_devices; + + for (udev_list_entry* item = devs; item && sorted_devices.size() < max_devices; item = udev_list_entry_get_next(item)) { const char* name = udev_list_entry_get_name(item); - struct udev_device* dev = udev_device_new_from_syspath(m_udev, name); + udev_device* dev = udev_device_new_from_syspath(m_udev, name); const char* devnode = udev_device_get_devnode(dev); - if (devnode != nullptr && sorted_count < 8) + if (devnode != nullptr) { - sorted[sorted_count].devnode = devnode; - sorted[sorted_count].item = item; - sorted_count++; + event_udev_entry new_device{}; + new_device.devnode = devnode; + new_device.item = item; + sorted_devices.push_back(std::move(new_device)); } else { @@ -253,57 +213,41 @@ bool evdev_gun_handler::init() } } - /* Sort the udev entries by devnode name so that they are - * created in the proper order */ - qsort(sorted, sorted_count, - sizeof(struct event_udev_entry), sort_devnodes); - - for (i = 0; i < sorted_count; i++) + // Sort the udev entries by devnode name so that they are created in the proper order + std::sort(sorted_devices.begin(), sorted_devices.end(), [](const event_udev_entry& a, const event_udev_entry& b) { - if (m_ndevices >= EVDEV_GUN_MAX_DEVICES) - break; + return event_strcmp_events(a.devnode, b.devnode); + }); - const char* name = udev_list_entry_get_name(sorted[i].item); - /* Get the filename of the /sys entry for the device - * and create a udev_device object (dev) representing it. */ - struct udev_device* dev = udev_device_new_from_syspath(m_udev, name); + for (const event_udev_entry& entry : sorted_devices) + { + // Get the filename of the /sys entry for the device and create a udev_device object (dev) representing it. + const char* name = udev_list_entry_get_name(entry.item); evdev_log.notice("Lightgun: found device %s", name); + + udev_device* dev = udev_device_new_from_syspath(m_udev, name); const char* devnode = udev_device_get_devnode(dev); if (devnode) { - struct input_absinfo absx, absy; - int valid = 0; - int fd = open(devnode, O_RDONLY | O_NONBLOCK); - if (fd != -1) + if (int fd = open(devnode, O_RDONLY | O_NONBLOCK); fd != -1) { - for (int b = 0; b < EVDEV_GUN_BUTTON_MAX; b++) + input_absinfo absx, absy; + if (ioctl(fd, EVIOCGABS(ABS_X), &absx) >= 0 && + ioctl(fd, EVIOCGABS(ABS_Y), &absy) >= 0) { - m_devices_buttons[m_ndevices][b] = 0; - } - for (int a = 0; a < 3; a++) - { - m_devices_axis[m_ndevices][0][a] = 0; - m_devices_axis[m_ndevices][1][a] = 0; - } - if (ioctl(fd, EVIOCGABS(ABS_X), &absx) >= 0) - { - if (ioctl(fd, EVIOCGABS(ABS_Y), &absy) >= 0) - { - evdev_log.notice("Lightgun: device %s, absx(%i, %i), absy(%i, %i)", name, absx.minimum, absx.maximum, absy.minimum, absy.maximum); + evdev_log.notice("Lightgun: Adding device %d: %s, absx(%i, %i), absy(%i, %i)", m_devices.size(), name, absx.minimum, absx.maximum, absy.minimum, absy.maximum); - m_devices_axis[m_ndevices][0][EVDEV_GUN_AXIS_VALS_MIN] = absx.minimum; - m_devices_axis[m_ndevices][0][EVDEV_GUN_AXIS_VALS_MAX] = absx.maximum; - m_devices_axis[m_ndevices][1][EVDEV_GUN_AXIS_VALS_MIN] = absy.minimum; - m_devices_axis[m_ndevices][1][EVDEV_GUN_AXIS_VALS_MAX] = absy.maximum; - valid = 1; - } - } + evdev_gun gun{}; + gun.fd = fd; + gun.axis[ABS_X].min = absx.minimum; + gun.axis[ABS_X].max = absx.maximum; + gun.axis[ABS_Y].min = absy.minimum; + gun.axis[ABS_Y].max = absy.maximum; + m_devices.push_back(gun); - if (valid == 1) - { - evdev_log.notice("Lightgun: device %s set as gun %i", name, m_ndevices); - m_devices[m_ndevices++] = fd; + if (m_devices.size() >= max_devices) + break; } else { @@ -315,7 +259,10 @@ bool evdev_gun_handler::init() udev_device_unref(dev); } udev_enumerate_unref(enumerate); - return true; + } + else + { + evdev_log.error("Lightgun: Failed udev enumeration"); } m_is_init = true; diff --git a/rpcs3/Input/evdev_gun_handler.h b/rpcs3/Input/evdev_gun_handler.h index c07d6e95dd..0abd9cf5a3 100644 --- a/rpcs3/Input/evdev_gun_handler.h +++ b/rpcs3/Input/evdev_gun_handler.h @@ -1,60 +1,60 @@ #pragma once #ifdef HAVE_LIBEVDEV -#include "util/types.hpp" -#include "util/logs.hpp" +#include +#include -#define EVDEV_GUN_MAX_DEVICES 8 - -enum evdev_gun_buttons +enum class gun_button { - EVDEV_GUN_BUTTON_LEFT, - EVDEV_GUN_BUTTON_RIGHT, - EVDEV_GUN_BUTTON_MIDDLE, - EVDEV_GUN_BUTTON_BTN1, - EVDEV_GUN_BUTTON_BTN2, - EVDEV_GUN_BUTTON_BTN3, - EVDEV_GUN_BUTTON_BTN4, - EVDEV_GUN_BUTTON_BTN5, - EVDEV_GUN_BUTTON_MAX, + btn_left, + btn_right, + btn_middle, + btn_1, + btn_2, + btn_3, + btn_4, + btn_5 }; -enum evdev_gun_axis_vals -{ - EVDEV_GUN_AXIS_VALS_MIN, - EVDEV_GUN_AXIS_VALS_CURRENT, - EVDEV_GUN_AXIS_VALS_MAX, -}; - -#define EVDEV_GUN_BUTTON_LEFT 1 - class evdev_gun_handler { public: evdev_gun_handler(); ~evdev_gun_handler(); - static evdev_gun_handler* getInstance(); - static void shutdown(); - bool init(); - int getNumGuns(); - int getButton(int gunno, int button); - int getAxisX(int gunno); - int getAxisY(int gunno); - int getAxisXMax(int gunno); - int getAxisYMax(int gunno); + bool is_init() const; + u32 get_num_guns() const; + int get_button(u32 gunno, gun_button button) const; + int get_axis_x(u32 gunno) const; + int get_axis_y(u32 gunno) const; + int get_axis_x_max(u32 gunno) const; + int get_axis_y_max(u32 gunno) const; - void pool(); + void poll(u32 index); + + shared_mutex mutex; private: - bool m_is_init; + atomic_t m_is_init{false}; struct udev* m_udev = nullptr; - int m_devices[EVDEV_GUN_MAX_DEVICES]; - int m_devices_buttons[EVDEV_GUN_MAX_DEVICES][EVDEV_GUN_BUTTON_MAX]; - int m_devices_axis[EVDEV_GUN_MAX_DEVICES][2][3]; - int m_ndevices; + + struct evdev_axis + { + int value = 0; + int min = 0; + int max = 0; + }; + + struct evdev_gun + { + int fd = -1; + std::map buttons; + std::map axis; + }; + + std::vector m_devices; }; #endif diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 871961e9d0..98d2cb1549 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -175,6 +175,7 @@ + @@ -725,6 +726,7 @@ + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index f363477f15..34169fbb58 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -873,6 +873,9 @@ Generated Files\Release + + Io\evdev + @@ -1031,6 +1034,9 @@ Gui\settings + + Io\evdev + diff --git a/rpcs3/rpcs3qt/emu_settings.cpp b/rpcs3/rpcs3qt/emu_settings.cpp index d70c0406a9..6d50f19db8 100644 --- a/rpcs3/rpcs3qt/emu_settings.cpp +++ b/rpcs3/rpcs3qt/emu_settings.cpp @@ -1057,7 +1057,9 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_ case move_handler::null: return tr("Null", "Move handler"); case move_handler::fake: return tr("Fake", "Move handler"); case move_handler::mouse: return tr("Mouse", "Move handler"); +#ifdef HAVE_LIBEVDEV case move_handler::gun: return tr("Gun", "Gun handler"); +#endif } break; case emu_settings_type::Buzz: diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 03b20e5473..d41ec8e989 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -808,7 +808,10 @@ void gs_frame::take_screenshot(std::vector data, const u32 sshot_width, cons void gs_frame::mouseDoubleClickEvent(QMouseEvent* ev) { - if (m_disable_mouse || g_cfg.io.move == move_handler::mouse || g_cfg.io.move == move_handler::gun) return; + if (m_disable_mouse || g_cfg.io.move == move_handler::mouse) return; +#ifdef HAVE_LIBEVDEV + if (g_cfg.io.move == move_handler::gun) return; +#endif if (ev->button() == Qt::LeftButton) {