mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 03:25:16 +00:00
evdev gun support cleanup
This commit is contained in:
parent
2805fe0a06
commit
5188293242
10 changed files with 342 additions and 350 deletions
|
@ -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 <cmath> // for fmod
|
||||
#include <type_traits>
|
||||
|
||||
LOG_CHANNEL(cellGem);
|
||||
|
||||
|
@ -89,6 +93,46 @@ void fmt_class_string<CellGemVideoConvertFormatEnum>::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<u32> 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<gun_handler>;
|
||||
|
||||
#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<gun_thread>();
|
||||
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>& pad, s32& x_
|
|||
}
|
||||
}
|
||||
|
||||
static void ds3_pos_to_gem_image_state(const u32 port_no, const gem_config::gem_controller& controller, vm::ptr<CellGemImageState>& gem_image_state)
|
||||
template <typename T>
|
||||
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<CellGemState>& gem_state)
|
||||
{
|
||||
if (!is_input_allowed())
|
||||
if constexpr (std::is_same<T, vm::ptr<CellGemState>>::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<T, vm::ptr<CellGemImageState>>::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<u16>& 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<CellGemImageState>& gem_image_state)
|
||||
{
|
||||
if (!gem_image_state || !is_input_allowed())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto& handler = g_fxo->get<MouseHandlerBase>();
|
||||
|
||||
std::scoped_lock lock(handler.mutex);
|
||||
|
||||
// Make sure that the mouse handler is initialized
|
||||
handler.Init(std::min<u32>(g_fxo->get<gem_config>().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<CellGemState>& gem_state)
|
||||
template <typename T>
|
||||
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<T, vm::ptr<CellGemState>>::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<T, vm::ptr<CellGemImageState>>::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<u16>& digital_buttons, be_t<u16>& 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<gun_thread>();
|
||||
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<CellGemState>& gem_state)
|
||||
template <typename T>
|
||||
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<gun_thread>();
|
||||
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<T, vm::ptr<CellGemState>>::value)
|
||||
{
|
||||
pos_to_gem_state(gem_no, controller, gem_state, x_pos, y_pos, x_max, y_max);
|
||||
}
|
||||
else if constexpr (std::is_same<T, vm::ptr<CellGemImageState>>::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<CellGemImageState> 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_camera_shared>();
|
||||
gem_image_state->frame_timestamp = shared_data.frame_timestamp.load();
|
||||
|
@ -1307,19 +1333,21 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr<CellGemImageState> 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::ptr<Ce
|
|||
|
||||
*gem_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], gem_state->ext);
|
||||
|
||||
|
@ -1533,22 +1563,24 @@ error_code cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptr<Ce
|
|||
gem_state->camera_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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -412,7 +412,9 @@ void fmt_class_string<move_handler>::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;
|
||||
|
|
|
@ -128,7 +128,9 @@ enum class move_handler
|
|||
null,
|
||||
fake,
|
||||
mouse,
|
||||
#ifdef HAVE_LIBEVDEV
|
||||
gun
|
||||
#endif
|
||||
};
|
||||
|
||||
enum class buzz_handler
|
||||
|
|
|
@ -3,74 +3,69 @@
|
|||
|
||||
#ifdef HAVE_LIBEVDEV
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "evdev_gun_handler.h"
|
||||
#include "util/logs.hpp"
|
||||
|
||||
#include <libudev.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/input.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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<gun_button, int> 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<const struct event_udev_entry*>(a);
|
||||
const struct event_udev_entry* bb = static_cast<const struct event_udev_entry*>(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_event, 32> 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<u32>(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<event_udev_entry> 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;
|
||||
|
|
|
@ -1,60 +1,60 @@
|
|||
#pragma once
|
||||
#ifdef HAVE_LIBEVDEV
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "util/logs.hpp"
|
||||
#include <array>
|
||||
#include <map>
|
||||
|
||||
#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<bool> 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<int, int> buttons;
|
||||
std::map<int, evdev_axis> axis;
|
||||
};
|
||||
|
||||
std::vector<evdev_gun> m_devices;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -175,6 +175,7 @@
|
|||
<ItemGroup>
|
||||
<ClCompile Include="display_sleep_control.cpp" />
|
||||
<ClCompile Include="Input\dualsense_pad_handler.cpp" />
|
||||
<ClCompile Include="Input\evdev_gun_handler.cpp" />
|
||||
<ClCompile Include="Input\hid_pad_handler.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="main_application.cpp" />
|
||||
|
@ -725,6 +726,7 @@
|
|||
<ClInclude Include="Input\ds3_pad_handler.h" />
|
||||
<ClInclude Include="Input\ds4_pad_handler.h" />
|
||||
<ClInclude Include="Input\dualsense_pad_handler.h" />
|
||||
<ClInclude Include="Input\evdev_gun_handler.h" />
|
||||
<ClInclude Include="Input\evdev_joystick_handler.h" />
|
||||
<ClInclude Include="Input\hid_pad_handler.h" />
|
||||
<ClInclude Include="Input\keyboard_pad_handler.h" />
|
||||
|
|
|
@ -873,6 +873,9 @@
|
|||
<ClCompile Include="QTGeneratedFiles\Release\moc_pad_motion_settings_dialog.cpp">
|
||||
<Filter>Generated Files\Release</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Input\evdev_gun_handler.cpp">
|
||||
<Filter>Io\evdev</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Input\ds4_pad_handler.h">
|
||||
|
@ -1031,6 +1034,9 @@
|
|||
<ClInclude Include="rpcs3qt\pad_device_info.h">
|
||||
<Filter>Gui\settings</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Input\evdev_gun_handler.h">
|
||||
<Filter>Io\evdev</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="debug\moc_predefs.h.cbt">
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -808,7 +808,10 @@ void gs_frame::take_screenshot(std::vector<u8> 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)
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue