This commit is contained in:
Jordan Woyak 2025-08-12 11:21:46 +00:00 committed by GitHub
commit 351b02bed8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 130 additions and 95 deletions

View file

@ -12,7 +12,6 @@
namespace ControllerEmu
{
class ControllerEmu;
class Buttons;
} // namespace ControllerEmu

View file

@ -206,7 +206,6 @@ void GeneralWidget::BackendWarning()
void GeneralWidget::OnEmulationStateChanged(bool running)
{
m_backend_combo->setEnabled(!running);
m_render_main_window->setEnabled(!running);
m_enable_fullscreen->setEnabled(!running);
const bool supports_adapters = !g_backend_info.Adapters.empty();

View file

@ -44,7 +44,6 @@
#include "Core/BootManager.h"
#include "Core/CommonTitles.h"
#include "Core/Config/AchievementSettings.h"
#include "Core/Config/FreeLookSettings.h"
#include "Core/Config/MainSettings.h"
#include "Core/Config/NetplaySettings.h"
#include "Core/Config/UISettings.h"
@ -58,10 +57,8 @@
#include "Core/HW/ProcessorInterface.h"
#include "Core/HW/SI/SI_Device.h"
#include "Core/HW/Wiimote.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
#include "Core/HotkeyManager.h"
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
#include "Core/IOS/USB/Bluetooth/WiimoteDevice.h"
#include "Core/Movie.h"
#include "Core/NetPlayClient.h"
#include "Core/NetPlayProto.h"
@ -126,18 +123,15 @@
#include "DolphinQt/WiiUpdate.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"
#include "InputCommon/GCAdapter.h"
#include "UICommon/DiscordPresence.h"
#include "UICommon/GameFile.h"
#include "UICommon/ResourcePack/Manager.h"
#include "UICommon/ResourcePack/Manifest.h"
#include "UICommon/ResourcePack/ResourcePack.h"
#include "UICommon/UICommon.h"
#include "VideoCommon/NetPlayChatUI.h"
#include "VideoCommon/VideoConfig.h"
#ifdef HAVE_XRANDR
#include "UICommon/X11Utils.h"
@ -435,7 +429,6 @@ void MainWindow::InitCoreCallbacks()
}
});
installEventFilter(this);
m_render_widget->installEventFilter(this);
// Handle file open events
auto* filter = new FileOpenEventFilter(QGuiApplication::instance());
@ -726,9 +719,6 @@ void MainWindow::ConnectGameList()
void MainWindow::ConnectRenderWidget()
{
m_rendering_to_main = false;
m_render_widget->hide();
connect(m_render_widget, &RenderWidget::Closed, this, &MainWindow::ForceStop);
connect(m_render_widget, &RenderWidget::FocusChanged, this, [this](bool focus) {
if (m_render_widget->isFullScreen())
SetFullScreenResolution(focus);
@ -893,7 +883,10 @@ void MainWindow::TogglePause()
void MainWindow::OnStopComplete()
{
m_stop_requested = false;
HideRenderWidget(!m_exit_requested, m_exit_requested);
HideRenderWidget();
if (!m_exit_requested)
RecreateRenderWidget();
#ifdef USE_DISCORD_PRESENCE
if (!m_netplay_dialog->isVisible())
Discord::UpdateDiscordPresence();
@ -932,15 +925,11 @@ bool MainWindow::RequestStop()
const bool rendered_widget_was_active =
Settings::Instance().IsKeepWindowOnTopEnabled() ||
(m_render_widget->isActiveWindow() && !m_render_widget->isFullScreen());
QWidget* confirm_parent = (!m_rendering_to_main && rendered_widget_was_active) ?
m_render_widget :
static_cast<QWidget*>(this);
const bool was_cursor_locked = m_render_widget->IsCursorLocked();
if (!m_render_widget->isFullScreen())
m_render_widget_geometry = m_render_widget->saveGeometry();
else
FullScreen();
auto* const confirm_parent =
m_render_window != nullptr ? m_render_window : static_cast<QWidget*>(this);
const bool was_cursor_locked = m_render_widget->IsCursorLocked();
bool confirm_on_stop = Config::Get(Config::MAIN_CONFIRM_ON_STOP);
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
@ -1062,24 +1051,30 @@ void MainWindow::FrameAdvance()
void MainWindow::FullScreen()
{
// If the render widget is fullscreen we want to reset it to whatever is in
// settings. If it's set to be fullscreen then it just remakes the window,
// which probably isn't ideal.
bool was_fullscreen = m_render_widget->isFullScreen();
const bool want_fullscreen = m_render_window == nullptr || !m_render_window->isFullScreen();
if (!was_fullscreen)
m_render_widget_geometry = m_render_widget->saveGeometry();
SetFullScreenResolution(want_fullscreen);
HideRenderWidget(false);
SetFullScreenResolution(!was_fullscreen);
if (was_fullscreen)
if (Config::Get(Config::MAIN_RENDER_TO_MAIN))
{
ShowRenderWidget();
HideRenderWidget();
ShowRenderWidget(!want_fullscreen);
if (want_fullscreen)
m_render_window->showFullScreen();
}
else
{
m_render_widget->showFullScreen();
if (want_fullscreen)
{
m_render_widget_geometry = m_render_window->saveGeometry();
m_render_window->showFullScreen();
}
else
{
m_render_window->showNormal();
m_render_window->restoreGeometry(m_render_widget_geometry);
}
}
}
@ -1159,7 +1154,7 @@ void MainWindow::StartGame(std::unique_ptr<BootParameters>&& parameters)
}
// We need the render widget before booting.
ShowRenderWidget();
ShowRenderWidget(Config::Get(Config::MAIN_RENDER_TO_MAIN));
// Boot up, show an error if it fails to load the game.
if (!BootManager::BootCore(m_system, std::move(parameters),
@ -1167,6 +1162,7 @@ void MainWindow::StartGame(std::unique_ptr<BootParameters>&& parameters)
{
ModalMessageBox::critical(this, tr("Error"), tr("Failed to init core"), QMessageBox::Ok);
HideRenderWidget();
RecreateRenderWidget();
return;
}
@ -1207,17 +1203,14 @@ void MainWindow::SetFullScreenResolution(bool fullscreen)
#endif
}
void MainWindow::ShowRenderWidget()
void MainWindow::ShowRenderWidget(bool render_to_main_window)
{
SetFullScreenResolution(false);
Host::GetInstance()->SetRenderFullscreen(false);
if (Config::Get(Config::MAIN_RENDER_TO_MAIN))
if (render_to_main_window)
{
// If we're rendering to main, add it to the stack and update our title when necessary.
m_rendering_to_main = true;
m_stack->setCurrentIndex(m_stack->addWidget(m_render_widget));
// TODO: fix title when showing render widget while in game.
connect(Host::GetInstance(), &Host::RequestTitle, this, &MainWindow::setWindowTitle);
m_stack->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
m_stack->repaint();
@ -1226,55 +1219,58 @@ void MainWindow::ShowRenderWidget()
}
else
{
// Otherwise, just show it.
m_rendering_to_main = false;
// Embed the render widget in another window.
// For some reason this is needed to observe window focus with Wayland + OpenGL.
m_render_window = new RenderWindow(nullptr, m_render_widget);
m_render_window->installEventFilter(this);
m_render_widget->showNormal();
m_render_widget->restoreGeometry(m_render_widget_geometry);
m_render_window->showNormal();
m_render_window->restoreGeometry(m_render_widget_geometry);
m_render_widget->show();
}
}
void MainWindow::HideRenderWidget(bool reinit, bool is_exit)
void MainWindow::HideRenderWidget()
{
if (m_rendering_to_main)
m_render_widget->hide();
if (m_render_window != nullptr)
{
// Remove the widget from the stack and reparent it to nullptr, so that it can draw
// itself in a new window if it wants. Disconnect the title updates.
if (!m_render_window->isFullScreen())
m_render_widget_geometry = m_render_window->saveGeometry();
}
else
{
// Remove the widget from the stack and disconnect the title updates.
m_stack->removeWidget(m_render_widget);
m_render_widget->setParent(nullptr);
m_rendering_to_main = false;
m_stack->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
disconnect(Host::GetInstance(), &Host::RequestTitle, this, &MainWindow::setWindowTitle);
setWindowTitle(QString::fromStdString(Common::GetScmRevStr()));
}
// Re-parent render widget to nullptr, so that it can draw itself in a new window if it wants.
m_render_widget->setParent(nullptr);
// Delete the render window if it exists.
delete std::exchange(m_render_window, nullptr);
}
void MainWindow::RecreateRenderWidget()
{
// The following code works around a driver bug that would lead to Dolphin crashing when changing
// graphics backends (e.g. OpenGL to Vulkan). To avoid this the render widget is (safely)
// recreated
if (reinit)
{
m_render_widget->hide();
disconnect(m_render_widget, &RenderWidget::Closed, this, &MainWindow::ForceStop);
m_render_widget->deleteLater();
m_render_widget->removeEventFilter(this);
m_render_widget->deleteLater();
m_render_widget = new RenderWidget;
ConnectRenderWidget();
m_render_widget = new RenderWidget;
m_render_widget->installEventFilter(this);
connect(m_render_widget, &RenderWidget::Closed, this, &MainWindow::ForceStop);
connect(m_render_widget, &RenderWidget::FocusChanged, this, [this](bool focus) {
if (m_render_widget->isFullScreen())
SetFullScreenResolution(focus);
});
// The controller interface will still be registered to the old render widget, if the core
// has booted. Therefore, we should re-bind it to the main window for now. When the core
// is next started, it will be swapped back to the new render widget.
g_controller_interface.ChangeWindow(::GetWindowSystemInfo(windowHandle()).render_window,
is_exit ? ControllerInterface::WindowChangeReason::Exit :
ControllerInterface::WindowChangeReason::Other);
}
// The controller interface will still be registered to the old render widget, if the core
// has booted. Therefore, we should re-bind it to the main window for now. When the core
// is next started, it will be swapped back to the new render widget.
g_controller_interface.ChangeWindow(::GetWindowSystemInfo(windowHandle()).render_window,
ControllerInterface::WindowChangeReason::Exit);
}
void MainWindow::ShowControllersWindow()
@ -1550,8 +1546,7 @@ void MainWindow::NetPlayInit()
Discord::InitNetPlayFunctionality(*m_netplay_discord);
m_netplay_discord->Start();
#endif
connect(&Settings::Instance(), &Settings::ConfigChanged, this,
&MainWindow::UpdateScreenSaverInhibition);
connect(&Settings::Instance(), &Settings::ConfigChanged, this, &MainWindow::OnConfigChanged);
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
&MainWindow::UpdateScreenSaverInhibition);
}
@ -1684,6 +1679,24 @@ void MainWindow::NetPlayQuit()
#endif
}
void MainWindow::OnConfigChanged()
{
UpdateScreenSaverInhibition();
if (m_render_widget->isVisible())
{
const bool is_rendering_to_main = m_render_window == nullptr;
const bool render_to_main_window = Config::Get(Config::MAIN_RENDER_TO_MAIN);
if (is_rendering_to_main != render_to_main_window)
{
HideRenderWidget();
ShowRenderWidget(render_to_main_window);
}
}
}
void MainWindow::UpdateScreenSaverInhibition()
{
const bool inhibit = Config::Get(Config::MAIN_DISABLE_SCREENSAVER) &&
@ -1714,7 +1727,8 @@ bool MainWindow::eventFilter(QObject* object, QEvent* event)
QMenu* MainWindow::createPopupMenu()
{
// Disable the default popup menu as it exposes the debugger UI even when the debugger UI is
// disabled, which can lead to user confusion (see e.g. https://bugs.dolphin-emu.org/issues/13306)
// disabled, which can lead to user confusion (see e.g.
// https://bugs.dolphin-emu.org/issues/13306)
return nullptr;
}
@ -2013,8 +2027,9 @@ void MainWindow::OnHardcoreChanged()
bool hardcore_active = AchievementManager::GetInstance().IsHardcoreModeActive();
if (hardcore_active)
Settings::Instance().SetDebugModeEnabled(false);
// EmulationStateChanged causes several dialogs to redraw, including anything affected by hardcore
// mode. Every dialog that depends on hardcore mode is redrawn by EmulationStateChanged.
// EmulationStateChanged causes several dialogs to redraw, including anything affected by
// hardcore mode. Every dialog that depends on hardcore mode is redrawn by
// EmulationStateChanged.
if (hardcore_active != m_former_hardcore_setting)
emit Settings::Instance().EmulationStateChanged(Core::GetState(Core::System::GetInstance()));
m_former_hardcore_setting = hardcore_active;

View file

@ -47,6 +47,7 @@ class NetPlaySetupDialog;
class NetworkWidget;
class RegisterWidget;
class RenderWidget;
class RenderWindow;
class SearchBar;
class SettingsWindow;
class SkylanderPortalWindow;
@ -160,8 +161,10 @@ private:
void StartGame(const std::vector<std::string>& paths,
std::unique_ptr<BootSessionData> boot_session_data = nullptr);
void StartGame(std::unique_ptr<BootParameters>&& parameters);
void ShowRenderWidget();
void HideRenderWidget(bool reinit = true, bool is_exit = false);
void ShowRenderWidget(bool render_to_main_window);
void HideRenderWidget();
void RecreateRenderWidget();
void ShowSettingsWindow();
void ShowGeneralWindow();
@ -216,6 +219,7 @@ private:
QStringList PromptFileNames();
void OnConfigChanged();
void UpdateScreenSaverInhibition();
void OnStopComplete();
@ -235,7 +239,7 @@ private:
SearchBar* m_search_bar;
GameList* m_game_list;
RenderWidget* m_render_widget = nullptr;
bool m_rendering_to_main;
RenderWindow* m_render_window = nullptr;
bool m_stop_confirm_showing = false;
bool m_stop_requested = false;
bool m_exit_requested = false;

View file

@ -3,8 +3,6 @@
#include "DolphinQt/RenderWidget.h"
#include <array>
#include <QApplication>
#include <QDragEnterEvent>
#include <QDropEvent>
@ -17,6 +15,7 @@
#include <QPalette>
#include <QScreen>
#include <QTimer>
#include <QVBoxLayout>
#include <QWindow>
#include "Core/Config/MainSettings.h"
@ -31,34 +30,51 @@
#include "InputCommon/ControllerInterface/ControllerInterface.h"
#include "VideoCommon/OnScreenUI.h"
#include "VideoCommon/Present.h"
#include "VideoCommon/VideoConfig.h"
#ifdef _WIN32
#include <Windows.h>
#include <dwmapi.h>
#endif
RenderWidget::RenderWidget(QWidget* parent) : QWidget(parent)
RenderWindow::RenderWindow(QWidget* parent, RenderWidget* render_widget) : QWidget{parent}
{
// TODO: fix title when creating render window while in game.
setWindowTitle(QStringLiteral("Dolphin"));
setWindowIcon(Resources::GetAppIcon());
setWindowRole(QStringLiteral("renderer"));
setAcceptDrops(true);
auto* const layout = new QVBoxLayout{this};
layout->setContentsMargins(QMargins{});
layout->addWidget(render_widget);
connect(Host::GetInstance(), &Host::RequestTitle, this, &QWidget::setWindowTitle);
}
RenderWidget::RenderWidget(QWidget* parent) : QWidget(parent)
{
hide();
QPalette p;
p.setColor(QPalette::Window, Qt::black);
setPalette(p);
connect(Host::GetInstance(), &Host::RequestTitle, this, &RenderWidget::setWindowTitle);
connect(Host::GetInstance(), &Host::RequestRenderSize, this, [this](int w, int h) {
if (!Config::Get(Config::MAIN_RENDER_WINDOW_AUTOSIZE) || isFullScreen() || isMaximized())
if (!Config::Get(Config::MAIN_RENDER_WINDOW_AUTOSIZE))
return;
const auto dpr = window()->windowHandle()->screen()->devicePixelRatio();
auto* const parent_window = window();
if (parent_window->isFullScreen() || parent_window->isMaximized())
return;
resize(w / dpr, h / dpr);
const auto dpr = int(window()->windowHandle()->screen()->devicePixelRatio());
const auto desired_size = QSize{w / dpr, h / dpr};
const auto adjustment = desired_size - size();
// Grow or shrink the parent window to achieve the desired render widget size.
parent_window->resize(parent_window->size() + adjustment);
});
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) {
@ -506,9 +522,6 @@ bool RenderWidget::event(QEvent* event)
SetCursorLocked(m_cursor_locked || (isFullScreen() && Settings::Instance().GetLockCursor()));
emit StateChanged(isFullScreen());
break;
case QEvent::Close:
emit Closed();
break;
default:
break;
}

View file

@ -26,7 +26,6 @@ public:
signals:
void EscapePressed();
void Closed();
void HandleChanged(void* handle);
void StateChanged(bool fullscreen);
void SizeChanged(int new_width, int new_height);
@ -57,3 +56,9 @@ private:
bool m_waiting_for_message_box = false;
bool m_should_unpause_on_focus = false;
};
class RenderWindow final : public QWidget
{
public:
RenderWindow(QWidget* parent, RenderWidget* render_widget);
};

View file

@ -29,7 +29,7 @@ public:
// Modifies the state
StateData GetState(bool adjusted);
StateData GetState(bool adjusted, const ControllerEmu::InputOverrideFunction& override_func);
StateData GetState(bool adjusted, const InputOverrideFunction& override_func);
// Yaw movement in radians.
ControlState GetTotalYaw() const;