mirror of https://github.com/PCSX2/pcsx2.git
Qt: Rework VM pausing when popup dialogs are opened
Also cleans up fullscreen transitions further.
This commit is contained in:
parent
ec0e9f078c
commit
90707c453d
|
@ -136,7 +136,7 @@ void EmuThread::setVMPaused(bool paused)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we were surfaceless (view->game list, system->unpause), get our display widget back
|
// if we were surfaceless (view->game list, system->unpause), get our display widget back
|
||||||
if (m_is_surfaceless)
|
if (!paused && m_is_surfaceless)
|
||||||
setSurfaceless(false);
|
setSurfaceless(false);
|
||||||
|
|
||||||
VMManager::SetPaused(paused);
|
VMManager::SetPaused(paused);
|
||||||
|
@ -872,23 +872,6 @@ void Host::RunOnCPUThread(std::function<void()> function, bool block /* = false
|
||||||
Q_ARG(const std::function<void()>&, std::move(function)));
|
Q_ARG(const std::function<void()>&, std::move(function)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedVMPause::ScopedVMPause(bool was_paused, bool was_fullscreen)
|
|
||||||
: m_was_paused(was_paused), m_was_fullscreen(was_fullscreen)
|
|
||||||
{
|
|
||||||
if (was_fullscreen)
|
|
||||||
g_emu_thread->setFullscreen(false);
|
|
||||||
if (!m_was_paused)
|
|
||||||
g_emu_thread->setVMPaused(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedVMPause::~ScopedVMPause()
|
|
||||||
{
|
|
||||||
if (!m_was_paused)
|
|
||||||
g_emu_thread->setVMPaused(false);
|
|
||||||
if (m_was_fullscreen)
|
|
||||||
g_emu_thread->setFullscreen(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
alignas(16) static SysMtgsThread s_mtgs_thread;
|
alignas(16) static SysMtgsThread s_mtgs_thread;
|
||||||
|
|
||||||
SysMtgsThread& GetMTGS()
|
SysMtgsThread& GetMTGS()
|
||||||
|
|
|
@ -167,21 +167,4 @@ private:
|
||||||
int m_last_internal_height = 0;
|
int m_last_internal_height = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Helper class to pause/unpause the emulation thread.
|
|
||||||
/// </summary>
|
|
||||||
class ScopedVMPause
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ScopedVMPause(bool was_paused, bool was_fullscreen);
|
|
||||||
~ScopedVMPause();
|
|
||||||
|
|
||||||
__fi bool WasPaused() const { return m_was_paused; }
|
|
||||||
__fi bool WasFullscreen() const { return m_was_fullscreen; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool m_was_paused;
|
|
||||||
bool m_was_fullscreen;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern EmuThread* g_emu_thread;
|
extern EmuThread* g_emu_thread;
|
||||||
|
|
|
@ -740,8 +740,8 @@ bool MainWindow::requestShutdown(bool allow_confirm /* = true */, bool allow_sav
|
||||||
// only confirm on UI thread because we need to display a msgbox
|
// only confirm on UI thread because we need to display a msgbox
|
||||||
if (allow_confirm && !GSDumpReplayer::IsReplayingDump() && QtHost::GetBaseBoolSettingValue("UI", "ConfirmShutdown", true))
|
if (allow_confirm && !GSDumpReplayer::IsReplayingDump() && QtHost::GetBaseBoolSettingValue("UI", "ConfirmShutdown", true))
|
||||||
{
|
{
|
||||||
ScopedVMPause pauser(m_vm_paused, isRenderingFullscreen());
|
VMLock lock(pauseAndLockVM());
|
||||||
if (QMessageBox::question(m_display_widget, tr("Confirm Shutdown"),
|
if (QMessageBox::question(lock.getDialogParent(), tr("Confirm Shutdown"),
|
||||||
tr("Are you sure you want to shut down the virtual machine?\n\nAll unsaved progress will be lost.")) != QMessageBox::Yes)
|
tr("Are you sure you want to shut down the virtual machine?\n\nAll unsaved progress will be lost.")) != QMessageBox::Yes)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -911,9 +911,8 @@ void MainWindow::onStartBIOSActionTriggered()
|
||||||
|
|
||||||
void MainWindow::onChangeDiscFromFileActionTriggered()
|
void MainWindow::onChangeDiscFromFileActionTriggered()
|
||||||
{
|
{
|
||||||
ScopedVMPause pauser(m_vm_paused, isRenderingFullscreen());
|
VMLock lock(pauseAndLockVM());
|
||||||
|
QString filename = QFileDialog::getOpenFileName(lock.getDialogParent(), tr("Select Disc Image"), QString(), tr(DISC_IMAGE_FILTER), nullptr);
|
||||||
QString filename = QFileDialog::getOpenFileName(this, tr("Select Disc Image"), QString(), tr(DISC_IMAGE_FILTER), nullptr);
|
|
||||||
if (filename.isEmpty())
|
if (filename.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1102,6 +1101,8 @@ void MainWindow::onVMResumed()
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
updateStatusBarWidgetVisibility();
|
updateStatusBarWidgetVisibility();
|
||||||
m_status_fps_widget->setText(m_last_fps_status);
|
m_status_fps_widget->setText(m_last_fps_status);
|
||||||
|
if (m_display_widget)
|
||||||
|
m_display_widget->setFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onVMStopped()
|
void MainWindow::onVMStopped()
|
||||||
|
@ -1227,12 +1228,13 @@ DisplayWidget* MainWindow::createDisplay(bool fullscreen, bool render_to_main)
|
||||||
|
|
||||||
DisplayWidget* MainWindow::updateDisplay(bool fullscreen, bool render_to_main, bool surfaceless)
|
DisplayWidget* MainWindow::updateDisplay(bool fullscreen, bool render_to_main, bool surfaceless)
|
||||||
{
|
{
|
||||||
DevCon.WriteLn("updateDisplay(%u, %u, %u)", static_cast<u32>(fullscreen), static_cast<u32>(render_to_main), static_cast<u32>(surfaceless));
|
DevCon.WriteLn("updateDisplay() fullscreen=%s render_to_main=%s surfaceless=%s",
|
||||||
|
fullscreen ? "true" : "false", render_to_main ? "true" : "false", surfaceless ? "true" : "false");
|
||||||
|
|
||||||
HostDisplay* host_display = Host::GetHostDisplay();
|
HostDisplay* host_display = Host::GetHostDisplay();
|
||||||
QWidget* container = m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget);
|
QWidget* container = m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget);
|
||||||
const bool is_fullscreen = (container && container->isFullScreen());
|
const bool is_fullscreen = isRenderingFullscreen();
|
||||||
const bool is_rendering_to_main = (!is_fullscreen && container && container->parent());
|
const bool is_rendering_to_main = isRenderingToMain();
|
||||||
const std::string fullscreen_mode(QtHost::GetBaseStringSettingValue("EmuCore/GS", "FullscreenMode", ""));
|
const std::string fullscreen_mode(QtHost::GetBaseStringSettingValue("EmuCore/GS", "FullscreenMode", ""));
|
||||||
const bool is_exclusive_fullscreen = (fullscreen && !fullscreen_mode.empty() && host_display->SupportsFullscreen());
|
const bool is_exclusive_fullscreen = (fullscreen && !fullscreen_mode.empty() && host_display->SupportsFullscreen());
|
||||||
const bool changing_surfaceless = (!m_display_widget != surfaceless);
|
const bool changing_surfaceless = (!m_display_widget != surfaceless);
|
||||||
|
@ -1245,10 +1247,14 @@ DisplayWidget* MainWindow::updateDisplay(bool fullscreen, bool render_to_main, b
|
||||||
const bool needs_container = DisplayContainer::IsNeeded(fullscreen, render_to_main);
|
const bool needs_container = DisplayContainer::IsNeeded(fullscreen, render_to_main);
|
||||||
if (!is_rendering_to_main && !render_to_main && !is_exclusive_fullscreen && has_container == needs_container && !needs_container && !changing_surfaceless)
|
if (!is_rendering_to_main && !render_to_main && !is_exclusive_fullscreen && has_container == needs_container && !needs_container && !changing_surfaceless)
|
||||||
{
|
{
|
||||||
Console.WriteLn("Toggling to %s without recreating surface", (fullscreen ? "fullscreen" : "windowed"));
|
DevCon.WriteLn("Toggling to %s without recreating surface", (fullscreen ? "fullscreen" : "windowed"));
|
||||||
if (host_display->IsFullscreen())
|
if (host_display->IsFullscreen())
|
||||||
host_display->SetFullscreen(false, 0, 0, 0.0f);
|
host_display->SetFullscreen(false, 0, 0, 0.0f);
|
||||||
|
|
||||||
|
// since we don't destroy the display widget, we need to save it here
|
||||||
|
if (!is_fullscreen && !is_rendering_to_main)
|
||||||
|
saveDisplayWindowGeometryToConfig();
|
||||||
|
|
||||||
if (fullscreen)
|
if (fullscreen)
|
||||||
{
|
{
|
||||||
container->showFullScreen();
|
container->showFullScreen();
|
||||||
|
@ -1395,6 +1401,13 @@ QWidget* MainWindow::getDisplayContainer() const
|
||||||
|
|
||||||
void MainWindow::saveDisplayWindowGeometryToConfig()
|
void MainWindow::saveDisplayWindowGeometryToConfig()
|
||||||
{
|
{
|
||||||
|
QWidget* container = getDisplayContainer();
|
||||||
|
if (container->windowState() & Qt::WindowFullScreen)
|
||||||
|
{
|
||||||
|
// if we somehow ended up here, don't save the fullscreen state to the config
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const QByteArray geometry = getDisplayContainer()->saveGeometry();
|
const QByteArray geometry = getDisplayContainer()->saveGeometry();
|
||||||
const QByteArray geometry_b64 = geometry.toBase64();
|
const QByteArray geometry_b64 = geometry.toBase64();
|
||||||
const std::string old_geometry_b64 = QtHost::GetBaseStringSettingValue("UI", "DisplayWindowGeometry");
|
const std::string old_geometry_b64 = QtHost::GetBaseStringSettingValue("UI", "DisplayWindowGeometry");
|
||||||
|
@ -1408,9 +1421,17 @@ void MainWindow::restoreDisplayWindowGeometryFromConfig()
|
||||||
const QByteArray geometry = QByteArray::fromBase64(QByteArray::fromStdString(geometry_b64));
|
const QByteArray geometry = QByteArray::fromBase64(QByteArray::fromStdString(geometry_b64));
|
||||||
QWidget* container = getDisplayContainer();
|
QWidget* container = getDisplayContainer();
|
||||||
if (!geometry.isEmpty())
|
if (!geometry.isEmpty())
|
||||||
|
{
|
||||||
container->restoreGeometry(geometry);
|
container->restoreGeometry(geometry);
|
||||||
|
|
||||||
|
// make sure we're not loading a dodgy config which had fullscreen set...
|
||||||
|
container->setWindowState(container->windowState() & ~(Qt::WindowFullScreen | Qt::WindowActive));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
// default size
|
||||||
container->resize(640, 480);
|
container->resize(640, 480);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::destroyDisplayWidget()
|
void MainWindow::destroyDisplayWidget()
|
||||||
|
@ -1418,8 +1439,7 @@ void MainWindow::destroyDisplayWidget()
|
||||||
if (!m_display_widget)
|
if (!m_display_widget)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const QWidget* container = m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget);
|
if (!isRenderingFullscreen() && !isRenderingToMain())
|
||||||
if (!container->parent() && !container->isFullScreen())
|
|
||||||
saveDisplayWindowGeometryToConfig();
|
saveDisplayWindowGeometryToConfig();
|
||||||
|
|
||||||
if (m_display_container)
|
if (m_display_container)
|
||||||
|
@ -1720,3 +1740,48 @@ void MainWindow::doDiscChange(const QString& path)
|
||||||
if (reset_system)
|
if (reset_system)
|
||||||
g_emu_thread->resetVM();
|
g_emu_thread->resetVM();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MainWindow::VMLock MainWindow::pauseAndLockVM()
|
||||||
|
{
|
||||||
|
const bool was_fullscreen = isRenderingFullscreen();
|
||||||
|
const bool was_paused = m_vm_paused;
|
||||||
|
|
||||||
|
// We use surfaceless rather than switching out of fullscreen, because
|
||||||
|
// we're paused, so we're not going to be rendering anyway.
|
||||||
|
if (was_fullscreen)
|
||||||
|
g_emu_thread->setSurfaceless(true);
|
||||||
|
if (!was_paused)
|
||||||
|
g_emu_thread->setVMPaused(true);
|
||||||
|
|
||||||
|
// We want to parent dialogs to the display widget, except if we were fullscreen,
|
||||||
|
// since it's going to get destroyed by the surfaceless call above.
|
||||||
|
QWidget* dialog_parent = was_fullscreen ? static_cast<QWidget*>(this) : getDisplayContainer();
|
||||||
|
|
||||||
|
return VMLock(dialog_parent, was_paused, was_fullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::VMLock::VMLock(QWidget* dialog_parent, bool was_paused, bool was_fullscreen)
|
||||||
|
: m_dialog_parent(dialog_parent)
|
||||||
|
, m_was_paused(was_paused)
|
||||||
|
, m_was_fullscreen(was_fullscreen)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::VMLock::VMLock(VMLock&& lock)
|
||||||
|
: m_dialog_parent(lock.m_dialog_parent)
|
||||||
|
, m_was_paused(lock.m_was_paused)
|
||||||
|
, m_was_fullscreen(lock.m_was_fullscreen)
|
||||||
|
{
|
||||||
|
lock.m_dialog_parent = nullptr;
|
||||||
|
lock.m_was_paused = false;
|
||||||
|
lock.m_was_fullscreen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::VMLock::~VMLock()
|
||||||
|
{
|
||||||
|
if (m_was_fullscreen)
|
||||||
|
g_emu_thread->setSurfaceless(false);
|
||||||
|
if (!m_was_paused)
|
||||||
|
g_emu_thread->setVMPaused(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,33 @@ class MainWindow final : public QMainWindow
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/// This class is a scoped lock on the VM, which prevents it from running while
|
||||||
|
/// the object exists. Its purpose is to be used for blocking/modal popup boxes,
|
||||||
|
/// where the VM needs to exit fullscreen temporarily.
|
||||||
|
class VMLock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VMLock(VMLock&& lock);
|
||||||
|
VMLock(const VMLock&) = delete;
|
||||||
|
~VMLock();
|
||||||
|
|
||||||
|
/// Returns the parent widget, which can be used for any popup dialogs.
|
||||||
|
__fi QWidget* getDialogParent() const { return m_dialog_parent; }
|
||||||
|
|
||||||
|
/// Cancels any pending unpause/fullscreen transition.
|
||||||
|
/// Call when you're going to destroy the VM anyway.
|
||||||
|
void cancelResume();
|
||||||
|
|
||||||
|
private:
|
||||||
|
VMLock(QWidget* dialog_parent, bool was_paused, bool was_fullscreen);
|
||||||
|
friend MainWindow;
|
||||||
|
|
||||||
|
QWidget* m_dialog_parent;
|
||||||
|
bool m_was_paused;
|
||||||
|
bool m_was_fullscreen;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Default theme name for the platform.
|
||||||
static const char* DEFAULT_THEME_NAME;
|
static const char* DEFAULT_THEME_NAME;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -52,6 +79,9 @@ public:
|
||||||
void initialize();
|
void initialize();
|
||||||
void connectVMThreadSignals(EmuThread* thread);
|
void connectVMThreadSignals(EmuThread* thread);
|
||||||
|
|
||||||
|
/// Locks the VM by pausing it, while a popup dialog is displayed.
|
||||||
|
VMLock pauseAndLockVM();
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void refreshGameList(bool invalidate_cache);
|
void refreshGameList(bool invalidate_cache);
|
||||||
void invalidateSaveStateCache();
|
void invalidateSaveStateCache();
|
||||||
|
|
Loading…
Reference in New Issue