mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-10-12 02:08:45 +00:00
Qt: Use QWindow as display surface
This commit is contained in:
committed by
lightningterror
parent
e5d94e255b
commit
cfea84b934
+84
-136
@@ -29,20 +29,12 @@
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#endif
|
||||
|
||||
DisplayWidget::DisplayWidget(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
DisplaySurface::DisplaySurface()
|
||||
: QWindow()
|
||||
{
|
||||
// We want a native window for both D3D and OpenGL.
|
||||
setAutoFillBackground(false);
|
||||
setAttribute(Qt::WA_NativeWindow, true);
|
||||
setAttribute(Qt::WA_NoSystemBackground, true);
|
||||
setAttribute(Qt::WA_PaintOnScreen, true);
|
||||
setAttribute(Qt::WA_KeyCompression, false);
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
DisplayWidget::~DisplayWidget()
|
||||
DisplaySurface::~DisplaySurface()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (m_clip_mouse_enabled)
|
||||
@@ -50,19 +42,27 @@ DisplayWidget::~DisplayWidget()
|
||||
#endif
|
||||
}
|
||||
|
||||
int DisplayWidget::scaledWindowWidth() const
|
||||
QWidget* DisplaySurface::createWindowContainer(QWidget* parent)
|
||||
{
|
||||
return std::max(static_cast<int>(std::ceil(static_cast<qreal>(width()) * devicePixelRatioF())), 1);
|
||||
m_container = QWidget::createWindowContainer(this, parent);
|
||||
m_container->installEventFilter(this);
|
||||
m_container->setFocusPolicy(Qt::StrongFocus);
|
||||
return m_container;
|
||||
}
|
||||
|
||||
int DisplayWidget::scaledWindowHeight() const
|
||||
int DisplaySurface::scaledWindowWidth() const
|
||||
{
|
||||
return std::max(static_cast<int>(std::ceil(static_cast<qreal>(height()) * devicePixelRatioF())), 1);
|
||||
return std::max(static_cast<int>(std::ceil(static_cast<qreal>(width()) * devicePixelRatio())), 1);
|
||||
}
|
||||
|
||||
std::optional<WindowInfo> DisplayWidget::getWindowInfo()
|
||||
int DisplaySurface::scaledWindowHeight() const
|
||||
{
|
||||
std::optional<WindowInfo> ret(QtUtils::GetWindowInfoForWidget(this));
|
||||
return std::max(static_cast<int>(std::ceil(static_cast<qreal>(height()) * devicePixelRatio())), 1);
|
||||
}
|
||||
|
||||
std::optional<WindowInfo> DisplaySurface::getWindowInfo()
|
||||
{
|
||||
std::optional<WindowInfo> ret(QtUtils::GetWindowInfoForWindow(this));
|
||||
if (ret.has_value())
|
||||
{
|
||||
m_last_window_width = ret->surface_width;
|
||||
@@ -72,7 +72,7 @@ std::optional<WindowInfo> DisplayWidget::getWindowInfo()
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DisplayWidget::updateRelativeMode(bool enabled)
|
||||
void DisplaySurface::updateRelativeMode(bool enabled)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// prefer ClipCursor() over warping movement when we're using raw input
|
||||
@@ -104,17 +104,17 @@ void DisplayWidget::updateRelativeMode(bool enabled)
|
||||
#endif
|
||||
m_relative_mouse_start_pos = QCursor::pos();
|
||||
updateCenterPos();
|
||||
grabMouse();
|
||||
setMouseGrabEnabled(true);
|
||||
}
|
||||
else if (m_relative_mouse_enabled)
|
||||
{
|
||||
m_relative_mouse_enabled = false;
|
||||
QCursor::setPos(m_relative_mouse_start_pos);
|
||||
releaseMouse();
|
||||
setMouseGrabEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayWidget::updateCursor(bool hidden)
|
||||
void DisplaySurface::updateCursor(bool hidden)
|
||||
{
|
||||
if (m_cursor_hidden == hidden)
|
||||
return;
|
||||
@@ -132,7 +132,7 @@ void DisplayWidget::updateCursor(bool hidden)
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayWidget::handleCloseEvent(QCloseEvent* event)
|
||||
void DisplaySurface::handleCloseEvent(QCloseEvent* event)
|
||||
{
|
||||
// Closing the separate widget will either cancel the close, or trigger shutdown.
|
||||
// In the latter case, it's going to destroy us, so don't let Qt do it first.
|
||||
@@ -152,28 +152,13 @@ void DisplayWidget::handleCloseEvent(QCloseEvent* event)
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
void DisplayWidget::destroy()
|
||||
bool DisplaySurface::isActuallyFullscreen() const
|
||||
{
|
||||
m_destroying = true;
|
||||
|
||||
#ifdef __APPLE__
|
||||
// See Qt documentation, entire application is in full screen state, and the main
|
||||
// window will get reopened fullscreen instead of windowed if we don't close the
|
||||
// fullscreen window first.
|
||||
if (isActuallyFullscreen())
|
||||
close();
|
||||
#endif
|
||||
deleteLater();
|
||||
// DisplaySurface is always in a container, so we need to check parent window
|
||||
return parent()->windowState() & Qt::WindowFullScreen;
|
||||
}
|
||||
|
||||
bool DisplayWidget::isActuallyFullscreen() const
|
||||
{
|
||||
// I hate you QtWayland... have to check the parent, not ourselves.
|
||||
QWidget* container = qobject_cast<QWidget*>(parent());
|
||||
return container ? container->isFullScreen() : isFullScreen();
|
||||
}
|
||||
|
||||
void DisplayWidget::updateCenterPos()
|
||||
void DisplaySurface::updateCenterPos()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (m_clip_mouse_enabled)
|
||||
@@ -203,12 +188,14 @@ void DisplayWidget::updateCenterPos()
|
||||
#endif
|
||||
}
|
||||
|
||||
QPaintEngine* DisplayWidget::paintEngine() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool DisplayWidget::event(QEvent* event)
|
||||
// Keyboard focus and child windows are inconsistant across platforms;
|
||||
// Windows: Can programmatically focus the child window, NVidia overlay can defocus it.
|
||||
// X11: Can programmatically focus the child window.
|
||||
// Wayland: Child window cannot be focused at all on most(?) DE.
|
||||
// Mac: Can programmatically focus the child window.
|
||||
// Thus for KB inputs we need to sometimes use the event filter.
|
||||
// Mouse events are always delivered to the child window, so that seems consistant.
|
||||
void DisplaySurface::handleKeyInputEvent(QEvent* event)
|
||||
{
|
||||
switch (event->type())
|
||||
{
|
||||
@@ -229,7 +216,7 @@ bool DisplayWidget::event(QEvent* event)
|
||||
}
|
||||
|
||||
if (key_event->isAutoRepeat())
|
||||
return true;
|
||||
return;
|
||||
|
||||
// For some reason, Windows sends "fake" key events.
|
||||
// Scenario: Press shift, press F1, release shift, release F1.
|
||||
@@ -246,7 +233,7 @@ bool DisplayWidget::event(QEvent* event)
|
||||
if (it != m_keys_pressed_with_modifiers.end())
|
||||
{
|
||||
if (pressed)
|
||||
return true;
|
||||
return;
|
||||
else
|
||||
m_keys_pressed_with_modifiers.erase(it);
|
||||
}
|
||||
@@ -259,6 +246,23 @@ bool DisplayWidget::event(QEvent* event)
|
||||
InputManager::InvokeEvents(InputManager::MakeHostKeyboardKey(key), static_cast<float>(pressed));
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
pxAssert(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool DisplaySurface::event(QEvent* event)
|
||||
{
|
||||
switch (event->type())
|
||||
{
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::KeyRelease:
|
||||
{
|
||||
handleKeyInputEvent(event);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -268,7 +272,7 @@ bool DisplayWidget::event(QEvent* event)
|
||||
|
||||
if (!m_relative_mouse_enabled)
|
||||
{
|
||||
const qreal dpr = devicePixelRatioF();
|
||||
const qreal dpr = devicePixelRatio();
|
||||
const QPoint mouse_pos = mouse_event->pos();
|
||||
|
||||
const float scaled_x = static_cast<float>(static_cast<qreal>(mouse_pos.x()) * dpr);
|
||||
@@ -352,9 +356,9 @@ bool DisplayWidget::event(QEvent* event)
|
||||
case QEvent::DevicePixelRatioChange:
|
||||
case QEvent::Resize:
|
||||
{
|
||||
QWidget::event(event);
|
||||
QWindow::event(event);
|
||||
|
||||
const float dpr = devicePixelRatioF();
|
||||
const float dpr = devicePixelRatio();
|
||||
const u32 scaled_width = static_cast<u32>(std::max(static_cast<int>(std::ceil(static_cast<qreal>(width()) * dpr)), 1));
|
||||
const u32 scaled_height = static_cast<u32>(std::max(static_cast<int>(std::ceil(static_cast<qreal>(height()) * dpr)), 1));
|
||||
|
||||
@@ -377,100 +381,44 @@ bool DisplayWidget::event(QEvent* event)
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::Close:
|
||||
{
|
||||
if (m_destroying)
|
||||
return QWidget::event(event);
|
||||
|
||||
handleCloseEvent(static_cast<QCloseEvent*>(event));
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::WindowStateChange:
|
||||
{
|
||||
QWidget::event(event);
|
||||
|
||||
if (static_cast<QWindowStateChangeEvent*>(event)->oldState() & Qt::WindowMinimized)
|
||||
emit windowRestoredEvent();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return QWidget::event(event);
|
||||
return QWindow::event(event);
|
||||
}
|
||||
}
|
||||
|
||||
DisplayContainer::DisplayContainer()
|
||||
: QStackedWidget(nullptr)
|
||||
bool DisplaySurface::eventFilter(QObject* object, QEvent* event)
|
||||
{
|
||||
}
|
||||
|
||||
DisplayContainer::~DisplayContainer() = default;
|
||||
|
||||
bool DisplayContainer::isNeeded(bool fullscreen, bool render_to_main)
|
||||
{
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
return false;
|
||||
#else
|
||||
if (!isRunningOnWayland())
|
||||
return false;
|
||||
|
||||
// We only need this on Wayland because of client-side decorations...
|
||||
return (fullscreen || !render_to_main);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool DisplayContainer::isRunningOnWayland()
|
||||
{
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
return false;
|
||||
#else
|
||||
const QString platform_name = QGuiApplication::platformName();
|
||||
return (platform_name == QStringLiteral("wayland"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void DisplayContainer::setDisplayWidget(DisplayWidget* widget)
|
||||
{
|
||||
pxAssert(!m_display_widget);
|
||||
m_display_widget = widget;
|
||||
addWidget(widget);
|
||||
}
|
||||
|
||||
DisplayWidget* DisplayContainer::removeDisplayWidget()
|
||||
{
|
||||
DisplayWidget* widget = m_display_widget;
|
||||
pxAssert(widget);
|
||||
m_display_widget = nullptr;
|
||||
removeWidget(widget);
|
||||
return widget;
|
||||
}
|
||||
|
||||
bool DisplayContainer::event(QEvent* event)
|
||||
{
|
||||
if (event->type() == QEvent::Close && m_display_widget)
|
||||
{
|
||||
m_display_widget->handleCloseEvent(static_cast<QCloseEvent*>(event));
|
||||
return true;
|
||||
}
|
||||
|
||||
const bool res = QStackedWidget::event(event);
|
||||
if (!m_display_widget)
|
||||
return res;
|
||||
|
||||
switch (event->type())
|
||||
{
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::KeyRelease:
|
||||
#ifdef _WIN32
|
||||
// Nvidia overlay causes the child window to lose focus, but not its parent.
|
||||
// Refocus the child window.
|
||||
requestActivate();
|
||||
#endif
|
||||
handleKeyInputEvent(event);
|
||||
return true;
|
||||
|
||||
// These events only work on the top level control.
|
||||
// Which is this container when render to seperate or fullscreen is active.
|
||||
case QEvent::Close:
|
||||
handleCloseEvent(static_cast<QCloseEvent*>(event));
|
||||
return true;
|
||||
case QEvent::WindowStateChange:
|
||||
{
|
||||
if (static_cast<QWindowStateChangeEvent*>(event)->oldState() & Qt::WindowMinimized)
|
||||
emit m_display_widget->windowRestoredEvent();
|
||||
}
|
||||
break;
|
||||
emit windowRestoredEvent();
|
||||
return false;
|
||||
|
||||
case QEvent::ChildRemoved:
|
||||
if (static_cast<QChildEvent*>(event)->child() == m_container)
|
||||
{
|
||||
object->removeEventFilter(this);
|
||||
m_container = nullptr;
|
||||
}
|
||||
return false;
|
||||
|
||||
default:
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
+13
-32
@@ -3,22 +3,25 @@
|
||||
|
||||
#pragma once
|
||||
#include "common/WindowInfo.h"
|
||||
#include <QtWidgets/QStackedWidget>
|
||||
#include <QtWidgets/QWidget>
|
||||
#include <QtGui/QWindow>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
class QCloseEvent;
|
||||
|
||||
class DisplayWidget final : public QWidget
|
||||
class DisplaySurface final : public QWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DisplayWidget(QWidget* parent);
|
||||
~DisplayWidget();
|
||||
explicit DisplaySurface();
|
||||
~DisplaySurface();
|
||||
|
||||
QPaintEngine* paintEngine() const override;
|
||||
// while QWindow can be used directly as a window, Popups requre a QWidget parent.
|
||||
// Additionally, we use saveGeometry/restoreGeometry for render to seperate window mode
|
||||
// but those functions only exist on QWidget.
|
||||
// Thus, we always need a container widget.
|
||||
QWidget* createWindowContainer(QWidget* parent = nullptr);
|
||||
|
||||
int scaledWindowWidth() const;
|
||||
int scaledWindowHeight() const;
|
||||
@@ -28,15 +31,15 @@ public:
|
||||
void updateRelativeMode(bool enabled);
|
||||
void updateCursor(bool hidden);
|
||||
|
||||
void handleCloseEvent(QCloseEvent* event);
|
||||
void destroy();
|
||||
|
||||
Q_SIGNALS:
|
||||
void windowResizedEvent(int width, int height, float scale);
|
||||
void windowRestoredEvent();
|
||||
|
||||
protected:
|
||||
void handleCloseEvent(QCloseEvent* event);
|
||||
void handleKeyInputEvent(QEvent* event);
|
||||
bool event(QEvent* event) override;
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
|
||||
private:
|
||||
bool isActuallyFullscreen() const;
|
||||
@@ -49,34 +52,12 @@ private:
|
||||
bool m_clip_mouse_enabled = false;
|
||||
#endif
|
||||
bool m_cursor_hidden = false;
|
||||
bool m_destroying = false;
|
||||
|
||||
std::vector<int> m_keys_pressed_with_modifiers;
|
||||
|
||||
u32 m_last_window_width = 0;
|
||||
u32 m_last_window_height = 0;
|
||||
float m_last_window_scale = 1.0f;
|
||||
};
|
||||
|
||||
class DisplayContainer final : public QStackedWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DisplayContainer();
|
||||
~DisplayContainer();
|
||||
|
||||
// Wayland is broken in lots of ways, so we need to check for it.
|
||||
static bool isRunningOnWayland();
|
||||
|
||||
static bool isNeeded(bool fullscreen, bool render_to_main);
|
||||
|
||||
void setDisplayWidget(DisplayWidget* widget);
|
||||
DisplayWidget* removeDisplayWidget();
|
||||
|
||||
protected:
|
||||
bool event(QEvent* event) override;
|
||||
|
||||
private:
|
||||
DisplayWidget* m_display_widget = nullptr;
|
||||
QWidget* m_container = nullptr;
|
||||
};
|
||||
|
||||
+93
-184
@@ -86,16 +86,6 @@ const char* MainWindow::DISC_IMAGE_FILTER = QT_TRANSLATE_NOOP("MainWindow", "All
|
||||
|
||||
MainWindow* g_main_window = nullptr;
|
||||
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
static const bool s_use_central_widget = false;
|
||||
#else
|
||||
// Qt Wayland is broken. Any sort of stacked widget usage fails to update,
|
||||
// leading to broken window resizes, no display rendering, etc. So, we mess
|
||||
// with the central widget instead. Which we can't do on xorg, because it
|
||||
// breaks window resizing there...
|
||||
static bool s_use_central_widget = false;
|
||||
#endif
|
||||
|
||||
// UI thread VM validity.
|
||||
static bool s_vm_valid = false;
|
||||
static bool s_vm_paused = false;
|
||||
@@ -113,19 +103,6 @@ MainWindow::MainWindow()
|
||||
{
|
||||
pxAssert(!g_main_window);
|
||||
g_main_window = this;
|
||||
|
||||
// Native window rendering is broken in wayland.
|
||||
// Let's work around it by disabling it for every widget besides
|
||||
// DisplayWidget.
|
||||
// Additionally, alien widget rendering is much more performant, so we
|
||||
// should have a nice responsiveness boost in our UI :)
|
||||
// QTBUG-133919, reported upstream by govanify
|
||||
QGuiApplication::setAttribute(Qt::AA_NativeWindows, false);
|
||||
QGuiApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true);
|
||||
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
s_use_central_widget = DisplayContainer::isRunningOnWayland();
|
||||
#endif
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
@@ -192,7 +169,7 @@ static void makeIconsMasks(QWidget* menu)
|
||||
|
||||
QWidget* MainWindow::getContentParent()
|
||||
{
|
||||
return s_use_central_widget ? static_cast<QWidget*>(this) : static_cast<QWidget*>(m_ui.mainContainer);
|
||||
return static_cast<QWidget*>(m_ui.mainContainer);
|
||||
}
|
||||
|
||||
void MainWindow::setupAdditionalUi()
|
||||
@@ -219,15 +196,7 @@ void MainWindow::setupAdditionalUi()
|
||||
m_game_list_widget = new GameListWidget(getContentParent());
|
||||
m_game_list_widget->initialize();
|
||||
m_ui.actionGridViewShowTitles->setChecked(m_game_list_widget->getShowGridCoverTitles());
|
||||
if (s_use_central_widget)
|
||||
{
|
||||
m_ui.mainContainer = nullptr; // setCentralWidget() will delete this
|
||||
setCentralWidget(m_game_list_widget);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui.mainContainer->addWidget(m_game_list_widget);
|
||||
}
|
||||
m_ui.mainContainer->addWidget(m_game_list_widget);
|
||||
|
||||
m_status_progress_widget = new QProgressBar(m_ui.statusBar);
|
||||
m_status_progress_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
@@ -532,7 +501,7 @@ void MainWindow::recreate()
|
||||
if (was_display_created)
|
||||
{
|
||||
g_emu_thread->setSurfaceless(true);
|
||||
while (m_display_widget || !g_emu_thread->isSurfaceless())
|
||||
while (m_display_surface || !g_emu_thread->isSurfaceless())
|
||||
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
|
||||
|
||||
m_display_created = false;
|
||||
@@ -1033,11 +1002,10 @@ void MainWindow::updateWindowTitle()
|
||||
if (windowTitle() != main_title)
|
||||
setWindowTitle(main_title);
|
||||
|
||||
if (m_display_widget && !isRenderingToMain())
|
||||
if (m_display_container && !isRenderingToMain())
|
||||
{
|
||||
QWidget* container = m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget);
|
||||
if (container->windowTitle() != display_title)
|
||||
container->setWindowTitle(display_title);
|
||||
if (m_display_container->windowTitle() != display_title)
|
||||
m_display_container->setWindowTitle(display_title);
|
||||
}
|
||||
|
||||
if (g_log_window)
|
||||
@@ -1052,7 +1020,7 @@ void MainWindow::updateWindowState(bool force_visible)
|
||||
|
||||
const bool hide_window = !isRenderingToMain() && shouldHideMainWindow();
|
||||
const bool disable_resize = Host::GetBoolSettingValue("UI", "DisableWindowResize", false);
|
||||
const bool has_window = s_vm_valid || m_display_widget;
|
||||
const bool has_window = s_vm_valid || m_display_surface;
|
||||
|
||||
// Need to test both valid and display widget because of startup (vm invalid while window is created).
|
||||
const bool visible = force_visible || !hide_window || !has_window;
|
||||
@@ -1065,8 +1033,8 @@ void MainWindow::updateWindowState(bool force_visible)
|
||||
QtUtils::SetWindowResizeable(this, resizeable);
|
||||
|
||||
// Update the display widget too if rendering separately.
|
||||
if (m_display_widget && !isRenderingToMain())
|
||||
QtUtils::SetWindowResizeable(getDisplayContainer(), resizeable);
|
||||
if (m_display_surface && !isRenderingToMain())
|
||||
QtUtils::SetWindowResizeable(m_display_container, resizeable);
|
||||
}
|
||||
|
||||
void MainWindow::setProgressBar(int current, int total)
|
||||
@@ -1093,26 +1061,20 @@ void MainWindow::clearProgressBar()
|
||||
|
||||
bool MainWindow::isShowingGameList() const
|
||||
{
|
||||
if (s_use_central_widget)
|
||||
return (centralWidget() == m_game_list_widget);
|
||||
else
|
||||
return (m_ui.mainContainer->currentIndex() == 0);
|
||||
return (m_ui.mainContainer->currentIndex() == 0);
|
||||
}
|
||||
|
||||
bool MainWindow::isRenderingFullscreen() const
|
||||
{
|
||||
if (!MTGS::IsOpen() || !m_display_widget)
|
||||
if (!MTGS::IsOpen() || !m_display_surface)
|
||||
return false;
|
||||
|
||||
return getDisplayContainer()->isFullScreen();
|
||||
return m_display_container->isFullScreen();
|
||||
}
|
||||
|
||||
bool MainWindow::isRenderingToMain() const
|
||||
{
|
||||
if (s_use_central_widget)
|
||||
return (m_display_widget && centralWidget() == m_display_widget);
|
||||
else
|
||||
return (m_display_widget && m_ui.mainContainer->indexOf(m_display_widget) == 1);
|
||||
return (m_display_surface && m_ui.mainContainer->indexOf(m_display_container) == 1);
|
||||
}
|
||||
|
||||
bool MainWindow::shouldHideMouseCursor() const
|
||||
@@ -1137,14 +1099,14 @@ bool MainWindow::shouldMouseLock() const
|
||||
if (!Host::GetBoolSettingValue("EmuCore", "EnableMouseLock", false))
|
||||
return false;
|
||||
|
||||
if (m_display_created == false || m_display_widget == nullptr && !isRenderingToMain())
|
||||
if (m_display_created == false || m_display_surface == nullptr && !isRenderingToMain())
|
||||
return false;
|
||||
|
||||
bool windowsHidden = (!g_debugger_window || g_debugger_window->isHidden()) &&
|
||||
(!m_controller_settings_window || m_controller_settings_window->isHidden()) &&
|
||||
(!m_settings_window || m_settings_window->isHidden());
|
||||
|
||||
auto* displayWindow = isRenderingToMain() ? window() : m_display_widget->window();
|
||||
auto* displayWindow = isRenderingToMain() ? window() : m_display_container->window();
|
||||
|
||||
if (displayWindow == nullptr)
|
||||
return false;
|
||||
@@ -1194,7 +1156,7 @@ void MainWindow::switchToGameListView()
|
||||
|
||||
// switch to surfaceless. we have to wait until the display widget is gone before we swap over.
|
||||
g_emu_thread->setSurfaceless(true);
|
||||
while (m_display_widget)
|
||||
while (m_display_surface)
|
||||
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
|
||||
}
|
||||
}
|
||||
@@ -1211,8 +1173,8 @@ void MainWindow::switchToEmulationView()
|
||||
if (s_vm_paused && !m_was_paused_on_surface_loss)
|
||||
g_emu_thread->setVMPaused(false);
|
||||
|
||||
if (m_display_widget)
|
||||
m_display_widget->setFocus();
|
||||
if (m_display_surface)
|
||||
m_display_container->setFocus();
|
||||
}
|
||||
|
||||
void MainWindow::refreshGameList(bool invalidate_cache, bool popup_on_error)
|
||||
@@ -1349,7 +1311,7 @@ void MainWindow::requestExit(bool allow_confirm)
|
||||
|
||||
void MainWindow::checkForSettingChanges()
|
||||
{
|
||||
if (m_display_widget)
|
||||
if (m_display_surface)
|
||||
updateDisplayWidgetCursor();
|
||||
|
||||
updateWindowState();
|
||||
@@ -1359,10 +1321,10 @@ void MainWindow::checkForSettingChanges()
|
||||
|
||||
std::optional<WindowInfo> MainWindow::getWindowInfo()
|
||||
{
|
||||
if (!m_display_widget || isRenderingToMain())
|
||||
return QtUtils::GetWindowInfoForWidget(this);
|
||||
else if (QWidget* widget = getDisplayContainer())
|
||||
return QtUtils::GetWindowInfoForWidget(widget);
|
||||
if (!m_display_surface || isRenderingToMain())
|
||||
return QtUtils::GetWindowInfoForWindow(this);
|
||||
else if (m_display_surface)
|
||||
return QtUtils::GetWindowInfoForWindow(m_display_surface);
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -1595,7 +1557,7 @@ void MainWindow::onSaveStateMenuAboutToShow()
|
||||
|
||||
void MainWindow::onStartFullscreenUITriggered()
|
||||
{
|
||||
if (m_display_widget)
|
||||
if (m_display_surface)
|
||||
g_emu_thread->stopFullscreenUI();
|
||||
else
|
||||
g_emu_thread->startFullscreenUI(Host::GetBaseBoolSettingValue("UI", "StartFullscreen", false));
|
||||
@@ -2033,7 +1995,7 @@ void MainWindow::onVMPaused()
|
||||
updateStatusBarWidgetVisibility();
|
||||
m_last_fps_status = m_status_verbose_widget->text();
|
||||
m_status_verbose_widget->setText(tr("Paused"));
|
||||
if (m_display_widget)
|
||||
if (m_display_surface)
|
||||
updateDisplayWidgetCursor();
|
||||
}
|
||||
|
||||
@@ -2055,10 +2017,10 @@ void MainWindow::onVMResumed()
|
||||
updateStatusBarWidgetVisibility();
|
||||
m_status_verbose_widget->setText(m_last_fps_status);
|
||||
m_last_fps_status = QString();
|
||||
if (m_display_widget)
|
||||
if (m_display_surface)
|
||||
{
|
||||
updateDisplayWidgetCursor();
|
||||
m_display_widget->setFocus();
|
||||
m_display_container->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2090,7 +2052,7 @@ void MainWindow::onVMStopped()
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_display_widget)
|
||||
if (m_display_surface)
|
||||
updateDisplayWidgetCursor();
|
||||
else
|
||||
switchToGameListView();
|
||||
@@ -2360,22 +2322,17 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool recreate_window,
|
||||
DevCon.WriteLn("acquireRenderWindow() recreate=%s fullscreen=%s render_to_main=%s surfaceless=%s", recreate_window ? "true" : "false",
|
||||
fullscreen ? "true" : "false", render_to_main ? "true" : "false", surfaceless ? "true" : "false");
|
||||
|
||||
QWidget* container = m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget);
|
||||
const bool is_fullscreen = isRenderingFullscreen();
|
||||
const bool is_rendering_to_main = isRenderingToMain();
|
||||
const bool changing_surfaceless = (!m_display_widget != surfaceless);
|
||||
const bool changing_surfaceless = (!m_display_surface != surfaceless);
|
||||
if (m_display_created && !recreate_window && fullscreen == is_fullscreen && is_rendering_to_main == render_to_main &&
|
||||
!changing_surfaceless)
|
||||
{
|
||||
return m_display_widget ? m_display_widget->getWindowInfo() : WindowInfo();
|
||||
return m_display_surface ? m_display_surface->getWindowInfo() : WindowInfo();
|
||||
}
|
||||
|
||||
// Skip recreating the surface if we're just transitioning between fullscreen and windowed with render-to-main off.
|
||||
// .. except on Wayland, where everything tends to break if you don't recreate.
|
||||
const bool has_container = (m_display_container != nullptr);
|
||||
const bool needs_container = DisplayContainer::isNeeded(fullscreen, render_to_main);
|
||||
if (m_display_created && !recreate_window && !is_rendering_to_main && !render_to_main && has_container == needs_container &&
|
||||
!needs_container && !changing_surfaceless)
|
||||
if (m_display_created && !recreate_window && !is_rendering_to_main && !render_to_main && !changing_surfaceless)
|
||||
{
|
||||
DevCon.WriteLn("Toggling to %s without recreating surface", (fullscreen ? "fullscreen" : "windowed"));
|
||||
|
||||
@@ -2385,23 +2342,23 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool recreate_window,
|
||||
|
||||
if (fullscreen)
|
||||
{
|
||||
container->showFullScreen();
|
||||
m_display_container->showFullScreen();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_is_temporarily_windowed && g_emu_thread->shouldRenderToMain())
|
||||
container->setGeometry(geometry());
|
||||
m_display_container->setGeometry(geometry());
|
||||
else
|
||||
restoreDisplayWindowGeometryFromConfig();
|
||||
container->showNormal();
|
||||
m_display_container->showNormal();
|
||||
}
|
||||
|
||||
updateDisplayWidgetCursor();
|
||||
m_display_widget->setFocus();
|
||||
m_display_container->setFocus();
|
||||
updateWindowState();
|
||||
|
||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
return m_display_widget->getWindowInfo();
|
||||
return m_display_surface->getWindowInfo();
|
||||
}
|
||||
|
||||
destroyDisplayWidget(surfaceless);
|
||||
@@ -2420,7 +2377,7 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool recreate_window,
|
||||
|
||||
createDisplayWidget(fullscreen, render_to_main);
|
||||
|
||||
std::optional<WindowInfo> wi = m_display_widget->getWindowInfo();
|
||||
std::optional<WindowInfo> wi = m_display_surface->getWindowInfo();
|
||||
if (!wi.has_value())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), tr("Failed to get window info from widget"));
|
||||
@@ -2428,14 +2385,13 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool recreate_window,
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
g_emu_thread->connectDisplaySignals(m_display_widget);
|
||||
g_emu_thread->connectDisplaySignals(m_display_surface);
|
||||
|
||||
updateWindowTitle();
|
||||
updateWindowState();
|
||||
|
||||
updateDisplayWidgetCursor();
|
||||
m_display_widget->setFocus();
|
||||
|
||||
m_display_container->setFocus();
|
||||
return wi;
|
||||
}
|
||||
|
||||
@@ -2449,61 +2405,39 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main)
|
||||
QGuiApplication::sync();
|
||||
}
|
||||
|
||||
QWidget* container;
|
||||
if (DisplayContainer::isNeeded(fullscreen, render_to_main))
|
||||
m_display_surface = new DisplaySurface();
|
||||
if (fullscreen || !render_to_main)
|
||||
{
|
||||
m_display_container = new DisplayContainer();
|
||||
m_display_widget = new DisplayWidget(m_display_container);
|
||||
m_display_container->setDisplayWidget(m_display_widget);
|
||||
container = m_display_container;
|
||||
m_display_container = m_display_surface->createWindowContainer();
|
||||
m_display_container->setWindowTitle(windowTitle());
|
||||
m_display_container->setWindowIcon(windowIcon());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_display_widget = new DisplayWidget((!fullscreen && render_to_main) ? getContentParent() : nullptr);
|
||||
container = m_display_widget;
|
||||
}
|
||||
|
||||
if (fullscreen || !render_to_main)
|
||||
{
|
||||
container->setWindowTitle(windowTitle());
|
||||
container->setWindowIcon(windowIcon());
|
||||
m_display_container = m_display_surface->createWindowContainer(getContentParent());
|
||||
}
|
||||
|
||||
if (fullscreen)
|
||||
{
|
||||
// Don't risk doing this on Wayland, it really doesn't like window state changes,
|
||||
// and positioning has no effect anyway.
|
||||
if (!s_use_central_widget)
|
||||
{
|
||||
if (isVisible() && g_emu_thread->shouldRenderToMain())
|
||||
container->move(pos());
|
||||
else
|
||||
restoreDisplayWindowGeometryFromConfig();
|
||||
}
|
||||
|
||||
container->showFullScreen();
|
||||
// On Wayland, while move/restoreGeometry can't position the window, it can influence which screen they show up on
|
||||
if (isVisible() && g_emu_thread->shouldRenderToMain())
|
||||
m_display_container->move(pos());
|
||||
else
|
||||
restoreDisplayWindowGeometryFromConfig();
|
||||
m_display_container->showFullScreen();
|
||||
}
|
||||
else if (!render_to_main)
|
||||
{
|
||||
if (m_is_temporarily_windowed && g_emu_thread->shouldRenderToMain())
|
||||
container->setGeometry(geometry());
|
||||
m_display_container->setGeometry(geometry());
|
||||
else
|
||||
restoreDisplayWindowGeometryFromConfig();
|
||||
container->showNormal();
|
||||
}
|
||||
else if (s_use_central_widget)
|
||||
{
|
||||
m_game_list_widget->setVisible(false);
|
||||
takeCentralWidget();
|
||||
m_game_list_widget->setParent(this); // takeCentralWidget() removes parent
|
||||
setCentralWidget(m_display_widget);
|
||||
m_display_widget->setFocus();
|
||||
update();
|
||||
m_display_container->showNormal();
|
||||
}
|
||||
else
|
||||
{
|
||||
pxAssertRel(m_ui.mainContainer->count() == 1, "Has no display widget");
|
||||
m_ui.mainContainer->addWidget(container);
|
||||
m_ui.mainContainer->addWidget(m_display_container);
|
||||
m_ui.mainContainer->setCurrentIndex(1);
|
||||
}
|
||||
|
||||
@@ -2515,7 +2449,7 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main)
|
||||
|
||||
void MainWindow::displayResizeRequested(qint32 width, qint32 height)
|
||||
{
|
||||
if (!m_display_widget)
|
||||
if (!m_display_surface)
|
||||
return;
|
||||
|
||||
// unapply the pixel scaling factor for hidpi
|
||||
@@ -2523,15 +2457,15 @@ void MainWindow::displayResizeRequested(qint32 width, qint32 height)
|
||||
width = static_cast<qint32>(std::max(static_cast<int>(std::lroundf(static_cast<float>(width) / dpr)), 1));
|
||||
height = static_cast<qint32>(std::max(static_cast<int>(std::lroundf(static_cast<float>(height) / dpr)), 1));
|
||||
|
||||
if (m_display_container || !m_display_widget->parent())
|
||||
if (!m_display_container->parent())
|
||||
{
|
||||
// no parent - rendering to separate window. easy.
|
||||
QtUtils::ResizePotentiallyFixedSizeWindow(getDisplayContainer(), width, height);
|
||||
QtUtils::ResizePotentiallyFixedSizeWindow(m_display_container, width, height);
|
||||
return;
|
||||
}
|
||||
|
||||
// we are rendering to the main window. we have to add in the extra height from the toolbar/status bar.
|
||||
const s32 extra_height = this->height() - m_display_widget->height();
|
||||
const s32 extra_height = this->height() - m_display_container->height();
|
||||
QtUtils::ResizePotentiallyFixedSizeWindow(this, width, height + extra_height);
|
||||
}
|
||||
|
||||
@@ -2542,7 +2476,7 @@ void MainWindow::mouseModeRequested(bool relative_mode, bool hide_cursor)
|
||||
|
||||
m_relative_mouse_mode = relative_mode;
|
||||
m_hide_mouse_cursor = hide_cursor;
|
||||
if (m_display_widget && !s_vm_paused)
|
||||
if (m_display_surface && !s_vm_paused)
|
||||
updateDisplayWidgetCursor();
|
||||
}
|
||||
|
||||
@@ -2558,44 +2492,30 @@ void MainWindow::releaseRenderWindow()
|
||||
|
||||
void MainWindow::destroyDisplayWidget(bool show_game_list)
|
||||
{
|
||||
if (!m_display_widget)
|
||||
if (!m_display_surface)
|
||||
return;
|
||||
|
||||
if (!isRenderingFullscreen() && !isRenderingToMain())
|
||||
saveDisplayWindowGeometryToConfig();
|
||||
|
||||
if (m_display_container)
|
||||
m_display_container->removeDisplayWidget();
|
||||
// Detatch display surface from container.
|
||||
m_display_surface->setParent(nullptr);
|
||||
|
||||
if (isRenderingToMain())
|
||||
{
|
||||
if (s_use_central_widget)
|
||||
pxAssertRel(m_ui.mainContainer->indexOf(m_display_container) == 1, "Display widget in stack");
|
||||
m_ui.mainContainer->removeWidget(m_display_container);
|
||||
if (show_game_list)
|
||||
{
|
||||
pxAssertRel(centralWidget() == m_display_widget, "Display widget is currently central");
|
||||
takeCentralWidget();
|
||||
if (show_game_list)
|
||||
{
|
||||
m_game_list_widget->setVisible(true);
|
||||
setCentralWidget(m_game_list_widget);
|
||||
m_game_list_widget->resizeTableViewColumnsToFit();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pxAssertRel(m_ui.mainContainer->indexOf(m_display_widget) == 1, "Display widget in stack");
|
||||
m_ui.mainContainer->removeWidget(m_display_widget);
|
||||
if (show_game_list)
|
||||
{
|
||||
m_ui.mainContainer->setCurrentIndex(0);
|
||||
m_game_list_widget->resizeTableViewColumnsToFit();
|
||||
}
|
||||
m_ui.mainContainer->setCurrentIndex(0);
|
||||
m_game_list_widget->resizeTableViewColumnsToFit();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_display_widget)
|
||||
if (m_display_surface)
|
||||
{
|
||||
m_display_widget->destroy();
|
||||
m_display_widget = nullptr;
|
||||
m_display_surface->destroy();
|
||||
m_display_surface = nullptr;
|
||||
}
|
||||
|
||||
if (m_display_container)
|
||||
@@ -2609,22 +2529,17 @@ void MainWindow::destroyDisplayWidget(bool show_game_list)
|
||||
|
||||
void MainWindow::updateDisplayWidgetCursor()
|
||||
{
|
||||
pxAssertRel(m_display_widget, "Should have a display widget");
|
||||
m_display_widget->updateRelativeMode(s_vm_valid && !s_vm_paused && m_relative_mouse_mode);
|
||||
m_display_widget->updateCursor(s_vm_valid && !s_vm_paused && shouldHideMouseCursor());
|
||||
pxAssertRel(m_display_surface, "Should have a display widget");
|
||||
m_display_surface->updateRelativeMode(s_vm_valid && !s_vm_paused && m_relative_mouse_mode);
|
||||
m_display_surface->updateCursor(s_vm_valid && !s_vm_paused && shouldHideMouseCursor());
|
||||
}
|
||||
|
||||
void MainWindow::focusDisplayWidget()
|
||||
{
|
||||
if (!m_display_widget || centralWidget() != m_display_widget)
|
||||
if (!m_display_surface || centralWidget() != m_display_container)
|
||||
return;
|
||||
|
||||
m_display_widget->setFocus();
|
||||
}
|
||||
|
||||
QWidget* MainWindow::getDisplayContainer() const
|
||||
{
|
||||
return (m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget));
|
||||
m_display_container->setFocus();
|
||||
}
|
||||
|
||||
void MainWindow::setupMouseMoveHandler()
|
||||
@@ -2653,18 +2568,19 @@ void MainWindow::checkMousePosition(int x, int y)
|
||||
// physical mouse position
|
||||
const QPoint physicalPos(x, y);
|
||||
|
||||
const auto* displayWindow = getDisplayContainer()->window();
|
||||
const auto* displayWindow = m_display_surface;
|
||||
|
||||
// logical (DIP) frame rect
|
||||
QRectF logicalBounds = displayWindow->geometry();
|
||||
const QSize logicalSize = displayWindow->size();
|
||||
const QPoint logicalPosition = displayWindow->position() + displayWindow->parent()->position();
|
||||
|
||||
// physical frame rect
|
||||
const qreal scale = displayWindow->devicePixelRatioF();
|
||||
QRectF physicalBounds(
|
||||
logicalBounds.x() * scale,
|
||||
logicalBounds.y() * scale,
|
||||
logicalBounds.width() * scale,
|
||||
logicalBounds.height() * scale);
|
||||
const qreal scale = displayWindow->devicePixelRatio();
|
||||
const QRectF physicalBounds(
|
||||
logicalPosition.x() * scale,
|
||||
logicalPosition.y() * scale,
|
||||
logicalSize.width() * scale,
|
||||
logicalSize.height() * scale);
|
||||
|
||||
if (physicalBounds.contains(physicalPos))
|
||||
return;
|
||||
@@ -2677,14 +2593,13 @@ void MainWindow::checkMousePosition(int x, int y)
|
||||
|
||||
void MainWindow::saveDisplayWindowGeometryToConfig()
|
||||
{
|
||||
QWidget* container = getDisplayContainer();
|
||||
if (container->windowState() & Qt::WindowFullScreen)
|
||||
if (m_display_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 = m_display_container->saveGeometry();
|
||||
const QByteArray geometry_b64 = geometry.toBase64();
|
||||
const std::string old_geometry_b64 = Host::GetBaseStringSettingValue("UI", "DisplayWindowGeometry");
|
||||
if (old_geometry_b64 != geometry_b64.constData())
|
||||
@@ -2698,18 +2613,17 @@ void MainWindow::restoreDisplayWindowGeometryFromConfig()
|
||||
{
|
||||
const std::string geometry_b64 = Host::GetBaseStringSettingValue("UI", "DisplayWindowGeometry");
|
||||
const QByteArray geometry = QByteArray::fromBase64(QByteArray::fromStdString(geometry_b64));
|
||||
QWidget* container = getDisplayContainer();
|
||||
if (!geometry.isEmpty())
|
||||
{
|
||||
container->restoreGeometry(geometry);
|
||||
m_display_container->restoreGeometry(geometry);
|
||||
|
||||
// make sure we're not loading a dodgy config which had fullscreen set...
|
||||
container->setWindowState(container->windowState() & ~(Qt::WindowFullScreen | Qt::WindowActive));
|
||||
m_display_container->setWindowState(m_display_container->windowState() & ~(Qt::WindowFullScreen | Qt::WindowActive));
|
||||
}
|
||||
else
|
||||
{
|
||||
// default size
|
||||
container->resize(640, 480);
|
||||
m_display_container->resize(640, 480);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3259,7 +3173,7 @@ MainWindow::VMLock MainWindow::pauseAndLockVM()
|
||||
// period when there's no window, and Qt might shut us down. So avoid it there.
|
||||
// On MacOS, it forces a workspace switch, which is kinda jarring.
|
||||
#ifndef __APPLE__
|
||||
const bool was_fullscreen = g_emu_thread->isFullscreen() && !s_use_central_widget;
|
||||
const bool was_fullscreen = g_emu_thread->isFullscreen();
|
||||
#else
|
||||
const bool was_fullscreen = false;
|
||||
#endif
|
||||
@@ -3278,25 +3192,20 @@ MainWindow::VMLock MainWindow::pauseAndLockVM()
|
||||
|
||||
g_emu_thread->setFullscreen(false, false);
|
||||
|
||||
// Container could change... thanks Wayland.
|
||||
QWidget* container;
|
||||
while (QtHost::IsVMValid() && (g_emu_thread->isFullscreen() ||
|
||||
!(container = getDisplayContainer()) || container->isFullScreen()))
|
||||
// Process events untill both EmuThread and Qt have finished exiting fullscreen
|
||||
while (QtHost::IsVMValid() && (g_emu_thread->isFullscreen() || m_display_container->isFullScreen()))
|
||||
{
|
||||
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
}
|
||||
}
|
||||
|
||||
// Now we'll either have a borderless window, or a regular window (if we were exclusive fullscreen).
|
||||
QWidget* dialog_parent = getDisplayContainer();
|
||||
|
||||
// Ensure main window is visible.
|
||||
if (!g_main_window->isVisible())
|
||||
g_main_window->show();
|
||||
g_main_window->raise();
|
||||
g_main_window->activateWindow();
|
||||
|
||||
return VMLock(dialog_parent, was_paused, was_fullscreen);
|
||||
return VMLock(m_display_container, was_paused, was_fullscreen);
|
||||
}
|
||||
|
||||
void MainWindow::rescanFile(const std::string& path)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
class QProgressBar;
|
||||
|
||||
class AutoUpdaterDialog;
|
||||
class DisplayWidget;
|
||||
class DisplaySurface;
|
||||
class DisplayContainer;
|
||||
class GameListWidget;
|
||||
class ControllerSettingsWindow;
|
||||
@@ -258,7 +258,6 @@ private:
|
||||
bool shouldAbortForMemcardBusy(const VMLock& lock);
|
||||
|
||||
QWidget* getContentParent();
|
||||
QWidget* getDisplayContainer() const;
|
||||
void saveDisplayWindowGeometryToConfig();
|
||||
void restoreDisplayWindowGeometryFromConfig();
|
||||
void createDisplayWidget(bool fullscreen, bool render_to_main);
|
||||
@@ -292,8 +291,8 @@ private:
|
||||
Ui::MainWindow m_ui;
|
||||
|
||||
GameListWidget* m_game_list_widget = nullptr;
|
||||
DisplayWidget* m_display_widget = nullptr;
|
||||
DisplayContainer* m_display_container = nullptr;
|
||||
DisplaySurface* m_display_surface = nullptr;
|
||||
QWidget* m_display_container = nullptr;
|
||||
|
||||
SettingsWindow* m_settings_window = nullptr;
|
||||
ControllerSettingsWindow* m_controller_settings_window = nullptr;
|
||||
|
||||
+3
-3
@@ -770,12 +770,12 @@ void EmuThread::enumerateVibrationMotors()
|
||||
onVibrationMotorsEnumerated(qmotors);
|
||||
}
|
||||
|
||||
void EmuThread::connectDisplaySignals(DisplayWidget* widget)
|
||||
void EmuThread::connectDisplaySignals(DisplaySurface* widget)
|
||||
{
|
||||
widget->disconnect(this);
|
||||
|
||||
connect(widget, &DisplayWidget::windowResizedEvent, this, &EmuThread::onDisplayWindowResized);
|
||||
connect(widget, &DisplayWidget::windowRestoredEvent, this, &EmuThread::redrawDisplayWindow);
|
||||
connect(widget, &DisplaySurface::windowResizedEvent, this, &EmuThread::onDisplayWindowResized);
|
||||
connect(widget, &DisplaySurface::windowRestoredEvent, this, &EmuThread::redrawDisplayWindow);
|
||||
}
|
||||
|
||||
void EmuThread::onDisplayWindowResized(int width, int height, float scale)
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@
|
||||
|
||||
class SettingsInterface;
|
||||
|
||||
class DisplayWidget;
|
||||
class DisplaySurface;
|
||||
struct VMBootParameters;
|
||||
|
||||
enum class CDVD_SourceType : uint8_t;
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
|
||||
/// Called back from the GS thread when the display state changes (e.g. fullscreen, render to main).
|
||||
std::optional<WindowInfo> acquireRenderWindow(bool recreate_window);
|
||||
void connectDisplaySignals(DisplayWidget* widget);
|
||||
void connectDisplaySignals(DisplaySurface* widget);
|
||||
void releaseRenderWindow();
|
||||
|
||||
void startBackgroundControllerPollTimer();
|
||||
|
||||
@@ -8,10 +8,8 @@
|
||||
#include <QtCore/QtGlobal>
|
||||
#include <QtCore/QMetaObject>
|
||||
#include <QtGui/QAction>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QDesktopServices>
|
||||
#include <QtGui/QKeyEvent>
|
||||
#include <QtGui/QScreen>
|
||||
#include <QtWidgets/QComboBox>
|
||||
#include <QtWidgets/QDialog>
|
||||
#include <QtWidgets/QHeaderView>
|
||||
@@ -40,8 +38,6 @@
|
||||
#if defined(_WIN32)
|
||||
#include "common/RedtapeWindows.h"
|
||||
#include <Shlobj.h>
|
||||
#elif !defined(APPLE)
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#endif
|
||||
|
||||
namespace QtUtils
|
||||
@@ -254,68 +250,6 @@ namespace QtUtils
|
||||
widget->resize(width, height);
|
||||
}
|
||||
|
||||
std::optional<WindowInfo> GetWindowInfoForWidget(QWidget* widget)
|
||||
{
|
||||
WindowInfo wi;
|
||||
|
||||
// Windows and Apple are easy here since there's no display connection.
|
||||
#if defined(_WIN32)
|
||||
wi.type = WindowInfo::Type::Win32;
|
||||
wi.window_handle = reinterpret_cast<void*>(widget->winId());
|
||||
#elif defined(__APPLE__)
|
||||
wi.type = WindowInfo::Type::MacOS;
|
||||
wi.window_handle = reinterpret_cast<void*>(widget->winId());
|
||||
#else
|
||||
QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
|
||||
const QString platform_name = QGuiApplication::platformName();
|
||||
if (platform_name == QStringLiteral("xcb"))
|
||||
{
|
||||
// Can't get a handle for an unmapped window in X, it doesn't like it.
|
||||
if (!widget->isVisible())
|
||||
{
|
||||
Console.WriteLn("Returning null window info for widget because it is not visible.");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
wi.type = WindowInfo::Type::X11;
|
||||
wi.display_connection = pni->nativeResourceForWindow("display", widget->windowHandle());
|
||||
wi.window_handle = reinterpret_cast<void*>(widget->winId());
|
||||
}
|
||||
else if (platform_name == QStringLiteral("wayland"))
|
||||
{
|
||||
wi.type = WindowInfo::Type::Wayland;
|
||||
wi.display_connection = pni->nativeResourceForWindow("display", widget->windowHandle());
|
||||
wi.window_handle = pni->nativeResourceForWindow("surface", widget->windowHandle());
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLn("Unknown PNI platform '%s'.", platform_name.toUtf8().constData());
|
||||
return std::nullopt;
|
||||
}
|
||||
#endif
|
||||
|
||||
const qreal dpr = widget->devicePixelRatioF();
|
||||
wi.surface_width = static_cast<u32>(static_cast<qreal>(widget->width()) * dpr);
|
||||
wi.surface_height = static_cast<u32>(static_cast<qreal>(widget->height()) * dpr);
|
||||
wi.surface_scale = static_cast<float>(dpr);
|
||||
|
||||
// Query refresh rate, we need it for sync.
|
||||
std::optional<float> surface_refresh_rate = WindowInfo::QueryRefreshRateForWindow(wi);
|
||||
if (!surface_refresh_rate.has_value())
|
||||
{
|
||||
// Fallback to using the screen, getting the rate for Wayland is an utter mess otherwise.
|
||||
const QScreen* widget_screen = widget->screen();
|
||||
if (!widget_screen)
|
||||
widget_screen = QGuiApplication::primaryScreen();
|
||||
surface_refresh_rate = widget_screen ? static_cast<float>(widget_screen->refreshRate()) : 0.0f;
|
||||
}
|
||||
|
||||
wi.surface_refresh_rate = surface_refresh_rate.value();
|
||||
INFO_LOG("Surface refresh rate: {} hz", wi.surface_refresh_rate);
|
||||
|
||||
return wi;
|
||||
}
|
||||
|
||||
QString AbstractItemModelToCSV(QAbstractItemModel* model, int role, bool useQuotes)
|
||||
{
|
||||
QString csv;
|
||||
|
||||
+85
-2
@@ -9,11 +9,20 @@
|
||||
#include <QtCore/QMetaType>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QAbstractItemModel>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QWindow>
|
||||
#if !defined(_WIN32) and !defined(__APPLE__)
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#endif
|
||||
#include <QtGui/QScreen>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <optional>
|
||||
|
||||
#include "common/Console.h"
|
||||
|
||||
class ByteStream;
|
||||
|
||||
class QAction;
|
||||
@@ -83,8 +92,82 @@ namespace QtUtils
|
||||
/// Adjusts the fixed size for a window if it's not resizeable.
|
||||
void ResizePotentiallyFixedSizeWindow(QWidget* widget, int width, int height);
|
||||
|
||||
/// Returns the common window info structure for a Qt widget.
|
||||
std::optional<WindowInfo> GetWindowInfoForWidget(QWidget* widget);
|
||||
/// Returns the common window info structure for a Qt Window/Widget.
|
||||
template <class T>
|
||||
requires std::is_base_of_v<QWidget, T> || std::is_base_of_v<QWindow, T>
|
||||
std::optional<WindowInfo> GetWindowInfoForWindow(T* window)
|
||||
{
|
||||
WindowInfo wi;
|
||||
|
||||
// Windows and Apple are easy here since there's no display connection.
|
||||
#if defined(_WIN32)
|
||||
wi.type = WindowInfo::Type::Win32;
|
||||
wi.window_handle = reinterpret_cast<void*>(window->winId());
|
||||
#elif defined(__APPLE__)
|
||||
wi.type = WindowInfo::Type::MacOS;
|
||||
wi.window_handle = reinterpret_cast<void*>(window->winId());
|
||||
#else
|
||||
QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
|
||||
const QString platform_name = QGuiApplication::platformName();
|
||||
|
||||
QWindow* windowHandle;
|
||||
if constexpr (std::is_base_of_v<QWidget, T>)
|
||||
windowHandle = window->windowHandle();
|
||||
else
|
||||
windowHandle = window;
|
||||
|
||||
if (platform_name == QStringLiteral("xcb"))
|
||||
{
|
||||
// Can't get a handle for an unmapped window in X, it doesn't like it.
|
||||
if (!window->isVisible())
|
||||
{
|
||||
Console.WriteLn("Returning null window info for widget because it is not visible.");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
wi.type = WindowInfo::Type::X11;
|
||||
wi.display_connection = pni->nativeResourceForWindow("display", windowHandle);
|
||||
wi.window_handle = reinterpret_cast<void*>(window->winId());
|
||||
}
|
||||
else if (platform_name == QStringLiteral("wayland"))
|
||||
{
|
||||
wi.type = WindowInfo::Type::Wayland;
|
||||
wi.display_connection = pni->nativeResourceForWindow("display", windowHandle);
|
||||
wi.window_handle = pni->nativeResourceForWindow("surface", windowHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLn("Unknown PNI platform '%s'.", platform_name.toUtf8().constData());
|
||||
return std::nullopt;
|
||||
}
|
||||
#endif
|
||||
|
||||
qreal dpr;
|
||||
if constexpr (std::is_base_of_v<QWidget, T>)
|
||||
dpr = window->devicePixelRatioF();
|
||||
else
|
||||
dpr = window->devicePixelRatio();
|
||||
|
||||
wi.surface_width = static_cast<u32>(static_cast<qreal>(window->width()) * dpr);
|
||||
wi.surface_height = static_cast<u32>(static_cast<qreal>(window->height()) * dpr);
|
||||
wi.surface_scale = static_cast<float>(dpr);
|
||||
|
||||
// Query refresh rate, we need it for sync.
|
||||
std::optional<float> surface_refresh_rate = WindowInfo::QueryRefreshRateForWindow(wi);
|
||||
if (!surface_refresh_rate.has_value())
|
||||
{
|
||||
// Fallback to using the screen, getting the rate for Wayland is an utter mess otherwise.
|
||||
const QScreen* widget_screen = window->screen();
|
||||
if (!widget_screen)
|
||||
widget_screen = QGuiApplication::primaryScreen();
|
||||
surface_refresh_rate = widget_screen ? static_cast<float>(widget_screen->refreshRate()) : 0.0f;
|
||||
}
|
||||
|
||||
wi.surface_refresh_rate = surface_refresh_rate.value();
|
||||
INFO_LOG("Surface refresh rate: {} hz", wi.surface_refresh_rate);
|
||||
|
||||
return wi;
|
||||
}
|
||||
|
||||
/// Converts a value to a QString of said value with a proper fixed width
|
||||
template <typename T>
|
||||
|
||||
Reference in New Issue
Block a user