Rework Qt window handling during SDL window creation

- Avoids blindly enumeration all top-level windows
- Properly tracks screen changes during the connection process
- Starts stream window minimized if the Qt window was minimized

Fixes #1231
This commit is contained in:
Cameron Gutman 2024-03-24 21:34:54 -05:00
parent 0d67fd1ff0
commit a45695a22a
3 changed files with 48 additions and 41 deletions

View file

@ -176,7 +176,7 @@ Item {
gc()
// Run the streaming session to completion
session.exec(Screen.virtualX, Screen.virtualY)
session.exec(Window.window)
}
sourceComponent: Item {}

View file

@ -52,6 +52,7 @@
#include <QGuiApplication>
#include <QCursor>
#include <QWindow>
#include <QScreen>
#define CONN_TEST_SERVER "qt.conntest.moonlight-stream.org"
@ -549,8 +550,7 @@ Session::Session(NvComputer* computer, NvApp& app, StreamingPreferences *prefere
m_DecoderLock(0),
m_AudioDisabled(false),
m_AudioMuted(false),
m_DisplayOriginX(0),
m_DisplayOriginY(0),
m_QtWindow(nullptr),
m_UnexpectedTermination(true), // Failure prior to streaming is unexpected
m_InputHandler(nullptr),
m_MouseEmulationRefCount(0),
@ -1117,26 +1117,38 @@ void Session::getWindowDimensions(int& x, int& y,
// Create our window on the same display that Qt's UI
// was being displayed on.
else {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Qt UI screen is at (%d,%d)",
m_DisplayOriginX, m_DisplayOriginY);
for (int i = 0; i < SDL_GetNumVideoDisplays(); i++) {
SDL_Rect displayBounds;
Q_ASSERT(m_QtWindow != nullptr);
if (m_QtWindow != nullptr) {
QScreen* screen = m_QtWindow->screen();
if (screen != nullptr) {
QRect displayRect = screen->geometry();
if (SDL_GetDisplayBounds(i, &displayBounds) == 0) {
if (displayBounds.x == m_DisplayOriginX &&
displayBounds.y == m_DisplayOriginY) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"SDL found matching display %d",
i);
displayIndex = i;
break;
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Qt UI screen is at (%d,%d)",
displayRect.x(), displayRect.y());
for (int i = 0; i < SDL_GetNumVideoDisplays(); i++) {
SDL_Rect displayBounds;
if (SDL_GetDisplayBounds(i, &displayBounds) == 0) {
if (displayBounds.x == displayRect.x() &&
displayBounds.y == displayRect.y()) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"SDL found matching display %d",
i);
displayIndex = i;
break;
}
}
else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetDisplayBounds(%d) failed: %s",
i, SDL_GetError());
}
}
}
else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetDisplayBounds(%d) failed: %s",
i, SDL_GetError());
"Qt window is not associated with a QScreen!");
}
}
}
@ -1503,10 +1515,9 @@ public:
Session* m_Session;
};
void Session::exec(int displayOriginX, int displayOriginY)
void Session::exec(QWindow* qtWindow)
{
m_DisplayOriginX = displayOriginX;
m_DisplayOriginY = displayOriginY;
m_QtWindow = qtWindow;
// Use a separate thread for the streaming session on X11 or Wayland
// to ensure we don't stomp on Qt's GL context. This breaks when using
@ -1610,15 +1621,14 @@ void Session::execInternal()
// We always want a resizable window with High DPI enabled
Uint32 defaultWindowFlags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
// If we're starting in windowed mode and the Moonlight GUI is maximized,
// match that with the streaming window.
if (!m_IsFullScreen) {
QWindowList windows = QGuiApplication::topLevelWindows();
for (const QWindow* window : windows) {
if (window->windowState() & Qt::WindowMaximized) {
defaultWindowFlags |= SDL_WINDOW_MAXIMIZED;
break;
}
// If we're starting in windowed mode and the Moonlight GUI is maximized or
// minimized, match that with the streaming window.
if (!m_IsFullScreen && m_QtWindow != nullptr) {
if (m_QtWindow->windowState() & Qt::WindowMaximized) {
defaultWindowFlags |= SDL_WINDOW_MAXIMIZED;
}
else if (m_QtWindow->windowState() & Qt::WindowMinimized) {
defaultWindowFlags |= SDL_WINDOW_MINIMIZED;
}
}
@ -1662,16 +1672,13 @@ void Session::execInternal()
// HACK: Remove once proper Dark Mode support lands in SDL
#ifdef Q_OS_WIN32
{
BOOL darkModeEnabled = FALSE;
if (m_QtWindow != nullptr) {
BOOL darkModeEnabled;
// Query whether dark mode is enabled for our Qt window (which tracks the OS dark mode state)
QWindowList windows = QGuiApplication::topLevelWindows();
for (const QWindow* window : windows) {
if (SUCCEEDED(DwmGetWindowAttribute((HWND)window->winId(), DWMWA_USE_IMMERSIVE_DARK_MODE, &darkModeEnabled, sizeof(darkModeEnabled))) ||
SUCCEEDED(DwmGetWindowAttribute((HWND)window->winId(), DWMWA_USE_IMMERSIVE_DARK_MODE_OLD, &darkModeEnabled, sizeof(darkModeEnabled)))) {
break;
}
if (FAILED(DwmGetWindowAttribute((HWND)m_QtWindow->winId(), DWMWA_USE_IMMERSIVE_DARK_MODE, &darkModeEnabled, sizeof(darkModeEnabled))) &&
FAILED(DwmGetWindowAttribute((HWND)m_QtWindow->winId(), DWMWA_USE_IMMERSIVE_DARK_MODE_OLD, &darkModeEnabled, sizeof(darkModeEnabled)))) {
darkModeEnabled = FALSE;
}
SDL_SysWMinfo info;

View file

@ -1,6 +1,7 @@
#pragma once
#include <QSemaphore>
#include <QWindow>
#include <Limelight.h>
#include <opus_multistream.h>
@ -26,7 +27,7 @@ public:
// Use Session::exec() or DeferredSessionCleanupTask instead.
virtual ~Session() {};
Q_INVOKABLE void exec(int displayOriginX, int displayOriginY);
Q_INVOKABLE void exec(QWindow* qtWindow);
static
void getDecoderInfo(SDL_Window* window,
@ -166,8 +167,7 @@ private:
bool m_AudioDisabled;
bool m_AudioMuted;
Uint32 m_FullScreenFlag;
int m_DisplayOriginX;
int m_DisplayOriginY;
QWindow* m_QtWindow;
bool m_ThreadedExec;
bool m_UnexpectedTermination;
SdlInputHandler* m_InputHandler;