From 6a1b089a501aca93cd275d853ef8f34a62b904c5 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Wed, 25 Jan 2023 21:16:04 -0500 Subject: [PATCH 1/3] main: Enable High DPI fixes for Qt >= 5.14 This uses Qt's new high DPI application attributes for scaling the current window. However, these aren't perfect as scaling with non integer scales will cause artifacts in UI, icons and other elements. Therefore, we use a heuristic to select an appropriate integer scale value depending on the current screen resolution and applies this to the application. --- src/yuzu/main.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 62aaf41bff..82e4adfe0a 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -4400,6 +4400,46 @@ void GMainWindow::changeEvent(QEvent* event) { #undef main #endif +static void SetHighDPIAttributes() { + // 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)); + + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + + if (max_ratio > real_ratio) { + QApplication::setHighDpiScaleFactorRoundingPolicy( + Qt::HighDpiScaleFactorRoundingPolicy::Round); + } else { + QApplication::setHighDpiScaleFactorRoundingPolicy( + Qt::HighDpiScaleFactorRoundingPolicy::Floor); + } +} + int main(int argc, char* argv[]) { std::unique_ptr config = std::make_unique(); bool has_broken_vulkan = false; @@ -4455,6 +4495,8 @@ int main(int argc, char* argv[]) { } #endif + SetHighDPIAttributes(); + #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // Disables the "?" button on all dialogs. Disabled by default on Qt6. QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); @@ -4462,6 +4504,7 @@ int main(int argc, char* argv[]) { // Enables the core to make the qt created contexts current on std::threads QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); + QApplication app(argc, argv); #ifdef _WIN32 From 5be85c556ed05cd9d751fb7b3f8a331800ee573d Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Wed, 25 Jan 2023 21:16:04 -0500 Subject: [PATCH 2/3] main: Use passthrough scaling for non-windows OSes They should be better than windows when handling fractional scaling ratios. --- src/yuzu/main.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 82e4adfe0a..53249426c0 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -4401,6 +4401,10 @@ void GMainWindow::changeEvent(QEvent* event) { #endif static void SetHighDPIAttributes() { +#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. + // Create a temporary QApplication. int temp_argc = 0; char** temp_argv = nullptr; @@ -4428,9 +4432,6 @@ static void SetHighDPIAttributes() { // 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)); - QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); - if (max_ratio > real_ratio) { QApplication::setHighDpiScaleFactorRoundingPolicy( Qt::HighDpiScaleFactorRoundingPolicy::Round); @@ -4438,6 +4439,14 @@ static void SetHighDPIAttributes() { QApplication::setHighDpiScaleFactorRoundingPolicy( Qt::HighDpiScaleFactorRoundingPolicy::Floor); } +#else + // Other OSes should be better than Windows at fractional scaling. + QApplication::setHighDpiScaleFactorRoundingPolicy( + Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); +#endif + + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); } int main(int argc, char* argv[]) { From ad6cec71ecd61aa2533d9efa89b68837516f8464 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Wed, 25 Jan 2023 21:16:05 -0500 Subject: [PATCH 3/3] main: Convert to device independent coordinates for scaling devicePixelRatioF() returns the scaling ratio when high dpi scaling is enabled. When high dpi scaling is enabled, the raw screen coordinate system is scaled to device independent coordinates. --- src/yuzu/applets/qt_software_keyboard.cpp | 2 +- src/yuzu/main.cpp | 17 +++++++++++------ src/yuzu/util/overlay_dialog.cpp | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp index 734b0ea40c..4ae49506d8 100644 --- a/src/yuzu/applets/qt_software_keyboard.cpp +++ b/src/yuzu/applets/qt_software_keyboard.cpp @@ -575,7 +575,7 @@ void QtSoftwareKeyboardDialog::MoveAndResizeWindow(QPoint pos, QSize size) { QDialog::resize(size); // High DPI - const float dpi_scale = qApp->screenAt(pos)->logicalDotsPerInch() / 96.0f; + const float dpi_scale = screen()->logicalDotsPerInch() / 96.0f; RescaleKeyboardElements(size.width(), size.height(), dpi_scale); } diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 53249426c0..ee8ea82fd7 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -680,8 +680,10 @@ void GMainWindow::SoftwareKeyboardShowNormal() { const auto y = layout.screen.top; const auto w = layout.screen.GetWidth(); const auto h = layout.screen.GetHeight(); + const auto scale_ratio = devicePixelRatioF(); - software_keyboard->ShowNormalKeyboard(render_window->mapToGlobal(QPoint(x, y)), QSize(w, h)); + software_keyboard->ShowNormalKeyboard(render_window->mapToGlobal(QPoint(x, y) / scale_ratio), + QSize(w, h) / scale_ratio); } void GMainWindow::SoftwareKeyboardShowTextCheck( @@ -714,9 +716,11 @@ void GMainWindow::SoftwareKeyboardShowInline( (1.0f - appear_parameters.key_top_scale_y)))); const auto w = static_cast(layout.screen.GetWidth() * appear_parameters.key_top_scale_x); const auto h = static_cast(layout.screen.GetHeight() * appear_parameters.key_top_scale_y); + const auto scale_ratio = devicePixelRatioF(); software_keyboard->ShowInlineKeyboard(std::move(appear_parameters), - render_window->mapToGlobal(QPoint(x, y)), QSize(w, h)); + render_window->mapToGlobal(QPoint(x, y) / scale_ratio), + QSize(w, h) / scale_ratio); } void GMainWindow::SoftwareKeyboardHideInline() { @@ -796,10 +800,11 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url, } const auto& layout = render_window->GetFramebufferLayout(); - web_browser_view.resize(layout.screen.GetWidth(), layout.screen.GetHeight()); - web_browser_view.move(layout.screen.left, layout.screen.top + menuBar()->height()); - web_browser_view.setZoomFactor(static_cast(layout.screen.GetWidth()) / - static_cast(Layout::ScreenUndocked::Width)); + const auto scale_ratio = devicePixelRatioF(); + web_browser_view.resize(layout.screen.GetWidth() / scale_ratio, + layout.screen.GetHeight() / scale_ratio); + web_browser_view.move(layout.screen.left / scale_ratio, + (layout.screen.top / scale_ratio) + menuBar()->height()); web_browser_view.setFocus(); web_browser_view.show(); diff --git a/src/yuzu/util/overlay_dialog.cpp b/src/yuzu/util/overlay_dialog.cpp index 796f5bf41e..ee35a3e151 100644 --- a/src/yuzu/util/overlay_dialog.cpp +++ b/src/yuzu/util/overlay_dialog.cpp @@ -163,7 +163,7 @@ void OverlayDialog::MoveAndResizeWindow() { const auto height = static_cast(parentWidget()->height()); // High DPI - const float dpi_scale = parentWidget()->windowHandle()->screen()->logicalDotsPerInch() / 96.0f; + const float dpi_scale = screen()->logicalDotsPerInch() / 96.0f; const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; const auto body_text_font_size =