Read high-dpi env. variables in initHighDpiScaling()

Move all environment access to initHighDpiScaling(),
which makes it so that all environment access happens
at one point in time during QGuiApplication construction.

Replace the “qt.scaling” logging category with “qt.highdpi”,
and log each recognized env. variable. Further logging
of DPI and computed scale factors will be introduced
later on.

This also enables auto-testing of the environment variable
settings, since the auto-test can now control when the
environment is read by (re-)creating the application
object.

Change-Id: I4e62a9c0050438357a58ace5b50fb7c5950c87ce
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
Morten Johan Sørvig 2021-03-09 12:13:41 +01:00
parent a5573cfbd5
commit 4d1f13f354
2 changed files with 118 additions and 98 deletions

View File

@ -49,10 +49,11 @@
#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
@ -68,30 +69,38 @@ static const char usePhysicalDpiEnvVar[] = "QT_USE_PHYSICAL_DPI";
// disconnect/connect cycles where the screen object may be deleted.
typedef QHash<QString, qreal> QScreenScaleFactorHash;
Q_GLOBAL_STATIC(QScreenScaleFactorHash, qNamedScreenScaleFactors);
// 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)
static std::optional<QString> qEnvironmentVariableOptionalString(const char *name)
{
bool ok = false;
int value = qEnvironmentVariableIntValue(name, &ok);
return ok ? value > 0 : defaultValue;
if (!qEnvironmentVariableIsSet(name))
return std::nullopt;
return std::optional(qEnvironmentVariable(name));
}
static inline qreal initialGlobalScaleFactor()
static std::optional<QByteArray> qEnvironmentVariableOptionalByteArray(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;
}
}
return std::optional(qgetenv(name));
}
return result;
static std::optional<int> qEnvironmentVariableOptionalInt(const char *name)
{
bool ok = false;
const int value = qEnvironmentVariableIntValue(name, &ok);
auto opt = ok ? std::optional(value) : std::nullopt;
return opt;
}
static std::optional<qreal> qEnvironmentVariableOptionalReal(const char *name)
{
if (!qEnvironmentVariableIsSet(name))
return std::nullopt;
bool ok = false;
const qreal value = qEnvironmentVariable(name).toDouble(&ok);
return ok ? std::optional(value) : std::nullopt;
}
/*!
@ -275,35 +284,16 @@ bool QHighDpiScaling::m_usePlatformPluginDpi = false; // use scale factor based
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 usePlatformPluginDpi()
{
// Determine if we should set a scale factor based on the logical DPI
// reported by the platform plugin.
bool enableEnvValueOk;
const int enableEnvValue = qEnvironmentVariableIntValue(enableHighDpiScalingEnvVar, &enableEnvValueOk);
if (enableEnvValueOk && enableEnvValue < 1)
return false;
// Enable by default
return true;
}
bool QHighDpiScaling::m_usePhysicalDpi = false;
QHighDpiScaling::DpiAdjustmentPolicy QHighDpiScaling::m_dpiAdjustmentPolicy = QHighDpiScaling::DpiAdjustmentPolicy::Unset;
QString QHighDpiScaling::m_screenFactorsSpec;
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) {
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);
@ -387,32 +377,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;
@ -452,56 +419,107 @@ 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();
// 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_usePlatformPluginDpi = usePlatformPluginDpi();
// 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.
QHighDpiScaling::m_screenFactorsSpec = envScreenFactors.value_or(QString());
m_platformPluginDpiScalingActive = false; //set in updateHighDpiScaling below
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 = envScaleFactorRoundingPolicy.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());
}
}
// Set initial active state
m_active = m_globalScalingActive || m_usePlatformPluginDpi;
}
/*
Update configuration based on available screens and screen properties.
This function may be called whenever the screen configuration changed.
*/
void QHighDpiScaling::updateHighDpiScaling()
{
m_usePlatformPluginDpi = usePlatformPluginDpi();
if (m_usePlatformPluginDpi && !m_platformPluginDpiScalingActive ) {
const auto screens = QGuiApplication::screens();
for (QScreen *screen : screens) {
@ -511,10 +529,9 @@ void QHighDpiScaling::updateHighDpiScaling()
}
}
}
if (qEnvironmentVariableIsSet(screenFactorsEnvVar)) {
if (!m_screenFactorsSpec.isNull()) {
int i = 0;
const QString spec = qEnvironmentVariable(screenFactorsEnvVar);
const auto specs = QStringView{spec}.split(u';');
const auto specs = QStringView{m_screenFactorsSpec}.split(u';');
for (const auto &spec : specs) {
int equalsPos = spec.lastIndexOf(QLatin1Char('='));
qreal factor = 0;

View File

@ -64,7 +64,7 @@
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcScaling);
Q_DECLARE_LOGGING_CATEGORY(lcHighDpi);
class QScreen;
class QPlatformScreen;
@ -138,6 +138,9 @@ private:
static bool m_platformPluginDpiScalingActive;
static bool m_globalScalingActive;
static bool m_screenFactorSet;
static bool m_usePhysicalDpi;
static QString m_screenFactorsSpec;
static DpiAdjustmentPolicy m_dpiAdjustmentPolicy;
};
namespace QHighDpi {