2019-03-23 19:05:08 +00:00
|
|
|
#include "systemproperties.h"
|
2020-02-09 05:31:04 +00:00
|
|
|
#include "utils.h"
|
2019-03-23 19:05:08 +00:00
|
|
|
|
2019-04-22 01:31:11 +00:00
|
|
|
#include <QGuiApplication>
|
|
|
|
|
2019-03-23 19:05:08 +00:00
|
|
|
#include "streaming/session.h"
|
|
|
|
#include "streaming/streamutils.h"
|
|
|
|
|
2020-12-28 18:27:22 +00:00
|
|
|
#ifdef Q_OS_WIN32
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
#include <Windows.h>
|
|
|
|
#endif
|
|
|
|
|
2019-03-23 19:05:08 +00:00
|
|
|
SystemProperties::SystemProperties()
|
|
|
|
{
|
2020-05-10 05:20:39 +00:00
|
|
|
versionString = QString(VERSION_STR);
|
2021-03-13 21:20:58 +00:00
|
|
|
hasDesktopEnvironment = WMUtils::isRunningDesktopEnvironment();
|
2020-02-09 05:31:04 +00:00
|
|
|
isRunningWayland = WMUtils::isRunningWayland();
|
|
|
|
isRunningXWayland = isRunningWayland && QGuiApplication::platformName() == "xcb";
|
2020-12-28 18:27:22 +00:00
|
|
|
QString nativeArch = QSysInfo::currentCpuArchitecture();
|
2019-03-23 19:05:08 +00:00
|
|
|
|
|
|
|
#ifdef Q_OS_WIN32
|
2020-12-28 18:27:22 +00:00
|
|
|
{
|
|
|
|
USHORT processArch, machineArch;
|
|
|
|
|
|
|
|
// Use IsWow64Process2 on TH2 and later, because it supports ARM64
|
2020-12-28 19:32:02 +00:00
|
|
|
auto fnIsWow64Process2 = (decltype(IsWow64Process2)*)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process2");
|
|
|
|
if (fnIsWow64Process2 != nullptr && fnIsWow64Process2(GetCurrentProcess(), &processArch, &machineArch)) {
|
2020-12-28 18:27:22 +00:00
|
|
|
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();
|
|
|
|
}
|
2019-03-23 19:05:08 +00:00
|
|
|
#else
|
|
|
|
isWow64 = false;
|
|
|
|
#endif
|
|
|
|
|
2020-12-28 18:27:22 +00:00
|
|
|
if (nativeArch == "i386") {
|
|
|
|
friendlyNativeArchName = "x86";
|
|
|
|
}
|
|
|
|
else if (nativeArch == "x86_64") {
|
|
|
|
friendlyNativeArchName = "x64";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
friendlyNativeArchName = nativeArch.toUpper();
|
|
|
|
}
|
|
|
|
|
2020-02-09 19:41:44 +00:00
|
|
|
// Assume we can probably launch a browser if we're in a GUI environment
|
2021-03-13 21:20:58 +00:00
|
|
|
hasBrowser = hasDesktopEnvironment;
|
2019-03-23 19:05:08 +00:00
|
|
|
|
2019-06-30 00:40:30 +00:00
|
|
|
#ifdef HAVE_DISCORD
|
|
|
|
hasDiscordIntegration = true;
|
|
|
|
#else
|
|
|
|
hasDiscordIntegration = false;
|
|
|
|
#endif
|
|
|
|
|
2019-03-23 19:05:08 +00:00
|
|
|
unmappedGamepads = SdlInputHandler::getUnmappedGamepads();
|
|
|
|
|
|
|
|
// Populate data that requires talking to SDL. We do it all in one shot
|
|
|
|
// and cache the results to speed up future queries on this data.
|
|
|
|
querySdlVideoInfo();
|
|
|
|
|
|
|
|
Q_ASSERT(maximumStreamingFrameRate >= 60);
|
|
|
|
Q_ASSERT(!monitorDesktopResolutions.isEmpty());
|
|
|
|
Q_ASSERT(!monitorNativeResolutions.isEmpty());
|
|
|
|
}
|
|
|
|
|
|
|
|
QRect SystemProperties::getDesktopResolution(int displayIndex)
|
|
|
|
{
|
|
|
|
// Returns default constructed QRect if out of bounds
|
|
|
|
return monitorDesktopResolutions.value(displayIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
QRect SystemProperties::getNativeResolution(int displayIndex)
|
|
|
|
{
|
|
|
|
// Returns default constructed QRect if out of bounds
|
|
|
|
return monitorNativeResolutions.value(displayIndex);
|
|
|
|
}
|
|
|
|
|
2021-03-07 22:48:10 +00:00
|
|
|
class QuerySdlVideoThread : public QThread
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
QuerySdlVideoThread(SystemProperties* me) :
|
|
|
|
QThread(nullptr),
|
|
|
|
m_Me(me) {}
|
|
|
|
|
|
|
|
void run() override
|
|
|
|
{
|
|
|
|
m_Me->querySdlVideoInfoInternal();
|
|
|
|
}
|
|
|
|
|
|
|
|
SystemProperties* m_Me;
|
|
|
|
};
|
|
|
|
|
2019-03-23 19:05:08 +00:00
|
|
|
void SystemProperties::querySdlVideoInfo()
|
2021-03-07 22:48:10 +00:00
|
|
|
{
|
|
|
|
if (WMUtils::isRunningX11() || WMUtils::isRunningWayland()) {
|
|
|
|
// Use a separate thread to temporarily initialize SDL
|
|
|
|
// video to avoid stomping on Qt's X11 and OGL state.
|
|
|
|
QuerySdlVideoThread thread(this);
|
|
|
|
thread.start();
|
|
|
|
thread.wait();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
querySdlVideoInfoInternal();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SystemProperties::querySdlVideoInfoInternal()
|
2019-03-23 19:05:08 +00:00
|
|
|
{
|
2019-03-24 00:46:42 +00:00
|
|
|
hasHardwareAcceleration = false;
|
2019-03-23 19:05:08 +00:00
|
|
|
|
2020-12-08 01:58:42 +00:00
|
|
|
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).
|
2021-03-07 22:48:10 +00:00
|
|
|
// We call the internal variant because we're already in a safe thread context.
|
|
|
|
refreshDisplaysInternal();
|
2020-12-08 01:58:42 +00:00
|
|
|
|
|
|
|
SDL_Window* 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Session::getDecoderInfo(testWindow, hasHardwareAcceleration, rendererAlwaysFullScreen, maximumResolution);
|
|
|
|
|
|
|
|
SDL_DestroyWindow(testWindow);
|
2019-03-23 19:05:08 +00:00
|
|
|
|
2020-12-08 01:58:42 +00:00
|
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
|
|
}
|
|
|
|
|
2021-03-07 22:48:10 +00:00
|
|
|
class RefreshDisplaysThread : public QThread
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
RefreshDisplaysThread(SystemProperties* me) :
|
|
|
|
QThread(nullptr),
|
|
|
|
m_Me(me) {}
|
|
|
|
|
|
|
|
void run() override
|
|
|
|
{
|
|
|
|
m_Me->refreshDisplaysInternal();
|
|
|
|
}
|
|
|
|
|
|
|
|
SystemProperties* m_Me;
|
|
|
|
};
|
|
|
|
|
2020-12-08 01:58:42 +00:00
|
|
|
void SystemProperties::refreshDisplays()
|
2021-03-07 22:48:10 +00:00
|
|
|
{
|
|
|
|
if (WMUtils::isRunningX11() || WMUtils::isRunningWayland()) {
|
|
|
|
// Use a separate thread to temporarily initialize SDL
|
|
|
|
// video to avoid stomping on Qt's X11 and OGL state.
|
|
|
|
RefreshDisplaysThread thread(this);
|
|
|
|
thread.start();
|
|
|
|
thread.wait();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
refreshDisplaysInternal();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SystemProperties::refreshDisplaysInternal()
|
2020-12-08 01:58:42 +00:00
|
|
|
{
|
2019-04-22 00:43:38 +00:00
|
|
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
|
|
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
|
|
"SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s",
|
|
|
|
SDL_GetError());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-08 01:58:42 +00:00
|
|
|
monitorDesktopResolutions.clear();
|
|
|
|
monitorNativeResolutions.clear();
|
|
|
|
|
|
|
|
// Never let the maximum drop below 60 FPS
|
|
|
|
maximumStreamingFrameRate = 60;
|
|
|
|
|
2019-03-23 19:05:08 +00:00
|
|
|
SDL_DisplayMode bestMode;
|
|
|
|
for (int displayIndex = 0; displayIndex < SDL_GetNumVideoDisplays(); displayIndex++) {
|
|
|
|
SDL_DisplayMode desktopMode;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = SDL_GetDesktopDisplayMode(displayIndex, &desktopMode);
|
|
|
|
if (err == 0) {
|
2020-04-04 19:46:42 +00:00
|
|
|
if (desktopMode.w <= 8192 && desktopMode.h <= 8192) {
|
|
|
|
monitorDesktopResolutions.insert(displayIndex, QRect(0, 0, desktopMode.w, desktopMode.h));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
|
|
|
"Skipping resolution over 8K: %dx%d",
|
|
|
|
desktopMode.w, desktopMode.h);
|
|
|
|
}
|
2019-03-23 19:05:08 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
|
|
"SDL_GetDesktopDisplayMode() failed: %s",
|
|
|
|
SDL_GetError());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (StreamUtils::getRealDesktopMode(displayIndex, &desktopMode)) {
|
2020-04-04 19:46:42 +00:00
|
|
|
if (desktopMode.w <= 8192 && desktopMode.h <= 8192) {
|
|
|
|
monitorNativeResolutions.insert(displayIndex, QRect(0, 0, desktopMode.w, desktopMode.h));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
|
|
|
"Skipping resolution over 8K: %dx%d",
|
|
|
|
desktopMode.w, desktopMode.h);
|
|
|
|
}
|
2019-03-23 19:05:08 +00:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
maximumStreamingFrameRate = qMax(maximumStreamingFrameRate, bestMode.refresh_rate);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-22 00:43:38 +00:00
|
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
2019-03-23 19:05:08 +00:00
|
|
|
}
|