mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2026-02-16 10:40:59 +00:00
274 lines
8.8 KiB
C++
274 lines
8.8 KiB
C++
#include "systemproperties.h"
|
|
#include "utils.h"
|
|
|
|
#include <QGuiApplication>
|
|
#include <QLibraryInfo>
|
|
|
|
#include "streaming/session.h"
|
|
#include "streaming/streamutils.h"
|
|
|
|
#ifdef Q_OS_WIN32
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <Windows.h>
|
|
#endif
|
|
|
|
class SystemPropertyQueryThread : public QThread
|
|
{
|
|
public:
|
|
SystemPropertyQueryThread(SystemProperties* properties)
|
|
: QThread(properties), m_Properties(properties)
|
|
{
|
|
setObjectName("System Properties Async Query Thread");
|
|
}
|
|
|
|
private:
|
|
void run() override
|
|
{
|
|
bool hasHardwareAcceleration;
|
|
bool rendererAlwaysFullScreen;
|
|
bool supportsHdr;
|
|
QSize maximumResolution;
|
|
|
|
Session::getDecoderInfo(m_Properties->testWindow, hasHardwareAcceleration, rendererAlwaysFullScreen, supportsHdr, maximumResolution);
|
|
|
|
// Propagate the decoder properties to the SystemProperties singleton and emit any change signals on the main thread
|
|
QMetaObject::invokeMethod(m_Properties, "updateDecoderProperties",
|
|
Qt::QueuedConnection,
|
|
Q_ARG(bool, hasHardwareAcceleration),
|
|
Q_ARG(bool, rendererAlwaysFullScreen),
|
|
Q_ARG(QSize, maximumResolution),
|
|
Q_ARG(bool, supportsHdr));
|
|
}
|
|
|
|
private:
|
|
SystemProperties* m_Properties;
|
|
};
|
|
|
|
SystemProperties::SystemProperties()
|
|
{
|
|
versionString = QString(VERSION_STR);
|
|
hasDesktopEnvironment = WMUtils::isRunningDesktopEnvironment();
|
|
isRunningWayland = WMUtils::isRunningWayland();
|
|
isRunningXWayland = isRunningWayland && QGuiApplication::platformName() == "xcb";
|
|
usesMaterial3Theme = QLibraryInfo::version() >= QVersionNumber(6, 5, 0);
|
|
QString nativeArch = QSysInfo::currentCpuArchitecture();
|
|
|
|
#ifdef Q_OS_WIN32
|
|
{
|
|
USHORT processArch, machineArch;
|
|
|
|
// Use IsWow64Process2() because it doesn't lie on ARM64
|
|
if (IsWow64Process2(GetCurrentProcess(), &processArch, &machineArch)) {
|
|
switch (machineArch) {
|
|
case IMAGE_FILE_MACHINE_I386:
|
|
nativeArch = "i386";
|
|
break;
|
|
case IMAGE_FILE_MACHINE_AMD64:
|
|
nativeArch = "x86_64";
|
|
break;
|
|
case IMAGE_FILE_MACHINE_ARM64:
|
|
nativeArch = "arm64";
|
|
break;
|
|
}
|
|
}
|
|
|
|
isWow64 = nativeArch != QSysInfo::buildCpuArchitecture();
|
|
}
|
|
#else
|
|
isWow64 = false;
|
|
#endif
|
|
|
|
if (nativeArch == "i386") {
|
|
friendlyNativeArchName = "x86";
|
|
}
|
|
else if (nativeArch == "x86_64") {
|
|
friendlyNativeArchName = "x64";
|
|
}
|
|
else {
|
|
friendlyNativeArchName = nativeArch.toUpper();
|
|
}
|
|
|
|
// Assume we can probably launch a browser if we're in a GUI environment
|
|
hasBrowser = hasDesktopEnvironment;
|
|
|
|
#ifdef HAVE_DISCORD
|
|
hasDiscordIntegration = true;
|
|
#else
|
|
hasDiscordIntegration = false;
|
|
#endif
|
|
|
|
// These will be queried asynchronously to avoid blocking the UI
|
|
hasHardwareAcceleration = true;
|
|
rendererAlwaysFullScreen = false;
|
|
supportsHdr = true;
|
|
maximumResolution = QSize(0, 0);
|
|
}
|
|
|
|
SystemProperties::~SystemProperties()
|
|
{
|
|
waitForAsyncLoad();
|
|
}
|
|
|
|
void SystemProperties::updateDecoderProperties(bool hasHardwareAcceleration, bool rendererAlwaysFullScreen, QSize maximumResolution, bool supportsHdr)
|
|
{
|
|
SDL_assert(testWindow);
|
|
|
|
if (hasHardwareAcceleration != this->hasHardwareAcceleration) {
|
|
this->hasHardwareAcceleration = hasHardwareAcceleration;
|
|
emit hasHardwareAccelerationChanged();
|
|
}
|
|
|
|
if (rendererAlwaysFullScreen != this->rendererAlwaysFullScreen) {
|
|
this->rendererAlwaysFullScreen = rendererAlwaysFullScreen;
|
|
emit rendererAlwaysFullScreenChanged();
|
|
}
|
|
|
|
if (maximumResolution != this->maximumResolution) {
|
|
this->maximumResolution = maximumResolution;
|
|
emit maximumResolutionChanged();
|
|
}
|
|
|
|
if (supportsHdr != this->supportsHdr) {
|
|
this->supportsHdr = supportsHdr;
|
|
emit supportsHdrChanged();
|
|
}
|
|
|
|
SDL_DestroyWindow(testWindow);
|
|
testWindow = nullptr;
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
}
|
|
|
|
QRect SystemProperties::getNativeResolution(int displayIndex)
|
|
{
|
|
// Returns default constructed QRect if out of bounds
|
|
Q_ASSERT(!monitorNativeResolutions.isEmpty());
|
|
return monitorNativeResolutions.value(displayIndex);
|
|
}
|
|
|
|
QRect SystemProperties::getSafeAreaResolution(int displayIndex)
|
|
{
|
|
// Returns default constructed QRect if out of bounds
|
|
Q_ASSERT(!monitorSafeAreaResolutions.isEmpty());
|
|
return monitorSafeAreaResolutions.value(displayIndex);
|
|
}
|
|
|
|
int SystemProperties::getRefreshRate(int displayIndex)
|
|
{
|
|
// Returns 0 if out of bounds
|
|
Q_ASSERT(!monitorRefreshRates.isEmpty());
|
|
return monitorRefreshRates.value(displayIndex);
|
|
}
|
|
|
|
void SystemProperties::startAsyncLoad()
|
|
{
|
|
if (systemPropertyQueryThread) {
|
|
// Already started/completed
|
|
return;
|
|
}
|
|
|
|
// This isn't actually asynchronous (due to the need to synchronize with
|
|
// SdlGamepadKeyNavigation), but we don't query it in the constructor
|
|
// because it's expensive.
|
|
unmappedGamepads = SdlInputHandler::getUnmappedGamepads();
|
|
if (!unmappedGamepads.isEmpty()) {
|
|
emit unmappedGamepadsChanged();
|
|
}
|
|
|
|
// We initialize the video subsystem and test window on the main thread
|
|
// because some platforms (macOS) do not support window creation on
|
|
// non-main threads.
|
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
|
|
SDL_GetError());
|
|
return;
|
|
}
|
|
|
|
// Update display related attributes (max FPS, native resolution, etc).
|
|
refreshDisplays();
|
|
|
|
testWindow = SDL_CreateWindow("", 0, 0, 1280, 720,
|
|
SDL_WINDOW_HIDDEN | StreamUtils::getPlatformWindowFlags());
|
|
if (!testWindow) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
|
"Failed to create test window with platform flags: %s",
|
|
SDL_GetError());
|
|
|
|
testWindow = SDL_CreateWindow("", 0, 0, 1280, 720, SDL_WINDOW_HIDDEN);
|
|
if (!testWindow) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
"Failed to create window for hardware decode test: %s",
|
|
SDL_GetError());
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
return;
|
|
}
|
|
}
|
|
|
|
systemPropertyQueryThread = new SystemPropertyQueryThread(this);
|
|
systemPropertyQueryThread->start();
|
|
}
|
|
|
|
void SystemProperties::waitForAsyncLoad()
|
|
{
|
|
if (systemPropertyQueryThread) {
|
|
systemPropertyQueryThread->wait();
|
|
}
|
|
}
|
|
|
|
void SystemProperties::refreshDisplays()
|
|
{
|
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
|
|
SDL_GetError());
|
|
return;
|
|
}
|
|
|
|
monitorNativeResolutions.clear();
|
|
|
|
SDL_DisplayMode bestMode;
|
|
for (int displayIndex = 0; displayIndex < SDL_GetNumVideoDisplays(); displayIndex++) {
|
|
SDL_DisplayMode desktopMode;
|
|
SDL_Rect safeArea;
|
|
|
|
if (StreamUtils::getNativeDesktopMode(displayIndex, &desktopMode, &safeArea)) {
|
|
if (desktopMode.w <= 8192 && desktopMode.h <= 8192) {
|
|
monitorNativeResolutions.insert(displayIndex, QRect(0, 0, desktopMode.w, desktopMode.h));
|
|
monitorSafeAreaResolutions.insert(displayIndex, QRect(0, 0, safeArea.w, safeArea.h));
|
|
}
|
|
else {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
|
"Skipping resolution over 8K: %dx%d",
|
|
desktopMode.w, desktopMode.h);
|
|
}
|
|
|
|
// Start at desktop mode and work our way up
|
|
bestMode = desktopMode;
|
|
for (int i = 0; i < SDL_GetNumDisplayModes(displayIndex); i++) {
|
|
SDL_DisplayMode mode;
|
|
if (SDL_GetDisplayMode(displayIndex, i, &mode) == 0) {
|
|
if (mode.w == desktopMode.w && mode.h == desktopMode.h) {
|
|
if (mode.refresh_rate > bestMode.refresh_rate) {
|
|
bestMode = mode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Try to normalize values around our our standard refresh rates.
|
|
// Some displays/OSes report values that are slightly off.
|
|
if (bestMode.refresh_rate >= 58 && bestMode.refresh_rate <= 62) {
|
|
monitorRefreshRates.append(60);
|
|
}
|
|
else if (bestMode.refresh_rate >= 28 && bestMode.refresh_rate <= 32) {
|
|
monitorRefreshRates.append(30);
|
|
}
|
|
else {
|
|
monitorRefreshRates.append(bestMode.refresh_rate);
|
|
}
|
|
}
|
|
}
|
|
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
}
|