summaryrefslogtreecommitdiffstats
path: root/src/gui/kernel/qhighdpiscaling.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/kernel/qhighdpiscaling.cpp')
-rw-r--r--src/gui/kernel/qhighdpiscaling.cpp728
1 files changed, 389 insertions, 339 deletions
diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp
index 496cae7c26..a0e1b48dcb 100644
--- a/src/gui/kernel/qhighdpiscaling.cpp
+++ b/src/gui/kernel/qhighdpiscaling.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtGui module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhighdpiscaling_p.h"
#include "qguiapplication.h"
@@ -49,17 +13,13 @@
#include <QtCore/qmetaobject.h>
#include <algorithm>
+#include <optional>
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(lcScaling, "qt.scaling");
+Q_LOGGING_CATEGORY(lcHighDpi, "qt.highdpi");
#ifndef QT_NO_HIGHDPISCALING
-static const char legacyDevicePixelEnvVar[] = "QT_DEVICE_PIXEL_RATIO";
-
-// Note: QT_AUTO_SCREEN_SCALE_FACTOR is Done on X11, and should be kept
-// working as-is. It's Deprecated on all other platforms.
-static const char legacyAutoScreenEnvVar[] = "QT_AUTO_SCREEN_SCALE_FACTOR";
static const char enableHighDpiScalingEnvVar[] = "QT_ENABLE_HIGHDPI_SCALING";
static const char scaleFactorEnvVar[] = "QT_SCALE_FACTOR";
@@ -68,47 +28,38 @@ static const char scaleFactorRoundingPolicyEnvVar[] = "QT_SCALE_FACTOR_ROUNDING_
static const char dpiAdjustmentPolicyEnvVar[] = "QT_DPI_ADJUSTMENT_POLICY";
static const char usePhysicalDpiEnvVar[] = "QT_USE_PHYSICAL_DPI";
-// Per-screen scale factors for named screens set with QT_SCREEN_SCALE_FACTORS
-// are stored here. Use a global hash to keep the factor across screen
-// disconnect/connect cycles where the screen object may be deleted.
-typedef QHash<QString, qreal> QScreenScaleFactorHash;
-Q_GLOBAL_STATIC(QScreenScaleFactorHash, qNamedScreenScaleFactors);
+static std::optional<QString> qEnvironmentVariableOptionalString(const char *name)
+{
+ if (!qEnvironmentVariableIsSet(name))
+ return std::nullopt;
-// Reads and interprets the given environment variable as a bool,
-// returns the default value if not set.
-static bool qEnvironmentVariableAsBool(const char *name, bool defaultValue)
+ return std::optional(qEnvironmentVariable(name));
+}
+
+static std::optional<QByteArray> qEnvironmentVariableOptionalByteArray(const char *name)
+{
+ if (!qEnvironmentVariableIsSet(name))
+ return std::nullopt;
+
+ return std::optional(qgetenv(name));
+}
+
+static std::optional<int> qEnvironmentVariableOptionalInt(const char *name)
{
bool ok = false;
- int value = qEnvironmentVariableIntValue(name, &ok);
- return ok ? value > 0 : defaultValue;
+ const int value = qEnvironmentVariableIntValue(name, &ok);
+ auto opt = ok ? std::optional(value) : std::nullopt;
+ return opt;
}
-static inline qreal initialGlobalScaleFactor()
+static std::optional<qreal> qEnvironmentVariableOptionalReal(const char *name)
{
+ if (!qEnvironmentVariableIsSet(name))
+ return std::nullopt;
- qreal result = 1;
- if (qEnvironmentVariableIsSet(scaleFactorEnvVar)) {
- bool ok;
- const qreal f = qEnvironmentVariable(scaleFactorEnvVar).toDouble(&ok);
- if (ok && f > 0) {
- qCDebug(lcScaling) << "Apply " << scaleFactorEnvVar << f;
- result = f;
- }
- } else {
- // Check for deprecated environment variables.
- if (qEnvironmentVariableIsSet(legacyDevicePixelEnvVar)) {
- qWarning("Warning: %s is deprecated. Instead use:\n"
- " %s to enable platform plugin controlled per-screen factors.\n"
- " %s to set per-screen DPI.\n"
- " %s to set the application global scale factor.",
- legacyDevicePixelEnvVar, legacyAutoScreenEnvVar, screenFactorsEnvVar, scaleFactorEnvVar);
-
- int dpr = qEnvironmentVariableIntValue(legacyDevicePixelEnvVar);
- if (dpr > 0)
- result = dpr;
- }
- }
- return result;
+ bool ok = false;
+ const qreal value = qEnvironmentVariable(name).toDouble(&ok);
+ return ok ? std::optional(value) : std::nullopt;
}
/*!
@@ -121,8 +72,9 @@ static inline qreal initialGlobalScaleFactor()
\brief Collection of utility functions for UI scaling.
QHighDpiScaling implements utility functions for high-dpi scaling for use
- on operating systems that provide limited support for native scaling. In
- addition this functionality can be used for simulation and testing purposes.
+ on operating systems that provide limited support for native scaling, such
+ as Windows, X11, and Android. In addition this functionality can be used
+ for simulation and testing purposes.
The functions support scaling between the device independent coordinate
system used by Qt applications and the native coordinate system used by
@@ -152,14 +104,13 @@ static inline qreal initialGlobalScaleFactor()
pixels.
The devicePixelRatio seen by applications is the product of the Qt scale
- factor and the OS scale factor. The value of the scale factors may be 1,
- in which case two or more of the coordinate systems are equivalent. Platforms
- that (may) have an OS scale factor include \macos, iOS and Wayland.
+ factor and the OS scale factor (see QWindow::devicePixelRatio()). The value
+ of the scale factors may be 1, in which case two or more of the coordinate
+ systems are equivalent. Platforms that (may) have an OS scale factor include
+ macOS, iOS, Wayland, and Web(Assembly).
- Note that the functions in this file do not work with the OS scale factor
- directly and are limited to converting between device independent and native
- pixels. The OS scale factor is accounted for by QWindow::devicePixelRatio()
- and similar functions.
+ Note that the API implemented in this file do use the OS scale factor, and
+ is used for converting between device independent and native pixels only.
Configuration Examples:
@@ -172,7 +123,7 @@ static inline qreal initialGlobalScaleFactor()
| Display 100 x 100 |
-----------------------------------------------------
- 'Retina Device': Device Independent Pixels = Native Pixels
+ '2x Apple Device': Device Independent Pixels = Native Pixels
--------------------------------------------------- devicePixelRatio: 2
| Application / Qt Gui 100 x 100 |
| | Qt Scale Factor: 1
@@ -181,7 +132,7 @@ static inline qreal initialGlobalScaleFactor()
| Display 200 x 200 |
-----------------------------------------------------
- '2x Qt Scaling': Native Pixels = Device Pixels
+ 'Windows at 200%': Native Pixels = Device Pixels
--------------------------------------------------- devicePixelRatio: 2
| Application / Qt Gui 100 x 100 |
|---------------------------------------------------| Qt Scale Factor: 2
@@ -190,112 +141,123 @@ static inline qreal initialGlobalScaleFactor()
| Display 200 x 200 |
-----------------------------------------------------
- The Qt Scale Factor is the product of two sub-scale factors, which
- are independently either set or determined by the platform plugin.
- Several APIs are offered for this, targeting both developers and
- end users. All scale factors are of type qreal.
-
- 1) A global scale factor
- The QT_SCALE_FACTOR environment variable can be used to set
- a global scale factor for all windows in the process. This
- is useful for testing and debugging (you can simulate any
- devicePixelRatio without needing access to special hardware),
- and perhaps also for targeting a specific application to
- a specific display type (embedded use cases).
-
- 2) Per-screen scale factors
- Some platform plugins support providing a per-screen scale
- factor based on display density information. These platforms
- include X11, Windows, and Android.
-
- There are two APIs for enabling or disabling this behavior:
- - The QT_AUTO_SCREEN_SCALE_FACTOR environment variable.
- - The AA_EnableHighDpiScaling and AA_DisableHighDpiScaling
- application attributes
-
- Enabling either will make QHighDpiScaling call QPlatformScreen::pixelDensity()
- and use the value provided as the scale factor for the screen in
- question. Disabling is done on a 'veto' basis where either the
- environment or the application can disable the scaling. The intended use
- cases are 'My system is not providing correct display density
- information' and 'My application needs to work in display pixels',
- respectively.
-
- The QT_SCREEN_SCALE_FACTORS environment variable can be used to set the screen
- scale factors manually. Set this to a semicolon-separated
- list of scale factors (matching the order of QGuiApplications::screens()),
- or to a list of name=value pairs (where name matches QScreen::name()).
-
- Coordinate conversion functions must be used when writing code that passes
- geometry across the Qt Gui / Platform plugin boundary. The main conversion
- functions are:
- T toNativePixels(T, QWindow *)
- T fromNativePixels(T, QWindow*)
-
- The following classes in QtGui use native pixels, for the convenience of the
- platform plugins:
- QPlatformWindow
- QPlatformScreen
- QWindowSystemInterface (API only - Events are in device independent pixels)
-
- As a special consideration platform plugin code should be careful about
- calling QtGui geometry accessor functions:
- QRect r = window->geometry();
- Here the returned geometry is in device independent pixels. Add a conversion call:
- QRect r = QHighDpi::toNativePixels(window->geometry());
- (Avoiding calling QWindow and instead using the QPlatformWindow geometry
- might be a better course of action in this case.)
+ * Configuration
+
+ - Enabling: In Qt 6, high-dpi scaling (the functionality implemented in this file)
+ is always enabled. The Qt scale factor value is typically determined by the
+ QPlatformScreen implementation - see below.
+
+ There is one environment variable based opt-out option: set QT_ENABLE_HIGHDPI_SCALING=0.
+ Keep in mind that this does not affect the OS scale factor, which is controlled by
+ the operating system.
+
+ - Qt scale factor value: The Qt scale factor is the product of the screen scale
+ factor and the global scale factor, which are independently either set or determined
+ by the platform plugin. Several APIs are offered for this, targeting both developers
+ and end users. All scale factors are of type qreal.
+
+ 1) Per-screen scale factors
+
+ Per-screen scale factors are computed based on logical DPI provided by
+ by the platform plugin.
+
+ The platform plugin implements DPI accessor functions:
+ QDpi QPlatformScreen::logicalDpi()
+ QDpi QPlatformScreen::logicalBaseDpi()
+
+ QHighDpiScaling then computes the per-screen scale factor as follows:
+
+ factor = logicalDpi / logicalBaseDpi
+
+ Alternatively, QT_SCREEN_SCALE_FACTORS can be used to set the screen
+ scale factors.
+
+ 2) The global scale factor
+
+ The QT_SCALE_FACTOR environment variable can be used to set a global scale
+ factor which applies to all application windows. This allows developing and
+ testing at any DPR, independently of available hardware and without changing
+ global desktop settings.
+
+ - Rounding
+
+ Qt 6 does not round scale factors by default. Qt 5 rounds the screen scale factor
+ to the nearest integer (except for Qt on Android which does not round).
+
+ The rounding policy can be set by the application, or on the environment:
+
+ Application (C++): QGuiApplication::setHighDpiScaleFactorRoundingPolicy()
+ User (environment): QT_SCALE_FACTOR_ROUNDING_POLICY
+
+ Note that the OS scale factor, and global scale factors set with QT_SCALE_FACTOR
+ are never rounded by Qt.
+
+ * C++ API Overview
+
+ - Coordinate Conversion ("scaling")
+
+ The QHighDpi namespace provides several functions for converting geometry
+ between the device independent and native coordinate systems. These should
+ be used when calling "QPlatform*" API from QtGui. Callers are responsible
+ for selecting a function variant based on geometry type:
+
+ Type From Native To Native
+ local : QHighDpi::fromNativeLocalPosition() QHighDpi::toNativeLocalPosition()
+ global (screen) : QHighDpi::fromNativeGlobalPosition() QHighDpi::toNativeGlobalPosition()
+ QWindow::geometry() : QHighDpi::fromNativeWindowGeometry() QHighDpi::toNativeWindowGeometry()
+ sizes, margins, etc : QHighDpi::fromNativePixels() QHighDpi::toNativePixels()
+
+ The conversion functions take two arguments; the geometry and a context:
+
+ QSize nativeSize = toNativePixels(deviceIndependentSize, window);
+
+ The context is usually a QWindow instance, but can also be a QScreen instance,
+ or the corresponding QPlatform classes.
+
+ - Activation
+
+ QHighDpiScaling::isActive() returns true iff
+ Qt high-dpi scaling is enabled (e.g. with AA_EnableHighDpiScaling) AND
+ there is a Qt scale factor != 1
+
+ (the value of the OS scale factor does not affect this API)
+
+ - Calling QtGui from the platform plugins
+
+ Platform plugin code should be careful about calling QtGui geometry accessor
+ functions like geometry():
+
+ QRect r = window->geometry();
+
+ In this case the returned geometry is in the wrong coordinate system (device independent
+ instead of native pixels). Fix this by adding a conversion call:
+
+ QRect r = QHighDpi::toNativeWindowGeometry(window->geometry());
+
+ (Also consider if the call to QtGui is really needed - prefer calling QPlatform* API.)
*/
qreal QHighDpiScaling::m_factor = 1.0;
bool QHighDpiScaling::m_active = false; //"overall active" - is there any scale factor set.
-bool QHighDpiScaling::m_usePixelDensity = false; // use scale factor from platform plugin
-bool QHighDpiScaling::m_pixelDensityScalingActive = false; // pixel density scale factor > 1
+bool QHighDpiScaling::m_usePlatformPluginDpi = false; // use scale factor based on platform plugin DPI
+bool QHighDpiScaling::m_platformPluginDpiScalingActive = false; // platform plugin DPI gives a scale factor > 1
bool QHighDpiScaling::m_globalScalingActive = false; // global scale factor is active
bool QHighDpiScaling::m_screenFactorSet = false; // QHighDpiScaling::setScreenFactor has been used
-
-/*
- Initializes the QHighDpiScaling global variables. Called before the
- platform plugin is created.
-*/
-
-static inline bool usePixelDensity()
-{
- // Determine if we should set a scale factor based on the pixel density
- // reported by the platform plugin. There are several enablers and several
- // disablers. A single disable may veto all other enablers.
-
- // First, check of there is an explicit disable.
- if (QCoreApplication::testAttribute(Qt::AA_DisableHighDpiScaling))
- return false;
- bool screenEnvValueOk;
- const int screenEnvValue = qEnvironmentVariableIntValue(legacyAutoScreenEnvVar, &screenEnvValueOk);
- if (screenEnvValueOk && screenEnvValue < 1)
- return false;
- bool enableEnvValueOk;
- const int enableEnvValue = qEnvironmentVariableIntValue(enableHighDpiScalingEnvVar, &enableEnvValueOk);
- if (enableEnvValueOk && enableEnvValue < 1)
- return false;
-
- // Then return if there was an enable.
- return QCoreApplication::testAttribute(Qt::AA_EnableHighDpiScaling)
- || (screenEnvValueOk && screenEnvValue > 0)
- || (enableEnvValueOk && enableEnvValue > 0)
- || (qEnvironmentVariableIsSet(legacyDevicePixelEnvVar)
- && qEnvironmentVariable(legacyDevicePixelEnvVar).compare(QLatin1String("auto"), Qt::CaseInsensitive) == 0);
-}
+bool QHighDpiScaling::m_usePhysicalDpi = false;
+QVector<QHighDpiScaling::ScreenFactor> QHighDpiScaling::m_screenFactors;
+QHighDpiScaling::DpiAdjustmentPolicy QHighDpiScaling::m_dpiAdjustmentPolicy = QHighDpiScaling::DpiAdjustmentPolicy::Unset;
+QHash<QString, qreal> QHighDpiScaling::m_namedScreenScaleFactors; // Per-screen scale factors (screen name -> factor)
qreal QHighDpiScaling::rawScaleFactor(const QPlatformScreen *screen)
{
- // Determine if physical DPI should be used
- static const bool usePhysicalDpi = qEnvironmentVariableAsBool(usePhysicalDpiEnvVar, false);
-
// Calculate scale factor beased on platform screen DPI values
qreal factor;
QDpi platformBaseDpi = screen->logicalBaseDpi();
- if (usePhysicalDpi) {
- qreal platformPhysicalDpi = screen->screen()->physicalDotsPerInch();
- factor = qreal(platformPhysicalDpi) / qreal(platformBaseDpi.first);
+ if (QHighDpiScaling::m_usePhysicalDpi) {
+ QSize sz = screen->geometry().size();
+ QSizeF psz = screen->physicalSize();
+ qreal platformPhysicalDpi = ((sz.height() / psz.height()) + (sz.width() / psz.width())) * qreal(25.4 * 0.5);
+ factor = qRound(platformPhysicalDpi) / qreal(platformBaseDpi.first);
} else {
const QDpi platformLogicalDpi = QPlatformScreen::overrideDpi(screen->logicalDpi());
factor = qreal(platformLogicalDpi.first) / qreal(platformBaseDpi.first);
@@ -375,32 +337,9 @@ qreal QHighDpiScaling::roundScaleFactor(qreal rawFactor)
// sizes that are smaller than the ideal size, and opposite for rounding up.
// Rounding down is then preferable since "small UI" is a more acceptable
// high-DPI experience than "large UI".
- static auto scaleFactorRoundingPolicy = Qt::HighDpiScaleFactorRoundingPolicy::Unset;
-
- // Determine rounding policy
- if (scaleFactorRoundingPolicy == Qt::HighDpiScaleFactorRoundingPolicy::Unset) {
- // Check environment
- if (qEnvironmentVariableIsSet(scaleFactorRoundingPolicyEnvVar)) {
- QByteArray policyText = qgetenv(scaleFactorRoundingPolicyEnvVar);
- auto policyEnumValue = lookupScaleFactorRoundingPolicy(policyText);
- if (policyEnumValue != Qt::HighDpiScaleFactorRoundingPolicy::Unset) {
- scaleFactorRoundingPolicy = policyEnumValue;
- } else {
- auto values = joinEnumValues(std::begin(scaleFactorRoundingPolicyLookup),
- std::end(scaleFactorRoundingPolicyLookup));
- qWarning("Unknown scale factor rounding policy: %s. Supported values are: %s.",
- policyText.constData(), values.constData());
- }
- }
- // Check application object if no environment value was set.
- if (scaleFactorRoundingPolicy == Qt::HighDpiScaleFactorRoundingPolicy::Unset) {
- scaleFactorRoundingPolicy = QGuiApplication::highDpiScaleFactorRoundingPolicy();
- } else {
- // Make application setting reflect environment
- QGuiApplication::setHighDpiScaleFactorRoundingPolicy(scaleFactorRoundingPolicy);
- }
- }
+ Qt::HighDpiScaleFactorRoundingPolicy scaleFactorRoundingPolicy =
+ QGuiApplication::highDpiScaleFactorRoundingPolicy();
// Apply rounding policy.
qreal roundedFactor = rawFactor;
@@ -424,11 +363,9 @@ qreal QHighDpiScaling::roundScaleFactor(qreal rawFactor)
break;
}
- // Don't round down to to zero; clamp the minimum (rounded) factor to 1.
- // This is not a common case but can happen if a display reports a very
- // low DPI.
- if (scaleFactorRoundingPolicy != Qt::HighDpiScaleFactorRoundingPolicy::PassThrough)
- roundedFactor = qMax(roundedFactor, qreal(1));
+ // Clamp the minimum factor to 1. Qt does not currently render
+ // correctly with factors less than 1.
+ roundedFactor = qMax(roundedFactor, qreal(1));
return roundedFactor;
}
@@ -442,100 +379,154 @@ QDpi QHighDpiScaling::effectiveLogicalDpi(const QPlatformScreen *screen, qreal r
// with the rest of the UI. The amount of out-of-synch-ness depends on how
// well user code handles a non-standard DPI values, but since the
// adjustment is small (typically +/- 48 max) this might be OK.
- static auto dpiAdjustmentPolicy = DpiAdjustmentPolicy::Unset;
-
- // Determine adjustment policy.
- if (dpiAdjustmentPolicy == DpiAdjustmentPolicy::Unset) {
- if (qEnvironmentVariableIsSet(dpiAdjustmentPolicyEnvVar)) {
- QByteArray policyText = qgetenv(dpiAdjustmentPolicyEnvVar);
- auto policyEnumValue = lookupDpiAdjustmentPolicy(policyText);
- if (policyEnumValue != DpiAdjustmentPolicy::Unset) {
- dpiAdjustmentPolicy = policyEnumValue;
- } else {
- auto values = joinEnumValues(std::begin(dpiAdjustmentPolicyLookup),
- std::end(dpiAdjustmentPolicyLookup));
- qWarning("Unknown DPI adjustment policy: %s. Supported values are: %s.",
- policyText.constData(), values.constData());
- }
- }
- if (dpiAdjustmentPolicy == DpiAdjustmentPolicy::Unset)
- dpiAdjustmentPolicy = DpiAdjustmentPolicy::UpOnly;
- }
// Apply adjustment policy.
const QDpi baseDpi = screen->logicalBaseDpi();
const qreal dpiAdjustmentFactor = rawFactor / roundedFactor;
// Return the base DPI for cases where there is no adjustment
- if (dpiAdjustmentPolicy == DpiAdjustmentPolicy::Disabled)
+ if (QHighDpiScaling::m_dpiAdjustmentPolicy == DpiAdjustmentPolicy::Disabled)
return baseDpi;
- if (dpiAdjustmentPolicy == DpiAdjustmentPolicy::UpOnly && dpiAdjustmentFactor < 1)
+ if (QHighDpiScaling::m_dpiAdjustmentPolicy == DpiAdjustmentPolicy::UpOnly && dpiAdjustmentFactor < 1)
return baseDpi;
return QDpi(baseDpi.first * dpiAdjustmentFactor, baseDpi.second * dpiAdjustmentFactor);
}
+/*
+ Determine and apply global/initial configuration which do not depend on
+ having access to QScreen objects - this function is called before they
+ have been created. Screen-dependent configuration happens later in
+ updateHighDpiScaling().
+*/
void QHighDpiScaling::initHighDpiScaling()
{
- // Determine if there is a global scale factor set.
- m_factor = initialGlobalScaleFactor();
+ qCDebug(lcHighDpi) << "Initializing high-DPI scaling";
+
+ // Read environment variables
+ static const char* envDebugStr = "environment variable set:";
+ std::optional<int> envEnableHighDpiScaling = qEnvironmentVariableOptionalInt(enableHighDpiScalingEnvVar);
+ if (envEnableHighDpiScaling.has_value())
+ qCDebug(lcHighDpi) << envDebugStr << enableHighDpiScalingEnvVar << envEnableHighDpiScaling.value();
+
+ std::optional<qreal> envScaleFactor = qEnvironmentVariableOptionalReal(scaleFactorEnvVar);
+ if (envScaleFactor.has_value())
+ qCDebug(lcHighDpi) << envDebugStr << scaleFactorEnvVar << envScaleFactor.value();
+
+ std::optional<QString> envScreenFactors = qEnvironmentVariableOptionalString(screenFactorsEnvVar);
+ if (envScreenFactors.has_value())
+ qCDebug(lcHighDpi) << envDebugStr << screenFactorsEnvVar << envScreenFactors.value();
+
+ std::optional<int> envUsePhysicalDpi = qEnvironmentVariableOptionalInt(usePhysicalDpiEnvVar);
+ if (envUsePhysicalDpi.has_value())
+ qCDebug(lcHighDpi) << envDebugStr << usePhysicalDpiEnvVar << envUsePhysicalDpi.value();
+
+ std::optional<QByteArray> envScaleFactorRoundingPolicy = qEnvironmentVariableOptionalByteArray(scaleFactorRoundingPolicyEnvVar);
+ if (envScaleFactorRoundingPolicy.has_value())
+ qCDebug(lcHighDpi) << envDebugStr << scaleFactorRoundingPolicyEnvVar << envScaleFactorRoundingPolicy.value();
+
+ std::optional<QByteArray> envDpiAdjustmentPolicy = qEnvironmentVariableOptionalByteArray(dpiAdjustmentPolicyEnvVar);
+ if (envDpiAdjustmentPolicy.has_value())
+ qCDebug(lcHighDpi) << envDebugStr << dpiAdjustmentPolicyEnvVar << envDpiAdjustmentPolicy.value();
+
+ // High-dpi scaling is enabled by default; check for global disable.
+ m_usePlatformPluginDpi = envEnableHighDpiScaling.value_or(1) > 0;
+ m_platformPluginDpiScalingActive = false; // see updateHighDpiScaling()
+
+ // Check for glabal scale factor (different from 1)
+ m_factor = envScaleFactor.value_or(qreal(1));
m_globalScalingActive = !qFuzzyCompare(m_factor, qreal(1));
- m_usePixelDensity = usePixelDensity();
+ // Store the envScreenFactors string for later use. The string format
+ // supports using screen names, which means that screen DPI cannot
+ // be resolved at this point.
+ QString screenFactorsSpec = envScreenFactors.value_or(QString());
+ m_screenFactors = parseScreenScaleFactorsSpec(QStringView{screenFactorsSpec});
+ m_namedScreenScaleFactors.clear();
+
+ m_usePhysicalDpi = envUsePhysicalDpi.value_or(0) > 0;
+
+ // Resolve HighDpiScaleFactorRoundingPolicy to QGuiApplication::highDpiScaleFactorRoundingPolicy
+ if (envScaleFactorRoundingPolicy.has_value()) {
+ QByteArray policyText = envScaleFactorRoundingPolicy.value();
+ auto policyEnumValue = lookupScaleFactorRoundingPolicy(policyText);
+ if (policyEnumValue != Qt::HighDpiScaleFactorRoundingPolicy::Unset) {
+ QGuiApplication::setHighDpiScaleFactorRoundingPolicy(policyEnumValue);
+ } else {
+ auto values = joinEnumValues(std::begin(scaleFactorRoundingPolicyLookup),
+ std::end(scaleFactorRoundingPolicyLookup));
+ qWarning("Unknown scale factor rounding policy: %s. Supported values are: %s.",
+ policyText.constData(), values.constData());
+ }
+ }
+
+ // Resolve DpiAdjustmentPolicy to m_dpiAdjustmentPolicy
+ if (envDpiAdjustmentPolicy.has_value()) {
+ QByteArray policyText = envDpiAdjustmentPolicy.value();
+ auto policyEnumValue = lookupDpiAdjustmentPolicy(policyText);
+ if (policyEnumValue != DpiAdjustmentPolicy::Unset) {
+ QHighDpiScaling::m_dpiAdjustmentPolicy = policyEnumValue;
+ } else {
+ auto values = joinEnumValues(std::begin(dpiAdjustmentPolicyLookup),
+ std::end(dpiAdjustmentPolicyLookup));
+ qWarning("Unknown DPI adjustment policy: %s. Supported values are: %s.",
+ policyText.constData(), values.constData());
+ }
+ }
- m_pixelDensityScalingActive = false; //set in updateHighDpiScaling below
+ // Set initial active state
+ m_active = m_globalScalingActive || m_usePlatformPluginDpi;
- m_active = m_globalScalingActive || m_usePixelDensity;
+ qCDebug(lcHighDpi) << "Initialization done, high-DPI scaling is"
+ << (m_active ? "active" : "inactive");
}
+/*
+ Update configuration based on available screens and screen properties.
+ This function may be called whenever the screen configuration changed.
+*/
void QHighDpiScaling::updateHighDpiScaling()
{
- if (QCoreApplication::testAttribute(Qt::AA_DisableHighDpiScaling))
- return;
+ qCDebug(lcHighDpi) << "Updating high-DPI scaling";
- if (m_usePixelDensity && !m_pixelDensityScalingActive) {
+ // Apply screen factors from environment
+ if (m_screenFactors.size() > 0) {
+ qCDebug(lcHighDpi) << "Applying screen factors" << m_screenFactors;
+ int i = -1;
const auto screens = QGuiApplication::screens();
- for (QScreen *screen : screens) {
- if (!qFuzzyCompare(screenSubfactor(screen->handle()), qreal(1))) {
- m_pixelDensityScalingActive = true;
- break;
+ for (const auto &[name, rawFactor]: m_screenFactors) {
+ const qreal factor = roundScaleFactor(rawFactor);
+ ++i;
+ if (name.isNull()) {
+ if (i < screens.size())
+ setScreenFactor(screens.at(i), factor);
+ } else {
+ for (QScreen *screen : screens) {
+ if (screen->name() == name) {
+ setScreenFactor(screen, factor);
+ break;
+ }
+ }
}
}
}
- if (qEnvironmentVariableIsSet(screenFactorsEnvVar)) {
- int i = 0;
- const QString spec = qEnvironmentVariable(screenFactorsEnvVar);
- const auto specs = QStringView{spec}.split(u';');
- for (const auto &spec : specs) {
- int equalsPos = spec.lastIndexOf(QLatin1Char('='));
- qreal factor = 0;
- if (equalsPos > 0) {
- // support "name=factor"
- bool ok;
- const auto name = spec.left(equalsPos);
- factor = spec.mid(equalsPos + 1).toDouble(&ok);
- if (ok && factor > 0 ) {
- const auto screens = QGuiApplication::screens();
- for (QScreen *s : screens) {
- if (s->name() == name) {
- setScreenFactor(s, factor);
- break;
- }
- }
- }
- } else {
- // listing screens in order
- bool ok;
- factor = spec.toDouble(&ok);
- if (ok && factor > 0 && i < QGuiApplication::screens().count()) {
- QScreen *screen = QGuiApplication::screens().at(i);
- setScreenFactor(screen, factor);
- }
+
+ // Check if any screens (now) has a scale factor != 1 and set
+ // m_platformPluginDpiScalingActive if so.
+ if (m_usePlatformPluginDpi && !m_platformPluginDpiScalingActive ) {
+ const auto screens = QGuiApplication::screens();
+ for (QScreen *screen : screens) {
+ if (!qFuzzyCompare(screenSubfactor(screen->handle()), qreal(1))) {
+ m_platformPluginDpiScalingActive = true;
+ break;
}
- ++i;
}
}
- m_active = m_globalScalingActive || m_screenFactorSet || m_pixelDensityScalingActive;
+
+ m_active = m_globalScalingActive || m_screenFactorSet || m_platformPluginDpiScalingActive;
+
+ qCDebug(lcHighDpi) << "Update done, high-DPI scaling is"
+ << (m_active ? "active" : "inactive");
}
/*
@@ -543,17 +534,24 @@ void QHighDpiScaling::updateHighDpiScaling()
*/
void QHighDpiScaling::setGlobalFactor(qreal factor)
{
+ qCDebug(lcHighDpi) << "Setting global scale factor to" << factor;
+
if (qFuzzyCompare(factor, m_factor))
return;
if (!QGuiApplication::allWindows().isEmpty())
qWarning("QHighDpiScaling::setFactor: Should only be called when no windows exist.");
+ const auto screens = QGuiApplication::screens();
+
+ std::vector<QScreenPrivate::UpdateEmitter> updateEmitters;
+ for (QScreen *screen : screens)
+ updateEmitters.emplace_back(screen);
+
m_globalScalingActive = !qFuzzyCompare(factor, qreal(1));
m_factor = m_globalScalingActive ? factor : qreal(1);
- m_active = m_globalScalingActive || m_screenFactorSet || m_pixelDensityScalingActive;
- const auto screens = QGuiApplication::screens();
+ m_active = m_globalScalingActive || m_screenFactorSet || m_platformPluginDpiScalingActive ;
for (QScreen *screen : screens)
- screen->d_func()->updateHighDpi();
+ screen->d_func()->updateGeometry();
}
static const char scaleFactorProperty[] = "_q_scaleFactor";
@@ -563,22 +561,24 @@ static const char scaleFactorProperty[] = "_q_scaleFactor";
*/
void QHighDpiScaling::setScreenFactor(QScreen *screen, qreal factor)
{
+ qCDebug(lcHighDpi) << "Setting screen scale factor for" << screen << "to" << factor;
+
if (!qFuzzyCompare(factor, qreal(1))) {
m_screenFactorSet = true;
m_active = true;
}
+ QScreenPrivate::UpdateEmitter updateEmitter(screen);
+
// Prefer associating the factor with screen name over the object
// since the screen object may be deleted on screen disconnects.
const QString name = screen->name();
if (name.isEmpty())
screen->setProperty(scaleFactorProperty, QVariant(factor));
else
- qNamedScreenScaleFactors()->insert(name, factor);
+ QHighDpiScaling::m_namedScreenScaleFactors.insert(name, factor);
- // hack to force re-evaluation of screen geometry
- if (screen->handle())
- screen->d_func()->setPlatformScreen(screen->handle()); // updates geometries based on scale factor
+ screen->d_func()->updateGeometry();
}
QPoint QHighDpiScaling::mapPositionToNative(const QPoint &pos, const QPlatformScreen *platformScreen)
@@ -599,46 +599,6 @@ QPoint QHighDpiScaling::mapPositionFromNative(const QPoint &pos, const QPlatform
return (pos - topLeft) / scaleFactor + topLeft;
}
-QPoint QHighDpiScaling::mapPositionToGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window)
-{
- QPoint globalPosCandidate = pos + windowGlobalPosition;
- if (QGuiApplicationPrivate::screen_list.size() <= 1)
- return globalPosCandidate;
-
- // The global position may be outside device independent screen geometry
- // in cases where a window spans screens. Detect this case and map via
- // native coordinates to the correct screen.
- auto currentScreen = window->screen();
- if (currentScreen && !currentScreen->geometry().contains(globalPosCandidate)) {
- auto nativeGlobalPos = QHighDpi::toNativePixels(globalPosCandidate, currentScreen);
- if (auto actualPlatformScreen = currentScreen->handle()->screenForPosition(nativeGlobalPos))
- return QHighDpi::fromNativePixels(nativeGlobalPos, actualPlatformScreen->screen());
- }
-
- return globalPosCandidate;
-}
-
-QPoint QHighDpiScaling::mapPositionFromGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window)
-{
- QPoint windowPosCandidate = pos - windowGlobalPosition;
- if (QGuiApplicationPrivate::screen_list.size() <= 1 || window->handle() == nullptr)
- return windowPosCandidate;
-
- // Device independent global (screen) space may discontiguous when high-dpi scaling
- // is active. This means that the normal subtracting of the window global position from the
- // position-to-be-mapped may not work in cases where a window spans multiple screens.
- // Map both positions to native global space (using the correct screens), subtract there,
- // and then map the difference back using the scale factor for the window.
- QScreen *posScreen = QGuiApplication::screenAt(pos);
- if (posScreen && posScreen != window->screen()) {
- QPoint nativePos = QHighDpi::toNativePixels(pos, posScreen);
- QPoint windowNativePos = window->handle()->geometry().topLeft();
- return QHighDpi::fromNativeLocalPosition(nativePos - windowNativePos, window);
- }
-
- return windowPosCandidate;
-}
-
qreal QHighDpiScaling::screenSubfactor(const QPlatformScreen *screen)
{
auto factor = qreal(1.0);
@@ -661,13 +621,13 @@ qreal QHighDpiScaling::screenSubfactor(const QPlatformScreen *screen)
}
if (!screenPropertyUsed) {
- auto byNameIt = qNamedScreenScaleFactors()->constFind(screen->name());
- if ((screenPropertyUsed = byNameIt != qNamedScreenScaleFactors()->cend()))
+ auto byNameIt = QHighDpiScaling::m_namedScreenScaleFactors.constFind(screen->name());
+ if ((screenPropertyUsed = byNameIt != QHighDpiScaling::m_namedScreenScaleFactors.cend()))
factor = *byNameIt;
}
}
- if (!screenPropertyUsed && m_usePixelDensity)
+ if (!screenPropertyUsed && m_usePlatformPluginDpi)
factor = roundScaleFactor(rawScaleFactor(screen));
return factor;
@@ -679,7 +639,7 @@ QDpi QHighDpiScaling::logicalDpi(const QScreen *screen)
if (!screen || !screen->handle())
return QDpi(96, 96);
- if (!m_usePixelDensity) {
+ if (!m_usePlatformPluginDpi) {
const qreal screenScaleFactor = screenSubfactor(screen->handle());
const QDpi dpi = QPlatformScreen::overrideDpi(screen->handle()->logicalDpi());
return QDpi{ dpi.first / screenScaleFactor, dpi.second / screenScaleFactor };
@@ -690,37 +650,123 @@ QDpi QHighDpiScaling::logicalDpi(const QScreen *screen)
return effectiveLogicalDpi(screen->handle(), scaleFactor, roundedScaleFactor);
}
-QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(const QPlatformScreen *platformScreen, QPoint *nativePosition)
+// Returns the screen containing \a position, using \a guess as a starting point
+// for the search. \a guess might be nullptr. Returns nullptr if \a position is outside
+// of all screens.
+QScreen *QHighDpiScaling::screenForPosition(QHighDpiScaling::Point position, QScreen *guess)
{
+ if (position.kind == QHighDpiScaling::Point::Invalid)
+ return nullptr;
+
+ auto getPlatformScreenGuess = [](QScreen *maybeScreen) -> QPlatformScreen * {
+ if (maybeScreen)
+ return maybeScreen->handle();
+ if (QScreen *primary = QGuiApplication::primaryScreen())
+ return primary->handle();
+ return nullptr;
+ };
+
+ QPlatformScreen *platformGuess = getPlatformScreenGuess(guess);
+ if (!platformGuess)
+ return nullptr;
+
+ auto onScreen = [](QHighDpiScaling::Point position, const QPlatformScreen *platformScreen) -> bool {
+ return position.kind == Point::Native
+ ? platformScreen->geometry().contains(position.point)
+ : platformScreen->screen()->geometry().contains(position.point);
+ };
+
+ // is the guessed screen correct?
+ if (onScreen(position, platformGuess))
+ return platformGuess->screen();
+
+ // search sibling screens
+ const auto screens = platformGuess->virtualSiblings();
+ for (const QPlatformScreen *screen : screens) {
+ if (onScreen(position, screen))
+ return screen->screen();
+ }
+
+ return nullptr;
+}
+
+QVector<QHighDpiScaling::ScreenFactor> QHighDpiScaling::parseScreenScaleFactorsSpec(const QStringView &screenScaleFactors)
+{
+ QVector<QHighDpiScaling::ScreenFactor> screenFactors;
+
+ // The spec is _either_
+ // - a semicolon-separated ordered factor list: "1.5;2;3"
+ // - a semicolon-separated name=factor list: "foo=1.5;bar=2;baz=3"
+ const auto specs = screenScaleFactors.split(u';');
+ for (const auto &spec : specs) {
+ const qsizetype equalsPos = spec.lastIndexOf(u'=');
+ if (equalsPos == -1) {
+ // screens in order
+ bool ok;
+ const qreal factor = spec.toDouble(&ok);
+ if (ok && factor > 0) {
+ screenFactors.append(QHighDpiScaling::ScreenFactor(QString(), factor));
+ }
+ } else {
+ // "name=factor"
+ bool ok;
+ const qreal factor = spec.mid(equalsPos + 1).toDouble(&ok);
+ if (ok && factor > 0) {
+ screenFactors.append(QHighDpiScaling::ScreenFactor(spec.left(equalsPos).toString(), factor));
+ }
+ }
+ } // for (specs)
+
+ return screenFactors;
+}
+
+QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(const QPlatformScreen *platformScreen, QHighDpiScaling::Point position)
+{
+ Q_UNUSED(position)
if (!m_active)
return { qreal(1), QPoint() };
if (!platformScreen)
return { m_factor, QPoint() }; // the global factor
- const QPlatformScreen *actualScreen = nativePosition ?
- platformScreen->screenForPosition(*nativePosition) : platformScreen;
- return { m_factor * screenSubfactor(actualScreen), actualScreen->geometry().topLeft() };
+ return { m_factor * screenSubfactor(platformScreen), platformScreen->geometry().topLeft() };
}
-QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(const QScreen *screen, QPoint *nativePosition)
+QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(const QScreen *screen, QHighDpiScaling::Point position)
{
+ Q_UNUSED(position)
if (!m_active)
return { qreal(1), QPoint() };
if (!screen)
return { m_factor, QPoint() }; // the global factor
- return scaleAndOrigin(screen->handle(), nativePosition);
+ return scaleAndOrigin(screen->handle(), position);
}
-QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(const QWindow *window, QPoint *nativePosition)
+QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(const QWindow *window, QHighDpiScaling::Point position)
{
if (!m_active)
return { qreal(1), QPoint() };
+ // Determine correct screen; use the screen which contains the given
+ // position if a valid position is passed.
QScreen *screen = window ? window->screen() : QGuiApplication::primaryScreen();
- const bool searchScreen = !window || window->isTopLevel();
- return scaleAndOrigin(screen, searchScreen ? nativePosition : nullptr);
+ QScreen *overrideScreen = QHighDpiScaling::screenForPosition(position, screen);
+ QScreen *targetScreen = overrideScreen ? overrideScreen : screen;
+ return scaleAndOrigin(targetScreen, position);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QHighDpiScaling::ScreenFactor &factor)
+{
+ const QDebugStateSaver saver(debug);
+ debug.nospace();
+ if (!factor.name.isEmpty())
+ debug << factor.name << "=";
+ debug << factor.factor;
+ return debug;
}
+#endif
+
+#else // QT_NO_HIGHDPISCALING
-#else
QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(const QPlatformScreen *, QPoint *)
{
return { qreal(1), QPoint() };
@@ -735,5 +781,9 @@ QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(const QWindow *,
{
return { qreal(1), QPoint() };
}
-#endif //QT_NO_HIGHDPISCALING
+
+#endif // QT_NO_HIGHDPISCALING
+
QT_END_NAMESPACE
+
+#include "moc_qhighdpiscaling_p.cpp"