diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index 9091cfd844..f00e1790ca 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -924,19 +924,6 @@ error_code cellPadLddRegisterController() if (handle < 0) return CELL_PAD_ERROR_TOO_MANY_DEVICES; - const auto product = input::get_product_info(input::product_type::playstation_3_controller); - - auto& pads = handler->GetPads(); - pads[handle]->Init - ( - CELL_PAD_STATUS_CONNECTED | CELL_PAD_STATUS_ASSIGN_CHANGES | CELL_PAD_STATUS_CUSTOM_CONTROLLER, - CELL_PAD_CAPABILITY_PS3_CONFORMITY, - CELL_PAD_DEV_TYPE_LDD, - CELL_PAD_PCLASS_TYPE_STANDARD, - product.pclass_profile, - product.vendor_id, - product.product_id - ); config->port_setting[handle] = 0; return not_an_error(handle); diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index e46aef72ca..4cc2049d9c 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "pad_thread.h" +#include "product_info.h" #include "ds3_pad_handler.h" #include "ds4_pad_handler.h" #ifdef _WIN32 @@ -24,9 +25,10 @@ namespace pad struct pad_setting { - u32 port_status; - u32 device_capability; - u32 device_type; + u32 port_status = 0; + u32 device_capability = 0; + u32 device_type = 0; + s32 ldd_handle = -1; }; pad_thread::pad_thread(void *_curthread, void *_curwindow, std::string_view title_id) : curthread(_curthread), curwindow(_curwindow) @@ -52,19 +54,33 @@ void pad_thread::Init() std::lock_guard lock(pad::g_pad_mutex); // Cache old settings if possible - std::vector pad_settings; + std::array pad_settings; for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; i++) // max 7 pads { - if (!m_pads[i]) + if (m_pads[i]) { - pad_settings.push_back({ CELL_PAD_STATUS_DISCONNECTED, CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_ACTUATOR, CELL_PAD_DEV_TYPE_STANDARD }); + pad_settings[i] = + { + m_pads[i]->m_port_status, + m_pads[i]->m_device_capability, + m_pads[i]->m_device_type, + m_pads[i]->ldd ? static_cast(i) : -1 + }; } else { - pad_settings.push_back({ m_pads[i]->m_port_status, m_pads[i]->m_device_capability, m_pads[i]->m_device_type }); + pad_settings[i] = + { + CELL_PAD_STATUS_DISCONNECTED, + CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_ACTUATOR, + CELL_PAD_DEV_TYPE_STANDARD, + -1 + }; } } + num_ldd_pad = 0; + m_info.now_connect = 0; handlers.clear(); @@ -81,7 +97,8 @@ void pad_thread::Init() { std::shared_ptr cur_pad_handler; - const auto &handler_type = g_cfg_input.player[i]->handler; + const bool is_ldd_pad = pad_settings[i].ldd_handle == static_cast(i); + const auto handler_type = is_ldd_pad ? pad_handler::null : g_cfg_input.player[i]->handler.get(); if (handlers.count(handler_type) != 0) { @@ -125,10 +142,14 @@ void pad_thread::Init() m_pads[i] = std::make_shared(CELL_PAD_STATUS_DISCONNECTED, pad_settings[i].device_capability, pad_settings[i].device_type); - if (cur_pad_handler->bindPadToDevice(m_pads[i], g_cfg_input.player[i]->device.to_string()) == false) + if (is_ldd_pad) + { + InitLddPad(pad_settings[i].ldd_handle); + } + else if (cur_pad_handler->bindPadToDevice(m_pads[i], g_cfg_input.player[i]->device.to_string()) == false) { // Failed to bind the device to cur_pad_handler so binds to NullPadHandler - input_log.error("Failed to bind device %s to handler %s", g_cfg_input.player[i]->device.to_string(), handler_type.to_string()); + input_log.error("Failed to bind device %s to handler %s", g_cfg_input.player[i]->device.to_string(), handler_type); nullpad->bindPadToDevice(m_pads[i], g_cfg_input.player[i]->device.to_string()); } } @@ -239,6 +260,30 @@ void pad_thread::ThreadFunc() } } +void pad_thread::InitLddPad(u32 handle) +{ + if (handle >= m_pads.size()) + { + return; + } + + static const auto product = input::get_product_info(input::product_type::playstation_3_controller); + + m_pads[handle]->ldd = true; + m_pads[handle]->Init + ( + CELL_PAD_STATUS_CONNECTED | CELL_PAD_STATUS_ASSIGN_CHANGES | CELL_PAD_STATUS_CUSTOM_CONTROLLER, + CELL_PAD_CAPABILITY_PS3_CONFORMITY, + CELL_PAD_DEV_TYPE_LDD, + 0, // CELL_PAD_PCLASS_TYPE_STANDARD + product.pclass_profile, + product.vendor_id, + product.product_id + ); + + num_ldd_pad++; +} + s32 pad_thread::AddLddPad() { // Look for first null pad @@ -246,8 +291,7 @@ s32 pad_thread::AddLddPad() { if (g_cfg_input.player[i]->handler == pad_handler::null && !m_pads[i]->ldd) { - m_pads[i]->ldd = true; - num_ldd_pad++; + InitLddPad(i); return i; } } diff --git a/rpcs3/Input/pad_thread.h b/rpcs3/Input/pad_thread.h index b50efa777d..4fe2bf2bf0 100644 --- a/rpcs3/Input/pad_thread.h +++ b/rpcs3/Input/pad_thread.h @@ -28,6 +28,7 @@ public: void UnregisterLddPad(u32 handle); protected: + void InitLddPad(u32 handle); void ThreadFunc(); // List of all handlers diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index df4d2aa8d4..0a600642ae 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -871,10 +871,10 @@ Utilities - + Utilities - + Utilities @@ -1764,7 +1764,7 @@ Emu\GPU\RSX\Overlays - + Utilities @@ -1788,7 +1788,7 @@ Utilities - + Emu\GPU\RSX\Common @@ -1799,6 +1799,6 @@ Emu\GPU\RSX\Common\Interpreter - + \ No newline at end of file diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index d929d0ec0a..7c1f746b57 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -119,13 +119,26 @@ void gs_frame::keyPressEvent(QKeyEvent *keyEvent) switch (keyEvent->key()) { case Qt::Key_L: - if (keyEvent->modifiers() == Qt::AltModifier) { static int count = 0; screenshot.success("Made forced mark %d in log", ++count); } + if (keyEvent->modifiers() == Qt::AltModifier) + { + static int count = 0; + screenshot.success("Made forced mark %d in log", ++count); + return; + } break; case Qt::Key_Return: - if (keyEvent->modifiers() == Qt::AltModifier) { toggle_fullscreen(); return; } + if (keyEvent->modifiers() == Qt::AltModifier) + { + toggle_fullscreen(); + return; + } break; case Qt::Key_Escape: - if (visibility() == FullScreen) { toggle_fullscreen(); return; } + if (visibility() == FullScreen) + { + toggle_fullscreen(); + return; + } break; case Qt::Key_P: if (keyEvent->modifiers() == Qt::ControlModifier && !m_disable_kb_hotkeys && Emu.IsRunning()) @@ -135,14 +148,14 @@ void gs_frame::keyPressEvent(QKeyEvent *keyEvent) } break; case Qt::Key_S: - if (keyEvent->modifiers() == Qt::ControlModifier && !m_disable_kb_hotkeys && (!Emu.IsStopped())) + if (keyEvent->modifiers() == Qt::ControlModifier && !m_disable_kb_hotkeys && !Emu.IsStopped()) { Emu.Stop(); return; } break; case Qt::Key_R: - if (keyEvent->modifiers() == Qt::ControlModifier && !m_disable_kb_hotkeys && (!Emu.GetBoot().empty())) + if (keyEvent->modifiers() == Qt::ControlModifier && !m_disable_kb_hotkeys && !Emu.GetBoot().empty()) { Emu.Restart(); return; @@ -151,8 +164,16 @@ void gs_frame::keyPressEvent(QKeyEvent *keyEvent) case Qt::Key_E: if (keyEvent->modifiers() == Qt::ControlModifier && !m_disable_kb_hotkeys) { - if (Emu.IsReady()) { Emu.Run(true); return; } - else if (Emu.IsPaused()) { Emu.Resume(); return; } + if (Emu.IsReady()) + { + Emu.Run(true); + return; + } + else if (Emu.IsPaused()) + { + Emu.Resume(); + return; + } } break; case Qt::Key_F12: diff --git a/rpcs3/rpcs3qt/pad_led_settings_dialog.h b/rpcs3/rpcs3qt/pad_led_settings_dialog.h index a9e3e96c6c..d974d24b83 100644 --- a/rpcs3/rpcs3qt/pad_led_settings_dialog.h +++ b/rpcs3/rpcs3qt/pad_led_settings_dialog.h @@ -17,7 +17,7 @@ public: explicit pad_led_settings_dialog(const int& colorR, const int& colorG, const int& colorB, const bool& led_low_battery_blink, const bool& led_battery_indicator, const int& led_battery_indicator_brightness, QDialog* parent = Q_NULLPTR); ~pad_led_settings_dialog(); -signals: +Q_SIGNALS: void pass_led_settings(int m_cR, int m_cG, int m_cB, bool m_low_battery_blink, bool m_battery_indicator, int m_battery_indicator_brightness); private Q_SLOTS: diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index b2c9400875..37918ab722 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -13,8 +13,10 @@ #include "ui_pad_settings_dialog.h" #include "tooltips.h" +#include "Emu/System.h" #include "Emu/Io/Null/NullPadHandler.h" +#include "Input/pad_thread.h" #include "Input/product_info.h" #include "Input/keyboard_pad_handler.h" #include "Input/ds3_pad_handler.h" @@ -89,14 +91,6 @@ pad_settings_dialog::pad_settings_dialog(QWidget *parent, const GameInfo *game) mainLayout->addWidget(m_tabs); setLayout(mainLayout); - // Fill input type combobox - std::vector str_inputs = g_cfg_input.player[0]->handler.to_list(); - for (size_t index = 0; index < str_inputs.size(); index++) - { - const QString item_data = qstr(str_inputs[index]); - ui->chooseHandler->addItem(GetLocalizedPadHandler(item_data, static_cast(index)), QVariant(item_data)); - } - // Combobox: Input type connect(ui->chooseHandler, &QComboBox::currentTextChanged, this, &pad_settings_dialog::ChangeInputType); @@ -1009,10 +1003,22 @@ void pad_settings_dialog::ChangeInputType() { bool force_enable = false; // enable configs even with disconnected devices const int player = m_tabs->currentIndex(); + const bool is_ldd_pad = GetIsLddPad(player); + + std::string handler; + std::string device; + std::string profile; - const std::string handler = sstr(ui->chooseHandler->currentData().toString()); - const std::string device = g_cfg_input.player[player]->device.to_string(); - const std::string profile = g_cfg_input.player[player]->profile.to_string(); + if (is_ldd_pad) + { + handler = fmt::format("%s", pad_handler::null); + } + else + { + handler = sstr(ui->chooseHandler->currentData().toString()); + device = g_cfg_input.player[player]->device.to_string(); + profile = g_cfg_input.player[player]->profile.to_string(); + } // Change this player's current handler if (!g_cfg_input.player[player]->handler.from_string(handler)) @@ -1037,7 +1043,11 @@ void pad_settings_dialog::ChangeInputType() switch (m_handler->m_type) { case pad_handler::null: - description = tooltips.gamepad_settings.null; break; + if (is_ldd_pad) + description = tooltips.gamepad_settings.ldd_pad; + else + description = tooltips.gamepad_settings.null; + break; case pad_handler::keyboard: description = tooltips.gamepad_settings.keyboard; break; #ifdef _WIN32 @@ -1091,6 +1101,15 @@ void pad_settings_dialog::ChangeInputType() force_enable = true; break; } + case pad_handler::null: + { + if (is_ldd_pad) + { + ui->chooseDevice->addItem(tr("Custom Controller")); + break; + } + [[fallthrough]]; + } default: { for (size_t i = 0; i < device_list.size(); i++) @@ -1120,14 +1139,15 @@ void pad_settings_dialog::ChangeInputType() m_handler->get_next_button_press(info.name, [this](u16, std::string, std::string pad_name, u32, pad_preview_values) { SwitchPadInfo(pad_name, true); }, [this](std::string pad_name) { SwitchPadInfo(pad_name, false); }, false); + if (info.name == device) { ui->chooseDevice->setCurrentIndex(i); } } - QString profile_dir = qstr(PadHandlerBase::get_config_dir(m_handler->m_type, m_title_id)); - QStringList profiles = gui::utils::get_dir_entries(QDir(profile_dir), QStringList() << "*.yml"); + const QString profile_dir = qstr(PadHandlerBase::get_config_dir(m_handler->m_type, m_title_id)); + const QStringList profiles = gui::utils::get_dir_entries(QDir(profile_dir), QStringList() << "*.yml"); if (profiles.isEmpty()) { @@ -1153,13 +1173,20 @@ void pad_settings_dialog::ChangeInputType() else { ui->chooseProfile->addItem(tr("No Profiles")); - ui->chooseDevice->addItem(tr("No Device Detected"), -1); + + if (ui->chooseDevice->count() == 0) + { + ui->chooseDevice->addItem(tr("No Device Detected"), -1); + } } // enable configuration and profile list if possible SwitchButtons(config_enabled && m_handler->m_type == pad_handler::keyboard); ui->b_addProfile->setEnabled(config_enabled); ui->chooseProfile->setEnabled(config_enabled); + + ui->b_reset->setEnabled(!is_ldd_pad); + ui->chooseHandler->setEnabled(!is_ldd_pad); } void pad_settings_dialog::ChangeProfile() @@ -1292,11 +1319,29 @@ void pad_settings_dialog::HandleDeviceClassChange(int index) void pad_settings_dialog::RefreshInputTypes() { - const auto& handler = g_cfg_input.player[m_tabs->currentIndex()]->handler; + const int index = m_tabs->currentIndex(); // Set the current input type from config. Disable signal to have ChangeInputType always executed exactly once ui->chooseHandler->blockSignals(true); - ui->chooseHandler->setCurrentText(GetLocalizedPadHandler(qstr(handler.to_string()), handler)); + ui->chooseHandler->clear(); + + if (GetIsLddPad(index)) + { + ui->chooseHandler->addItem(tr("Reserved")); + } + else + { + const std::vector str_inputs = g_cfg_input.player[0]->handler.to_list(); + for (size_t index = 0; index < str_inputs.size(); index++) + { + const QString item_data = qstr(str_inputs[index]); + ui->chooseHandler->addItem(GetLocalizedPadHandler(item_data, static_cast(index)), QVariant(item_data)); + } + + const auto& handler = g_cfg_input.player[index]->handler; + ui->chooseHandler->setCurrentText(GetLocalizedPadHandler(qstr(handler.to_string()), handler)); + } + ui->chooseHandler->blockSignals(false); // Force Change @@ -1398,3 +1443,17 @@ QString pad_settings_dialog::GetLocalizedPadHandler(const QString& original, pad } return original; } + +bool pad_settings_dialog::GetIsLddPad(int index) const +{ + // We only check for ldd pads if the current dialog may affect the running application. + // To simplify this we include the global pad config indiscriminately as well as the relevant custom pad config. + if (index >= 0 && !Emu.IsStopped() && (m_title_id.empty() || m_title_id == Emu.GetTitleID())) + { + std::lock_guard lock(pad::g_pad_mutex); + const auto handler = pad::get_current_handler(); + return handler && handler->GetPads().at(index)->ldd; + } + + return false; +} diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.h b/rpcs3/rpcs3qt/pad_settings_dialog.h index c159879da0..e1992b79aa 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.h +++ b/rpcs3/rpcs3qt/pad_settings_dialog.h @@ -176,9 +176,12 @@ private: void RepaintPreviewLabel(QLabel* l, int deadzone, int desired_width, int x, int y); std::shared_ptr GetHandler(pad_handler type); - + QString GetLocalizedPadHandler(const QString& original, pad_handler handler); + /** Checks if the port at the given index is already reserved by the application as custom controller (ldd pad) */ + bool GetIsLddPad(int index) const; + protected: /** Handle keyboard handler input */ void keyPressEvent(QKeyEvent *keyEvent) override; diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index 5a72e8befe..a9f23c23dd 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -195,6 +195,7 @@ public: const struct gamepad_settings { const QString null = tr("This controller is disabled and will appear as disconnected to software. Choose another handler to enable it."); + const QString ldd_pad = tr("This port is currently assigned to a custom controller by the application and can't be changed."); const QString keyboard = tr("While it is possible to use a keyboard as a pad in RPCS3, the use of an actual controller is strongly recommended.\nTo bind mouse movement to a button or joystick, click on the desired button to activate it, then click and hold while dragging the mouse to a direction."); const QString ds3_windows = tr("In order to use the DualShock 3 handler, you need to install the official DualShock 3 driver first.\nSee the RPCS3 Wiki for instructions."); const QString ds3_linux = tr("In order to use the DualShock 3 handler, you might need to add udev rules to let RPCS3 access the controller.\nSee the RPCS3 Wiki for instructions.");