diff --git a/src/core/host.h b/src/core/host.h index aa2856b50..ac48f214d 100644 --- a/src/core/host.h +++ b/src/core/host.h @@ -78,6 +78,9 @@ bool ChangeLanguage(const char* new_language); /// Safely executes a function on the VM thread. void RunOnCPUThread(std::function function, bool block = false); +/// Safely executes a function on the main/UI thread. +void RunOnUIThread(std::function function, bool block = false); + /// Called when the core is creating a render device. /// This could also be fullscreen transition. std::optional AcquireRenderWindow(RenderAPI render_api, bool fullscreen, bool exclusive_fullscreen, diff --git a/src/duckstation-mini/mini_host.cpp b/src/duckstation-mini/mini_host.cpp index dd6ce81f4..15e40b478 100644 --- a/src/duckstation-mini/mini_host.cpp +++ b/src/duckstation-mini/mini_host.cpp @@ -77,11 +77,9 @@ static bool SetDataDirectory(); static bool SetCriticalFolders(); static void SetDefaultSettings(SettingsInterface& si, bool system, bool controller); static std::string GetResourcePath(std::string_view name, bool allow_override); -static void ProcessCPUThreadEvents(bool block); static bool PerformEarlyHardwareChecks(); static bool EarlyProcessStartup(); static void WarnAboutInterface(); -static void RunOnUIThread(std::function func); static void StartCPUThread(); static void StopCPUThread(); static void ProcessCPUThreadEvents(bool block); @@ -580,7 +578,7 @@ std::optional Host::AcquireRenderWindow(RenderAPI render_api, bool f std::optional wi; - MiniHost::RunOnUIThread([render_api, fullscreen, error, &wi]() { + Host::RunOnUIThread([render_api, fullscreen, error, &wi]() { const std::string window_title = GetWindowTitle(System::GetGameTitle()); const SDL_PropertiesID props = SDL_CreateProperties(); SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, window_title.c_str()); @@ -656,7 +654,7 @@ void Host::ReleaseRenderWindow() if (!s_state.sdl_window) return; - MiniHost::RunOnUIThread([]() { + Host::RunOnUIThread([]() { if (!s_state.fullscreen.load(std::memory_order_acquire)) { int window_x = SDL_WINDOWPOS_UNDEFINED, window_y = SDL_WINDOWPOS_UNDEFINED; @@ -909,17 +907,6 @@ void MiniHost::ProcessSDLEvent(const SDL_Event* ev) } } -void MiniHost::RunOnUIThread(std::function func) -{ - std::function* pfunc = new std::function(std::move(func)); - - SDL_Event ev; - ev.user = {}; - ev.type = s_state.func_event_id; - ev.user.data1 = pfunc; - SDL_PushEvent(&ev); -} - void MiniHost::ProcessCPUThreadPlatformMessages() { // This is lame. On Win32, we need to pump messages, even though *we* don't have any windows @@ -1038,7 +1025,7 @@ void MiniHost::CPUThreadEntryPoint() System::CPUThreadShutdown(); // Tell the UI thread to shut down. - RunOnUIThread([]() { s_state.ui_thread_running = false; }); + Host::RunOnUIThread([]() { s_state.ui_thread_running = false; }); } void MiniHost::CPUThreadMainLoop() @@ -1211,6 +1198,19 @@ void Host::RunOnCPUThread(std::function function, bool block /* = false s_state.cpu_thread_event_done.wait(lock, []() { return s_state.blocking_cpu_events_pending == 0; }); } +void Host::RunOnUIThread(std::function function, bool block /* = false */) +{ + using namespace MiniHost; + + std::function* pfunc = new std::function(std::move(function)); + + SDL_Event ev; + ev.user = {}; + ev.type = s_state.func_event_id; + ev.user.data1 = pfunc; + SDL_PushEvent(&ev); +} + void Host::RefreshGameListAsync(bool invalidate_cache) { using namespace MiniHost; diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index 7ecac152c..9a26d4bad 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -661,7 +661,7 @@ void DebuggerWindow::toggleBreakpoint(VirtualMemoryAddress address) return; } - QtHost::RunOnUIThread([this, address, new_bp_state, bps = CPU::CopyBreakpointList()]() { + Host::RunOnUIThread([this, address, new_bp_state, bps = CPU::CopyBreakpointList()]() { m_code_model->setBreakpointState(address, new_bp_state); refreshBreakpointList(bps); }); @@ -715,7 +715,7 @@ bool DebuggerWindow::scrollToMemoryAddress(VirtualMemoryAddress address) void DebuggerWindow::refreshBreakpointList() { Host::RunOnCPUThread( - [this]() { QtHost::RunOnUIThread([this, bps = CPU::CopyBreakpointList()]() { refreshBreakpointList(bps); }); }); + [this]() { Host::RunOnUIThread([this, bps = CPU::CopyBreakpointList()]() { refreshBreakpointList(bps); }); }); } void DebuggerWindow::refreshBreakpointList(const CPU::BreakpointList& bps) @@ -743,7 +743,7 @@ void DebuggerWindow::addBreakpoint(CPU::BreakpointType type, u32 address) { Host::RunOnCPUThread([this, address, type]() { const bool result = CPU::AddBreakpoint(type, address); - QtHost::RunOnUIThread([this, address, type, result, bps = CPU::CopyBreakpointList()]() { + Host::RunOnUIThread([this, address, type, result, bps = CPU::CopyBreakpointList()]() { if (!result) { QMessageBox::critical(this, windowTitle(), @@ -763,7 +763,7 @@ void DebuggerWindow::removeBreakpoint(CPU::BreakpointType type, u32 address) { Host::RunOnCPUThread([this, address, type]() { const bool result = CPU::RemoveBreakpoint(type, address); - QtHost::RunOnUIThread([this, address, type, result, bps = CPU::CopyBreakpointList()]() { + Host::RunOnUIThread([this, address, type, result, bps = CPU::CopyBreakpointList()]() { if (!result) { QMessageBox::critical(this, windowTitle(), tr("Failed to remove breakpoint. This breakpoint may not exist.")); diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 2c5c22771..98116a8db 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -1037,7 +1037,7 @@ void MainWindow::populateCheatsMenu(QMenu* menu) if (Cheats::AreCheatsEnabled() && names.empty()) return; - QtHost::RunOnUIThread([menu, names = std::move(names)]() { + Host::RunOnUIThread([menu, names = std::move(names)]() { if (names.empty()) { QAction* action = menu->addAction(tr("Cheats are not enabled.")); @@ -2368,9 +2368,9 @@ void MainWindow::openGamePropertiesForCurrentGame(const char* category /* = null if (path.empty() || serial.empty()) return; - QtHost::RunOnUIThread([title = std::string(System::GetGameTitle()), path = std::string(path), - serial = std::string(serial), hash = System::GetGameHash(), region = System::GetDiscRegion(), - category]() { + Host::RunOnUIThread([title = std::string(System::GetGameTitle()), path = std::string(path), + serial = std::string(serial), hash = System::GetGameHash(), region = System::GetDiscRegion(), + category]() { SettingsWindow::openGamePropertiesDialog(path, title, std::move(serial), hash, region, category); }); }); diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index f664004cd..6acf1e5e7 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -1149,7 +1149,7 @@ void EmuThread::confirmActionIfMemoryCardBusy(const QString& action, bool cancel return; } - QtHost::RunOnUIThread([action, cancel_resume_on_accept, callback = std::move(callback)]() mutable { + Host::RunOnUIThread([action, cancel_resume_on_accept, callback = std::move(callback)]() mutable { auto lock = g_main_window->pauseAndLockSystem(); const bool result = @@ -1378,7 +1378,7 @@ void EmuThread::startControllerTest() return; } - QtHost::RunOnUIThread([path = std::move(path)]() mutable { + Host::RunOnUIThread([path = std::move(path)]() mutable { { auto lock = g_main_window->pauseAndLockSystem(); if (QMessageBox::question( @@ -1397,7 +1397,7 @@ void EmuThread::startControllerTest() }); } -void EmuThread::runOnEmuThread(std::function callback) +void EmuThread::runOnEmuThread(const std::function& callback) { callback(); } @@ -1411,11 +1411,11 @@ void Host::RunOnCPUThread(std::function function, bool block /* = false Q_ARG(std::function, std::move(function))); } -void QtHost::RunOnUIThread(const std::function& func, bool block /*= false*/) +void Host::RunOnUIThread(std::function function, bool block /* = false*/) { // main window always exists, so it's fine to attach it to that. QMetaObject::invokeMethod(g_main_window, "runOnUIThread", block ? Qt::BlockingQueuedConnection : Qt::QueuedConnection, - Q_ARG(const std::function&, func)); + Q_ARG(std::function, std::move(function))); } QtAsyncTask::QtAsyncTask(WorkCallback callback) @@ -1432,7 +1432,7 @@ void QtAsyncTask::create(QObject* owner, WorkCallback callback) connect(task, &QtAsyncTask::completed, owner, [task]() { std::get(task->m_callback)(); }); System::QueueAsyncTask([task]() { task->m_callback = std::get(task->m_callback)(); - QtHost::RunOnUIThread([task]() { + Host::RunOnUIThread([task]() { emit task->completed(task); delete task; }); @@ -1710,10 +1710,9 @@ void Host::OpenHostFileSelectorAsync(std::string_view title, bool select_directo } } - QtHost::RunOnUIThread([title = QtUtils::StringViewToQString(title), select_directory, callback = std::move(callback), - filters_str = std::move(filters_str), - initial_directory = QtUtils::StringViewToQString(initial_directory), - from_cpu_thread]() mutable { + Host::RunOnUIThread([title = QtUtils::StringViewToQString(title), select_directory, callback = std::move(callback), + filters_str = std::move(filters_str), + initial_directory = QtUtils::StringViewToQString(initial_directory), from_cpu_thread]() mutable { auto lock = g_main_window->pauseAndLockSystem(); QString path; @@ -2103,9 +2102,9 @@ void Host::ConfirmMessageAsync(std::string_view title, std::string_view message, else { // Otherwise, use the desktop UI. - QtHost::RunOnUIThread([title = QtUtils::StringViewToQString(title), message = QtUtils::StringViewToQString(message), - callback = std::move(callback), yes_text = QtUtils::StringViewToQString(yes_text), - no_text = QtUtils::StringViewToQString(no_text), needs_pause]() mutable { + Host::RunOnUIThread([title = QtUtils::StringViewToQString(title), message = QtUtils::StringViewToQString(message), + callback = std::move(callback), yes_text = QtUtils::StringViewToQString(yes_text), + no_text = QtUtils::StringViewToQString(no_text), needs_pause]() mutable { auto lock = g_main_window->pauseAndLockSystem(); bool result; @@ -2136,14 +2135,14 @@ void Host::ConfirmMessageAsync(std::string_view title, std::string_view message, void Host::OpenURL(std::string_view url) { - QtHost::RunOnUIThread([url = QtUtils::StringViewToQString(url)]() { QtUtils::OpenURL(g_main_window, QUrl(url)); }); + Host::RunOnUIThread([url = QtUtils::StringViewToQString(url)]() { QtUtils::OpenURL(g_main_window, QUrl(url)); }); } std::string Host::GetClipboardText() { // Hope this doesn't deadlock... std::string ret; - QtHost::RunOnUIThread( + Host::RunOnUIThread( [&ret]() { QClipboard* clipboard = QGuiApplication::clipboard(); if (clipboard) @@ -2155,7 +2154,7 @@ std::string Host::GetClipboardText() bool Host::CopyTextToClipboard(std::string_view text) { - QtHost::RunOnUIThread([text = QtUtils::StringViewToQString(text)]() { + Host::RunOnUIThread([text = QtUtils::StringViewToQString(text)]() { QClipboard* clipboard = QGuiApplication::clipboard(); if (clipboard) clipboard->setText(text); @@ -2543,7 +2542,7 @@ void QtHost::QueueSettingsSave() { if (!QThread::isMainThread()) { - QtHost::RunOnUIThread(QueueSettingsSave); + Host::RunOnUIThread(QueueSettingsSave); return; } diff --git a/src/duckstation-qt/qthost.h b/src/duckstation-qt/qthost.h index 9c343a887..713a9905e 100644 --- a/src/duckstation-qt/qthost.h +++ b/src/duckstation-qt/qthost.h @@ -225,7 +225,7 @@ private Q_SLOTS: void onDisplayWindowKeyEvent(int key, bool pressed); void onDisplayWindowTextEntered(const QString& text); void doBackgroundControllerPoll(); - void runOnEmuThread(std::function callback); + void runOnEmuThread(const std::function& callback); void processAuxiliaryRenderWindowInputEvent(void* userdata, quint32 event, quint32 param1, quint32 param2, quint32 param3); @@ -358,9 +358,6 @@ bool IsRunningOnWayland(); /// Returns true if rendering to the main window should be allowed. bool CanRenderToMainWindow(); -/// Executes a function on the UI thread. -void RunOnUIThread(const std::function& func, bool block = false); - /// Default language for the platform. const char* GetDefaultLanguage(); diff --git a/src/duckstation-qt/qttranslations.cpp b/src/duckstation-qt/qttranslations.cpp index 34694c36a..245cd1479 100644 --- a/src/duckstation-qt/qttranslations.cpp +++ b/src/duckstation-qt/qttranslations.cpp @@ -225,7 +225,7 @@ std::span> Host::GetAvailableLanguageL bool Host::ChangeLanguage(const char* new_language) { - QtHost::RunOnUIThread([new_language = std::string(new_language)]() { + Host::RunOnUIThread([new_language = std::string(new_language)]() { Host::SetBaseStringSettingValue("Main", "Language", new_language.c_str()); Host::CommitBaseSettingChanges(); QtHost::UpdateApplicationLanguage(g_main_window); @@ -283,8 +283,7 @@ void QtHost::UpdateGlyphRangesAndClearCache(QWidget* dialog_parent, std::string_ // If we don't have any specific glyph range, assume Central European, except if English, then keep the size down. if ((!gi || !gi->used_glyphs) && language != "en") { - glyph_ranges.insert(glyph_ranges.end(), std::begin(s_central_european_ranges), - std::end(s_central_european_ranges)); + glyph_ranges.insert(glyph_ranges.end(), std::begin(s_central_european_ranges), std::end(s_central_european_ranges)); } // List terminator. diff --git a/src/duckstation-regtest/regtest_host.cpp b/src/duckstation-regtest/regtest_host.cpp index dc03b1cef..f42cf8fbb 100644 --- a/src/duckstation-regtest/regtest_host.cpp +++ b/src/duckstation-regtest/regtest_host.cpp @@ -381,6 +381,11 @@ void RegTestHost::ProcessCPUThreadEvents() } } +void Host::RunOnUIThread(std::function function, bool block /* = false */) +{ + RunOnCPUThread(std::move(function), block); +} + void Host::RequestResizeHostDisplay(s32 width, s32 height) { //