Merge "Add auto-test for QT_USE_PHYSICAL_DPI"

This commit is contained in:
Morten Johan Sørvig 2021-03-12 14:49:09 +01:00 committed by Qt CI Bot
commit 68e69588e0
3 changed files with 200 additions and 106 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
@ -63,35 +64,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);
// 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 +279,17 @@ 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;
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) {
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 +373,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 +415,108 @@ 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_namedScreenScaleFactors.clear();
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 +526,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;
@ -583,7 +597,7 @@ void QHighDpiScaling::setScreenFactor(QScreen *screen, qreal factor)
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())
@ -630,8 +644,8 @@ 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;
}
}

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,10 @@ 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;
static QHash<QString, qreal> m_namedScreenScaleFactors;
};
namespace QHighDpi {

View File

@ -47,12 +47,16 @@ private: // helpers
static void standardScreenDpiTestData();
private slots:
void initTestCase();
void cleanup();
void qhighdpiscaling_data();
void qhighdpiscaling();
void minimumDpr();
void noscreens();
void screenDpiAndDpr_data();
void screenDpiAndDpr();
void environment_QT_SCALE_FACTOR();
void environment_QT_SCREEN_SCALE_FACTORS();
void environment_QT_USE_PHYSICAL_DPI();
void screenAt_data();
void screenAt();
void screenGeometry_data();
@ -178,6 +182,15 @@ void tst_QHighDpi::initTestCase()
#endif
}
void tst_QHighDpi::cleanup()
{
// Some test functions set environment variables. Unset them here,
// in order to avoid getting confusing follow-on errors on test failures.
qunsetenv("QT_SCALE_FACTOR");
qunsetenv("QT_SCREEN_SCALE_FACTORS");
qunsetenv("QT_USE_PHYSICAL_DPI");
}
void tst_QHighDpi::qhighdpiscaling_data()
{
standardScreenDpiTestData();
@ -224,6 +237,69 @@ void tst_QHighDpi::screenDpiAndDpr()
}
}
void tst_QHighDpi::environment_QT_SCALE_FACTOR()
{
qreal factor = 3.1415;
qputenv("QT_SCALE_FACTOR", QByteArray::number(factor));
QList<qreal> dpiValues { 96, 144, 192 };
std::unique_ptr<QGuiApplication> app(createStandardOffscreenApp(dpiValues));
int i = 0;
for (QScreen *screen : app->screens()) {
// Verify that QT_SCALE_FACTOR applies as a multiplicative factor.
qreal expextedDpr = (dpiValues[i] / standardBaseDpi) * factor;
++i;
QCOMPARE(screen->devicePixelRatio(), expextedDpr);
QCOMPARE(screen->logicalDotsPerInch(), 96);
QWindow window(screen);
QCOMPARE(window.devicePixelRatio(), expextedDpr);
}
}
void tst_QHighDpi::environment_QT_SCREEN_SCALE_FACTORS()
{
qreal factors[] = {1, 1.5, 2};
qputenv("QT_SCREEN_SCALE_FACTORS", "1;1.5;2");
QList<qreal> dpiValues { 192, 216, 240 };
std::unique_ptr<QGuiApplication> app(createStandardOffscreenApp(dpiValues));
int i = 0;
for (QScreen *screen : app->screens()) {
qreal expextedDpr = factors[i];
++i;
// Verify that setting QT_SCREEN_SCALE_FACTORS overrides the from-dpi DPR
QCOMPARE(screen->devicePixelRatio(), expextedDpr);
QCOMPARE(screen->logicalDotsPerInch(), 96);
QWindow window(screen);
QCOMPARE(window.devicePixelRatio(), expextedDpr);
}
}
void tst_QHighDpi::environment_QT_USE_PHYSICAL_DPI()
{
qputenv("QT_USE_PHYSICAL_DPI", "1");
QList<qreal> dpiValues { 96, 144, 192 };
std::unique_ptr<QGuiApplication> app(createStandardOffscreenApp(dpiValues));
// Verify that the device pixel ratio is computed as physicalDpi / baseDpi.
// (which in practice uses physicalSize since this is what QPlatformScreen provides)
// The default QPlatformScreen::physicalSize() implementation (which QOffscreenScreen
// currerently uses) assumes a default DPI of 100 and calculates a fake physical size
// based on that value. Use DPI 100 here as well: if you have changed the default value
// in QPlatformScreen and get a test failure then update the value below.
const qreal platformScreenDefualtDpi = 100;
qreal expextedDpr = (platformScreenDefualtDpi / qreal(standardBaseDpi));
for (QScreen *screen : app->screens()) {
QCOMPARE(screen->devicePixelRatio(), expextedDpr);
QCOMPARE(screen->logicalDotsPerInch(), 96);
QWindow window(screen);
QCOMPARE(window.devicePixelRatio(), expextedDpr);
}
}
void tst_QHighDpi::minimumDpr()
{
QList<qreal> dpiValues { 40, 60, 95 };