From a859e14e1940a2005bbc39611dd181229e66f6e9 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 2 Dec 2025 03:09:27 +0100 Subject: [PATCH 01/15] Qt: update main window elements on language change --- rpcs3/rpcs3qt/main_window.cpp | 54 +++++++++++++++++++++++------------ rpcs3/rpcs3qt/main_window.h | 10 ++++--- rpcs3/rpcs3qt/main_window.ui | 12 ++++++++ 3 files changed, 53 insertions(+), 23 deletions(-) diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index c0dcb9bfcd..fac0ea5b92 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -230,36 +230,28 @@ bool main_window::Init([[maybe_unused]] bool with_cli_boot) // RPCS3 Updater - QMenu* download_menu = new QMenu(tr("Update Available!")); - - QAction* download_action = new QAction(tr("Download Update"), download_menu); - connect(download_action, &QAction::triggered, this, [this] + connect(ui->actionDownload_Update, &QAction::triggered, this, [this] { m_updater.update(false); }); - download_menu->addAction(download_action); - #ifdef _WIN32 // Use a menu at the top right corner to indicate the new version. - QMenuBar *corner_bar = new QMenuBar(ui->menuBar); - m_download_menu_action = corner_bar->addMenu(download_menu); + // Some distros just can't handle corner widgets at the moment. + QMenuBar* corner_bar = new QMenuBar(ui->menuBar); + corner_bar->addMenu(ui->menuUpdate_Available); ui->menuBar->setCornerWidget(corner_bar); ui->menuBar->cornerWidget()->setVisible(false); -#else - // Append a menu to the right of the regular menus to indicate the new version. - // Some distros just can't handle corner widgets at the moment. - m_download_menu_action = ui->menuBar->addMenu(download_menu); + ui->menuBar->removeAction(ui->menuUpdate_Available->menuAction()); #endif - ensure(m_download_menu_action); - m_download_menu_action->setVisible(false); + ui->menuUpdate_Available->setVisible(false); connect(&m_updater, &update_manager::signal_update_available, this, [this](bool update_available) { - if (m_download_menu_action) + if (ui->menuUpdate_Available) { - m_download_menu_action->setVisible(update_available); + ui->menuUpdate_Available->setVisible(update_available); } if (ui->menuBar && ui->menuBar->cornerWidget()) { @@ -1933,9 +1925,11 @@ void main_window::OnEmuRun(bool /*start_playtime*/) EnableMenus(true); update_gui_pad_thread(); + + m_system_state = system_state::running; } -void main_window::OnEmuResume() const +void main_window::OnEmuResume() { const QString title = GetCurrentTitle(); const QString restart_tooltip = tr("Restart %0").arg(title); @@ -1948,9 +1942,11 @@ void main_window::OnEmuResume() const ui->toolbar_start->setText(tr("Pause")); ui->toolbar_start->setToolTip(pause_tooltip); ui->toolbar_stop->setToolTip(stop_tooltip); + + m_system_state = system_state::starting; // Let's just use this state to distinguish between resumed and running } -void main_window::OnEmuPause() const +void main_window::OnEmuPause() { const QString title = GetCurrentTitle(); const QString resume_tooltip = tr("Resume %0").arg(title); @@ -1966,6 +1962,8 @@ void main_window::OnEmuPause() const { m_game_list_frame->Refresh(); } + + m_system_state = system_state::paused; } void main_window::OnEmuStop() @@ -2026,9 +2024,11 @@ void main_window::OnEmuStop() } update_gui_pad_thread(); + + m_system_state = system_state::stopped; } -void main_window::OnEmuReady() const +void main_window::OnEmuReady() { const QString title = GetCurrentTitle(); const QString play_tooltip = tr("Play %0").arg(title); @@ -2054,6 +2054,8 @@ void main_window::OnEmuReady() const ui->removeAllCachesAct->setEnabled(false); ui->removeSavestatesAct->setEnabled(false); ui->cleanUpGameListAct->setEnabled(false); + + m_system_state = system_state::ready; } void main_window::EnableMenus(bool enabled) const @@ -2340,6 +2342,20 @@ void main_window::RetranslateUI(const QStringList& language_codes, const QString ui->retranslateUi(this); + // Update menu bar size (needed if the corner widget changes its size) + ui->menuBar->adjustSize(); + + // Update toolbar elements + switch (m_system_state) + { + case system_state::running: OnEmuRun(false); break; + case system_state::stopped: OnEmuStop(); break; + case system_state::paused: OnEmuPause(); break; + case system_state::starting: OnEmuResume(); break; + case system_state::ready: OnEmuReady(); break; + default: break; + } + if (m_game_list_frame) { m_game_list_frame->Refresh(true); diff --git a/rpcs3/rpcs3qt/main_window.h b/rpcs3/rpcs3qt/main_window.h index 4e5b498587..960a70c722 100644 --- a/rpcs3/rpcs3qt/main_window.h +++ b/rpcs3/rpcs3qt/main_window.h @@ -11,6 +11,7 @@ #include "settings.h" #include "shortcut_handler.h" #include "Emu/config_mode.h" +#include "Emu/System.h" #include @@ -88,9 +89,9 @@ Q_SIGNALS: public Q_SLOTS: void OnEmuStop(); void OnEmuRun(bool start_playtime); - void OnEmuResume() const; - void OnEmuPause() const; - void OnEmuReady() const; + void OnEmuResume(); + void OnEmuPause(); + void OnEmuReady(); void OnEnableDiscEject(bool enabled) const; void OnEnableDiscInsert(bool enabled) const; void OnAddBreakpoint(u32 addr) const; @@ -196,9 +197,10 @@ private: std::shared_ptr m_persistent_settings; update_manager m_updater; - QAction* m_download_menu_action = nullptr; shortcut_handler* m_shortcut_handler = nullptr; std::unique_ptr m_gui_pad_thread; + + system_state m_system_state = system_state::stopped; }; diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index 5ef0b98f25..72861a5d72 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -412,6 +412,12 @@ + + + Update Available! + + + @@ -419,6 +425,7 @@ + @@ -1448,6 +1455,11 @@ Sound Effects + + + Download Update + + From 3c747b377f8e7112cfe85ed3b2a8147a9946ca39 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 2 Dec 2025 04:23:20 +0100 Subject: [PATCH 02/15] Qt: fix translation of update dialog if the language was changed between downloading the json and clicking on update --- rpcs3/rpcs3qt/update_manager.cpp | 138 +++++++++++++++---------------- rpcs3/rpcs3qt/update_manager.h | 20 +++-- 2 files changed, 83 insertions(+), 75 deletions(-) diff --git a/rpcs3/rpcs3qt/update_manager.cpp b/rpcs3/rpcs3qt/update_manager.cpp index 8b07693e34..6929e08318 100644 --- a/rpcs3/rpcs3qt/update_manager.cpp +++ b/rpcs3/rpcs3qt/update_manager.cpp @@ -56,8 +56,7 @@ void update_manager::check_for_updates(bool automatic, bool check_only, bool aut { update_log.notice("Checking for updates: automatic=%d, check_only=%d, auto_accept=%d", automatic, check_only, auto_accept); - m_update_message.clear(); - m_changelog.clear(); + m_update_info = {}; if (automatic) { @@ -103,7 +102,7 @@ void update_manager::check_for_updates(bool automatic, bool check_only, bool aut } } - Q_EMIT signal_update_available(result_json && !m_update_message.isEmpty()); + Q_EMIT signal_update_available(result_json && m_update_info.update_found); }); const utils::OS_version os = utils::get_OS_version(); @@ -121,7 +120,7 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce const QJsonObject json_data = QJsonDocument::fromJson(data).object(); const int return_code = json_data["return_code"].toInt(-255); - bool hash_found = true; + m_update_info.hash_found = true; if (return_code < 0) { @@ -143,7 +142,7 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce // If a user clicks "Check for Updates" with a custom build ask him if he's sure he wants to update to latest version if (!automatic && return_code == -1) { - hash_found = false; + m_update_info.hash_found = false; } else { @@ -187,7 +186,7 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce check_json(latest["version"].isString(), "Node 'latest_build: version' not found or not a string") && check_json(latest["datetime"].isString(), "Node 'latest_build: datetime' not found or not a string") ) || - (hash_found && !( + (m_update_info.hash_found && !( check_json(current.isObject(), "JSON doesn't contain current_build section") && check_json(current["version"].isString(), "Node 'current_build: datetime' not found or not a string") && check_json(current["datetime"].isString(), "Node 'current_build: version' not found or not a string") @@ -196,7 +195,7 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce return false; } - if (hash_found && return_code == 0) + if (m_update_info.hash_found && return_code == 0) { update_log.success("RPCS3 is up to date!"); m_downloader->close_progress_dialog(); @@ -210,59 +209,26 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce // Calculate how old the build is const QString date_fmt = QStringLiteral("yyyy-MM-dd hh:mm:ss"); - const QDateTime cur_date = hash_found ? QDateTime::fromString(current["datetime"].toString(), date_fmt) : QDateTime::currentDateTimeUtc(); + const QDateTime cur_date = m_update_info.hash_found ? QDateTime::fromString(current["datetime"].toString(), date_fmt) : QDateTime::currentDateTimeUtc(); const QDateTime lts_date = QDateTime::fromString(latest["datetime"].toString(), date_fmt); - const QString cur_str = cur_date.toString(date_fmt); - const QString lts_str = lts_date.toString(date_fmt); + m_update_info.update_found = true; + m_update_info.cur_date = cur_date.toString(date_fmt); + m_update_info.lts_date = lts_date.toString(date_fmt); + m_update_info.diff_msec = cur_date.msecsTo(lts_date); + m_update_info.new_version = latest["version"].toString(); - const qint64 diff_msec = cur_date.msecsTo(lts_date); - - update_log.notice("Current: %s, latest: %s, difference: %lld ms", cur_str, lts_str, diff_msec); - - const Localized localized; - - const QString new_version = latest["version"].toString(); - m_new_version = new_version.toStdString(); - const QString support_message = tr("
You can empower our project at RPCS3 Patreon.
"); - - if (hash_found) + if (m_update_info.hash_found) { - const QString old_version = current["version"].toString(); - m_old_version = old_version.toStdString(); - - if (diff_msec < 0) - { - // This usually means that the current version was marked as broken and won't be shipped anymore, so we need to downgrade to avoid certain bugs. - m_update_message = tr("A better version of RPCS3 is available!

Current version: %0 (%1)
Better version: %2 (%3)
%4
Do you want to update?") - .arg(old_version) - .arg(cur_str) - .arg(new_version) - .arg(lts_str) - .arg(support_message); - } - else - { - m_update_message = tr("A new version of RPCS3 is available!

Current version: %0 (%1)
Latest version: %2 (%3)
Your version is %4 behind.
%5
Do you want to update?") - .arg(old_version) - .arg(cur_str) - .arg(new_version) - .arg(lts_str) - .arg(localized.GetVerboseTimeByMs(diff_msec, true)) - .arg(support_message); - } + m_update_info.old_version = current["version"].toString(); } else { - m_old_version = fmt::format("%s-%s-%s", rpcs3::get_full_branch(), rpcs3::get_branch(), rpcs3::get_version().to_string()); - - m_update_message = tr("You're currently using a custom or PR build.

Latest version: %0 (%1)
The latest version is %2 old.
%3
Do you want to update to the latest official RPCS3 version?") - .arg(new_version) - .arg(lts_str) - .arg(localized.GetVerboseTimeByMs(std::abs(diff_msec), true)) - .arg(support_message); + m_update_info.old_version = QString::fromStdString(fmt::format("%s-%s-%s", rpcs3::get_full_branch(), rpcs3::get_branch(), rpcs3::get_version().to_string())); } + update_log.notice("Current: %s, latest: %s, difference: %lld ms", m_update_info.cur_date, m_update_info.lts_date, m_update_info.diff_msec); + m_request_url = latest[os]["download"].toString().toStdString(); m_expected_hash = latest[os]["checksum"].toString().toStdString(); m_expected_size = latest[os]["size"].toInt(); @@ -277,9 +243,9 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce if (!auto_accept) { - if (automatic && m_gui_settings->GetValue(gui::ib_skip_version).toString() == new_version) + if (automatic && m_gui_settings->GetValue(gui::ib_skip_version).toString() == m_update_info.new_version) { - update_log.notice("Skipping automatic update notification for version '%s' due to user preference", new_version); + update_log.notice("Skipping automatic update notification for version '%s' due to user preference", m_update_info.new_version); m_downloader->close_progress_dialog(); return true; } @@ -300,7 +266,6 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce } else { - entry.version = tr("N/A"); update_log.notice("JSON changelog entry does not contain a version string."); } @@ -310,11 +275,10 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce } else { - entry.title = tr("N/A"); update_log.notice("JSON changelog entry does not contain a title string."); } - m_changelog.push_back(entry); + m_update_info.changelog.push_back(std::move(entry)); } else { @@ -351,25 +315,61 @@ void update_manager::update(bool auto_accept) if (!auto_accept) { - if (m_update_message.isEmpty()) + if (!m_update_info.update_found) { // This can happen if we abort the check_for_updates download. Just check again in this case. - update_log.notice("Aborting update: Update message is empty. Trying again..."); + update_log.notice("Aborting update: Update not found. Trying again..."); m_downloader->close_progress_dialog(); check_for_updates(false, false, false, m_parent); return; } + const Localized localized; + const QString support_message = tr("
You can empower our project at RPCS3 Patreon.
"); + QString update_message; + + if (m_update_info.hash_found) + { + if (m_update_info.diff_msec < 0) + { + // This usually means that the current version was marked as broken and won't be shipped anymore, so we need to downgrade to avoid certain bugs. + update_message = tr("A better version of RPCS3 is available!

Current version: %0 (%1)
Better version: %2 (%3)
%4
Do you want to update?") + .arg(m_update_info.old_version) + .arg(m_update_info.cur_date) + .arg(m_update_info.new_version) + .arg(m_update_info.lts_date) + .arg(support_message); + } + else + { + update_message = tr("A new version of RPCS3 is available!

Current version: %0 (%1)
Latest version: %2 (%3)
Your version is %4 behind.
%5
Do you want to update?") + .arg(m_update_info.old_version) + .arg(m_update_info.cur_date) + .arg(m_update_info.new_version) + .arg(m_update_info.lts_date) + .arg(localized.GetVerboseTimeByMs(m_update_info.diff_msec, true)) + .arg(support_message); + } + } + else + { + update_message = tr("You're currently using a custom or PR build.

Latest version: %0 (%1)
The latest version is %2 old.
%3
Do you want to update to the latest official RPCS3 version?") + .arg(m_update_info.new_version) + .arg(m_update_info.lts_date) + .arg(localized.GetVerboseTimeByMs(std::abs(m_update_info.diff_msec), true)) + .arg(support_message); + } + QString changelog_content; - for (const changelog_data& entry : m_changelog) + for (const changelog_data& entry : m_update_info.changelog) { if (!changelog_content.isEmpty()) changelog_content.append('\n'); - changelog_content.append(tr("• %0: %1").arg(entry.version, entry.title)); + changelog_content.append(tr("• %0: %1").arg(entry.version.isEmpty() ? tr("N/A") : entry.version, entry.title.isEmpty() ? tr("N/A") : entry.title)); } - QMessageBox mb(QMessageBox::Icon::Question, tr("Update Available"), m_update_message, QMessageBox::Yes | QMessageBox::No, m_downloader->get_progress_dialog() ? m_downloader->get_progress_dialog() : m_parent); + QMessageBox mb(QMessageBox::Icon::Question, tr("Update Available"), update_message, QMessageBox::Yes | QMessageBox::No, m_downloader->get_progress_dialog() ? m_downloader->get_progress_dialog() : m_parent); mb.setTextFormat(Qt::RichText); mb.setCheckBox(new QCheckBox(tr("Don't show again for this version"))); @@ -380,16 +380,16 @@ void update_manager::update(bool auto_accept) // Smartass hack to make the unresizeable message box wide enough for the changelog const int changelog_width = QLabel(changelog_content).sizeHint().width(); - if (QLabel(m_update_message).sizeHint().width() < changelog_width) + if (QLabel(update_message).sizeHint().width() < changelog_width) { - m_update_message += "  "; - while (QLabel(m_update_message).sizeHint().width() < changelog_width) + update_message += "  "; + while (QLabel(update_message).sizeHint().width() < changelog_width) { - m_update_message += " "; + update_message += " "; } } - mb.setText(m_update_message); + mb.setText(update_message); } update_log.notice("Asking user for permission to update..."); @@ -400,8 +400,8 @@ void update_manager::update(bool auto_accept) if (mb.checkBox()->isChecked()) { - update_log.notice("User requested to skip further automatic update notifications for version '%s'", m_new_version); - m_gui_settings->SetValue(gui::ib_skip_version, QString::fromStdString(m_new_version)); + update_log.notice("User requested to skip further automatic update notifications for version '%s'", m_update_info.new_version); + m_gui_settings->SetValue(gui::ib_skip_version, m_update_info.new_version); } m_downloader->close_progress_dialog(); @@ -751,7 +751,7 @@ bool update_manager::handle_rpcs3(const QByteArray& data, bool auto_accept) if (fs::file update_file{fs::get_config_dir() + "update_history.log", fs::create + fs::write + fs::append}) { const std::string update_time = QDateTime::currentDateTime().toString("yyyy/MM/dd hh:mm:ss").toStdString(); - const std::string entry = fmt::format("%s: Updated from \"%s\" to \"%s\"", update_time, m_old_version, m_new_version); + const std::string entry = fmt::format("%s: Updated from \"%s\" to \"%s\"", update_time, m_update_info.old_version, m_update_info.new_version); update_file.write(fmt::format("%s\n", entry)); update_log.notice("Added entry '%s' to update_history.log", entry); } diff --git a/rpcs3/rpcs3qt/update_manager.h b/rpcs3/rpcs3qt/update_manager.h index f6a534b0b2..8e7d9e8eb0 100644 --- a/rpcs3/rpcs3qt/update_manager.h +++ b/rpcs3/rpcs3qt/update_manager.h @@ -19,20 +19,28 @@ private: std::shared_ptr m_gui_settings; - // This message is empty if there is no download available - QString m_update_message; - struct changelog_data { QString version; QString title; }; - std::vector m_changelog; + + struct update_info + { + bool update_found = false; + bool hash_found = false; + qint64 diff_msec = 0; + QString cur_date; + QString lts_date; + QString old_version; + QString new_version; + std::vector changelog; + }; + + update_info m_update_info {}; std::string m_request_url; std::string m_expected_hash; - std::string m_old_version; - std::string m_new_version; u64 m_expected_size = 0; bool handle_json(bool automatic, bool check_only, bool auto_accept, const QByteArray& data); From 6489fca16b81e4517a71ae7773819b1bbadd312f Mon Sep 17 00:00:00 2001 From: Ani Date: Sun, 30 Nov 2025 21:44:03 +0100 Subject: [PATCH 03/15] YoRHa: Fix the height of QSpinBox --- bin/GuiConfigs/YoRHa by Ani.qss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/GuiConfigs/YoRHa by Ani.qss b/bin/GuiConfigs/YoRHa by Ani.qss index bcfd059c2e..c772f25196 100644 --- a/bin/GuiConfigs/YoRHa by Ani.qss +++ b/bin/GuiConfigs/YoRHa by Ani.qss @@ -201,7 +201,7 @@ QPushButton::disabled { /* QSpinBox (Settings -> Emulator -> width/height) */ /* QDoubleSpinBox (Pads -> Mouse Acceleration -> x/y) */ QSpinBox, QDoubleSpinBox { - height: 0.1em; + height: 1.50em; background-color: #b3ac98; } QSpinBox::disabled, QDoubleSpinBox::disabled { From 613d428ced78c345fd5c0077b0e72d484bce10d5 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 3 Dec 2025 00:38:35 +0100 Subject: [PATCH 04/15] Qt: fix update note visiblity on linux/macOs --- rpcs3/rpcs3qt/main_window.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index fac0ea5b92..e2c2aea78b 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -243,21 +243,19 @@ bool main_window::Init([[maybe_unused]] bool with_cli_boot) ui->menuBar->setCornerWidget(corner_bar); ui->menuBar->cornerWidget()->setVisible(false); ui->menuBar->removeAction(ui->menuUpdate_Available->menuAction()); -#endif - - ui->menuUpdate_Available->setVisible(false); connect(&m_updater, &update_manager::signal_update_available, this, [this](bool update_available) { - if (ui->menuUpdate_Available) - { - ui->menuUpdate_Available->setVisible(update_available); - } - if (ui->menuBar && ui->menuBar->cornerWidget()) - { - ui->menuBar->cornerWidget()->setVisible(update_available); - } + ui->menuBar->cornerWidget()->setVisible(update_available); }); +#else + ui->menuUpdate_Available->menuAction()->setVisible(false); + + connect(&m_updater, &update_manager::signal_update_available, this, [this](bool update_available) + { + ui->menuUpdate_Available->menuAction()->setVisible(update_available); + }); +#endif #ifdef RPCS3_UPDATE_SUPPORTED if (const auto update_value = m_gui_settings->GetValue(gui::m_check_upd_start).toString(); update_value != gui::update_off) From cbba687ffab9b0831116a4926bbf343546669880 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 3 Dec 2025 00:08:13 +0100 Subject: [PATCH 05/15] Qt: Relax game_list deselection checks --- rpcs3/rpcs3qt/game_list.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rpcs3/rpcs3qt/game_list.cpp b/rpcs3/rpcs3qt/game_list.cpp index c5eba4efd3..70089d650e 100644 --- a/rpcs3/rpcs3qt/game_list.cpp +++ b/rpcs3/rpcs3qt/game_list.cpp @@ -117,7 +117,8 @@ void game_list::fix_narrow_columns() void game_list::mousePressEvent(QMouseEvent* event) { - if (QTableWidgetItem* item = itemAt(event->pos()); !item || !item->data(Qt::UserRole).isValid()) + // Handle deselction when clicking on empty space in the table + if (!itemAt(event->pos())) { clearSelection(); setCurrentItem(nullptr); // Needed for currentItemChanged From 23ffa4ccdbd7075e051b6c8136f1c70cb91b8d1a Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 3 Dec 2025 09:01:37 +0100 Subject: [PATCH 06/15] Update FAudio to 25.12 --- 3rdparty/FAudio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/FAudio b/3rdparty/FAudio index 8de3616b5b..4ea8afea6b 160000 --- a/3rdparty/FAudio +++ b/3rdparty/FAudio @@ -1 +1 @@ -Subproject commit 8de3616b5b204260fe639e76587731d8a73b8d2c +Subproject commit 4ea8afea6ba857c24e40877f487d000d559b196d From b86b4d15c638644e3ef42ea6fe915733adb7a1bc Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 3 Dec 2025 09:02:03 +0100 Subject: [PATCH 07/15] Update SDL to 3.2.28 --- 3rdparty/libsdl-org/SDL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/libsdl-org/SDL b/3rdparty/libsdl-org/SDL index badbf8da4e..7f3ae3d574 160000 --- a/3rdparty/libsdl-org/SDL +++ b/3rdparty/libsdl-org/SDL @@ -1 +1 @@ -Subproject commit badbf8da4ee72b3ef599c721ffc9899e8d7c8d90 +Subproject commit 7f3ae3d57459e59943a4ecfefc8f6277ec6bf540 From 2a6f1cf35c00e1edb5e8bed78b1ce7fa334b583e Mon Sep 17 00:00:00 2001 From: kd-11 <15904127+kd-11@users.noreply.github.com> Date: Wed, 3 Dec 2025 02:25:17 +0300 Subject: [PATCH 08/15] rsx/common - work around macos posix_memalign quirks --- rpcs3/Emu/RSX/Common/simple_array.hpp | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/RSX/Common/simple_array.hpp b/rpcs3/Emu/RSX/Common/simple_array.hpp index 4f8d2a5100..00dd6e7d95 100644 --- a/rpcs3/Emu/RSX/Common/simple_array.hpp +++ b/rpcs3/Emu/RSX/Common/simple_array.hpp @@ -12,28 +12,45 @@ namespace rsx namespace aligned_allocator { template + requires (Align != 0) && ((Align & (Align - 1)) == 0) + size_t align_up(size_t size) + { + return (size + (Align - 1)) & ~(Align - 1); + } + + template + requires (Align != 0) && ((Align & (Align - 1)) == 0) void* malloc(size_t size) { -#ifdef _WIN32 +#if defined(_WIN32) return _aligned_malloc(size, Align); +#elif defined(__APPLE__) + constexpr size_t NativeAlign = std::max(Align, sizeof(void*)); + return std::aligned_alloc(NativeAlign, align_up(size)); #else - return std::aligned_alloc(Align, size); + return std::aligned_alloc(Align, align_up(size)); #endif } template + requires (Align != 0) && ((Align & (Align - 1)) == 0) void* realloc(void* prev_ptr, [[maybe_unused]] size_t prev_size, size_t new_size) { - if (prev_size >= new_size) + if (align_up(prev_size) >= new_size) { return prev_ptr; } ensure(reinterpret_cast(prev_ptr) % Align == 0, "Pointer not aligned to Align"); -#ifdef _WIN32 +#if defined(_WIN32) return _aligned_realloc(prev_ptr, new_size, Align); #else - void* ret = std::aligned_alloc(Align, new_size); +#if defined(__APPLE__) + constexpr size_t NativeAlign = std::max(Align, sizeof(void*)); + void* ret = std::aligned_alloc(NativeAlign, align_up(new_size)); +#else + void* ret = std::aligned_alloc(Align, align_up(new_size)); +#endif std::memcpy(ret, prev_ptr, std::min(prev_size, new_size)); std::free(prev_ptr); return ret; From ff9401303b387cb11b97cf5984a9ab7672f487fc Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 3 Dec 2025 09:39:22 +0100 Subject: [PATCH 09/15] hidapi: switch to official libusb remote --- .gitmodules | 2 +- 3rdparty/hidapi/hidapi | 2 +- rpcs3/Input/ds3_pad_handler.cpp | 8 -------- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/.gitmodules b/.gitmodules index 6f0cd78a5b..77fae5cf55 100644 --- a/.gitmodules +++ b/.gitmodules @@ -21,7 +21,7 @@ ignore = dirty [submodule "3rdparty/hidapi"] path = 3rdparty/hidapi/hidapi - url = ../../RPCS3/hidapi.git + url = ../../libusb/hidapi.git branch = master ignore = dirty [submodule "3rdparty/pugixml"] diff --git a/3rdparty/hidapi/hidapi b/3rdparty/hidapi/hidapi index f42423643e..d6b2a97460 160000 --- a/3rdparty/hidapi/hidapi +++ b/3rdparty/hidapi/hidapi @@ -1 +1 @@ -Subproject commit f42423643ec9011c98cccc0bb790722bbbd3f30b +Subproject commit d6b2a974608dec3b76fb1e36c189f22b9cf3650c diff --git a/rpcs3/Input/ds3_pad_handler.cpp b/rpcs3/Input/ds3_pad_handler.cpp index 0495ef1c05..8d8318ce45 100644 --- a/rpcs3/Input/ds3_pad_handler.cpp +++ b/rpcs3/Input/ds3_pad_handler.cpp @@ -266,14 +266,6 @@ void ds3_pad_handler::check_add_device(hid_device* hidDevice, hid_enumerated_dev } device->report_id = buf[0]; -#elif defined (__APPLE__) - int res = hid_init_sixaxis_usb(hidDevice); - if (res < 0) - { - ds3_log.error("check_add_device: hid_init_sixaxis_usb failed! (result=%d, error=%s)", res, hid_error(hidDevice)); - HidDevice::close(hidDevice); - return; - } #endif for (wchar_t ch : wide_serial) From e3f5f2d14e44a44eec9f8c0f79f53893ff04fdbc Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sun, 30 Nov 2025 13:39:32 +0100 Subject: [PATCH 10/15] cellPad: fix pad mode setters --- rpcs3/Emu/Cell/Modules/cellPad.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index a217a37313..9c8e05f74f 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -1051,7 +1051,15 @@ error_code cellPadSetPortSetting(u32 port_no, u32 port_setting) if (port_no >= CELL_PAD_MAX_PORT_NUM) return CELL_OK; - config.port_setting[port_no] = port_setting; + if (port_setting & CELL_PAD_SETTING_PRESS_ON) + config.port_setting[port_no] |= CELL_PAD_SETTING_PRESS_ON; + else + config.port_setting[port_no] &= ~CELL_PAD_SETTING_PRESS_ON; + + if (port_setting & CELL_PAD_SETTING_SENSOR_ON) + config.port_setting[port_no] |= CELL_PAD_SETTING_SENSOR_ON; + else + config.port_setting[port_no] &= ~CELL_PAD_SETTING_SENSOR_ON; // can also return CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD <- Update: seems to be just internal and ignored @@ -1123,7 +1131,7 @@ error_code cellPadSetPressMode(u32 port_no, u32 mode) if (!config.max_connect) return CELL_PAD_ERROR_UNINITIALIZED; - if (port_no >= CELL_PAD_MAX_PORT_NUM) + if (port_no >= CELL_MAX_PADS || mode > 1) return CELL_PAD_ERROR_INVALID_PARAMETER; // CELL_PAD_ERROR_NO_DEVICE is not returned in this case. @@ -1157,7 +1165,7 @@ error_code cellPadSetSensorMode(u32 port_no, u32 mode) if (!config.max_connect) return CELL_PAD_ERROR_UNINITIALIZED; - if (port_no >= CELL_MAX_PADS) + if (port_no >= CELL_MAX_PADS || mode > 1) return CELL_PAD_ERROR_INVALID_PARAMETER; // CELL_PAD_ERROR_NO_DEVICE is not returned in this case. From b9a9c1af07e85d3d542416039accaef3e1020fc8 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 3 Dec 2025 20:38:33 +0100 Subject: [PATCH 11/15] Qt: revert setting UserRole. this doesn't seem to have been the culprit for multiselection --- rpcs3/rpcs3qt/movie_item.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/rpcs3/rpcs3qt/movie_item.cpp b/rpcs3/rpcs3qt/movie_item.cpp index eee10cc3ba..3cfb269709 100644 --- a/rpcs3/rpcs3qt/movie_item.cpp +++ b/rpcs3/rpcs3qt/movie_item.cpp @@ -3,15 +3,12 @@ movie_item::movie_item() : QTableWidgetItem(), movie_item_base() { - setData(Qt::UserRole, {}); // Set any value to UserRole for proper indexing (e.g. for multiselection) } movie_item::movie_item(const QString& text, int type) : QTableWidgetItem(text, type), movie_item_base() { - setData(Qt::UserRole, {}); // Set any value to UserRole for proper indexing (e.g. for multiselection) } movie_item::movie_item(const QIcon& icon, const QString& text, int type) : QTableWidgetItem(icon, text, type), movie_item_base() { - setData(Qt::UserRole, {}); // Set any value to UserRole for proper indexing (e.g. for multiselection) } From dc27047ed44b5d61c8c9eee58ec4b839e6f3ced9 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 3 Dec 2025 20:39:22 +0100 Subject: [PATCH 12/15] Qt: fix game list refresh order when changing the language --- rpcs3/rpcs3qt/main_window.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index e2c2aea78b..5509a6f69c 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -2340,6 +2340,12 @@ void main_window::RetranslateUI(const QStringList& language_codes, const QString ui->retranslateUi(this); + // Refresh game list first to prevent localization mismatches in further Refresh calls + if (m_game_list_frame) + { + m_game_list_frame->Refresh(true); + } + // Update menu bar size (needed if the corner widget changes its size) ui->menuBar->adjustSize(); @@ -2354,11 +2360,6 @@ void main_window::RetranslateUI(const QStringList& language_codes, const QString default: break; } - if (m_game_list_frame) - { - m_game_list_frame->Refresh(true); - } - Q_EMIT RequestDialogRepaint(); } From 7d3cf831d542f7a919cb5f82cb3ee2fb09174374 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 3 Dec 2025 20:50:59 +0100 Subject: [PATCH 13/15] Qt/macOS: Fix GUI freezes on Qt 6.8+ --- rpcs3/rpcs3.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rpcs3/rpcs3.cpp b/rpcs3/rpcs3.cpp index f7efb7728b..e7257577dc 100644 --- a/rpcs3/rpcs3.cpp +++ b/rpcs3/rpcs3.cpp @@ -435,6 +435,9 @@ QCoreApplication* create_application(std::span qt_argv) { qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0"); } +#elif __APPLE__ + // set the QT_MTL_NO_TRANSACTION variable in order to prevent Qt GUI freeze + qputenv("QT_MTL_NO_TRANSACTION", "1"); #endif bool use_high_dpi = true; From fce393024a1ba71414210909c859e050740fa2ac Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 3 Dec 2025 21:17:31 +0100 Subject: [PATCH 14/15] Update Qt for macOs to 6.9.3 --- .ci/build-mac-arm64.sh | 11 ++++------- .ci/build-mac.sh | 11 ++++------- .github/workflows/rpcs3.yml | 2 +- rpcs3/rpcs3qt/qt_camera_video_sink.cpp | 4 ---- 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/.ci/build-mac-arm64.sh b/.ci/build-mac-arm64.sh index 0efe2fda87..42e0f61fb3 100755 --- a/.ci/build-mac-arm64.sh +++ b/.ci/build-mac-arm64.sh @@ -38,17 +38,14 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then git clone https://github.com/engnr/qt-downloader.git cd qt-downloader git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597 - # nested Qt 6.10.1 URL workaround - # sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader - # sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader - # archived Qt 6.7.3 URL workaround - sed -i '' "s/official_releases/archive/g" qt-downloader + sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader + sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader cd "/tmp/Qt" "$BREW_PATH/bin/pipenv" run pip3 uninstall py7zr requests semantic_version lxml "$BREW_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml --no-cache mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64" - # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.10.1 workaround - "$BREW_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64" + sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" + "$BREW_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats -o "$QT_VER/clang_64" fi cd "$WORKDIR" diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh index 6993c7ec9c..afd687061c 100755 --- a/.ci/build-mac.sh +++ b/.ci/build-mac.sh @@ -38,16 +38,13 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then git clone https://github.com/engnr/qt-downloader.git cd qt-downloader git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597 - # nested Qt 6.10.1 URL workaround - # sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader - # sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader - # archived Qt 6.7.3 URL workaround - sed -i '' "s/official_releases/archive/g" qt-downloader + sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader + sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader cd "/tmp/Qt" "/opt/homebrew/bin/pipenv" --python "/opt/homebrew/bin/python3" run pip3 install py7zr requests semantic_version lxml mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64" - # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.10.1 workaround - "/opt/homebrew/bin/pipenv" --python "/opt/homebrew/bin/python3" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64" + sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" + "/opt/homebrew/bin/pipenv" --python "/opt/homebrew/bin/python3" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats -o "$QT_VER/clang_64" fi cd "$WORKDIR" diff --git a/.github/workflows/rpcs3.yml b/.github/workflows/rpcs3.yml index 4a906c024d..9913b632f3 100644 --- a/.github/workflows/rpcs3.yml +++ b/.github/workflows/rpcs3.yml @@ -134,7 +134,7 @@ jobs: runs-on: macos-14 env: CCACHE_DIR: /tmp/ccache_dir - QT_VER: '6.7.3' + QT_VER: '6.9.3' QT_VER_MAIN: '6' LLVM_COMPILER_VER: '21' RELEASE_MESSAGE: ../GitHubReleaseMessage.txt diff --git a/rpcs3/rpcs3qt/qt_camera_video_sink.cpp b/rpcs3/rpcs3qt/qt_camera_video_sink.cpp index c13fcb781d..de63d80b5d 100644 --- a/rpcs3/rpcs3qt/qt_camera_video_sink.cpp +++ b/rpcs3/rpcs3qt/qt_camera_video_sink.cpp @@ -71,14 +71,10 @@ bool qt_camera_video_sink::present(const QVideoFrame& frame) // Flip image if necessary if (flip_horizontally || flip_vertically) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0) Qt::Orientations orientation {}; orientation.setFlag(Qt::Orientation::Horizontal, flip_horizontally); orientation.setFlag(Qt::Orientation::Vertical, flip_vertically); image.flip(orientation); -#else - image.mirror(flip_horizontally, flip_vertically); -#endif } if (image.format() != QImage::Format_RGBA8888) From 133b19f2059a8c8bf2a79f8ab04bffd1ad87a89e Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 3 Dec 2025 22:17:52 +0100 Subject: [PATCH 15/15] Update Qt for macOs to 6.10.1 --- .github/workflows/rpcs3.yml | 2 +- rpcs3/rpcs3.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rpcs3.yml b/.github/workflows/rpcs3.yml index 9913b632f3..1edd6aa9b8 100644 --- a/.github/workflows/rpcs3.yml +++ b/.github/workflows/rpcs3.yml @@ -134,7 +134,7 @@ jobs: runs-on: macos-14 env: CCACHE_DIR: /tmp/ccache_dir - QT_VER: '6.9.3' + QT_VER: '6.10.1' QT_VER_MAIN: '6' LLVM_COMPILER_VER: '21' RELEASE_MESSAGE: ../GitHubReleaseMessage.txt diff --git a/rpcs3/rpcs3.cpp b/rpcs3/rpcs3.cpp index e7257577dc..007de26bdb 100644 --- a/rpcs3/rpcs3.cpp +++ b/rpcs3/rpcs3.cpp @@ -438,6 +438,9 @@ QCoreApplication* create_application(std::span qt_argv) #elif __APPLE__ // set the QT_MTL_NO_TRANSACTION variable in order to prevent Qt GUI freeze qputenv("QT_MTL_NO_TRANSACTION", "1"); + + // set the QT_MAC_NO_CONTAINER_LAYER variable in order to prevent swapchain crash + qputenv("QT_MAC_NO_CONTAINER_LAYER", "1"); #endif bool use_high_dpi = true;