input: implement raw_mouse_handler

This commit is contained in:
Megamouse 2024-02-05 23:49:00 +01:00
parent f6f8996aa9
commit 68f3df4975
18 changed files with 654 additions and 52 deletions

View file

@ -75,6 +75,7 @@ target_sources(rpcs3
Input/keyboard_pad_handler.cpp
Input/mm_joystick_handler.cpp
Input/pad_thread.cpp
Input/raw_mouse_handler.cpp
Input/sdl_pad_handler.cpp
Input/skateboard_pad_handler.cpp
Input/xinput_pad_handler.cpp

View file

@ -317,10 +317,16 @@ public:
break;
}
case move_handler::mouse:
case move_handler::raw_mouse:
{
connected_controllers = 1;
auto& handler = g_fxo->get<MouseHandlerBase>();
if (gem_num == 0)
// Make sure that the mouse handler is initialized
handler.Init(std::min<u32>(attribute.max_connect, CELL_GEM_MAX_NUM));
const MouseInfo& info = handler.GetInfo();
connected_controllers = std::min<u32>({ info.now_connect, attribute.max_connect, CELL_GEM_MAX_NUM });
if (gem_num < connected_controllers)
{
is_connected = true;
}
@ -1417,6 +1423,7 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr<CellGemImageState> gem_imag
ds3_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_image_state);
break;
case move_handler::mouse:
case move_handler::raw_mouse:
mouse_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_image_state);
break;
#ifdef HAVE_LIBEVDEV
@ -1471,6 +1478,7 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v
ds3_input_to_pad(gem_num, inertial_state->pad.digitalbuttons, inertial_state->pad.analog_T);
break;
case move_handler::mouse:
case move_handler::raw_mouse:
mouse_input_to_pad(gem_num, inertial_state->pad.digitalbuttons, inertial_state->pad.analog_T);
break;
#ifdef HAVE_LIBEVDEV
@ -1675,6 +1683,7 @@ error_code cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptr<Ce
ds3_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_state);
break;
case move_handler::mouse:
case move_handler::raw_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);
break;

View file

@ -5,6 +5,7 @@
#include <vector>
#include "Utilities/mutex.h"
#include "util/init_mutex.hpp"
#include "Emu/system_config_types.h"
// TODO: HLE info (constants, structs, etc.) should not be available here
@ -131,23 +132,25 @@ public:
SAVESTATE_INIT_POS(18);
MouseHandlerBase(){};
MouseHandlerBase(const MouseHandlerBase&) = delete;
MouseHandlerBase(utils::serial* ar);
MouseHandlerBase(utils::serial& ar) : MouseHandlerBase(&ar) {}
void save(utils::serial& ar);
void Button(u8 button, bool pressed)
void Button(u32 index, u8 button, bool pressed)
{
std::lock_guard lock(mutex);
for (u32 p = 0; p < m_mice.size(); ++p)
if (index < m_mice.size())
{
if (m_info.status[p] != CELL_MOUSE_STATUS_CONNECTED)
if (m_info.status[index] != CELL_MOUSE_STATUS_CONNECTED)
{
continue;
return;
}
MouseDataList& datalist = GetDataList(p);
Mouse& mouse = m_mice[index];
MouseDataList& datalist = GetDataList(index);
if (datalist.size() > MOUSE_MAX_DATA_LIST_NUM)
{
@ -155,30 +158,31 @@ public:
}
if (pressed)
m_mice[p].buttons |= button;
mouse.buttons |= button;
else
m_mice[p].buttons &= ~button;
mouse.buttons &= ~button;
MouseData new_data;
new_data.update = CELL_MOUSE_DATA_UPDATE;
new_data.buttons = m_mice[p].buttons;
new_data.buttons = mouse.buttons;
datalist.push_back(new_data);
}
}
void Scroll(const s8 rotation)
void Scroll(u32 index, s32 rotation)
{
std::lock_guard lock(mutex);
for (u32 p = 0; p < m_mice.size(); ++p)
if (index < m_mice.size())
{
if (m_info.status[p] != CELL_MOUSE_STATUS_CONNECTED)
if (m_info.status[index] != CELL_MOUSE_STATUS_CONNECTED)
{
continue;
return;
}
MouseDataList& datalist = GetDataList(p);
Mouse& mouse = m_mice[index];
MouseDataList& datalist = GetDataList(index);
if (datalist.size() > MOUSE_MAX_DATA_LIST_NUM)
{
@ -187,25 +191,26 @@ public:
MouseData new_data;
new_data.update = CELL_MOUSE_DATA_UPDATE;
new_data.wheel = rotation / 120; //120=event.GetWheelDelta()
new_data.buttons = m_mice[p].buttons;
new_data.wheel = std::clamp(rotation / 120, -128, 127); //120=event.GetWheelDelta()
new_data.buttons = mouse.buttons;
datalist.push_back(new_data);
}
}
void Move(s32 x_pos_new, s32 y_pos_new, s32 x_max, s32 y_max, const bool is_qt_fullscreen = false, s32 x_delta = 0, s32 y_delta = 0)
void Move(u32 index, s32 x_pos_new, s32 y_pos_new, s32 x_max, s32 y_max, const bool is_relative = false, s32 x_delta = 0, s32 y_delta = 0)
{
std::lock_guard lock(mutex);
for (u32 p = 0; p < m_mice.size(); ++p)
if (index < m_mice.size())
{
if (m_info.status[p] != CELL_MOUSE_STATUS_CONNECTED)
if (m_info.status[index] != CELL_MOUSE_STATUS_CONNECTED)
{
continue;
return;
}
MouseDataList& datalist = GetDataList(p);
Mouse& mouse = m_mice[index];
MouseDataList& datalist = GetDataList(index);
if (datalist.size() > MOUSE_MAX_DATA_LIST_NUM)
{
@ -214,21 +219,22 @@ public:
MouseData new_data;
new_data.update = CELL_MOUSE_DATA_UPDATE;
new_data.buttons = m_mice[p].buttons;
new_data.buttons = mouse.buttons;
if (!is_qt_fullscreen)
if (!is_relative)
{
x_delta = x_pos_new - m_mice[p].x_pos;
y_delta = y_pos_new - m_mice[p].y_pos;
// The PS3 expects relative mouse movement, so we have to calculate it with the last absolute position.
x_delta = x_pos_new - mouse.x_pos;
y_delta = y_pos_new - mouse.y_pos;
}
new_data.x_axis = static_cast<s8>(std::clamp(x_delta, -127, 128));
new_data.y_axis = static_cast<s8>(std::clamp(y_delta, -127, 128));
m_mice[p].x_max = x_max;
m_mice[p].y_max = y_max;
m_mice[p].x_pos = x_pos_new;
m_mice[p].y_pos = y_pos_new;
mouse.x_max = x_max;
mouse.y_max = y_max;
mouse.x_pos = x_pos_new;
mouse.y_pos = y_pos_new;
/*CellMouseRawData& rawdata = GetRawData(p);
rawdata.data[rawdata.len % CELL_MOUSE_MAX_CODES] = 0; // (TODO)
@ -260,4 +266,6 @@ public:
MouseRawData& GetRawData(const u32 mouse) { return m_mice[mouse].m_rawdata; }
stx::init_mutex init;
mouse_handler type = mouse_handler::null;
};

View file

@ -10,6 +10,7 @@ void fmt_class_string<mouse_handler>::format(std::string& out, u64 arg)
{
case mouse_handler::null: return "Null";
case mouse_handler::basic: return "Basic";
case mouse_handler::raw: return "Raw";
}
return unknown;
@ -412,6 +413,7 @@ 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";
case move_handler::raw_mouse: return "Raw Mouse";
#ifdef HAVE_LIBEVDEV
case move_handler::gun: return "Gun";
#endif

View file

@ -38,6 +38,7 @@ enum class mouse_handler
{
null,
basic,
raw
};
enum class video_renderer
@ -127,6 +128,7 @@ enum class move_handler
null,
fake,
mouse,
raw_mouse,
#ifdef HAVE_LIBEVDEV
gun
#endif

View file

@ -33,12 +33,14 @@ void basic_mouse_handler::Init(const u32 max_connect)
m_info.status[0] = CELL_MOUSE_STATUS_CONNECTED; // (TODO: Support for more mice)
m_info.vendor_id[0] = 0x1234;
m_info.product_id[0] = 0x1234;
type = mouse_handler::basic;
}
/* Sets the target window for the event handler, and also installs an event filter on the target. */
void basic_mouse_handler::SetTargetWindow(QWindow* target)
{
if (target != nullptr)
if (target)
{
m_target = target;
target->installEventFilter(this);
@ -86,33 +88,33 @@ bool basic_mouse_handler::eventFilter(QObject* target, QEvent* ev)
void basic_mouse_handler::MouseButtonDown(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton) MouseHandlerBase::Button(CELL_MOUSE_BUTTON_1, true);
else if (event->button() == Qt::RightButton) MouseHandlerBase::Button(CELL_MOUSE_BUTTON_2, true);
else if (event->button() == Qt::MiddleButton) MouseHandlerBase::Button(CELL_MOUSE_BUTTON_3, true);
if (event->button() == Qt::LeftButton) MouseHandlerBase::Button(0, CELL_MOUSE_BUTTON_1, true);
else if (event->button() == Qt::RightButton) MouseHandlerBase::Button(0, CELL_MOUSE_BUTTON_2, true);
else if (event->button() == Qt::MiddleButton) MouseHandlerBase::Button(0, CELL_MOUSE_BUTTON_3, true);
// TODO: verify these
else if (event->button() == Qt::ExtraButton1) MouseHandlerBase::Button(CELL_MOUSE_BUTTON_4, true);
else if (event->button() == Qt::ExtraButton2) MouseHandlerBase::Button(CELL_MOUSE_BUTTON_5, true);
else if (event->button() == Qt::ExtraButton3) MouseHandlerBase::Button(CELL_MOUSE_BUTTON_6, true);
else if (event->button() == Qt::ExtraButton4) MouseHandlerBase::Button(CELL_MOUSE_BUTTON_7, true);
else if (event->button() == Qt::ExtraButton5) MouseHandlerBase::Button(CELL_MOUSE_BUTTON_8, true);
else if (event->button() == Qt::ExtraButton1) MouseHandlerBase::Button(0, CELL_MOUSE_BUTTON_4, true);
else if (event->button() == Qt::ExtraButton2) MouseHandlerBase::Button(0, CELL_MOUSE_BUTTON_5, true);
else if (event->button() == Qt::ExtraButton3) MouseHandlerBase::Button(0, CELL_MOUSE_BUTTON_6, true);
else if (event->button() == Qt::ExtraButton4) MouseHandlerBase::Button(0, CELL_MOUSE_BUTTON_7, true);
else if (event->button() == Qt::ExtraButton5) MouseHandlerBase::Button(0, CELL_MOUSE_BUTTON_8, true);
}
void basic_mouse_handler::MouseButtonUp(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton) MouseHandlerBase::Button(CELL_MOUSE_BUTTON_1, false);
else if (event->button() == Qt::RightButton) MouseHandlerBase::Button(CELL_MOUSE_BUTTON_2, false);
else if (event->button() == Qt::MiddleButton) MouseHandlerBase::Button(CELL_MOUSE_BUTTON_3, false);
if (event->button() == Qt::LeftButton) MouseHandlerBase::Button(0, CELL_MOUSE_BUTTON_1, false);
else if (event->button() == Qt::RightButton) MouseHandlerBase::Button(0, CELL_MOUSE_BUTTON_2, false);
else if (event->button() == Qt::MiddleButton) MouseHandlerBase::Button(0, CELL_MOUSE_BUTTON_3, false);
// TODO: verify these
else if (event->button() == Qt::ExtraButton1) MouseHandlerBase::Button(CELL_MOUSE_BUTTON_4, false);
else if (event->button() == Qt::ExtraButton2) MouseHandlerBase::Button(CELL_MOUSE_BUTTON_5, false);
else if (event->button() == Qt::ExtraButton3) MouseHandlerBase::Button(CELL_MOUSE_BUTTON_6, false);
else if (event->button() == Qt::ExtraButton4) MouseHandlerBase::Button(CELL_MOUSE_BUTTON_7, false);
else if (event->button() == Qt::ExtraButton5) MouseHandlerBase::Button(CELL_MOUSE_BUTTON_8, false);
else if (event->button() == Qt::ExtraButton1) MouseHandlerBase::Button(0, CELL_MOUSE_BUTTON_4, false);
else if (event->button() == Qt::ExtraButton2) MouseHandlerBase::Button(0, CELL_MOUSE_BUTTON_5, false);
else if (event->button() == Qt::ExtraButton3) MouseHandlerBase::Button(0, CELL_MOUSE_BUTTON_6, false);
else if (event->button() == Qt::ExtraButton4) MouseHandlerBase::Button(0, CELL_MOUSE_BUTTON_7, false);
else if (event->button() == Qt::ExtraButton5) MouseHandlerBase::Button(0, CELL_MOUSE_BUTTON_8, false);
}
void basic_mouse_handler::MouseScroll(QWheelEvent* event)
{
MouseHandlerBase::Scroll(event->angleDelta().y());
MouseHandlerBase::Scroll(0, event->angleDelta().y());
}
bool basic_mouse_handler::get_mouse_lock_state() const
@ -152,11 +154,12 @@ void basic_mouse_handler::MouseMove(QMouseEvent* event)
p_real.setY(std::clamp(p_real.y() + p_delta.y(), 0, screen.height()));
// pass the 'real' position and the current delta to the screen center
MouseHandlerBase::Move(p_real.x(), p_real.y(), screen.width(), screen.height(), true, p_delta.x(), p_delta.y());
MouseHandlerBase::Move(0, p_real.x(), p_real.y(), screen.width(), screen.height(), true, p_delta.x(), p_delta.y());
}
else
{
MouseHandlerBase::Move(e_pos.x(), e_pos.y(), screen.width(), screen.height());
// pass the absolute position
MouseHandlerBase::Move(0, e_pos.x(), e_pos.y(), screen.width(), screen.height());
}
}
}

View file

@ -0,0 +1,79 @@
#pragma once
#include "Utilities/Config.h"
#include "Utilities/mutex.h"
#include <array>
LOG_CHANNEL(cfg_log, "CFG");
struct raw_mouse_config : cfg::node
{
public:
using cfg::node::node;
cfg::_float<10, 1000> mouse_acceleration{ this, "Mouse Acceleration", 100.0f, true };
};
struct raw_mice_config : cfg::node
{
raw_mice_config()
{
for (u32 i = 0; i < ::size32(players); i++)
{
players.at(i) = std::make_shared<raw_mouse_config>(this, fmt::format("Player %d", i + 1));
}
}
shared_mutex m_mutex;
static constexpr std::string_view cfg_id = "raw_mouse";
std::array<std::shared_ptr<raw_mouse_config>, 4> players;
bool load()
{
m_mutex.lock();
bool result = false;
const std::string cfg_name = fmt::format("%sconfig/%s.yml", fs::get_config_dir(), cfg_id);
cfg_log.notice("Loading %s config: %s", cfg_id, cfg_name);
from_default();
if (fs::file cfg_file{ cfg_name, fs::read })
{
if (std::string content = cfg_file.to_string(); !content.empty())
{
result = from_string(content);
}
m_mutex.unlock();
}
else
{
m_mutex.unlock();
save();
}
return result;
}
void save()
{
std::lock_guard lock(m_mutex);
const std::string cfg_name = fmt::format("%sconfig/%s.yml", fs::get_config_dir(), cfg_id);
cfg_log.notice("Saving %s config to '%s'", cfg_id, cfg_name);
if (!fs::create_path(fs::get_parent_dir(cfg_name)))
{
cfg_log.fatal("Failed to create path: %s (%s)", cfg_name, fs::g_tls_error);
}
if (!cfg::node::save(cfg_name))
{
cfg_log.error("Failed to save %s config to '%s' (error=%s)", cfg_id, cfg_name, fs::g_tls_error);
}
}
};
extern raw_mice_config g_cfg_raw_mouse;

View file

@ -0,0 +1,366 @@
#include "stdafx.h"
#include "raw_mouse_handler.h"
#include "Emu/RSX/GSRender.h"
#include "Emu/RSX/RSXThread.h"
#include "Emu/Io/interception.h"
#include "Input/raw_mouse_config.h"
#ifdef _WIN32
#include <hidusage.h>
#endif
#define RAW_MOUSE_DEBUG_CURSOR_ENABLED 0
#if RAW_MOUSE_DEBUG_CURSOR_ENABLED
#include "Emu/RSX/Overlays/overlay_cursor.h"
static inline void draw_overlay_cursor(u32 index, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max)
{
const u16 x = static_cast<u16>(x_pos / (x_max / static_cast<f32>(rsx::overlays::overlay::virtual_width)));
const u16 y = static_cast<u16>(y_pos / (y_max / static_cast<f32>(rsx::overlays::overlay::virtual_height)));
const color4f color = { 1.0f, 0.0f, 0.0f, 1.0f };
rsx::overlays::set_cursor(rsx::overlays::cursor_offset::last + index, x, y, color, 2'000'000, false);
}
#else
static inline void draw_overlay_cursor(u32, s32, s32, s32, s32) {}
#endif
LOG_CHANNEL(input_log, "Input");
raw_mice_config g_cfg_raw_mouse;
raw_mouse::raw_mouse(u32 index, const std::string& device_name, void* handle, raw_mouse_handler* handler)
: m_index(index), m_device_name(device_name), m_handle(handle), m_handler(handler)
{
if (m_index < ::size32(g_cfg_raw_mouse.players))
{
if (const auto& player = ::at32(g_cfg_raw_mouse.players, m_index))
{
m_mouse_acceleration = static_cast<float>(player->mouse_acceleration.get()) / 100.0f;
}
}
}
raw_mouse::~raw_mouse()
{
}
void raw_mouse::update_window_handle()
{
#ifdef _WIN32
if (GSRender* render = static_cast<GSRender*>(g_fxo->try_get<rsx::thread>()))
{
if (GSFrameBase* frame = render->get_frame())
{
if (display_handle_t window_handle = frame->handle(); window_handle && window_handle == GetActiveWindow())
{
m_window_handle = window_handle;
m_window_width = frame->client_width();
m_window_height = frame->client_height();
return;
}
}
}
#endif
m_window_handle = {};
m_window_width = 0;
m_window_height = 0;
}
#ifdef _WIN32
void raw_mouse::update_values(const RAWMOUSE& state)
{
ensure(m_handler != nullptr);
// Update window handle and size
update_window_handle();
// Check if the window handle is active
if (!m_window_handle)
{
return;
}
const auto get_button_pressed = [this](u8 button, int button_flags, int down, int up)
{
// Only update the value if either down or up flags are present
if ((button_flags & down))
{
m_handler->Button(m_index, button, true);
}
else if ((button_flags & up))
{
m_handler->Button(m_index, button, false);
}
};
// Get mouse buttons
get_button_pressed(CELL_MOUSE_BUTTON_1, state.usButtonFlags, RI_MOUSE_BUTTON_1_DOWN, RI_MOUSE_BUTTON_1_UP);
get_button_pressed(CELL_MOUSE_BUTTON_2, state.usButtonFlags, RI_MOUSE_BUTTON_2_DOWN, RI_MOUSE_BUTTON_2_UP);
get_button_pressed(CELL_MOUSE_BUTTON_3, state.usButtonFlags, RI_MOUSE_BUTTON_3_DOWN, RI_MOUSE_BUTTON_3_UP);
get_button_pressed(CELL_MOUSE_BUTTON_4, state.usButtonFlags, RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP);
get_button_pressed(CELL_MOUSE_BUTTON_5, state.usButtonFlags, RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP);
// Get vertical mouse wheel
if ((state.usButtonFlags & RI_MOUSE_WHEEL))
{
m_handler->Scroll(m_index, static_cast<s16>(state.usButtonData));
}
// Get horizontal mouse wheel. Ignored until needed.
//if ((state.usButtonFlags & RI_MOUSE_HWHEEL))
//{
// m_handler->Scroll(m_index, static_cast<s16>(state.usButtonData));
//}
// Get mouse movement
if ((state.usFlags & MOUSE_MOVE_ABSOLUTE))
{
// Get absolute mouse movement
if (m_window_handle && m_window_width && m_window_height)
{
// Convert virtual coordinates to screen coordinates
const bool is_virtual_desktop = (state.usFlags & MOUSE_VIRTUAL_DESKTOP) == MOUSE_VIRTUAL_DESKTOP;
const int width = GetSystemMetrics(is_virtual_desktop ? SM_CXVIRTUALSCREEN : SM_CXSCREEN);
const int height = GetSystemMetrics(is_virtual_desktop ? SM_CYVIRTUALSCREEN : SM_CYSCREEN);
if (width && height)
{
POINT pt {
.x = long(state.lLastX / 65535.0f * width),
.y = long(state.lLastY / 65535.0f * height)
};
if (ScreenToClient(m_window_handle, &pt))
{
// Move mouse with absolute position
m_handler->Move(m_index, pt.x, pt.y, m_window_width, m_window_height);
draw_overlay_cursor(m_index, pt.x, pt.y, m_window_width, m_window_height);
}
}
}
}
else if (state.lLastX || state.lLastY)
{
// Get relative mouse movement (units most likely in raw dpi)
if (m_window_handle && m_window_width && m_window_height)
{
// Apply mouse acceleration
const int delta_x = static_cast<int>(state.lLastX * m_mouse_acceleration);
const int delta_y = static_cast<int>(state.lLastY * m_mouse_acceleration);
// Calculate new position
m_pos_x = std::max(0, std::min(m_pos_x + delta_x, m_window_width - 1));
m_pos_y = std::max(0, std::min(m_pos_y + delta_y, m_window_height - 1));
// Move mouse relative to old position
m_handler->Move(m_index, m_pos_x, m_pos_y, m_window_width, m_window_height, true, delta_x, delta_y);
draw_overlay_cursor(m_index, m_pos_x, m_pos_y, m_window_width, m_window_height);
}
}
}
#endif
raw_mouse_handler::~raw_mouse_handler()
{
if (m_raw_mice.empty())
{
return;
}
#ifdef _WIN32
std::vector<RAWINPUTDEVICE> raw_input_devices;
raw_input_devices.push_back(RAWINPUTDEVICE {
.usUsagePage = HID_USAGE_PAGE_GENERIC,
.usUsage = HID_USAGE_GENERIC_MOUSE,
.dwFlags = RIDEV_REMOVE,
.hwndTarget = nullptr
});
if (!RegisterRawInputDevices(raw_input_devices.data(), ::size32(raw_input_devices), sizeof(RAWINPUTDEVICE)))
{
input_log.error("raw_mouse_handler: RegisterRawInputDevices (destructor) failed: %s", fmt::win_error{GetLastError(), nullptr});
}
#endif
}
void raw_mouse_handler::Init(const u32 max_connect)
{
if (m_info.max_connect > 0)
{
// Already initialized
return;
}
if (!g_cfg_raw_mouse.load())
{
input_log.notice("raw_mouse_handler: Could not load raw mouse config. Using defaults.");
}
enumerate_devices(max_connect);
m_mice.clear();
for (u32 i = 0; i < std::min(::size32(m_raw_mice), max_connect); i++)
{
m_mice.emplace_back(Mouse());
}
m_info = {};
m_info.max_connect = max_connect;
m_info.now_connect = std::min(::size32(m_mice), max_connect);
m_info.info = input::g_mice_intercepted ? CELL_MOUSE_INFO_INTERCEPTED : 0; // Ownership of mouse data: 0=Application, 1=System
for (u32 i = 0; i < m_info.now_connect; i++)
{
m_info.status[i] = CELL_MOUSE_STATUS_CONNECTED;
m_info.mode[i] = CELL_MOUSE_INFO_TABLET_MOUSE_MODE;
m_info.tablet_is_supported[i] = CELL_MOUSE_INFO_TABLET_NOT_SUPPORTED;
m_info.vendor_id[0] = 0x1234;
m_info.product_id[0] = 0x1234;
}
#ifdef _WIN32
if (max_connect && !m_raw_mice.empty())
{
// Get the window handle of the first mouse
raw_mouse& mouse = m_raw_mice.begin()->second;
mouse.update_window_handle();
std::vector<RAWINPUTDEVICE> raw_input_devices;
raw_input_devices.push_back(RAWINPUTDEVICE {
.usUsagePage = HID_USAGE_PAGE_GENERIC,
.usUsage = HID_USAGE_GENERIC_MOUSE,
.dwFlags = 0,
.hwndTarget = mouse.window_handle()
});
if (!RegisterRawInputDevices(raw_input_devices.data(), ::size32(raw_input_devices), sizeof(RAWINPUTDEVICE)))
{
input_log.error("raw_mouse_handler: RegisterRawInputDevices failed: %s", fmt::win_error{GetLastError(), nullptr});
}
}
#endif
type = mouse_handler::raw;
}
void raw_mouse_handler::enumerate_devices(u32 max_connect)
{
input_log.notice("raw_mouse_handler: enumerating devices (max_connect=%d)", max_connect);
m_raw_mice.clear();
if (max_connect == 0)
{
return;
}
#ifdef _WIN32
u32 num_devices{};
u32 res = GetRawInputDeviceList(nullptr, &num_devices, sizeof(RAWINPUTDEVICELIST));
if (res == umax)
{
input_log.error("raw_mouse_handler: GetRawInputDeviceList (count) failed: %s", fmt::win_error{GetLastError(), nullptr});
return;
}
if (num_devices == 0)
{
return;
}
std::vector<RAWINPUTDEVICELIST> device_list(num_devices);
res = GetRawInputDeviceList(device_list.data(), &num_devices, sizeof(RAWINPUTDEVICELIST));
if (res == umax)
{
input_log.error("raw_mouse_handler: GetRawInputDeviceList (fetch) failed: %s", fmt::win_error{GetLastError(), nullptr});
return;
}
for (RAWINPUTDEVICELIST& device : device_list)
{
if (m_raw_mice.size() >= max_connect)
{
return;
}
if (device.dwType != RIM_TYPEMOUSE)
{
continue;
}
u32 size = 0;
res = GetRawInputDeviceInfoW(device.hDevice, RIDI_DEVICENAME, nullptr, &size);
if (res == umax)
{
input_log.error("raw_mouse_handler: GetRawInputDeviceInfoA (RIDI_DEVICENAME count) failed: %s", fmt::win_error{GetLastError(), nullptr});
continue;
}
if (size == 0)
{
continue;
}
std::vector<wchar_t> buf(size);
res = GetRawInputDeviceInfoW(device.hDevice, RIDI_DEVICENAME, buf.data(), &size);
if (res == umax)
{
input_log.error("raw_mouse_handler: GetRawInputDeviceInfoA (RIDI_DEVICENAME fetch) failed: %s", fmt::win_error{GetLastError(), nullptr});
continue;
}
const std::string device_name = wchar_to_utf8(buf.data());
input_log.notice("raw_mouse_handler: adding device %d: '%s'", m_raw_mice.size(), device_name);
m_raw_mice[device.hDevice] = raw_mouse(::size32(m_raw_mice), device_name, device.hDevice, this);
}
#endif
input_log.notice("raw_mouse_handler: found %d devices", m_raw_mice.size());
}
#ifdef _WIN32
void raw_mouse_handler::handle_native_event(const MSG& msg)
{
if (msg.message != WM_INPUT)
{
return;
}
if (GET_RAWINPUT_CODE_WPARAM(msg.wParam) != RIM_INPUT)
{
return;
}
RAWINPUT raw_input{};
UINT size = sizeof(RAWINPUT);
u32 res = GetRawInputData(reinterpret_cast<HRAWINPUT>(msg.lParam), RID_INPUT, &raw_input, &size, sizeof(RAWINPUTHEADER));
if (res == umax)
{
return;
}
switch (raw_input.header.dwType)
{
case RIM_TYPEMOUSE:
{
if (auto it = m_raw_mice.find(raw_input.header.hDevice); it != m_raw_mice.end())
{
it->second.update_values(raw_input.data.mouse);
}
break;
}
default:
{
break;
}
}
}
#endif

View file

@ -0,0 +1,56 @@
#pragma once
#include "Emu/Io/MouseHandler.h"
#include "Emu/RSX/display.h"
#ifdef _WIN32
#include <windows.h>
#endif
class raw_mouse_handler;
class raw_mouse
{
public:
raw_mouse() {}
raw_mouse(u32 index, const std::string& device_name, void* handle, raw_mouse_handler* handler);
virtual ~raw_mouse();
void update_window_handle();
display_handle_t window_handle() const { return m_window_handle; }
#ifdef _WIN32
void update_values(const RAWMOUSE& state);
#endif
private:
u32 m_index = 0;
std::string m_device_name;
void* m_handle{};
display_handle_t m_window_handle{};
int m_window_width{};
int m_window_height{};
int m_pos_x{};
int m_pos_y{};
float m_mouse_acceleration = 1.0f;
raw_mouse_handler* m_handler{};
};
class raw_mouse_handler final : public MouseHandlerBase
{
using MouseHandlerBase::MouseHandlerBase;
public:
virtual ~raw_mouse_handler();
void Init(const u32 max_connect) override;
#ifdef _WIN32
void handle_native_event(const MSG& msg);
#endif
private:
void enumerate_devices(u32 max_connect);
std::map<void*, raw_mouse> m_raw_mice;
};

View file

@ -631,6 +631,7 @@
<ClInclude Include="Emu\system_config.h" />
<ClInclude Include="Emu\system_config_types.h" />
<ClInclude Include="Emu\vfs_config.h" />
<ClInclude Include="Input\raw_mouse_config.h" />
<ClInclude Include="Loader\disc.h" />
<ClInclude Include="Loader\mself.hpp" />
<ClInclude Include="util\atomic.hpp" />

View file

@ -2434,6 +2434,9 @@
<ClInclude Include="Emu\Io\rb3drums_config.h">
<Filter>Emu\Io</Filter>
</ClInclude>
<ClInclude Include="Input\raw_mouse_config.h">
<Filter>Emu\Io</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="Emu\RSX\Program\GLSLSnippets\GPUDeswizzle.glsl">

View file

@ -19,6 +19,7 @@
#include "Emu/Io/MouseHandler.h"
#include "Input/basic_keyboard_handler.h"
#include "Input/basic_mouse_handler.h"
#include "Input/raw_mouse_handler.h"
#include "Emu/Audio/AudioBackend.h"
#include "Emu/Audio/Null/NullAudioBackend.h"
@ -141,15 +142,25 @@ EmuCallbacks main_application::CreateCallbacks()
{
case mouse_handler::null:
{
if (g_cfg.io.move == move_handler::mouse)
switch (g_cfg.io.move)
{
case move_handler::mouse:
{
basic_mouse_handler* ret = g_fxo->init<MouseHandlerBase, basic_mouse_handler>(Emu.DeserialManager());
ret->moveToThread(get_thread());
ret->SetTargetWindow(m_game_window);
break;
}
else
case move_handler::raw_mouse:
{
g_fxo->init<MouseHandlerBase, raw_mouse_handler>(Emu.DeserialManager());
break;
}
default:
{
g_fxo->init<MouseHandlerBase, NullMouseHandler>(Emu.DeserialManager());
break;
}
}
break;
@ -161,6 +172,11 @@ EmuCallbacks main_application::CreateCallbacks()
ret->SetTargetWindow(m_game_window);
break;
}
case mouse_handler::raw:
{
g_fxo->init<MouseHandlerBase, raw_mouse_handler>(Emu.DeserialManager());
break;
}
}
};

View file

@ -180,6 +180,7 @@
<ClCompile Include="Input\dualsense_pad_handler.cpp" />
<ClCompile Include="Input\evdev_gun_handler.cpp" />
<ClCompile Include="Input\hid_pad_handler.cpp" />
<ClCompile Include="Input\raw_mouse_handler.cpp" />
<ClCompile Include="Input\sdl_pad_handler.cpp" />
<ClCompile Include="Input\skateboard_pad_handler.cpp" />
<ClCompile Include="main.cpp" />
@ -924,6 +925,7 @@
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
</CustomBuild>
<ClInclude Include="Input\raw_mouse_handler.h" />
<ClInclude Include="Input\sdl_pad_handler.h" />
<ClInclude Include="Input\skateboard_pad_handler.h" />
<ClInclude Include="main_application.h" />

View file

@ -169,6 +169,9 @@
<Filter Include="Io\Skateboard">
<UniqueIdentifier>{bd1fd182-dc0d-45cb-bb9e-f68b2282d7eb}</UniqueIdentifier>
</Filter>
<Filter Include="Io\raw">
<UniqueIdentifier>{f44f5837-8651-456a-8584-5a2005ac613e}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
@ -1050,6 +1053,9 @@
<ClCompile Include="Input\skateboard_pad_handler.cpp">
<Filter>Io\Skateboard</Filter>
</ClCompile>
<ClCompile Include="Input\raw_mouse_handler.cpp">
<Filter>Io\raw</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Input\ds4_pad_handler.h">
@ -1232,6 +1238,9 @@
<ClInclude Include="Input\skateboard_pad_handler.h">
<Filter>Io\Skateboard</Filter>
</ClInclude>
<ClInclude Include="Input\raw_mouse_handler.h">
<Filter>Io\raw</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="debug\moc_predefs.h.cbt">

View file

@ -1009,6 +1009,7 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_
{
case mouse_handler::null: return tr("Null", "Mouse handler");
case mouse_handler::basic: return tr("Basic", "Mouse handler");
case mouse_handler::raw: return tr("Raw", "Mouse handler");
}
break;
case emu_settings_type::CameraType:
@ -1057,6 +1058,7 @@ 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");
case move_handler::raw_mouse: return tr("Raw Mouse", "Move handler");
#ifdef HAVE_LIBEVDEV
case move_handler::gun: return tr("Gun", "Gun handler");
#endif

View file

@ -20,6 +20,7 @@
#include "Emu/Io/Null/null_camera_handler.h"
#include "Emu/Io/Null/null_music_handler.h"
#include "Emu/vfs_config.h"
#include "Input/raw_mouse_handler.h"
#include "trophy_notification_helper.h"
#include "save_data_dialog.h"
#include "msg_dialog_frame.h"
@ -174,6 +175,11 @@ bool gui_application::Init()
}
#endif
// Install native event filter
#ifdef _WIN32 // Currently only needed for raw mouse input on windows
installNativeEventFilter(&m_native_event_filter);
#endif
return true;
}
@ -974,3 +980,26 @@ void gui_application::OnAppStateChanged(Qt::ApplicationState state)
// Delay pause so it won't immediately pause the emulated application
QTimer::singleShot(1000, this, pause_callback);
}
bool gui_application::native_event_filter::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, [[maybe_unused]] void* message, [[maybe_unused]] qintptr* result)
{
#ifdef _WIN32
if (!Emu.IsRunning())
{
return false;
}
if (eventType == "windows_generic_MSG")
{
if (MSG* msg = static_cast<MSG*>(message); msg && msg->message == WM_INPUT)
{
if (auto* handler = g_fxo->try_get<MouseHandlerBase>(); handler && handler->type == mouse_handler::raw)
{
static_cast<raw_mouse_handler*>(handler)->handle_native_event(*msg);
}
}
}
#endif
return false;
}

View file

@ -4,6 +4,7 @@
#include "util/atomic.hpp"
#include <QApplication>
#include <QAbstractNativeEventFilter>
#include <QElapsedTimer>
#include <QTimer>
#include <QTranslator>
@ -81,6 +82,13 @@ private:
void UpdatePlaytime();
void StopPlaytime();
class native_event_filter : public QAbstractNativeEventFilter
{
public:
bool nativeEventFilter(const QByteArray& eventType, void* message, qintptr* result) override;
} m_native_event_filter;
QTranslator m_translator;
QTranslator m_translator_qt;
QString m_language_code;

View file

@ -1234,6 +1234,12 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
ui->loadSdlMappings->setVisible(false);
#endif
#ifndef _WIN32
// Remove raw mouse handler
remove_item(ui->mouseHandlerBox, static_cast<int>(mouse_handler::raw), static_cast<int>(g_cfg.io.mouse.def));
remove_item(ui->moveBox, static_cast<int>(move_handler::raw_mouse), static_cast<int>(g_cfg.io.move.def));
#endif
// Midi
const QString midi_none = m_emu_settings->m_midi_creator.get_none();
const midi_device def_midi_device{ .type = midi_device_type::keyboard, .name = midi_none.toStdString() };