Qt: Work around QtWayland bugs

- Render to main no longer screws up the game list/menu bar.
- Toggling render to main no longer breaks the main window.

Positioning still sucks, but the various groups involved would
rather sit around arguing with each other rather than actually
shipping solutions ¯\_(ツ)_/¯.
This commit is contained in:
Stenzek 2025-02-16 22:06:14 +10:00
parent cb10c6fbf4
commit 0c30acb285
No known key found for this signature in database
6 changed files with 74 additions and 115 deletions

View File

@ -37,6 +37,7 @@ DisplayWidget::DisplayWidget(QWidget* parent) : QWidget(parent)
// We want a native window for both D3D and OpenGL. // We want a native window for both D3D and OpenGL.
setAutoFillBackground(false); setAutoFillBackground(false);
setAttribute(Qt::WA_NativeWindow, true); setAttribute(Qt::WA_NativeWindow, true);
setAttribute(Qt::WA_DontCreateNativeAncestors, true);
setAttribute(Qt::WA_NoSystemBackground, true); setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_PaintOnScreen, true); setAttribute(Qt::WA_PaintOnScreen, true);
setAttribute(Qt::WA_KeyCompression, false); setAttribute(Qt::WA_KeyCompression, false);
@ -404,7 +405,7 @@ bool DisplayContainer::isNeeded(bool fullscreen, bool render_to_main)
#if defined(_WIN32) || defined(__APPLE__) #if defined(_WIN32) || defined(__APPLE__)
return false; return false;
#else #else
if (!isRunningOnWayland()) if (!QtHost::IsRunningOnWayland())
return false; return false;
// We only need this on Wayland because of client-side decorations... // We only need this on Wayland because of client-side decorations...
@ -412,16 +413,6 @@ bool DisplayContainer::isNeeded(bool fullscreen, bool render_to_main)
#endif #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) void DisplayContainer::setDisplayWidget(DisplayWidget* widget)
{ {
Assert(!m_display_widget); Assert(!m_display_widget);
@ -473,6 +464,7 @@ AuxiliaryDisplayWidget::AuxiliaryDisplayWidget(QWidget* parent, u32 width, u32 h
// We want a native window for both D3D and OpenGL. // We want a native window for both D3D and OpenGL.
setAutoFillBackground(false); setAutoFillBackground(false);
setAttribute(Qt::WA_NativeWindow, true); setAttribute(Qt::WA_NativeWindow, true);
setAttribute(Qt::WA_DontCreateNativeAncestors, true);
setAttribute(Qt::WA_NoSystemBackground, true); setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_PaintOnScreen, true); setAttribute(Qt::WA_PaintOnScreen, true);
setAttribute(Qt::WA_KeyCompression, false); setAttribute(Qt::WA_KeyCompression, false);

View File

@ -77,9 +77,6 @@ public:
DisplayContainer(); DisplayContainer();
~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); static bool isNeeded(bool fullscreen, bool render_to_main);
void setDisplayWidget(DisplayWidget* widget); void setDisplayWidget(DisplayWidget* widget);

View File

@ -101,16 +101,6 @@ static constexpr char DISC_IMAGE_FILTER[] = QT_TRANSLATE_NOOP(
MainWindow* g_main_window = nullptr; 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. // UI thread VM validity.
static bool s_disable_window_rounded_corners = false; static bool s_disable_window_rounded_corners = false;
static bool s_system_valid = false; static bool s_system_valid = false;
@ -151,11 +141,6 @@ MainWindow::MainWindow() : QMainWindow(nullptr)
{ {
Assert(!g_main_window); Assert(!g_main_window);
g_main_window = this; g_main_window = this;
#if !defined(_WIN32) && !defined(__APPLE__)
s_use_central_widget = DisplayContainer::isRunningOnWayland();
#endif
initialize(); initialize();
} }
@ -260,12 +245,14 @@ bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr
#endif #endif
std::optional<WindowInfo> MainWindow::acquireRenderWindow(RenderAPI render_api, bool fullscreen, bool render_to_main, std::optional<WindowInfo> MainWindow::acquireRenderWindow(RenderAPI render_api, bool fullscreen,
bool exclusive_fullscreen, bool render_to_main,
bool surfaceless, bool use_main_window_pos, Error* error) bool surfaceless, bool use_main_window_pos, Error* error)
{ {
DEV_LOG("acquireRenderWindow() fullscreen={} render_to_main={} surfaceless={} use_main_window_pos={}", DEV_LOG("acquireRenderWindow() fullscreen={} exclusive_fullscreen={}, render_to_main={} surfaceless={} "
fullscreen ? "true" : "false", render_to_main ? "true" : "false", surfaceless ? "true" : "false", "use_main_window_pos={}",
use_main_window_pos ? "true" : "false"); fullscreen ? "true" : "false", exclusive_fullscreen ? "true" : "false", render_to_main ? "true" : "false",
surfaceless ? "true" : "false", use_main_window_pos ? "true" : "false");
QWidget* container = QWidget* container =
m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget); m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget);
@ -273,6 +260,9 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(RenderAPI render_api,
const bool is_rendering_to_main = isRenderingToMain(); const bool is_rendering_to_main = isRenderingToMain();
const bool changing_surfaceless = (!m_display_widget != surfaceless); const bool changing_surfaceless = (!m_display_widget != surfaceless);
// Always update exclusive fullscreen state, it controls main window visibility
m_exclusive_fullscreen_requested = !surfaceless && exclusive_fullscreen;
// Skip recreating the surface if we're just transitioning between fullscreen and windowed with render-to-main off. // 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. // .. except on Wayland, where everything tends to break if you don't recreate.
const bool has_container = (m_display_container != nullptr); const bool has_container = (m_display_container != nullptr);
@ -281,6 +271,7 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(RenderAPI render_api,
!needs_container && !changing_surfaceless) !needs_container && !changing_surfaceless)
{ {
DEV_LOG("Toggling to {} without recreating surface", (fullscreen ? "fullscreen" : "windowed")); DEV_LOG("Toggling to {} without recreating surface", (fullscreen ? "fullscreen" : "windowed"));
m_exclusive_fullscreen_requested = exclusive_fullscreen;
// since we don't destroy the display widget, we need to save it here // since we don't destroy the display widget, we need to save it here
if (!is_fullscreen && !is_rendering_to_main) if (!is_fullscreen && !is_rendering_to_main)
@ -357,7 +348,7 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main, bool
} }
else else
{ {
m_display_widget = new DisplayWidget((!fullscreen && render_to_main) ? getContentParent() : nullptr); m_display_widget = new DisplayWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr);
container = m_display_widget; container = m_display_widget;
} }
@ -369,22 +360,16 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main, bool
if (fullscreen) if (fullscreen)
{ {
// Don't risk doing this on Wayland, it really doesn't like window state changes, if (isVisible() && QtHost::CanRenderToMainWindow())
// and positioning has no effect anyway. container->move(pos());
if (!s_use_central_widget) else
{ restoreDisplayWindowGeometryFromConfig();
if (isVisible() && g_emu_thread->shouldRenderToMain())
container->move(pos());
else
restoreDisplayWindowGeometryFromConfig();
}
container->showFullScreen(); container->showFullScreen();
} }
else if (!render_to_main) else if (!render_to_main)
{ {
// See lameland comment above. if (use_main_window_pos)
if (use_main_window_pos && !s_use_central_widget)
container->setGeometry(geometry()); container->setGeometry(geometry());
else else
restoreDisplayWindowGeometryFromConfig(); restoreDisplayWindowGeometryFromConfig();
@ -393,15 +378,6 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main, bool
if (s_disable_window_rounded_corners) if (s_disable_window_rounded_corners)
PlatformMisc::SetWindowRoundedCornerState(reinterpret_cast<void*>(container->winId()), false); PlatformMisc::SetWindowRoundedCornerState(reinterpret_cast<void*>(container->winId()), false);
} }
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();
}
else else
{ {
AssertMsg(m_ui.mainContainer->count() == 1, "Has no display widget"); AssertMsg(m_ui.mainContainer->count() == 1, "Has no display widget");
@ -443,6 +419,7 @@ void MainWindow::releaseRenderWindow()
// Now we can safely destroy the display window. // Now we can safely destroy the display window.
destroyDisplayWidget(true); destroyDisplayWidget(true);
m_display_created = false; m_display_created = false;
m_exclusive_fullscreen_requested = false;
updateDisplayRelatedActions(false, false, false); updateDisplayRelatedActions(false, false, false);
updateShortcutActions(false); updateShortcutActions(false);
@ -464,26 +441,12 @@ void MainWindow::destroyDisplayWidget(bool show_game_list)
if (isRenderingToMain()) if (isRenderingToMain())
{ {
if (s_use_central_widget) AssertMsg(m_ui.mainContainer->indexOf(m_display_widget) == 1, "Display widget in stack");
m_ui.mainContainer->removeWidget(m_display_widget);
if (show_game_list)
{ {
AssertMsg(centralWidget() == m_display_widget, "Display widget is currently central"); m_ui.mainContainer->setCurrentIndex(0);
takeCentralWidget(); m_game_list_widget->resizeTableViewColumnsToFit();
if (show_game_list)
{
m_game_list_widget->setVisible(true);
setCentralWidget(m_game_list_widget);
m_game_list_widget->resizeTableViewColumnsToFit();
}
}
else
{
AssertMsg(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();
}
} }
} }
@ -527,11 +490,6 @@ void MainWindow::focusDisplayWidget()
m_display_widget->setFocus(); m_display_widget->setFocus();
} }
QWidget* MainWindow::getContentParent()
{
return s_use_central_widget ? static_cast<QWidget*>(this) : static_cast<QWidget*>(m_ui.mainContainer);
}
QWidget* MainWindow::getDisplayContainer() const QWidget* MainWindow::getDisplayContainer() const
{ {
return (m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget)); return (m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget));
@ -1667,17 +1625,9 @@ void MainWindow::setupAdditionalUi()
m_ui.actionViewLockToolbar->setChecked(toolbars_locked); m_ui.actionViewLockToolbar->setChecked(toolbars_locked);
m_ui.toolBar->setMovable(!toolbars_locked); m_ui.toolBar->setMovable(!toolbars_locked);
m_game_list_widget = new GameListWidget(getContentParent()); m_game_list_widget = new GameListWidget(m_ui.mainContainer);
m_game_list_widget->initialize(); m_game_list_widget->initialize();
if (s_use_central_widget) m_ui.mainContainer->addWidget(m_game_list_widget);
{
m_ui.mainContainer = nullptr; // setCentralWidget() will delete this
setCentralWidget(m_game_list_widget);
}
else
{
m_ui.mainContainer->addWidget(m_game_list_widget);
}
m_status_progress_widget = new QProgressBar(m_ui.statusBar); m_status_progress_widget = new QProgressBar(m_ui.statusBar);
m_status_progress_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); m_status_progress_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
@ -2014,26 +1964,20 @@ void MainWindow::clearProgressBar()
bool MainWindow::isShowingGameList() const bool MainWindow::isShowingGameList() const
{ {
if (s_use_central_widget) return (m_ui.mainContainer->currentIndex() == 0);
return (centralWidget() == m_game_list_widget);
else
return (m_ui.mainContainer->currentIndex() == 0);
} }
bool MainWindow::isRenderingFullscreen() const bool MainWindow::isRenderingFullscreen() const
{ {
if (!g_gpu_device || !m_display_widget) if (!m_display_widget)
return false; return false;
return getDisplayContainer()->isFullScreen(); return (m_exclusive_fullscreen_requested || getDisplayContainer()->isFullScreen());
} }
bool MainWindow::isRenderingToMain() const bool MainWindow::isRenderingToMain() const
{ {
if (s_use_central_widget) return (m_display_widget && m_ui.mainContainer->indexOf(m_display_widget) == 1);
return (m_display_widget && centralWidget() == m_display_widget);
else
return (m_display_widget && m_ui.mainContainer->indexOf(m_display_widget) == 1);
} }
bool MainWindow::shouldHideMouseCursor() const bool MainWindow::shouldHideMouseCursor() const
@ -2045,7 +1989,7 @@ bool MainWindow::shouldHideMouseCursor() const
bool MainWindow::shouldHideMainWindow() const bool MainWindow::shouldHideMainWindow() const
{ {
return Host::GetBoolSettingValue("Main", "HideMainWindowWhenRunning", false) || return Host::GetBoolSettingValue("Main", "HideMainWindowWhenRunning", false) ||
(g_emu_thread->shouldRenderToMain() && !isRenderingToMain()) || QtHost::InNoGUIMode(); (QtHost::CanRenderToMainWindow() && isRenderingFullscreen()) || QtHost::InNoGUIMode();
} }
void MainWindow::switchToGameListView() void MainWindow::switchToGameListView()
@ -3030,7 +2974,7 @@ MainWindow::SystemLock MainWindow::pauseAndLockSystem()
// On MacOS, it forces a workspace switch, which is kinda jarring. // On MacOS, it forces a workspace switch, which is kinda jarring.
#ifndef __APPLE__ #ifndef __APPLE__
const bool was_fullscreen = g_emu_thread->isFullscreen() && !s_use_central_widget; const bool was_fullscreen = g_emu_thread->isFullscreen();
#else #else
const bool was_fullscreen = false; const bool was_fullscreen = false;
#endif #endif

View File

@ -132,8 +132,9 @@ private Q_SLOTS:
bool confirmMessage(const QString& title, const QString& message); bool confirmMessage(const QString& title, const QString& message);
void onStatusMessage(const QString& message); void onStatusMessage(const QString& message);
std::optional<WindowInfo> acquireRenderWindow(RenderAPI render_api, bool fullscreen, bool render_to_main, std::optional<WindowInfo> acquireRenderWindow(RenderAPI render_api, bool fullscreen, bool exclusive_fullscreen,
bool surfaceless, bool use_main_window_pos, Error* error); bool render_to_main, bool surfaceless, bool use_main_window_pos,
Error* error);
void displayResizeRequested(qint32 width, qint32 height); void displayResizeRequested(qint32 width, qint32 height);
void releaseRenderWindow(); void releaseRenderWindow();
void focusDisplayWidget(); void focusDisplayWidget();
@ -242,7 +243,6 @@ private:
void setProgressBar(int current, int total); void setProgressBar(int current, int total);
void clearProgressBar(); void clearProgressBar();
QWidget* getContentParent();
QWidget* getDisplayContainer() const; QWidget* getDisplayContainer() const;
bool isShowingGameList() const; bool isShowingGameList() const;
bool isRenderingFullscreen() const; bool isRenderingFullscreen() const;
@ -334,6 +334,7 @@ private:
bool m_hide_mouse_cursor = false; bool m_hide_mouse_cursor = false;
bool m_display_created = false; bool m_display_created = false;
bool m_exclusive_fullscreen_requested = false;
bool m_save_states_invalidated = false; bool m_save_states_invalidated = false;
bool m_was_paused_on_surface_loss = false; bool m_was_paused_on_surface_loss = false;
bool m_was_disc_change_request = false; bool m_was_disc_change_request = false;

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> // SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0 // SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "qthost.h" #include "qthost.h"
@ -173,6 +173,15 @@ bool QtHost::PerformEarlyHardwareChecks()
bool QtHost::EarlyProcessStartup() bool QtHost::EarlyProcessStartup()
{ {
#if !defined(_WIN32) && !defined(__APPLE__)
// On Wayland, turning any window into a native window causes DPI scaling to break, as well as window
// updates, creating a complete mess of a window. Setting this attribute isn't ideal, since you'd think
// that setting WA_DontCreateNativeAncestors on the widget would be sufficient, but apparently not.
// TODO: Re-evaluate this on Qt 6.9.
if (QtHost::IsRunningOnWayland())
QGuiApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true);
#endif
// Config-based RAIntegration switch must happen before the main window is displayed. // Config-based RAIntegration switch must happen before the main window is displayed.
#ifdef ENABLE_RAINTEGRATION #ifdef ENABLE_RAINTEGRATION
if (!Achievements::IsUsingRAIntegration() && Host::GetBaseBoolSettingValue("Cheevos", "UseRAIntegration", false)) if (!Achievements::IsUsingRAIntegration() && Host::GetBaseBoolSettingValue("Cheevos", "UseRAIntegration", false))
@ -198,6 +207,16 @@ bool QtHost::InNoGUIMode()
return s_nogui_mode; return s_nogui_mode;
} }
bool QtHost::IsRunningOnWayland()
{
#if defined(_WIN32) || defined(__APPLE__)
return false;
#else
const QString platform_name = QGuiApplication::platformName();
return (platform_name == QStringLiteral("wayland"));
#endif
}
QString QtHost::GetAppNameAndVersion() QString QtHost::GetAppNameAndVersion()
{ {
return QStringLiteral("DuckStation %1").arg(QLatin1StringView(g_scm_tag_str)); return QStringLiteral("DuckStation %1").arg(QLatin1StringView(g_scm_tag_str));
@ -591,7 +610,7 @@ void EmuThread::checkForSettingsChanges(const Settings& old_settings)
// don't mess with fullscreen while locked // don't mess with fullscreen while locked
if (!QtHost::IsSystemLocked()) if (!QtHost::IsSystemLocked())
{ {
const bool render_to_main = shouldRenderToMain(); const bool render_to_main = QtHost::CanRenderToMainWindow();
if (m_is_rendering_to_main != render_to_main && !m_is_fullscreen) if (m_is_rendering_to_main != render_to_main && !m_is_fullscreen)
{ {
m_is_rendering_to_main = render_to_main; m_is_rendering_to_main = render_to_main;
@ -656,9 +675,9 @@ void QtHost::MigrateSettings()
} }
} }
bool EmuThread::shouldRenderToMain() const bool QtHost::CanRenderToMainWindow()
{ {
return !Host::GetBoolSettingValue("Main", "RenderToSeparateWindow", false) && !QtHost::InNoGUIMode(); return !Host::GetBoolSettingValue("Main", "RenderToSeparateWindow", false) && !InNoGUIMode();
} }
void Host::RequestResizeHostDisplay(s32 new_window_width, s32 new_window_height) void Host::RequestResizeHostDisplay(s32 new_window_width, s32 new_window_height)
@ -741,7 +760,7 @@ void EmuThread::startFullscreenUI()
// we want settings loaded so we choose the correct renderer // we want settings loaded so we choose the correct renderer
// this also sorts out input sources. // this also sorts out input sources.
System::LoadSettings(false); System::LoadSettings(false);
m_is_rendering_to_main = shouldRenderToMain(); m_is_rendering_to_main = QtHost::CanRenderToMainWindow();
// borrow the game start fullscreen flag // borrow the game start fullscreen flag
const bool start_fullscreen = const bool start_fullscreen =
@ -796,7 +815,7 @@ void EmuThread::bootSystem(std::shared_ptr<SystemBootParameters> params)
if (System::IsValidOrInitializing()) if (System::IsValidOrInitializing())
return; return;
m_is_rendering_to_main = shouldRenderToMain(); m_is_rendering_to_main = QtHost::CanRenderToMainWindow();
Error error; Error error;
if (!System::BootSystem(std::move(*params), &error)) if (!System::BootSystem(std::move(*params), &error))
@ -928,7 +947,7 @@ void EmuThread::setFullscreen(bool fullscreen, bool allow_render_to_main)
return; return;
m_is_fullscreen = fullscreen; m_is_fullscreen = fullscreen;
m_is_rendering_to_main = allow_render_to_main && shouldRenderToMain(); m_is_rendering_to_main = allow_render_to_main && QtHost::CanRenderToMainWindow();
GPUThread::UpdateDisplayWindow(fullscreen); GPUThread::UpdateDisplayWindow(fullscreen);
} }
@ -984,10 +1003,10 @@ std::optional<WindowInfo> EmuThread::acquireRenderWindow(RenderAPI render_api, b
const bool window_fullscreen = m_is_fullscreen && !exclusive_fullscreen; const bool window_fullscreen = m_is_fullscreen && !exclusive_fullscreen;
const bool render_to_main = !fullscreen && m_is_rendering_to_main; const bool render_to_main = !fullscreen && m_is_rendering_to_main;
const bool use_main_window_pos = shouldRenderToMain(); const bool use_main_window_pos = QtHost::CanRenderToMainWindow();
return emit onAcquireRenderWindowRequested(render_api, window_fullscreen, render_to_main, m_is_surfaceless, return emit onAcquireRenderWindowRequested(render_api, window_fullscreen, exclusive_fullscreen, render_to_main,
use_main_window_pos, error); m_is_surfaceless, use_main_window_pos, error);
} }
void EmuThread::releaseRenderWindow() void EmuThread::releaseRenderWindow()

View File

@ -110,7 +110,6 @@ public:
void stopBackgroundControllerPollTimer(); void stopBackgroundControllerPollTimer();
void wakeThread(); void wakeThread();
bool shouldRenderToMain() const;
void checkForSettingsChanges(const Settings& old_settings); void checkForSettingsChanges(const Settings& old_settings);
void bootOrLoadState(std::string path); void bootOrLoadState(std::string path);
@ -142,7 +141,8 @@ Q_SIGNALS:
void systemResumed(); void systemResumed();
void gameListRefreshed(); void gameListRefreshed();
void gameListRowsChanged(const QList<int>& rows_changed); void gameListRowsChanged(const QList<int>& rows_changed);
std::optional<WindowInfo> onAcquireRenderWindowRequested(RenderAPI render_api, bool fullscreen, bool render_to_main, std::optional<WindowInfo> onAcquireRenderWindowRequested(RenderAPI render_api, bool fullscreen,
bool exclusive_fullscreen, bool render_to_main,
bool surfaceless, bool use_main_window_pos, Error* error); bool surfaceless, bool use_main_window_pos, Error* error);
void onResizeRenderWindowRequested(qint32 width, qint32 height); void onResizeRenderWindowRequested(qint32 width, qint32 height);
void onReleaseRenderWindowRequested(); void onReleaseRenderWindowRequested();
@ -329,6 +329,12 @@ bool InBatchMode();
/// Sets NoGUI mode (implys batch mode, does not display main window, exits on shutdown). /// Sets NoGUI mode (implys batch mode, does not display main window, exits on shutdown).
bool InNoGUIMode(); bool InNoGUIMode();
/// Returns true if the application is running under Wayland.
bool IsRunningOnWayland();
/// Returns true if rendering to the main window should be allowed.
bool CanRenderToMainWindow();
/// Executes a function on the UI thread. /// Executes a function on the UI thread.
void RunOnUIThread(const std::function<void()>& func, bool block = false); void RunOnUIThread(const std::function<void()>& func, bool block = false);