Host: Expose RunOnUIThread() to core

I hate this, but sadly needed for RAIntegration...
This commit is contained in:
Stenzek 2025-04-11 21:41:13 +10:00
parent f0d4816de7
commit c1e01af511
No known key found for this signature in database
8 changed files with 51 additions and 48 deletions

View File

@ -78,6 +78,9 @@ bool ChangeLanguage(const char* new_language);
/// Safely executes a function on the VM thread. /// Safely executes a function on the VM thread.
void RunOnCPUThread(std::function<void()> function, bool block = false); void RunOnCPUThread(std::function<void()> function, bool block = false);
/// Safely executes a function on the main/UI thread.
void RunOnUIThread(std::function<void()> function, bool block = false);
/// Called when the core is creating a render device. /// Called when the core is creating a render device.
/// This could also be fullscreen transition. /// This could also be fullscreen transition.
std::optional<WindowInfo> AcquireRenderWindow(RenderAPI render_api, bool fullscreen, bool exclusive_fullscreen, std::optional<WindowInfo> AcquireRenderWindow(RenderAPI render_api, bool fullscreen, bool exclusive_fullscreen,

View File

@ -77,11 +77,9 @@ static bool SetDataDirectory();
static bool SetCriticalFolders(); static bool SetCriticalFolders();
static void SetDefaultSettings(SettingsInterface& si, bool system, bool controller); static void SetDefaultSettings(SettingsInterface& si, bool system, bool controller);
static std::string GetResourcePath(std::string_view name, bool allow_override); static std::string GetResourcePath(std::string_view name, bool allow_override);
static void ProcessCPUThreadEvents(bool block);
static bool PerformEarlyHardwareChecks(); static bool PerformEarlyHardwareChecks();
static bool EarlyProcessStartup(); static bool EarlyProcessStartup();
static void WarnAboutInterface(); static void WarnAboutInterface();
static void RunOnUIThread(std::function<void()> func);
static void StartCPUThread(); static void StartCPUThread();
static void StopCPUThread(); static void StopCPUThread();
static void ProcessCPUThreadEvents(bool block); static void ProcessCPUThreadEvents(bool block);
@ -580,7 +578,7 @@ std::optional<WindowInfo> Host::AcquireRenderWindow(RenderAPI render_api, bool f
std::optional<WindowInfo> wi; std::optional<WindowInfo> wi;
MiniHost::RunOnUIThread([render_api, fullscreen, error, &wi]() { Host::RunOnUIThread([render_api, fullscreen, error, &wi]() {
const std::string window_title = GetWindowTitle(System::GetGameTitle()); const std::string window_title = GetWindowTitle(System::GetGameTitle());
const SDL_PropertiesID props = SDL_CreateProperties(); const SDL_PropertiesID props = SDL_CreateProperties();
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, window_title.c_str()); 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) if (!s_state.sdl_window)
return; return;
MiniHost::RunOnUIThread([]() { Host::RunOnUIThread([]() {
if (!s_state.fullscreen.load(std::memory_order_acquire)) if (!s_state.fullscreen.load(std::memory_order_acquire))
{ {
int window_x = SDL_WINDOWPOS_UNDEFINED, window_y = SDL_WINDOWPOS_UNDEFINED; 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<void()> func)
{
std::function<void()>* pfunc = new std::function<void()>(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() void MiniHost::ProcessCPUThreadPlatformMessages()
{ {
// This is lame. On Win32, we need to pump messages, even though *we* don't have any windows // 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(); System::CPUThreadShutdown();
// Tell the UI thread to shut down. // 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() void MiniHost::CPUThreadMainLoop()
@ -1211,6 +1198,19 @@ void Host::RunOnCPUThread(std::function<void()> function, bool block /* = false
s_state.cpu_thread_event_done.wait(lock, []() { return s_state.blocking_cpu_events_pending == 0; }); s_state.cpu_thread_event_done.wait(lock, []() { return s_state.blocking_cpu_events_pending == 0; });
} }
void Host::RunOnUIThread(std::function<void()> function, bool block /* = false */)
{
using namespace MiniHost;
std::function<void()>* pfunc = new std::function<void()>(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) void Host::RefreshGameListAsync(bool invalidate_cache)
{ {
using namespace MiniHost; using namespace MiniHost;

View File

@ -661,7 +661,7 @@ void DebuggerWindow::toggleBreakpoint(VirtualMemoryAddress address)
return; 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); m_code_model->setBreakpointState(address, new_bp_state);
refreshBreakpointList(bps); refreshBreakpointList(bps);
}); });
@ -715,7 +715,7 @@ bool DebuggerWindow::scrollToMemoryAddress(VirtualMemoryAddress address)
void DebuggerWindow::refreshBreakpointList() void DebuggerWindow::refreshBreakpointList()
{ {
Host::RunOnCPUThread( 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) void DebuggerWindow::refreshBreakpointList(const CPU::BreakpointList& bps)
@ -743,7 +743,7 @@ void DebuggerWindow::addBreakpoint(CPU::BreakpointType type, u32 address)
{ {
Host::RunOnCPUThread([this, address, type]() { Host::RunOnCPUThread([this, address, type]() {
const bool result = CPU::AddBreakpoint(type, address); 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) if (!result)
{ {
QMessageBox::critical(this, windowTitle(), QMessageBox::critical(this, windowTitle(),
@ -763,7 +763,7 @@ void DebuggerWindow::removeBreakpoint(CPU::BreakpointType type, u32 address)
{ {
Host::RunOnCPUThread([this, address, type]() { Host::RunOnCPUThread([this, address, type]() {
const bool result = CPU::RemoveBreakpoint(type, address); 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) if (!result)
{ {
QMessageBox::critical(this, windowTitle(), tr("Failed to remove breakpoint. This breakpoint may not exist.")); QMessageBox::critical(this, windowTitle(), tr("Failed to remove breakpoint. This breakpoint may not exist."));

View File

@ -1037,7 +1037,7 @@ void MainWindow::populateCheatsMenu(QMenu* menu)
if (Cheats::AreCheatsEnabled() && names.empty()) if (Cheats::AreCheatsEnabled() && names.empty())
return; return;
QtHost::RunOnUIThread([menu, names = std::move(names)]() { Host::RunOnUIThread([menu, names = std::move(names)]() {
if (names.empty()) if (names.empty())
{ {
QAction* action = menu->addAction(tr("Cheats are not enabled.")); QAction* action = menu->addAction(tr("Cheats are not enabled."));
@ -2368,7 +2368,7 @@ void MainWindow::openGamePropertiesForCurrentGame(const char* category /* = null
if (path.empty() || serial.empty()) if (path.empty() || serial.empty())
return; return;
QtHost::RunOnUIThread([title = std::string(System::GetGameTitle()), path = std::string(path), Host::RunOnUIThread([title = std::string(System::GetGameTitle()), path = std::string(path),
serial = std::string(serial), hash = System::GetGameHash(), region = System::GetDiscRegion(), serial = std::string(serial), hash = System::GetGameHash(), region = System::GetDiscRegion(),
category]() { category]() {
SettingsWindow::openGamePropertiesDialog(path, title, std::move(serial), hash, region, category); SettingsWindow::openGamePropertiesDialog(path, title, std::move(serial), hash, region, category);

View File

@ -1149,7 +1149,7 @@ void EmuThread::confirmActionIfMemoryCardBusy(const QString& action, bool cancel
return; 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(); auto lock = g_main_window->pauseAndLockSystem();
const bool result = const bool result =
@ -1378,7 +1378,7 @@ void EmuThread::startControllerTest()
return; return;
} }
QtHost::RunOnUIThread([path = std::move(path)]() mutable { Host::RunOnUIThread([path = std::move(path)]() mutable {
{ {
auto lock = g_main_window->pauseAndLockSystem(); auto lock = g_main_window->pauseAndLockSystem();
if (QMessageBox::question( if (QMessageBox::question(
@ -1397,7 +1397,7 @@ void EmuThread::startControllerTest()
}); });
} }
void EmuThread::runOnEmuThread(std::function<void()> callback) void EmuThread::runOnEmuThread(const std::function<void()>& callback)
{ {
callback(); callback();
} }
@ -1411,11 +1411,11 @@ void Host::RunOnCPUThread(std::function<void()> function, bool block /* = false
Q_ARG(std::function<void()>, std::move(function))); Q_ARG(std::function<void()>, std::move(function)));
} }
void QtHost::RunOnUIThread(const std::function<void()>& func, bool block /*= false*/) void Host::RunOnUIThread(std::function<void()> function, bool block /* = false*/)
{ {
// main window always exists, so it's fine to attach it to that. // main window always exists, so it's fine to attach it to that.
QMetaObject::invokeMethod(g_main_window, "runOnUIThread", block ? Qt::BlockingQueuedConnection : Qt::QueuedConnection, QMetaObject::invokeMethod(g_main_window, "runOnUIThread", block ? Qt::BlockingQueuedConnection : Qt::QueuedConnection,
Q_ARG(const std::function<void()>&, func)); Q_ARG(std::function<void()>, std::move(function)));
} }
QtAsyncTask::QtAsyncTask(WorkCallback callback) QtAsyncTask::QtAsyncTask(WorkCallback callback)
@ -1432,7 +1432,7 @@ void QtAsyncTask::create(QObject* owner, WorkCallback callback)
connect(task, &QtAsyncTask::completed, owner, [task]() { std::get<CompletionCallback>(task->m_callback)(); }); connect(task, &QtAsyncTask::completed, owner, [task]() { std::get<CompletionCallback>(task->m_callback)(); });
System::QueueAsyncTask([task]() { System::QueueAsyncTask([task]() {
task->m_callback = std::get<WorkCallback>(task->m_callback)(); task->m_callback = std::get<WorkCallback>(task->m_callback)();
QtHost::RunOnUIThread([task]() { Host::RunOnUIThread([task]() {
emit task->completed(task); emit task->completed(task);
delete 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), Host::RunOnUIThread([title = QtUtils::StringViewToQString(title), select_directory, callback = std::move(callback),
filters_str = std::move(filters_str), filters_str = std::move(filters_str),
initial_directory = QtUtils::StringViewToQString(initial_directory), initial_directory = QtUtils::StringViewToQString(initial_directory), from_cpu_thread]() mutable {
from_cpu_thread]() mutable {
auto lock = g_main_window->pauseAndLockSystem(); auto lock = g_main_window->pauseAndLockSystem();
QString path; QString path;
@ -2103,7 +2102,7 @@ void Host::ConfirmMessageAsync(std::string_view title, std::string_view message,
else else
{ {
// Otherwise, use the desktop UI. // Otherwise, use the desktop UI.
QtHost::RunOnUIThread([title = QtUtils::StringViewToQString(title), message = QtUtils::StringViewToQString(message), Host::RunOnUIThread([title = QtUtils::StringViewToQString(title), message = QtUtils::StringViewToQString(message),
callback = std::move(callback), yes_text = QtUtils::StringViewToQString(yes_text), callback = std::move(callback), yes_text = QtUtils::StringViewToQString(yes_text),
no_text = QtUtils::StringViewToQString(no_text), needs_pause]() mutable { no_text = QtUtils::StringViewToQString(no_text), needs_pause]() mutable {
auto lock = g_main_window->pauseAndLockSystem(); auto lock = g_main_window->pauseAndLockSystem();
@ -2136,14 +2135,14 @@ void Host::ConfirmMessageAsync(std::string_view title, std::string_view message,
void Host::OpenURL(std::string_view url) 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() std::string Host::GetClipboardText()
{ {
// Hope this doesn't deadlock... // Hope this doesn't deadlock...
std::string ret; std::string ret;
QtHost::RunOnUIThread( Host::RunOnUIThread(
[&ret]() { [&ret]() {
QClipboard* clipboard = QGuiApplication::clipboard(); QClipboard* clipboard = QGuiApplication::clipboard();
if (clipboard) if (clipboard)
@ -2155,7 +2154,7 @@ std::string Host::GetClipboardText()
bool Host::CopyTextToClipboard(std::string_view text) bool Host::CopyTextToClipboard(std::string_view text)
{ {
QtHost::RunOnUIThread([text = QtUtils::StringViewToQString(text)]() { Host::RunOnUIThread([text = QtUtils::StringViewToQString(text)]() {
QClipboard* clipboard = QGuiApplication::clipboard(); QClipboard* clipboard = QGuiApplication::clipboard();
if (clipboard) if (clipboard)
clipboard->setText(text); clipboard->setText(text);
@ -2543,7 +2542,7 @@ void QtHost::QueueSettingsSave()
{ {
if (!QThread::isMainThread()) if (!QThread::isMainThread())
{ {
QtHost::RunOnUIThread(QueueSettingsSave); Host::RunOnUIThread(QueueSettingsSave);
return; return;
} }

View File

@ -225,7 +225,7 @@ private Q_SLOTS:
void onDisplayWindowKeyEvent(int key, bool pressed); void onDisplayWindowKeyEvent(int key, bool pressed);
void onDisplayWindowTextEntered(const QString& text); void onDisplayWindowTextEntered(const QString& text);
void doBackgroundControllerPoll(); void doBackgroundControllerPoll();
void runOnEmuThread(std::function<void()> callback); void runOnEmuThread(const std::function<void()>& callback);
void processAuxiliaryRenderWindowInputEvent(void* userdata, quint32 event, quint32 param1, quint32 param2, void processAuxiliaryRenderWindowInputEvent(void* userdata, quint32 event, quint32 param1, quint32 param2,
quint32 param3); quint32 param3);
@ -358,9 +358,6 @@ bool IsRunningOnWayland();
/// Returns true if rendering to the main window should be allowed. /// Returns true if rendering to the main window should be allowed.
bool CanRenderToMainWindow(); bool CanRenderToMainWindow();
/// Executes a function on the UI thread.
void RunOnUIThread(const std::function<void()>& func, bool block = false);
/// Default language for the platform. /// Default language for the platform.
const char* GetDefaultLanguage(); const char* GetDefaultLanguage();

View File

@ -225,7 +225,7 @@ std::span<const std::pair<const char*, const char*>> Host::GetAvailableLanguageL
bool Host::ChangeLanguage(const char* new_language) 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::SetBaseStringSettingValue("Main", "Language", new_language.c_str());
Host::CommitBaseSettingChanges(); Host::CommitBaseSettingChanges();
QtHost::UpdateApplicationLanguage(g_main_window); 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 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") if ((!gi || !gi->used_glyphs) && language != "en")
{ {
glyph_ranges.insert(glyph_ranges.end(), std::begin(s_central_european_ranges), glyph_ranges.insert(glyph_ranges.end(), std::begin(s_central_european_ranges), std::end(s_central_european_ranges));
std::end(s_central_european_ranges));
} }
// List terminator. // List terminator.

View File

@ -381,6 +381,11 @@ void RegTestHost::ProcessCPUThreadEvents()
} }
} }
void Host::RunOnUIThread(std::function<void()> function, bool block /* = false */)
{
RunOnCPUThread(std::move(function), block);
}
void Host::RequestResizeHostDisplay(s32 width, s32 height) void Host::RequestResizeHostDisplay(s32 width, s32 height)
{ {
// //