From 32a938abd222bb12dc119933b1eb29da72c630d1 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 20 May 2024 14:43:17 +0200 Subject: [PATCH] input/qt: allow to map mouse buttons in the guncon config --- rpcs3/Emu/Io/GunCon3.cpp | 53 +-- rpcs3/Emu/Io/emulated_pad_config.h | 16 + rpcs3/Emu/Io/pad_types.cpp | 27 +- rpcs3/Emu/Io/pad_types.h | 12 +- rpcs3/Input/raw_mouse_config.cpp | 32 ++ rpcs3/Input/raw_mouse_config.h | 3 + rpcs3/Input/raw_mouse_handler.cpp | 8 +- rpcs3/Input/raw_mouse_handler.h | 14 + rpcs3/rpcs3qt/basic_mouse_settings_dialog.cpp | 29 +- rpcs3/rpcs3qt/basic_mouse_settings_dialog.h | 2 +- .../rpcs3qt/emulated_pad_settings_dialog.cpp | 12 + rpcs3/rpcs3qt/gui_application.cpp | 2 +- rpcs3/rpcs3qt/localized_emu.cpp | 8 + rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp | 335 +++++++++++++++--- rpcs3/rpcs3qt/raw_mouse_settings_dialog.h | 37 +- 15 files changed, 479 insertions(+), 111 deletions(-) diff --git a/rpcs3/Emu/Io/GunCon3.cpp b/rpcs3/Emu/Io/GunCon3.cpp index 9dcbee9aaf..00f41905f6 100644 --- a/rpcs3/Emu/Io/GunCon3.cpp +++ b/rpcs3/Emu/Io/GunCon3.cpp @@ -220,6 +220,32 @@ void usb_device_guncon3::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, return; } + const auto input_callback = [&gc](guncon3_btn btn, u16 value, bool pressed) + { + if (!pressed) + return; + + switch (btn) + { + case guncon3_btn::trigger: gc.btn_trigger |= 1; break; + case guncon3_btn::a1: gc.btn_a1 |= 1; break; + case guncon3_btn::a2: gc.btn_a2 |= 1; break; + case guncon3_btn::a3: gc.btn_a3 |= 1; break; + case guncon3_btn::b1: gc.btn_b1 |= 1; break; + case guncon3_btn::b2: gc.btn_b2 |= 1; break; + case guncon3_btn::b3: gc.btn_b3 |= 1; break; + case guncon3_btn::c1: gc.btn_c1 |= 1; break; + case guncon3_btn::c2: gc.btn_c2 |= 1; break; + case guncon3_btn::as_x: gc.stick_ax = static_cast(value); break; + case guncon3_btn::as_y: gc.stick_ay = static_cast(value); break; + case guncon3_btn::bs_x: gc.stick_bx = static_cast(value); break; + case guncon3_btn::bs_y: gc.stick_by = static_cast(value); break; + case guncon3_btn::count: break; + } + }; + + const auto& cfg = ::at32(g_cfg_guncon3.players, m_controller_index); + { std::lock_guard lock(pad::g_pad_mutex); const auto gamepad_handler = pad::get_current_handler(); @@ -227,30 +253,7 @@ void usb_device_guncon3::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, const auto& pad = ::at32(pads, m_controller_index); if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) { - const auto& cfg = ::at32(g_cfg_guncon3.players, m_controller_index); - cfg->handle_input(pad, true, [&](guncon3_btn btn, u16 value, bool pressed) - { - if (!pressed) - return; - - switch (btn) - { - case guncon3_btn::trigger: gc.btn_trigger |= 1; break; - case guncon3_btn::a1: gc.btn_a1 |= 1; break; - case guncon3_btn::a2: gc.btn_a2 |= 1; break; - case guncon3_btn::a3: gc.btn_a3 |= 1; break; - case guncon3_btn::b1: gc.btn_b1 |= 1; break; - case guncon3_btn::b2: gc.btn_b2 |= 1; break; - case guncon3_btn::b3: gc.btn_b3 |= 1; break; - case guncon3_btn::c1: gc.btn_c1 |= 1; break; - case guncon3_btn::c2: gc.btn_c2 |= 1; break; - case guncon3_btn::as_x: gc.stick_ax = static_cast(value); break; - case guncon3_btn::as_y: gc.stick_ay = static_cast(value); break; - case guncon3_btn::bs_x: gc.stick_bx = static_cast(value); break; - case guncon3_btn::bs_y: gc.stick_by = static_cast(value); break; - case guncon3_btn::count: break; - } - }); + cfg->handle_input(pad, true, input_callback); } } @@ -277,7 +280,7 @@ void usb_device_guncon3::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, gc.gun_x = (mouse_data.x_pos * USHRT_MAX / mouse_data.x_max) - SHRT_MAX; gc.gun_y = (mouse_data.y_pos * -USHRT_MAX / mouse_data.y_max) + SHRT_MAX; - gc.btn_trigger |= !!(mouse_data.buttons & CELL_MOUSE_BUTTON_1); + cfg->handle_input(mouse_data, true, input_callback); } guncon3_encode(&gc, buf, m_key.data()); diff --git a/rpcs3/Emu/Io/emulated_pad_config.h b/rpcs3/Emu/Io/emulated_pad_config.h index 3dcc1f05ac..e1a992f167 100644 --- a/rpcs3/Emu/Io/emulated_pad_config.h +++ b/rpcs3/Emu/Io/emulated_pad_config.h @@ -3,6 +3,7 @@ #include "Utilities/Config.h" #include "Utilities/mutex.h" #include "pad_types.h" +#include "MouseHandler.h" #include #include @@ -106,6 +107,21 @@ public: } } + void handle_input(const Mouse& mouse, bool press_only, const std::function& func) const + { + for (int i = 0; i < 7; i++) + { + const MouseButtonCodes cell_code = get_mouse_button_code(i); + if ((mouse.buttons & cell_code)) + { + const pad_button button = static_cast(static_cast(pad_button::mouse_button_1) + i); + const u32 offset = pad_button_offset(button); + const u32 keycode = pad_button_keycode(button); + handle_input(func, offset, keycode, 255, true, true); + } + } + } + protected: mutable shared_mutex m_mutex; std::vector*> buttons; diff --git a/rpcs3/Emu/Io/pad_types.cpp b/rpcs3/Emu/Io/pad_types.cpp index 43ab5ffcb7..9b58ba6d09 100644 --- a/rpcs3/Emu/Io/pad_types.cpp +++ b/rpcs3/Emu/Io/pad_types.cpp @@ -38,6 +38,14 @@ void fmt_class_string::format(std::string& out, u64 arg) case pad_button::rs_x: return "Right Stick X-Axis"; case pad_button::rs_y: return "Right Stick Y-Axis"; case pad_button::pad_button_max_enum: return unknown; + case pad_button::mouse_button_1: return "Mouse Button 1"; + case pad_button::mouse_button_2: return "Mouse Button 2"; + case pad_button::mouse_button_3: return "Mouse Button 3"; + case pad_button::mouse_button_4: return "Mouse Button 4"; + case pad_button::mouse_button_5: return "Mouse Button 5"; + case pad_button::mouse_button_6: return "Mouse Button 6"; + case pad_button::mouse_button_7: return "Mouse Button 7"; + case pad_button::mouse_button_8: return "Mouse Button 8"; } return unknown; @@ -77,7 +85,16 @@ u32 pad_button_offset(pad_button button) case pad_button::rs_right: return CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X; case pad_button::rs_x: return CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X; case pad_button::rs_y: return CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y; - case pad_button::pad_button_max_enum: return 0; + case pad_button::pad_button_max_enum: + case pad_button::mouse_button_1: + case pad_button::mouse_button_2: + case pad_button::mouse_button_3: + case pad_button::mouse_button_4: + case pad_button::mouse_button_5: + case pad_button::mouse_button_6: + case pad_button::mouse_button_7: + case pad_button::mouse_button_8: + return 0; } return 0; } @@ -116,6 +133,14 @@ u32 pad_button_keycode(pad_button button) case pad_button::rs_x: return static_cast(axis_direction::both); case pad_button::rs_y: return static_cast(axis_direction::both); case pad_button::pad_button_max_enum: return 0; + case pad_button::mouse_button_1: return 1; + case pad_button::mouse_button_2: return 2; + case pad_button::mouse_button_3: return 3; + case pad_button::mouse_button_4: return 4; + case pad_button::mouse_button_5: return 5; + case pad_button::mouse_button_6: return 6; + case pad_button::mouse_button_7: return 7; + case pad_button::mouse_button_8: return 8; } return 0; } diff --git a/rpcs3/Emu/Io/pad_types.h b/rpcs3/Emu/Io/pad_types.h index de818179b5..162f47b52a 100644 --- a/rpcs3/Emu/Io/pad_types.h +++ b/rpcs3/Emu/Io/pad_types.h @@ -41,7 +41,17 @@ enum class pad_button : u8 rs_x, rs_y, - pad_button_max_enum + pad_button_max_enum, + + // Special buttons for mouse input + mouse_button_1, + mouse_button_2, + mouse_button_3, + mouse_button_4, + mouse_button_5, + mouse_button_6, + mouse_button_7, + mouse_button_8, }; u32 pad_button_offset(pad_button button); diff --git a/rpcs3/Input/raw_mouse_config.cpp b/rpcs3/Input/raw_mouse_config.cpp index d7c6a583c8..ba4d606a98 100644 --- a/rpcs3/Input/raw_mouse_config.cpp +++ b/rpcs3/Input/raw_mouse_config.cpp @@ -2,6 +2,38 @@ #include "raw_mouse_config.h" #include "Emu/Io/MouseHandler.h" +std::string mouse_button_id(int code) +{ + switch (code) + { + case CELL_MOUSE_BUTTON_1: return "Button 1"; + case CELL_MOUSE_BUTTON_2: return "Button 2"; + case CELL_MOUSE_BUTTON_3: return "Button 3"; + case CELL_MOUSE_BUTTON_4: return "Button 4"; + case CELL_MOUSE_BUTTON_5: return "Button 5"; + case CELL_MOUSE_BUTTON_6: return "Button 6"; + case CELL_MOUSE_BUTTON_7: return "Button 7"; + case CELL_MOUSE_BUTTON_8: return "Button 8"; + } + return ""; +} + +cfg::string& raw_mouse_config::get_button_by_index(int index) +{ + switch (index) + { + case 0: return mouse_button_1; + case 1: return mouse_button_2; + case 2: return mouse_button_3; + case 3: return mouse_button_4; + case 4: return mouse_button_5; + case 5: return mouse_button_6; + case 6: return mouse_button_7; + case 7: return mouse_button_8; + default: fmt::throw_exception("Invalid index %d", index); + } +} + cfg::string& raw_mouse_config::get_button(int code) { switch (code) diff --git a/rpcs3/Input/raw_mouse_config.h b/rpcs3/Input/raw_mouse_config.h index e44ed01f96..64b9b15d7b 100644 --- a/rpcs3/Input/raw_mouse_config.h +++ b/rpcs3/Input/raw_mouse_config.h @@ -7,6 +7,8 @@ LOG_CHANNEL(cfg_log, "CFG"); +std::string mouse_button_id(int code); + struct raw_mouse_config : cfg::node { public: @@ -25,6 +27,7 @@ public: cfg::string mouse_button_7{this, "Button 7", "No Button"}; cfg::string mouse_button_8{this, "Button 8", "No Button"}; + cfg::string& get_button_by_index(int index); cfg::string& get_button(int code); }; diff --git a/rpcs3/Input/raw_mouse_handler.cpp b/rpcs3/Input/raw_mouse_handler.cpp index ccd585a620..74f8fdb091 100644 --- a/rpcs3/Input/raw_mouse_handler.cpp +++ b/rpcs3/Input/raw_mouse_handler.cpp @@ -116,12 +116,6 @@ void raw_mouse::update_values(const RAWMOUSE& state) // 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) { const auto& [down, up] = ::at32(m_buttons, button); @@ -130,10 +124,12 @@ void raw_mouse::update_values(const RAWMOUSE& state) if ((button_flags & down)) { m_handler->Button(m_index, button, true); + m_handler->mouse_press_callback(m_device_name, button, true); } else if ((button_flags & up)) { m_handler->Button(m_index, button, false); + m_handler->mouse_press_callback(m_device_name, button, false); } }; diff --git a/rpcs3/Input/raw_mouse_handler.h b/rpcs3/Input/raw_mouse_handler.h index 9f4494d91a..e111b21b6e 100644 --- a/rpcs3/Input/raw_mouse_handler.h +++ b/rpcs3/Input/raw_mouse_handler.h @@ -69,6 +69,19 @@ public: const std::map& get_mice() const { return m_raw_mice; }; + void set_mouse_press_callback(std::function cb) + { + m_mouse_press_callback = std::move(cb); + } + + void mouse_press_callback(const std::string& device_name, s32 cell_code, bool pressed) + { + if (m_mouse_press_callback) + { + m_mouse_press_callback(device_name, cell_code, pressed); + } + } + #ifdef _WIN32 void handle_native_event(const MSG& msg); #endif @@ -78,4 +91,5 @@ private: bool m_ignore_config = false; std::map m_raw_mice; + std::function m_mouse_press_callback; }; diff --git a/rpcs3/rpcs3qt/basic_mouse_settings_dialog.cpp b/rpcs3/rpcs3qt/basic_mouse_settings_dialog.cpp index 227e07547d..a96fe1a2c3 100644 --- a/rpcs3/rpcs3qt/basic_mouse_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/basic_mouse_settings_dialog.cpp @@ -162,16 +162,18 @@ void basic_mouse_settings_dialog::reset_config() void basic_mouse_settings_dialog::on_button_click(int id) { - if (id <= 0) + if (id < 0) { return; } for (auto but : m_buttons->buttons()) { + but->setEnabled(false); but->setFocusPolicy(Qt::ClickFocus); } + m_button_box->setEnabled(false); for (auto but : m_button_box->buttons()) { but->setFocusPolicy(Qt::ClickFocus); @@ -185,19 +187,12 @@ void basic_mouse_settings_dialog::on_button_click(int id) button->grabMouse(); } - // Disable all buttons - m_button_box->setEnabled(false); - for (auto button : m_buttons->buttons()) - { - button->setEnabled(false); - } - m_remap_timer.start(1000); } void basic_mouse_settings_dialog::mouseReleaseEvent(QMouseEvent* event) { - if (m_button_id <= 0) + if (m_button_id < 0) { // We are not remapping a button, so pass the event to the base class. QDialog::mouseReleaseEvent(event); @@ -222,7 +217,7 @@ bool basic_mouse_settings_dialog::eventFilter(QObject* object, QEvent* event) case QEvent::MouseButtonRelease: { // On right click clear binding if we are not remapping pad button - if (m_button_id <= 0) + if (m_button_id < 0) { QMouseEvent* mouse_event = static_cast(event); if (const auto button = qobject_cast(object); button && mouse_event->button() == Qt::RightButton) @@ -254,7 +249,7 @@ void basic_mouse_settings_dialog::reactivate_buttons() m_remap_timer.stop(); m_seconds = MAX_SECONDS; - if (m_button_id <= 0) + if (m_button_id < 0) { return; } @@ -265,19 +260,19 @@ void basic_mouse_settings_dialog::reactivate_buttons() button->releaseMouse(); } - m_button_id = 0; + m_button_id = -1; // Enable all buttons m_button_box->setEnabled(true); + for (auto but : m_button_box->buttons()) + { + but->setFocusPolicy(Qt::StrongFocus); + } + for (auto but : m_buttons->buttons()) { but->setEnabled(true); but->setFocusPolicy(Qt::StrongFocus); } - - for (auto but : m_button_box->buttons()) - { - but->setFocusPolicy(Qt::StrongFocus); - } } diff --git a/rpcs3/rpcs3qt/basic_mouse_settings_dialog.h b/rpcs3/rpcs3qt/basic_mouse_settings_dialog.h index 7e22c14ad0..ab7dd48450 100644 --- a/rpcs3/rpcs3qt/basic_mouse_settings_dialog.h +++ b/rpcs3/rpcs3qt/basic_mouse_settings_dialog.h @@ -28,7 +28,7 @@ private: QDialogButtonBox* m_button_box = nullptr; QButtonGroup* m_buttons = nullptr; std::unordered_map m_push_buttons; - u32 m_button_id = 0; + int m_button_id = -1; // Backup for standard button palette QPalette m_palette; diff --git a/rpcs3/rpcs3qt/emulated_pad_settings_dialog.cpp b/rpcs3/rpcs3qt/emulated_pad_settings_dialog.cpp index 1c2daa1d83..60f1e12748 100644 --- a/rpcs3/rpcs3qt/emulated_pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/emulated_pad_settings_dialog.cpp @@ -158,6 +158,18 @@ void emulated_pad_settings_dialog::add_tabs(QTabWidget* tabs) combo->setItemData(index, i, button_role::emulated_button); } + if constexpr (std::is_same_v) + { + for (int p = static_cast(pad_button::mouse_button_1); p <= static_cast(pad_button::mouse_button_8); p++) + { + const QString translated = localized_emu::translated_pad_button(static_cast(p)); + combo->addItem(translated); + const int index = combo->findText(translated); + combo->setItemData(index, p, button_role::button); + combo->setItemData(index, i, button_role::emulated_button); + } + } + pad_button saved_btn_id = pad_button::pad_button_max_enum; switch (m_type) { diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index 1079cb6efa..e56fec0d6a 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -1113,7 +1113,7 @@ void gui_application::OnAppStateChanged(Qt::ApplicationState state) 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()) + if (!Emu.IsRunning() && !g_raw_mouse_handler) { return false; } diff --git a/rpcs3/rpcs3qt/localized_emu.cpp b/rpcs3/rpcs3qt/localized_emu.cpp index 443b1dbdcb..4fb2b4b9f9 100644 --- a/rpcs3/rpcs3qt/localized_emu.cpp +++ b/rpcs3/rpcs3qt/localized_emu.cpp @@ -36,6 +36,14 @@ QString localized_emu::translated_pad_button(pad_button btn) case pad_button::rs_x: return tr("Right Stick X-Axis"); case pad_button::rs_y: return tr("Right Stick Y-Axis"); case pad_button::pad_button_max_enum: return ""; + case pad_button::mouse_button_1: return tr("Mouse Button 1"); + case pad_button::mouse_button_2: return tr("Mouse Button 2"); + case pad_button::mouse_button_3: return tr("Mouse Button 3"); + case pad_button::mouse_button_4: return tr("Mouse Button 4"); + case pad_button::mouse_button_5: return tr("Mouse Button 5"); + case pad_button::mouse_button_6: return tr("Mouse Button 6"); + case pad_button::mouse_button_7: return tr("Mouse Button 7"); + case pad_button::mouse_button_8: return tr("Mouse Button 8"); } return ""; } diff --git a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp index f737545063..843903a877 100644 --- a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp @@ -6,17 +6,11 @@ #include "Input/raw_mouse_handler.h" #include "util/asm.hpp" -#include #include #include -#include #include -enum button_role -{ - button_name = Qt::UserRole, - button_code -}; +constexpr u32 button_count = 8; raw_mouse_settings_dialog::raw_mouse_settings_dialog(QWidget* parent) : QDialog(parent) @@ -29,30 +23,30 @@ raw_mouse_settings_dialog::raw_mouse_settings_dialog(QWidget* parent) QVBoxLayout* v_layout = new QVBoxLayout(this); - QTabWidget* tabs = new QTabWidget(); - tabs->setUsesScrollButtons(false); + m_tab_widget = new QTabWidget(); + m_tab_widget->setUsesScrollButtons(false); - QDialogButtonBox* buttons = new QDialogButtonBox(this); - buttons->setStandardButtons(QDialogButtonBox::Apply | QDialogButtonBox::Cancel | QDialogButtonBox::Save | QDialogButtonBox::RestoreDefaults); + m_button_box = new QDialogButtonBox(this); + m_button_box->setStandardButtons(QDialogButtonBox::Apply | QDialogButtonBox::Cancel | QDialogButtonBox::Save | QDialogButtonBox::RestoreDefaults); - connect(buttons, &QDialogButtonBox::clicked, this, [this, buttons](QAbstractButton* button) + connect(m_button_box, &QDialogButtonBox::clicked, this, [this](QAbstractButton* button) { - if (button == buttons->button(QDialogButtonBox::Apply)) + if (button == m_button_box->button(QDialogButtonBox::Apply)) { g_cfg_raw_mouse.save(); } - else if (button == buttons->button(QDialogButtonBox::Save)) + else if (button == m_button_box->button(QDialogButtonBox::Save)) { g_cfg_raw_mouse.save(); accept(); } - else if (button == buttons->button(QDialogButtonBox::RestoreDefaults)) + else if (button == m_button_box->button(QDialogButtonBox::RestoreDefaults)) { if (QMessageBox::question(this, tr("Confirm Reset"), tr("Reset settings of all players?")) != QMessageBox::Yes) return; reset_config(); } - else if (button == buttons->button(QDialogButtonBox::Cancel)) + else if (button == m_button_box->button(QDialogButtonBox::Cancel)) { // Restore config if (!g_cfg_raw_mouse.load()) @@ -70,12 +64,58 @@ raw_mouse_settings_dialog::raw_mouse_settings_dialog(QWidget* parent) g_raw_mouse_handler = std::make_unique(true); g_raw_mouse_handler->Init(::size32(g_cfg_raw_mouse.players)); + g_raw_mouse_handler->set_mouse_press_callback([this](const std::string& device_name, s32 cell_code, bool pressed) + { + mouse_press(device_name, cell_code, pressed); + }); - add_tabs(tabs); + m_buttons = new QButtonGroup(this); + connect(m_buttons, &QButtonGroup::idClicked, this, &raw_mouse_settings_dialog::on_button_click); - v_layout->addWidget(tabs); - v_layout->addWidget(buttons); + connect(&m_remap_timer, &QTimer::timeout, this, [this]() + { + auto button = m_buttons->button(m_button_id); + + if (--m_seconds <= 0) + { + if (button) + { + if (const int button_id = m_buttons->id(button); button_id >= 0) + { + auto& config = ::at32(g_cfg_raw_mouse.players, m_tab_widget->currentIndex()); + const std::string name = config->get_button_by_index(button_id).to_string(); + button->setText(name.empty() ? QStringLiteral("-") : QString::fromStdString(name)); + } + } + reactivate_buttons(); + return; + } + if (button) + { + button->setText(tr("[ Waiting %1 ]").arg(m_seconds)); + } + }); + + connect(&m_mouse_release_timer, &QTimer::timeout, this, [this]() + { + m_mouse_release_timer.stop(); + m_disable_mouse_release_event = false; + }); + + add_tabs(m_tab_widget); + + v_layout->addWidget(m_tab_widget); + v_layout->addWidget(m_button_box); setLayout(v_layout); + + m_palette = m_push_buttons[0][CELL_MOUSE_BUTTON_1]->palette(); // save normal palette + + connect(m_tab_widget, &QTabWidget::currentChanged, this, [this](int index) + { + handle_device_change(get_current_device_name(index)); + }); + + handle_device_change(get_current_device_name(0)); } raw_mouse_settings_dialog::~raw_mouse_settings_dialog() @@ -87,7 +127,6 @@ void raw_mouse_settings_dialog::add_tabs(QTabWidget* tabs) { ensure(!!tabs); - constexpr u32 button_count = 8; constexpr u32 max_items_per_column = 6; int rows = button_count; @@ -98,11 +137,17 @@ void raw_mouse_settings_dialog::add_tabs(QTabWidget* tabs) const usz players = g_cfg_raw_mouse.players.size(); - m_combos.resize(players); + m_push_buttons.resize(players); ensure(g_raw_mouse_handler); const auto& mice = g_raw_mouse_handler->get_mice(); + const auto insert_button = [this](int id, QPushButton* button) + { + m_buttons->addButton(button, id); + button->installEventFilter(this); + }; + for (usz player = 0; player < players; player++) { QWidget* widget = new QWidget(this); @@ -131,8 +176,12 @@ void raw_mouse_settings_dialog::add_tabs(QTabWidget* tabs) if (!data.isValid() || !data.canConvert()) return; + const std::string device_name = data.toString().toStdString(); + auto& config = ::at32(g_cfg_raw_mouse.players, player)->device; - config.from_string(data.toString().toStdString()); + config.from_string(device_name); + + handle_device_change(device_name); }); h_layout->addWidget(combo); @@ -148,33 +197,13 @@ void raw_mouse_settings_dialog::add_tabs(QTabWidget* tabs) QHBoxLayout* h_layout = new QHBoxLayout(this); QGroupBox* gb = new QGroupBox(translated_cell_button, this); - QComboBox* combo = new QComboBox; + QPushButton* pb = new QPushButton; - for (const auto& [name, btn] : raw_mouse_button_map) - { - const QString id = QString::fromStdString(name); - const QString translated_id = id; // We could localize the id, but I am too lazy at the moment - combo->addItem(translated_id); - const int index = combo->findText(translated_id); - combo->setItemData(index, id, button_role::button_name); - combo->setItemData(index, cell_code, button_role::button_code); - } + insert_button(static_cast(player * button_count + i), pb); const std::string saved_btn = config->get_button(cell_code); - combo->setCurrentIndex(combo->findData(QString::fromStdString(saved_btn), button_role::button_name)); - - connect(combo, QOverload::of(&QComboBox::currentIndexChanged), this, [this, player, cell_code, combo](int index) - { - if (index < 0 || !combo) - return; - - const QVariant data = combo->itemData(index, button_role::button_name); - if (!data.isValid() || !data.canConvert()) - return; - - ::at32(g_cfg_raw_mouse.players, player)->get_button(cell_code).from_string(data.toString().toStdString()); - }); + pb->setText(saved_btn.empty() ? QStringLiteral("-") : QString::fromStdString(saved_btn)); if (row >= rows + first_row) { @@ -182,8 +211,8 @@ void raw_mouse_settings_dialog::add_tabs(QTabWidget* tabs) col++; } - ::at32(m_combos, player).push_back(combo); - h_layout->addWidget(combo); + m_push_buttons[player][cell_code] = pb; + h_layout->addWidget(pb); gb->setLayout(h_layout); grid_layout->addWidget(gb, row, col); } @@ -213,23 +242,17 @@ void raw_mouse_settings_dialog::reset_config() { g_cfg_raw_mouse.from_default(); - for (usz player = 0; player < m_combos.size(); player++) + for (usz player = 0; player < m_push_buttons.size(); player++) { auto& config = ::at32(g_cfg_raw_mouse.players, player); - for (QComboBox* combo : m_combos.at(player)) + for (auto& [cell_code, pb] : m_push_buttons[player]) { - if (!combo) + if (!pb) continue; - const QVariant data = combo->itemData(0, button_role::button_code); - if (!data.isValid() || !data.canConvert()) - continue; - - const int cell_code = data.toInt(); - const QString def_btn_id = QString::fromStdString(config->get_button(cell_code).def); - - combo->setCurrentIndex(combo->findData(def_btn_id, button_role::button_name)); + const QString text = QString::fromStdString(config->get_button(cell_code).def); + pb->setText(text.isEmpty() ? QStringLiteral("-") : text); } if (QComboBox* combo = ::at32(m_device_combos, player)) @@ -243,3 +266,199 @@ void raw_mouse_settings_dialog::reset_config() } } } + +void raw_mouse_settings_dialog::mouse_press(const std::string& device_name, s32 cell_code, bool pressed) +{ + if (m_button_id < 0 || pressed) // Let's only react to mouse releases + { + return; + } + + const int player = m_tab_widget->currentIndex(); + const std::string current_device_name = get_current_device_name(player); + + if (device_name != current_device_name) + { + return; + } + + auto& config = ::at32(g_cfg_raw_mouse.players, m_tab_widget->currentIndex()); + config->get_button_by_index(m_button_id % button_count).from_string(mouse_button_id(cell_code)); + + if (auto button = m_buttons->button(m_button_id)) + { + button->setText(localized_emu::translated_mouse_button(cell_code)); + } + + reactivate_buttons(); +} + +void raw_mouse_settings_dialog::handle_device_change(const std::string& device_name) +{ + if (is_device_active(device_name)) + { + reactivate_buttons(); + } + else + { + for (auto but : m_buttons->buttons()) + { + but->setEnabled(false); + } + } +} + +bool raw_mouse_settings_dialog::is_device_active(const std::string& device_name) +{ + if (!g_raw_mouse_handler || device_name.empty()) + { + return false; + } + + const auto& mice = g_raw_mouse_handler->get_mice(); + + return std::any_of(mice.cbegin(), mice.cend(), [&device_name](const auto& entry){ return entry.second.device_name() == device_name; }); +} + +std::string raw_mouse_settings_dialog::get_current_device_name(int player) +{ + if (player < 0) + return {}; + + const QVariant data = ::at32(m_device_combos, player)->currentData(); + + if (!data.canConvert()) + return {}; + + return data.toString().toStdString(); +} + +bool raw_mouse_settings_dialog::eventFilter(QObject* object, QEvent* event) +{ + switch (event->type()) + { + case QEvent::MouseButtonRelease: + { + // On right click clear binding if we are not remapping pad button + if (m_button_id < 0 && !m_disable_mouse_release_event) + { + QMouseEvent* mouse_event = static_cast(event); + if (const auto button = qobject_cast(object); button && mouse_event->button() == Qt::RightButton) + { + if (const int button_id = m_buttons->id(button); button_id >= 0) + { + button->setText(QStringLiteral("-")); + auto& config = ::at32(g_cfg_raw_mouse.players, m_tab_widget->currentIndex()); + config->get_button_by_index(button_id).from_string(""); + return true; + } + } + } + + // Disabled buttons should not absorb mouseclicks + event->ignore(); + break; + } + default: + { + break; + } + } + + return QDialog::eventFilter(object, event); +} + +void raw_mouse_settings_dialog::on_button_click(int id) +{ + if (id < 0) + { + return; + } + + for (auto sb : m_accel_spin_boxes) + { + sb->setEnabled(false); + sb->setFocusPolicy(Qt::ClickFocus); + } + + for (auto cb : m_device_combos) + { + cb->setEnabled(false); + cb->setFocusPolicy(Qt::ClickFocus); + } + + for (auto but : m_buttons->buttons()) + { + but->setEnabled(false); + but->setFocusPolicy(Qt::ClickFocus); + } + + m_button_box->setEnabled(false); + for (auto but : m_button_box->buttons()) + { + but->setFocusPolicy(Qt::ClickFocus); + } + + m_button_id = id; + if (auto button = m_buttons->button(m_button_id)) + { + button->setText(tr("[ Waiting %1 ]").arg(MAX_SECONDS)); + button->setPalette(QPalette(Qt::blue)); + button->grabMouse(); + } + + m_tab_widget->setEnabled(false); + + m_remap_timer.start(1000); + + // We need to disable the mouse release event filter or we will clear the button if the raw mouse does a right click + m_mouse_release_timer.stop(); + m_disable_mouse_release_event = true; +} + +void raw_mouse_settings_dialog::reactivate_buttons() +{ + m_remap_timer.stop(); + m_seconds = MAX_SECONDS; + + if (m_button_id >= 0) + { + if (auto button = m_buttons->button(m_button_id)) + { + button->setPalette(m_palette); + button->releaseMouse(); + } + + m_button_id = -1; + } + + // Enable all buttons + m_button_box->setEnabled(true); + + for (auto but : m_button_box->buttons()) + { + but->setFocusPolicy(Qt::StrongFocus); + } + + for (auto sb : m_accel_spin_boxes) + { + sb->setEnabled(true); + sb->setFocusPolicy(Qt::StrongFocus); + } + + for (auto cb : m_device_combos) + { + cb->setEnabled(true); + cb->setFocusPolicy(Qt::StrongFocus); + } + + for (auto but : m_buttons->buttons()) + { + but->setEnabled(true); + but->setFocusPolicy(Qt::StrongFocus); + } + + m_tab_widget->setEnabled(true); + + m_mouse_release_timer.start(100ms); +} diff --git a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h index 325fcb4785..91702a0c7c 100644 --- a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h +++ b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h @@ -1,11 +1,20 @@ #pragma once +#include "util/types.hpp" + +#include #include +#include #include #include +#include +#include +#include #include +#include #include +#include class raw_mouse_settings_dialog : public QDialog { @@ -20,7 +29,33 @@ private: void reset_config(); + void on_button_click(int id); + void mouse_press(const std::string& device_name, s32 cell_code, bool pressed); + void handle_device_change(const std::string& device_name); + bool is_device_active(const std::string& device_name); + std::string get_current_device_name(int player); + void reactivate_buttons(); + + QTabWidget* m_tab_widget = nullptr; std::vector m_device_combos; std::vector m_accel_spin_boxes; - std::vector> m_combos; + + // Buttons + QDialogButtonBox* m_button_box = nullptr; + QButtonGroup* m_buttons = nullptr; + std::vector> m_push_buttons; + int m_button_id = -1; + + // Backup for standard button palette + QPalette m_palette; + + // Remap Timer + static constexpr int MAX_SECONDS = 5; + int m_seconds = MAX_SECONDS; + QTimer m_remap_timer; + QTimer m_mouse_release_timer; + bool m_disable_mouse_release_event = false; + +protected: + bool eventFilter(QObject* object, QEvent* event) override; };