From 17b72ae84a4c3c53f20a601021c1bfb88c6c5a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Hamil?= Date: Sat, 7 Feb 2026 14:30:23 +0300 Subject: [PATCH] Wiimote to GunCon3 PR review updates --- rpcs3/Emu/Io/GunCon3.cpp | 147 +++++++++++----------- rpcs3/Emu/Io/WiimoteManager.cpp | 54 ++++---- rpcs3/Emu/Io/WiimoteManager.h | 60 ++++----- rpcs3/rpcs3qt/gui_application.cpp | 2 +- rpcs3/rpcs3qt/gui_application.h | 4 +- rpcs3/rpcs3qt/wiimote_settings_dialog.cpp | 48 +++---- 6 files changed, 155 insertions(+), 160 deletions(-) diff --git a/rpcs3/Emu/Io/GunCon3.cpp b/rpcs3/Emu/Io/GunCon3.cpp index c26463cdad..e6949fbb0a 100644 --- a/rpcs3/Emu/Io/GunCon3.cpp +++ b/rpcs3/Emu/Io/GunCon3.cpp @@ -229,88 +229,83 @@ void usb_device_guncon3::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, GunCon3_data gc{}; gc.stick_ax = gc.stick_ay = gc.stick_bx = gc.stick_by = 0x7f; - if (auto* wm = WiimoteManager::get_instance()) + auto* wm = wiimote_manager::get_instance() + auto states = wm->get_states(); + + // Determine which Wiimote to use based on our ordinal position among all GunCons + int my_wiimote_index = -1; { - auto states = wm->get_states(); - - // Determine which Wiimote to use based on our ordinal position among all GunCons - int my_wiimote_index = -1; + std::lock_guard lock(s_instances_mutex); + // Since we sort by pointer adress/controller_index in add, and search by this ptr + // Actually lower_bound needs a value. std::find is safer for pointer identity. + auto found = std::find(s_instances.begin(), s_instances.end(), this); + if (found != s_instances.end()) { - std::lock_guard lock(s_instances_mutex); - auto it = std::lower_bound(s_instances.begin(), s_instances.end(), this, [](auto* a, auto* b) { - return a->m_controller_index < b->m_controller_index; - }); - // Since we sort by pointer adress/controller_index in add, and search by this ptr - // Actually lower_bound needs a value. std::find is safer for pointer identity. - auto found = std::find(s_instances.begin(), s_instances.end(), this); - if (found != s_instances.end()) + my_wiimote_index = std::distance(s_instances.begin(), found); + } + } + + if (my_wiimote_index >= 0 && static_cast(my_wiimote_index) < states.size()) + { + const auto& ws = states[my_wiimote_index]; + const auto map = wm->get_mapping(); + + auto is_pressed = [&](wiimote_button btn) { return (ws.buttons & static_cast(btn)) != 0; }; + + if (is_pressed(map.trigger)) gc.btn_trigger = 1; + + // Wiimote to GunCon3 Button Mapping + if (is_pressed(map.a1)) gc.btn_a1 = 1; + if (is_pressed(map.a2)) gc.btn_a2 = 1; + if (is_pressed(map.a3)) gc.btn_a3 = 1; + if (is_pressed(map.b1)) gc.btn_b1 = 1; + if (is_pressed(map.b2)) gc.btn_b2 = 1; + if (is_pressed(map.b3)) gc.btn_b3 = 1; + if (is_pressed(map.c1)) gc.btn_c1 = 1; + if (is_pressed(map.c2)) gc.btn_c2 = 1; + + // Secondary / Hardcoded Alts (if kept in mapping struct) + if (is_pressed(map.b1_alt)) gc.btn_b1 = 1; + if (is_pressed(map.b2_alt)) gc.btn_b2 = 1; + + if (ws.ir[0].x < 1023) + { + // Only use the primary pointer to avoid jumping between multiple IR points + s32 raw_x = ws.ir[0].x; + s32 raw_y = ws.ir[0].y; + + // Map to GunCon3 range (-32768..32767) + // X calculation (Right = 32767, Left = -32768) + s32 x_res = 32767 - (raw_x * 65535 / 1023); + // Y calculation (Top = 32767, Bottom = -32768) + // Swapping to inverted mapping as per user feedback + s32 y_res = 32767 - (raw_y * 65535 / 767); + + gc.gun_x = static_cast(std::clamp(x_res, -32768, 32767)); + gc.gun_y = static_cast(std::clamp(y_res, -32768, 32767)); + + // Draw the actual GunCon3 output to the overlay + // Mapping GunCon3 range back to virtual_width/height + s16 ax = static_cast((gc.gun_x + 32768) * rsx::overlays::overlay::virtual_width / 65535); + s16 ay = static_cast((32767 - gc.gun_y) * rsx::overlays::overlay::virtual_height / 65535); + + if (g_cfg.io.show_move_cursor) { - my_wiimote_index = std::distance(s_instances.begin(), found); + // Use my_wiimote_index for color/cursor selection (0=Red, 1=Green...) + rsx::overlays::set_cursor(rsx::overlays::cursor_offset::cell_gem + my_wiimote_index, ax, ay, { 1.0f, 1.0f, 1.0f, 1.0f }, 100'000, false); + } + + if (ws.ir[1].x < 1023) + { + // Calculate "Z" (distance) based on spread of first two points to emulate depth sensor + s32 dx = static_cast(ws.ir[0].x) - ws.ir[1].x; + s32 dy = static_cast(ws.ir[0].y) - ws.ir[1].y; + gc.gun_z = static_cast(std::sqrt(dx * dx + dy * dy)); } } - if (my_wiimote_index >= 0 && static_cast(my_wiimote_index) < states.size()) - { - const auto& ws = states[my_wiimote_index]; - const auto map = wm->get_mapping(); - - auto is_pressed = [&](WiimoteButton btn) { return (ws.buttons & static_cast(btn)) != 0; }; - - if (is_pressed(map.trigger)) gc.btn_trigger = 1; - - // Wiimote to GunCon3 Button Mapping - if (is_pressed(map.a1)) gc.btn_a1 = 1; - if (is_pressed(map.a2)) gc.btn_a2 = 1; - if (is_pressed(map.a3)) gc.btn_a3 = 1; - if (is_pressed(map.b1)) gc.btn_b1 = 1; - if (is_pressed(map.b2)) gc.btn_b2 = 1; - if (is_pressed(map.b3)) gc.btn_b3 = 1; - if (is_pressed(map.c1)) gc.btn_c1 = 1; - if (is_pressed(map.c2)) gc.btn_c2 = 1; - - // Secondary / Hardcoded Alts (if kept in mapping struct) - if (is_pressed(map.b1_alt)) gc.btn_b1 = 1; - if (is_pressed(map.b2_alt)) gc.btn_b2 = 1; - - if (ws.ir[0].x < 1023) - { - // Only use the primary pointer to avoid jumping between multiple IR points - s32 raw_x = ws.ir[0].x; - s32 raw_y = ws.ir[0].y; - - // Map to GunCon3 range (-32768..32767) - // X calculation (Right = 32767, Left = -32768) - s32 x_res = 32767 - (raw_x * 65535 / 1023); - // Y calculation (Top = 32767, Bottom = -32768) - // Swapping to inverted mapping as per user feedback - s32 y_res = 32767 - (raw_y * 65535 / 767); - - gc.gun_x = static_cast(std::clamp(x_res, -32768, 32767)); - gc.gun_y = static_cast(std::clamp(y_res, -32768, 32767)); - - // Draw the actual GunCon3 output to the overlay - // Mapping GunCon3 range back to virtual_width/height - s16 ax = static_cast((gc.gun_x + 32768) * rsx::overlays::overlay::virtual_width / 65535); - s16 ay = static_cast((32767 - gc.gun_y) * rsx::overlays::overlay::virtual_height / 65535); - - if (g_cfg.io.show_move_cursor) - { - // Use my_wiimote_index for color/cursor selection (0=Red, 1=Green...) - rsx::overlays::set_cursor(rsx::overlays::cursor_offset::cell_gem + my_wiimote_index, ax, ay, { 1.0f, 1.0f, 1.0f, 1.0f }, 100'000, false); - } - - if (ws.ir[1].x < 1023) - { - // Calculate "Z" (distance) based on spread of first two points to emulate depth sensor - s32 dx = static_cast(ws.ir[0].x) - ws.ir[1].x; - s32 dy = static_cast(ws.ir[0].y) - ws.ir[1].y; - gc.gun_z = static_cast(std::sqrt(dx * dx + dy * dy)); - } - } - - guncon3_encode(&gc, buf, m_key.data()); - return; - } + guncon3_encode(&gc, buf, m_key.data()); + return; } if (!is_input_allowed()) diff --git a/rpcs3/Emu/Io/WiimoteManager.cpp b/rpcs3/Emu/Io/WiimoteManager.cpp index 0c94ff0f73..0813354d83 100644 --- a/rpcs3/Emu/Io/WiimoteManager.cpp +++ b/rpcs3/Emu/Io/WiimoteManager.cpp @@ -16,7 +16,7 @@ static constexpr u16 VID_MAYFLASH = 0x0079; static constexpr u16 PID_DOLPHINBAR_START = 0x1800; static constexpr u16 PID_DOLPHINBAR_END = 0x1803; -WiimoteDevice::WiimoteDevice(hid_device_info* info) +wiimote_device::wiimote_device(hid_device_info* info) : m_path(info->path) , m_serial(info->serial_number ? info->serial_number : L"") { @@ -45,12 +45,12 @@ WiimoteDevice::WiimoteDevice(hid_device_info* info) } } -WiimoteDevice::~WiimoteDevice() +wiimote_device::~wiimote_device() { if (m_handle) hid_close(m_handle); } -bool WiimoteDevice::initialize_ir() +bool wiimote_device::initialize_ir() { auto write_reg = [&](u32 addr, const std::vector& data) { u8 buf[22] = {0}; @@ -92,7 +92,7 @@ bool WiimoteDevice::initialize_ir() return true; } -bool WiimoteDevice::update() +bool wiimote_device::update() { if (!m_handle) return false; @@ -135,24 +135,24 @@ bool WiimoteDevice::update() return true; } -static WiimoteManager* s_instance = nullptr; +static wiimote_manager* s_instance = nullptr; static std::string get_config_path() { return fs::get_config_dir(true) + "wiimote.yml"; } -void WiimoteManager::load_config() +void wiimote_manager::load_config() { fs::file f(get_config_path(), fs::read); if (!f) return; std::string line; std::stringstream ss(f.to_string()); - WiimoteGunConMapping map; + wiimote_guncon_mapping map; - auto parse_btn = [](const std::string& val) -> WiimoteButton { - return static_cast(std::strtoul(val.c_str(), nullptr, 0)); + auto parse_btn = [](const std::string& val) -> wiimote_button { + return static_cast(std::strtoul(val.c_str(), nullptr, 0)); }; while (std::getline(ss, line)) @@ -184,14 +184,14 @@ void WiimoteManager::load_config() m_mapping = map; } -void WiimoteManager::save_config() +void wiimote_manager::save_config() { fs::file f(get_config_path(), fs::write + fs::create + fs::trunc); if (!f) return; std::stringstream ss; // Helper to write lines - auto write_line = [&](const char* key, WiimoteButton btn) { + auto write_line = [&](const char* key, wiimote_button btn) { ss << key << ": " << static_cast(btn) << "\n"; }; @@ -211,7 +211,7 @@ void WiimoteManager::save_config() f.write(ss.str()); } -WiimoteManager::WiimoteManager() +wiimote_manager::wiimote_manager() { if (!s_instance) s_instance = this; @@ -219,27 +219,27 @@ WiimoteManager::WiimoteManager() // Set default mapping explicitly to match user preference: C1=Plus, A3=Left // (Struct default constructor might have different values if I didn't edit header defaults) // Let's force it here before loading config. - m_mapping.c1 = WiimoteButton::Plus; - m_mapping.a3 = WiimoteButton::Left; + m_mapping.c1 = wiimote_button::Plus; + m_mapping.a3 = wiimote_button::Left; // Defaults for others from struct: // a1=A, a2=Minus, etc. load_config(); } -WiimoteManager::~WiimoteManager() +wiimote_manager::~wiimote_manager() { stop(); if (s_instance == this) s_instance = nullptr; } -WiimoteManager* WiimoteManager::get_instance() +wiimote_manager* wiimote_manager::get_instance() { return s_instance; } -void WiimoteManager::start() +void wiimote_manager::start() { if (m_running) return; @@ -247,23 +247,23 @@ void WiimoteManager::start() if (hid_init() != 0) return; m_running = true; - m_thread = std::thread(&WiimoteManager::thread_proc, this); + m_thread = std::thread(&wiimote_manager::thread_proc, this); } -void WiimoteManager::stop() +void wiimote_manager::stop() { m_running = false; if (m_thread.joinable()) m_thread.join(); hid_exit(); } -size_t WiimoteManager::get_device_count() +size_t wiimote_manager::get_device_count() { std::shared_lock lock(m_mutex); return m_devices.size(); } -void WiimoteManager::set_mapping(const WiimoteGunConMapping& mapping) +void wiimote_manager::set_mapping(const wiimote_guncon_mapping& mapping) { { std::unique_lock lock(m_mutex); @@ -272,16 +272,16 @@ void WiimoteManager::set_mapping(const WiimoteGunConMapping& mapping) save_config(); } -WiimoteGunConMapping WiimoteManager::get_mapping() const +wiimote_guncon_mapping wiimote_manager::get_mapping() const { // shared_lock not strictly needed for trivial copy but good practice if it becomes complex return m_mapping; } -std::vector WiimoteManager::get_states() +std::vector wiimote_manager::get_states() { std::shared_lock lock(m_mutex); - std::vector states; + std::vector states; states.reserve(m_devices.size()); for (const auto& dev : m_devices) @@ -292,7 +292,7 @@ std::vector WiimoteManager::get_states() } -void WiimoteManager::thread_proc() +void wiimote_manager::thread_proc() { u32 counter = 0; while (m_running) @@ -324,7 +324,7 @@ void WiimoteManager::thread_proc() if (!already_owned) { - auto dev = std::make_unique(cur); + auto dev = std::make_unique(cur); if (dev->get_state().connected) { std::unique_lock lock(m_mutex); @@ -351,7 +351,7 @@ void WiimoteManager::thread_proc() std::unique_lock lock(m_mutex); m_devices.erase(std::remove_if(m_devices.begin(), m_devices.end(), [](const auto& d) { - return !const_cast(*d).update(); + return !const_cast(*d).update(); }), m_devices.end()); } diff --git a/rpcs3/Emu/Io/WiimoteManager.h b/rpcs3/Emu/Io/WiimoteManager.h index a9188b1e46..6eb0c138f8 100644 --- a/rpcs3/Emu/Io/WiimoteManager.h +++ b/rpcs3/Emu/Io/WiimoteManager.h @@ -11,14 +11,14 @@ #include #include -struct WiimoteIRPoint +struct wiimote_ir_point { u16 x = 1023; u16 y = 1023; u8 size = 0; }; -enum class WiimoteButton : u16 +enum class wiimote_button : u16 { None = 0, Left = 0x0001, @@ -34,39 +34,39 @@ enum class WiimoteButton : u16 Home = 0x8000 }; -struct WiimoteGunConMapping +struct wiimote_guncon_mapping { - WiimoteButton trigger = WiimoteButton::B; - WiimoteButton a1 = WiimoteButton::A; - WiimoteButton a2 = WiimoteButton::Minus; - WiimoteButton a3 = WiimoteButton::Left; - WiimoteButton b1 = WiimoteButton::One; - WiimoteButton b2 = WiimoteButton::Two; - WiimoteButton b3 = WiimoteButton::Home; - WiimoteButton c1 = WiimoteButton::Plus; - WiimoteButton c2 = WiimoteButton::Right; + wiimote_button trigger = wiimote_button::B; + wiimote_button a1 = wiimote_button::A; + wiimote_button a2 = wiimote_button::Minus; + wiimote_button a3 = wiimote_button::Left; + wiimote_button b1 = wiimote_button::One; + wiimote_button b2 = wiimote_button::Two; + wiimote_button b3 = wiimote_button::Home; + wiimote_button c1 = wiimote_button::Plus; + wiimote_button c2 = wiimote_button::Right; // Secondary mappings (optional, e.g. D-Pad acting as buttons) - WiimoteButton b1_alt = WiimoteButton::Up; - WiimoteButton b2_alt = WiimoteButton::Down; + wiimote_button b1_alt = wiimote_button::Up; + wiimote_button b2_alt = wiimote_button::Down; }; -struct WiimoteState +struct wiimote_state { u16 buttons = 0; s16 acc_x = 0, acc_y = 0, acc_z = 0; - WiimoteIRPoint ir[4]; + wiimote_ir_point ir[4]; bool connected = false; }; -class WiimoteDevice +class wiimote_device { public: - WiimoteDevice(hid_device_info* info); - ~WiimoteDevice(); + wiimote_device(hid_device_info* info); + ~wiimote_device(); bool update(); - const WiimoteState& get_state() const { return m_state; } + const wiimote_state& get_state() const { return m_state; } std::string get_path() const { return m_path; } std::wstring get_serial() const { return m_serial; } @@ -74,34 +74,34 @@ private: hid_device* m_handle = nullptr; std::string m_path; std::wstring m_serial; - WiimoteState m_state; + wiimote_state m_state; bool initialize_ir(); }; -class WiimoteManager +class wiimote_manager { public: - WiimoteManager(); - ~WiimoteManager(); + wiimote_manager(); + ~wiimote_manager(); - static WiimoteManager* get_instance(); + static wiimote_manager* get_instance(); void start(); void stop(); - std::vector get_states(); + std::vector get_states(); size_t get_device_count(); - void set_mapping(const WiimoteGunConMapping& mapping); - WiimoteGunConMapping get_mapping() const; + void set_mapping(const wiimote_guncon_mapping& mapping); + wiimote_guncon_mapping get_mapping() const; private: std::thread m_thread; atomic_t m_running{false}; - std::vector> m_devices; + std::vector> m_devices; shared_mutex m_mutex; - WiimoteGunConMapping m_mapping; + wiimote_guncon_mapping m_mapping; void thread_proc(); void load_config(); diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index f39d8cda8d..4c7aaf7c0c 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -127,7 +127,7 @@ bool gui_application::Init() } m_emu_settings = std::make_shared(); - m_wiimote_manager = std::make_unique(); + m_wiimote_manager = std::make_unique(); m_wiimote_manager->start(); m_gui_settings = std::make_shared(); m_persistent_settings = std::make_shared(); diff --git a/rpcs3/rpcs3qt/gui_application.h b/rpcs3/rpcs3qt/gui_application.h index 2fec3d72a2..25a86615a1 100644 --- a/rpcs3/rpcs3qt/gui_application.h +++ b/rpcs3/rpcs3qt/gui_application.h @@ -13,6 +13,7 @@ #include "main_application.h" #include "Emu/System.h" +#include "Emu/Io/WiimoteManager.h" #include "Input/raw_mouse_handler.h" #include @@ -34,7 +35,6 @@ extern std::unique_ptr g_raw_mouse_handler; // Only used for /** RPCS3 GUI Application Class * The main point of this class is to do application initialization, to hold the main and game windows and to initialize callbacks. */ -#include "Emu/Io/WiimoteManager.h" class gui_application : public QApplication, public main_application { @@ -113,7 +113,7 @@ private: std::deque> m_sound_effects{}; - std::unique_ptr m_wiimote_manager; + std::unique_ptr m_wiimote_manager; std::shared_ptr m_emu_settings; std::shared_ptr m_gui_settings; std::shared_ptr m_persistent_settings; diff --git a/rpcs3/rpcs3qt/wiimote_settings_dialog.cpp b/rpcs3/rpcs3qt/wiimote_settings_dialog.cpp index 2c323ddb2a..c22777ee71 100644 --- a/rpcs3/rpcs3qt/wiimote_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/wiimote_settings_dialog.cpp @@ -26,22 +26,22 @@ wiimote_settings_dialog::~wiimote_settings_dialog() = default; void wiimote_settings_dialog::populate_mappings() { - auto* wm = WiimoteManager::get_instance(); + auto* wm = wiimote_manager::get_instance(); if (!wm) return; - const QPair buttons[] = { - { tr("None"), WiimoteButton::None }, - { tr("A"), WiimoteButton::A }, - { tr("B"), WiimoteButton::B }, - { tr("Plus (+)"), WiimoteButton::Plus }, - { tr("Minus (-)"), WiimoteButton::Minus }, - { tr("Home"), WiimoteButton::Home }, - { tr("1"), WiimoteButton::One }, - { tr("2"), WiimoteButton::Two }, - { tr("D-Pad Up"), WiimoteButton::Up }, - { tr("D-Pad Down"), WiimoteButton::Down }, - { tr("D-Pad Left"), WiimoteButton::Left }, - { tr("D-Pad Right"), WiimoteButton::Right }, + const QPair buttons[] = { + { tr("None"), wiimote_button::None }, + { tr("A"), wiimote_button::A }, + { tr("B"), wiimote_button::B }, + { tr("Plus (+)"), wiimote_button::Plus }, + { tr("Minus (-)"), wiimote_button::Minus }, + { tr("Home"), wiimote_button::Home }, + { tr("1"), wiimote_button::One }, + { tr("2"), wiimote_button::Two }, + { tr("D-Pad Up"), wiimote_button::Up }, + { tr("D-Pad Down"), wiimote_button::Down }, + { tr("D-Pad Left"), wiimote_button::Left }, + { tr("D-Pad Right"), wiimote_button::Right }, }; QComboBox* boxes[] = { @@ -49,8 +49,8 @@ void wiimote_settings_dialog::populate_mappings() ui->cb_b1, ui->cb_b2, ui->cb_b3, ui->cb_a3, ui->cb_c2 }; - WiimoteGunConMapping current = wm->get_mapping(); - WiimoteButton* targets[] = { + wiimote_guncon_mapping current = wm->get_mapping(); + wiimote_button* targets[] = { ¤t.trigger, ¤t.a1, ¤t.a2, ¤t.c1, ¤t.b1, ¤t.b2, ¤t.b3, ¤t.a3, ¤t.c2 }; @@ -77,11 +77,11 @@ void wiimote_settings_dialog::populate_mappings() void wiimote_settings_dialog::restore_defaults() { - auto* wm = WiimoteManager::get_instance(); + auto* wm = wiimote_manager::get_instance(); if (!wm) return; // Reset to default mapping - WiimoteGunConMapping default_map; + wiimote_guncon_mapping default_map; wm->set_mapping(default_map); // Update UI @@ -95,7 +95,7 @@ void wiimote_settings_dialog::restore_defaults() ui->cb_a3->blockSignals(true); ui->cb_c2->blockSignals(true); - auto set_box = [](QComboBox* box, WiimoteButton btn) { + auto set_box = [](QComboBox* box, wiimote_button btn) { int index = box->findData(QVariant::fromValue(static_cast(btn))); if (index >= 0) box->setCurrentIndex(index); }; @@ -123,12 +123,12 @@ void wiimote_settings_dialog::restore_defaults() void wiimote_settings_dialog::apply_mappings() { - auto* wm = WiimoteManager::get_instance(); + auto* wm = wiimote_manager::get_instance(); if (!wm) return; - WiimoteGunConMapping map; + wiimote_guncon_mapping map; auto get_btn = [](QComboBox* box) { - return static_cast(box->currentData().toUInt()); + return static_cast(box->currentData().toUInt()); }; map.trigger = get_btn(ui->cb_trigger); @@ -152,7 +152,7 @@ void wiimote_settings_dialog::apply_mappings() void wiimote_settings_dialog::update_state() { int index = ui->wiimoteList->currentRow(); - auto* wm = WiimoteManager::get_instance(); + auto* wm = wiimote_manager::get_instance(); if (!wm || index < 0) { ui->connectionStatus->setText(tr("N/A")); @@ -230,7 +230,7 @@ void wiimote_settings_dialog::update_state() void wiimote_settings_dialog::update_list() { ui->wiimoteList->clear(); - auto* wm = WiimoteManager::get_instance(); + auto* wm = wiimote_manager::get_instance(); if (!wm) { ui->wiimoteList->addItem(tr("Wiimote Manager not initialized."));