mirror of
https://git.citron-emu.org/citron/emulator
synced 2026-01-30 06:23:29 +00:00
fix(gamescope): DPI Re-architecture For GameScope Compatability
Signed-off-by: Collecting <collecting@noreply.localhost>
This commit is contained in:
@@ -6089,96 +6089,71 @@ void VolumeButton::ResetMultiplier() {
|
||||
#endif
|
||||
|
||||
static void SetHighDPIAttributes() {
|
||||
const bool is_gamescope = !qgetenv("GAMESCOPE_WIDTH").isEmpty() || qgetenv("XDG_CURRENT_DESKTOP") == "gamescope";
|
||||
|
||||
#ifdef _WIN32
|
||||
// For Windows, we want to avoid scaling artifacts on fractional scaling ratios.
|
||||
// This is done by setting the optimal scaling policy for the primary screen.
|
||||
// Windows logic: Set policy globally.
|
||||
// removed the 'temp QApplication' here because in Qt 6 it locks the DPI logic
|
||||
// before our environment overrides in main() can take effect.
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
|
||||
Qt::HighDpiScaleFactorRoundingPolicy::Round);
|
||||
|
||||
// Create a temporary QApplication.
|
||||
int temp_argc = 0;
|
||||
char** temp_argv = nullptr;
|
||||
QApplication temp{temp_argc, temp_argv};
|
||||
|
||||
// Get the current screen geometry.
|
||||
const QScreen* primary_screen = QGuiApplication::primaryScreen();
|
||||
if (primary_screen == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QRect screen_rect = primary_screen->geometry();
|
||||
const int real_width = screen_rect.width();
|
||||
const int real_height = screen_rect.height();
|
||||
const float real_ratio = primary_screen->logicalDotsPerInch() / 96.0f;
|
||||
|
||||
// Recommended minimum width and height for proper window fit.
|
||||
// Any screen with a lower resolution than this will still have a scale of 1.
|
||||
constexpr float minimum_width = 1350.0f;
|
||||
constexpr float minimum_height = 900.0f;
|
||||
|
||||
const float width_ratio = std::max(1.0f, real_width / minimum_width);
|
||||
const float height_ratio = std::max(1.0f, real_height / minimum_height);
|
||||
|
||||
// Get the lower of the 2 ratios and truncate, this is the maximum integer scale.
|
||||
const float max_ratio = std::trunc(std::min(width_ratio, height_ratio));
|
||||
|
||||
if (max_ratio > real_ratio) {
|
||||
QApplication::setHighDpiScaleFactorRoundingPolicy(
|
||||
Qt::HighDpiScaleFactorRoundingPolicy::Round);
|
||||
} else {
|
||||
QApplication::setHighDpiScaleFactorRoundingPolicy(
|
||||
Qt::HighDpiScaleFactorRoundingPolicy::Floor);
|
||||
}
|
||||
#else
|
||||
// Other OSes should be better than Windows at fractional scaling.
|
||||
QApplication::setHighDpiScaleFactorRoundingPolicy(
|
||||
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
#endif
|
||||
|
||||
// Set the DPI awareness for better scaling on Windows
|
||||
#ifdef _WIN32
|
||||
// Enable Per Monitor DPI Awareness for Windows 8.1+
|
||||
SetProcessDPIAware();
|
||||
|
||||
// For Windows 10+, use Per Monitor v2 DPI Awareness
|
||||
// This provides better scaling for multi-monitor setups
|
||||
HMODULE shcore = LoadLibrary(L"shcore.dll");
|
||||
if (shcore) {
|
||||
typedef HRESULT(WINAPI* SetProcessDpiAwarenessFunc)(int);
|
||||
SetProcessDpiAwarenessFunc setProcessDpiAwareness =
|
||||
(SetProcessDpiAwarenessFunc)GetProcAddress(shcore, "SetProcessDpiAwareness");
|
||||
if (setProcessDpiAwareness) {
|
||||
// PROCESS_PER_MONITOR_DPI_AWARE_V2 = 2
|
||||
setProcessDpiAwareness(2);
|
||||
setProcessDpiAwareness(2); // PROCESS_PER_MONITOR_DPI_AWARE_V2
|
||||
}
|
||||
FreeLibrary(shcore);
|
||||
}
|
||||
#else
|
||||
if (is_gamescope) {
|
||||
// Force 1:1 pixel mapping for Steam Deck to prevent bloated windows.
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
|
||||
Qt::HighDpiScaleFactorRoundingPolicy::Floor);
|
||||
} else {
|
||||
// Standard Linux desktops handle fractional scaling better via PassThrough
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
|
||||
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// 1. Detect Gamescope/Steam Deck hardware
|
||||
const bool is_gamescope = qgetenv("XDG_CURRENT_DESKTOP") == "gamescope" ||
|
||||
!qgetenv("GAMESCOPE_WIDTH").isEmpty() ||
|
||||
const bool is_gamescope = !qgetenv("GAMESCOPE_WIDTH").isEmpty() ||
|
||||
qgetenv("XDG_CURRENT_DESKTOP") == "gamescope" ||
|
||||
!qgetenv("STEAM_DECK").isEmpty();
|
||||
|
||||
if (is_gamescope) {
|
||||
// Kill the SteamOS scaling requests before they can bloat the UI
|
||||
QGuiApplication::setDesktopSettingsAware(false);
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Floor);
|
||||
|
||||
// Force 1:1 pixel ratio
|
||||
qputenv("QT_ENABLE_HIGHDPI_SCALING", "0");
|
||||
qputenv("QT_SCALE_FACTOR", "1");
|
||||
qputenv("QT_SCREEN_SCALE_FACTORS", "1");
|
||||
qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0");
|
||||
|
||||
// Steam Deck has a high physical DPI. Hard-coding 96 DPI prevents text
|
||||
// from being oversized in dialogs like "About" or "Updater".
|
||||
qputenv("QT_FONT_DPI", "96");
|
||||
|
||||
// FORCE X11 backend: Qt 6 scaling overrides are reliably respected under XCB in Gamescope.
|
||||
// Wayland mode in Gamescope often ignores scaling overrides for child windows.
|
||||
qputenv("QT_QPA_PLATFORM", "xcb");
|
||||
|
||||
// Ensure Gamescope compositor handles Citron menus correctly
|
||||
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
|
||||
QCoreApplication::setAttribute(Qt::AA_DontUseNativeDialogs);
|
||||
qputenv("QT_WAYLAND_SHELL_INTEGRATION", "xdg-shell");
|
||||
}
|
||||
|
||||
// 2. NOW setup AppImage environment
|
||||
// 2. Setup AppImage environment
|
||||
const bool is_appimage = !qgetenv("APPIMAGE").isEmpty();
|
||||
if (is_appimage) {
|
||||
qputenv("QT_WAYLAND_DISABLE_EXPLICIT_SYNC", "1");
|
||||
@@ -6231,6 +6206,7 @@ int main(int argc, char* argv[]) {
|
||||
QGuiApplication::setDesktopFileName(QStringLiteral("org.citron_emu.citron"));
|
||||
#endif
|
||||
|
||||
// Call policy attributes BEFORE creating the real QApplication instance
|
||||
SetHighDPIAttributes();
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
@@ -6252,21 +6228,16 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
#ifdef _WIN32
|
||||
// On Windows, updates are applied by the helper script after the app exits.
|
||||
// If we find a staging directory here, it means the helper script failed.
|
||||
// Clean it up to avoid confusion.
|
||||
std::filesystem::path staging_path = app_dir / "update_staging";
|
||||
if (std::filesystem::exists(staging_path)) {
|
||||
try {
|
||||
std::filesystem::remove_all(staging_path);
|
||||
} catch (...) {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
} catch (...) {}
|
||||
}
|
||||
#else
|
||||
// On Linux, apply staged updates at startup as before
|
||||
// On Linux, apply staged updates at startup
|
||||
if (Updater::UpdaterService::HasStagedUpdate(app_dir)) {
|
||||
if (Updater::UpdaterService::ApplyStagedUpdate(app_dir)) {
|
||||
// Show a simple message that update was applied
|
||||
QMessageBox::information(nullptr, QObject::tr("Update Applied"),
|
||||
QObject::tr("Citron has been updated successfully!"));
|
||||
}
|
||||
@@ -6279,10 +6250,7 @@ int main(int argc, char* argv[]) {
|
||||
#endif
|
||||
|
||||
|
||||
// Workaround for QTBUG-85409, for Suzhou numerals the number 1 is actually \u3021
|
||||
// so we can see if we get \u3008 instead
|
||||
// TL;DR all other number formats are consecutive in unicode code points
|
||||
// This bug is fixed in Qt6, specifically 6.0.0-alpha1
|
||||
// Workaround for QTBUG-85409
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
const QLocale locale = QLocale::system();
|
||||
if (QStringLiteral("\u3008") == locale.toString(1)) {
|
||||
@@ -6314,8 +6282,7 @@ int main(int argc, char* argv[]) {
|
||||
QObject::connect(&app, &QGuiApplication::applicationStateChanged, &main_window,
|
||||
&GMainWindow::OnAppFocusStateChanged);
|
||||
|
||||
int result = app.exec();
|
||||
return result;
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
void GMainWindow::OnCheckForUpdates() {
|
||||
|
||||
Reference in New Issue
Block a user