10#include "private/qscreen_p.h"
11#include <private/qguiapplication_p.h>
13#include <QtCore/qdebug.h>
14#include <QtCore/qmetaobject.h>
23#ifndef QT_NO_HIGHDPISCALING
35 QString value = qEnvironmentVariable(name);
36 return value.isNull() ?
std::nullopt :
std::optional(
std::move(value));
41 QByteArray value = qgetenv(name);
42 return value.isNull() ?
std::nullopt :
std::optional(
std::move(value));
47 const QByteArray val = qgetenv(name);
52 const qreal value = val.toDouble(&ok);
53 return ok ?
std::optional(value) :
std::nullopt;
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
231qreal QHighDpiScaling::m_factor = 1.0;
232bool QHighDpiScaling::m_active =
false;
233bool QHighDpiScaling::m_usePlatformPluginDpi =
false;
234bool QHighDpiScaling::m_platformPluginDpiScalingActive =
false;
235bool QHighDpiScaling::m_globalScalingActive =
false;
236bool QHighDpiScaling::m_screenFactorSet =
false;
237bool QHighDpiScaling::m_usePhysicalDpi =
false;
238QVector<QHighDpiScaling::ScreenFactor> QHighDpiScaling::m_screenFactors;
239QHighDpiScaling::DpiAdjustmentPolicy QHighDpiScaling::m_dpiAdjustmentPolicy = QHighDpiScaling::DpiAdjustmentPolicy::Unset;
240QHash<QString, qreal> QHighDpiScaling::m_namedScreenScaleFactors;
242qreal QHighDpiScaling::rawScaleFactor(
const QPlatformScreen *screen)
246 QDpi platformBaseDpi = screen->logicalBaseDpi();
247 if (QHighDpiScaling::m_usePhysicalDpi) {
248 QSize sz = screen->geometry().size();
249 QSizeF psz = screen->physicalSize();
250 qreal platformPhysicalDpi = ((sz.height() / psz.height()) + (sz.width() / psz.width())) * qreal(25.4 * 0.5);
251 factor = qRound(platformPhysicalDpi) / qreal(platformBaseDpi.first);
253 const QDpi platformLogicalDpi = QPlatformScreen::overrideDpi(screen->logicalDpi());
254 factor = qreal(platformLogicalDpi.first) / qreal(platformBaseDpi.first);
260template <
class EnumType>
267template <
class EnumType>
270 return qstricmp(e1.name, e2.name) == 0;
273template <
class EnumType>
277 for (; i1 < i2; ++i1) {
278 if (!result.isEmpty())
279 result += QByteArrayLiteral(
", ");
285using ScaleFactorRoundingPolicyLookup = EnumLookup<Qt::HighDpiScaleFactorRoundingPolicy>;
289 {
"Round", Qt::HighDpiScaleFactorRoundingPolicy::Round},
290 {
"Ceil", Qt::HighDpiScaleFactorRoundingPolicy::Ceil},
291 {
"Floor", Qt::HighDpiScaleFactorRoundingPolicy::Floor},
292 {
"RoundPreferFloor", Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor},
293 {
"PassThrough", Qt::HighDpiScaleFactorRoundingPolicy::PassThrough}
299 auto end =
std::end(scaleFactorRoundingPolicyLookup);
300 auto it =
std::find(
std::begin(scaleFactorRoundingPolicyLookup), end,
301 ScaleFactorRoundingPolicyLookup{v.constData(), Qt::HighDpiScaleFactorRoundingPolicy::Unset});
302 return it != end ? it->value : Qt::HighDpiScaleFactorRoundingPolicy::Unset;
305using DpiAdjustmentPolicyLookup = EnumLookup<QHighDpiScaling::DpiAdjustmentPolicy>;
309 {
"AdjustDpi", QHighDpiScaling::DpiAdjustmentPolicy::Enabled},
310 {
"DontAdjustDpi", QHighDpiScaling::DpiAdjustmentPolicy::Disabled},
311 {
"AdjustUpOnly", QHighDpiScaling::DpiAdjustmentPolicy::UpOnly}
317 auto end =
std::end(dpiAdjustmentPolicyLookup);
318 auto it =
std::find(
std::begin(dpiAdjustmentPolicyLookup), end,
319 DpiAdjustmentPolicyLookup{v.constData(), QHighDpiScaling::DpiAdjustmentPolicy::Unset});
320 return it != end ? it->value : QHighDpiScaling::DpiAdjustmentPolicy::Unset;
323qreal QHighDpiScaling::roundScaleFactor(qreal rawFactor)
332 Qt::HighDpiScaleFactorRoundingPolicy scaleFactorRoundingPolicy =
333 QGuiApplication::highDpiScaleFactorRoundingPolicy();
336 qreal roundedFactor = rawFactor;
337 switch (scaleFactorRoundingPolicy) {
338 case Qt::HighDpiScaleFactorRoundingPolicy::Round:
339 roundedFactor = qRound(rawFactor);
341 case Qt::HighDpiScaleFactorRoundingPolicy::Ceil:
342 roundedFactor = qCeil(rawFactor);
344 case Qt::HighDpiScaleFactorRoundingPolicy::Floor:
345 roundedFactor = qFloor(rawFactor);
347 case Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor:
349 roundedFactor = rawFactor - qFloor(rawFactor) < 0.75
350 ? qFloor(rawFactor) : qCeil(rawFactor);
352 case Qt::HighDpiScaleFactorRoundingPolicy::PassThrough:
353 case Qt::HighDpiScaleFactorRoundingPolicy::Unset:
359 roundedFactor = qMax(roundedFactor, qreal(1));
361 return roundedFactor;
364QDpi QHighDpiScaling::effectiveLogicalDpi(
const QPlatformScreen *screen, qreal rawFactor, qreal roundedFactor)
375 const QDpi baseDpi = screen->logicalBaseDpi();
376 const qreal dpiAdjustmentFactor = rawFactor / roundedFactor;
379 if (QHighDpiScaling::m_dpiAdjustmentPolicy == DpiAdjustmentPolicy::Disabled)
381 if (QHighDpiScaling::m_dpiAdjustmentPolicy == DpiAdjustmentPolicy::UpOnly && dpiAdjustmentFactor < 1)
384 return QDpi(baseDpi.first * dpiAdjustmentFactor, baseDpi.second * dpiAdjustmentFactor);
388
389
390
391
392
393void QHighDpiScaling::initHighDpiScaling()
395 qCDebug(lcHighDpi) <<
"Initializing high-DPI scaling";
398 static const char* envDebugStr =
"environment variable set:";
399 std::optional envEnableHighDpiScaling = qEnvironmentVariableIntegerValue(enableHighDpiScalingEnvVar);
400 if (envEnableHighDpiScaling.has_value())
401 qCDebug(lcHighDpi) << envDebugStr << enableHighDpiScalingEnvVar << envEnableHighDpiScaling.value();
403 std::optional<qreal> envScaleFactor = qEnvironmentVariableOptionalReal(scaleFactorEnvVar);
404 if (envScaleFactor.has_value())
405 qCDebug(lcHighDpi) << envDebugStr << scaleFactorEnvVar << envScaleFactor.value();
407 const QString envScreenFactors = qEnvironmentVariable(screenFactorsEnvVar);
408 if (envScreenFactors.isNull())
409 qCDebug(lcHighDpi) << envDebugStr << screenFactorsEnvVar << envScreenFactors;
411 std::optional envUsePhysicalDpi = qEnvironmentVariableIntegerValue(usePhysicalDpiEnvVar);
412 if (envUsePhysicalDpi.has_value())
413 qCDebug(lcHighDpi) << envDebugStr << usePhysicalDpiEnvVar << envUsePhysicalDpi.value();
415 std::optional<QByteArray> envScaleFactorRoundingPolicy = qEnvironmentVariableOptionalByteArray(scaleFactorRoundingPolicyEnvVar);
416 if (envScaleFactorRoundingPolicy.has_value())
417 qCDebug(lcHighDpi) << envDebugStr << scaleFactorRoundingPolicyEnvVar << envScaleFactorRoundingPolicy.value();
419 std::optional<QByteArray> envDpiAdjustmentPolicy = qEnvironmentVariableOptionalByteArray(dpiAdjustmentPolicyEnvVar);
420 if (envDpiAdjustmentPolicy.has_value())
421 qCDebug(lcHighDpi) << envDebugStr << dpiAdjustmentPolicyEnvVar << envDpiAdjustmentPolicy.value();
424 m_usePlatformPluginDpi = envEnableHighDpiScaling.value_or(1) > 0;
425 m_platformPluginDpiScalingActive =
false;
428 m_factor = envScaleFactor.value_or(qreal(1));
429 m_globalScalingActive = !qFuzzyCompare(m_factor, qreal(1));
434 m_screenFactors = parseScreenScaleFactorsSpec(envScreenFactors);
435 m_namedScreenScaleFactors.clear();
437 m_usePhysicalDpi = envUsePhysicalDpi.value_or(0) > 0;
440 if (envScaleFactorRoundingPolicy.has_value()) {
441 QByteArray policyText = envScaleFactorRoundingPolicy.value();
442 auto policyEnumValue = lookupScaleFactorRoundingPolicy(policyText);
443 if (policyEnumValue != Qt::HighDpiScaleFactorRoundingPolicy::Unset) {
445 QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy = policyEnumValue;
447 auto values = joinEnumValues(std::begin(scaleFactorRoundingPolicyLookup),
448 std::end(scaleFactorRoundingPolicyLookup));
449 qWarning(
"Unknown scale factor rounding policy: %s. Supported values are: %s.",
450 policyText.constData(), values.constData());
455 if (envDpiAdjustmentPolicy.has_value()) {
456 QByteArray policyText = envDpiAdjustmentPolicy.value();
457 auto policyEnumValue = lookupDpiAdjustmentPolicy(policyText);
458 if (policyEnumValue != DpiAdjustmentPolicy::Unset) {
459 QHighDpiScaling::m_dpiAdjustmentPolicy = policyEnumValue;
461 auto values = joinEnumValues(std::begin(dpiAdjustmentPolicyLookup),
462 std::end(dpiAdjustmentPolicyLookup));
463 qWarning(
"Unknown DPI adjustment policy: %s. Supported values are: %s.",
464 policyText.constData(), values.constData());
469 m_active = m_globalScalingActive || m_usePlatformPluginDpi;
471 qCDebug(lcHighDpi) <<
"Initialization done, high-DPI scaling is"
472 << (m_active ?
"active" :
"inactive");
476
477
478
479void QHighDpiScaling::updateHighDpiScaling()
481 qCDebug(lcHighDpi) <<
"Updating high-DPI scaling";
484 if (m_screenFactors.size() > 0) {
485 qCDebug(lcHighDpi) <<
"Applying screen factors" << m_screenFactors;
487 const auto screens = QGuiApplication::screens();
488 for (
const auto &[name, rawFactor]: m_screenFactors) {
489 const qreal factor = roundScaleFactor(rawFactor);
492 if (i < screens.size())
493 setScreenFactor(screens.at(i), factor);
495 for (QScreen *screen : screens) {
496 if (screen->name() == name) {
497 setScreenFactor(screen, factor);
507 if (m_usePlatformPluginDpi && !m_platformPluginDpiScalingActive ) {
508 const auto screens = QGuiApplication::screens();
509 for (QScreen *screen : screens) {
510 if (!qFuzzyCompare(screenSubfactor(screen->handle()), qreal(1))) {
511 m_platformPluginDpiScalingActive =
true;
517 m_active = m_globalScalingActive || m_screenFactorSet || m_platformPluginDpiScalingActive;
519 qCDebug(lcHighDpi) <<
"Update done, high-DPI scaling is"
520 << (m_active ?
"active" :
"inactive");
524
525
526void QHighDpiScaling::setGlobalFactor(qreal factor)
528 qCDebug(lcHighDpi) <<
"Setting global scale factor to" << factor;
530 if (qFuzzyCompare(factor, m_factor))
532 if (!QGuiApplication::allWindows().isEmpty())
533 qWarning(
"QHighDpiScaling::setFactor: Should only be called when no windows exist.");
535 const auto screens = QGuiApplication::screens();
537 std::vector<QScreenPrivate::UpdateEmitter> updateEmitters;
538 for (QScreen *screen : screens)
539 updateEmitters.emplace_back(screen);
541 m_globalScalingActive = !qFuzzyCompare(factor, qreal(1));
542 m_factor = m_globalScalingActive ? factor : qreal(1);
543 m_active = m_globalScalingActive || m_screenFactorSet || m_platformPluginDpiScalingActive ;
544 for (QScreen *screen : screens)
545 screen->d_func()->updateGeometry();
551
552
553void QHighDpiScaling::setScreenFactor(QScreen *screen, qreal factor)
555 qCDebug(lcHighDpi) <<
"Setting screen scale factor for" << screen <<
"to" << factor;
557 if (!qFuzzyCompare(factor, qreal(1))) {
558 m_screenFactorSet =
true;
562 QScreenPrivate::UpdateEmitter updateEmitter(screen);
566 const QString name = screen->name();
568 screen->setProperty(scaleFactorProperty, QVariant(factor));
570 QHighDpiScaling::m_namedScreenScaleFactors.insert(name, factor);
572 screen->d_func()->updateGeometry();
575QPoint QHighDpiScaling::mapPositionToNative(
const QPoint &pos,
const QPlatformScreen *platformScreen)
579 const qreal scaleFactor = factor(platformScreen);
580 const QPoint topLeft = platformScreen->geometry().topLeft();
581 return (pos - topLeft) * scaleFactor + topLeft;
584QPoint QHighDpiScaling::mapPositionFromNative(
const QPoint &pos,
const QPlatformScreen *platformScreen)
588 const qreal scaleFactor = factor(platformScreen);
589 const QPoint topLeft = platformScreen->geometry().topLeft();
590 return (pos - topLeft) / scaleFactor + topLeft;
593qreal QHighDpiScaling::screenSubfactor(
const QPlatformScreen *screen)
595 auto factor = qreal(1.0);
603 bool screenPropertyUsed =
false;
604 if (m_screenFactorSet) {
608 if (
auto qScreen = screen->screen()) {
609 auto screenFactor = qScreen->property(scaleFactorProperty).toReal(&screenPropertyUsed);
610 if (screenPropertyUsed)
611 factor = screenFactor;
614 if (!screenPropertyUsed) {
615 auto byNameIt = QHighDpiScaling::m_namedScreenScaleFactors.constFind(screen->name());
616 if ((screenPropertyUsed = byNameIt != QHighDpiScaling::m_namedScreenScaleFactors.cend()))
621 if (!screenPropertyUsed && m_usePlatformPluginDpi)
622 factor = roundScaleFactor(rawScaleFactor(screen));
627QDpi QHighDpiScaling::logicalDpi(
const QScreen *screen)
630 if (!screen || !screen->handle())
633 if (!m_usePlatformPluginDpi) {
634 const qreal screenScaleFactor = screenSubfactor(screen->handle());
635 const QDpi dpi = QPlatformScreen::overrideDpi(screen->handle()->logicalDpi());
636 return QDpi{ dpi.first / screenScaleFactor, dpi.second / screenScaleFactor };
639 const qreal scaleFactor = rawScaleFactor(screen->handle());
640 const qreal roundedScaleFactor = roundScaleFactor(scaleFactor);
641 return effectiveLogicalDpi(screen->handle(), scaleFactor, roundedScaleFactor);
647QScreen *QHighDpiScaling::screenForPosition(QHighDpiScaling::Point position, QScreen *guess)
649 if (position.kind == QHighDpiScaling::Point::Invalid)
652 auto getPlatformScreenGuess = [](QScreen *maybeScreen) -> QPlatformScreen * {
654 return maybeScreen->handle();
655 if (QScreen *primary = QGuiApplication::primaryScreen())
656 return primary->handle();
660 QPlatformScreen *platformGuess = getPlatformScreenGuess(guess);
664 auto onScreen = [](QHighDpiScaling::Point position,
const QPlatformScreen *platformScreen) ->
bool {
665 return position.kind == Point::Native
666 ? platformScreen->geometry().contains(position.point)
667 : platformScreen->screen()->geometry().contains(position.point);
671 if (onScreen(position, platformGuess))
672 return platformGuess->screen();
675 const auto screens = platformGuess->virtualSiblings();
676 for (
const QPlatformScreen *screen : screens) {
677 if (onScreen(position, screen))
678 return screen->screen();
684QList<QHighDpiScaling::ScreenFactor> QHighDpiScaling::parseScreenScaleFactorsSpec(QStringView screenScaleFactors)
686 QVector<QHighDpiScaling::ScreenFactor> screenFactors;
691 const auto specs = screenScaleFactors.split(u';');
692 for (
const auto &spec : specs) {
693 const qsizetype equalsPos = spec.lastIndexOf(u'=');
694 if (equalsPos == -1) {
697 const qreal factor = spec.toDouble(&ok);
698 if (ok && factor > 0) {
699 screenFactors.append(QHighDpiScaling::ScreenFactor(QString(), factor));
704 const qreal factor = spec.mid(equalsPos + 1).toDouble(&ok);
705 if (ok && factor > 0) {
706 screenFactors.append(QHighDpiScaling::ScreenFactor(spec.left(equalsPos).toString(), factor));
711 return screenFactors;
714QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(
const QPlatformScreen *platformScreen, QHighDpiScaling::Point position)
718 return { qreal(1), QPoint() };
720 return { m_factor, QPoint() };
721 return { m_factor * screenSubfactor(platformScreen), platformScreen->geometry().topLeft() };
724QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(
const QScreen *screen, QHighDpiScaling::Point position)
728 return { qreal(1), QPoint() };
730 return { m_factor, QPoint() };
731 return scaleAndOrigin(screen->handle(), position);
734QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(
const QWindow *window, QHighDpiScaling::Point position)
737 return { qreal(1), QPoint() };
741 QScreen *screen = window ? window->screen() : QGuiApplication::primaryScreen();
742 QScreen *overrideScreen = QHighDpiScaling::screenForPosition(position, screen);
743 QScreen *targetScreen = overrideScreen ? overrideScreen : screen;
744 return scaleAndOrigin(targetScreen, position);
747#ifndef QT_NO_DEBUG_STREAM
748QDebug operator<<(QDebug debug,
const QHighDpiScaling::ScreenFactor &factor)
750 const QDebugStateSaver saver(debug);
752 if (!factor.name.isEmpty())
753 debug << factor.name <<
"=";
754 debug << factor.factor;
761QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(
const QPlatformScreen *, QPoint *)
763 return { qreal(1), QPoint() };
766QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(
const QScreen *, QPoint *)
768 return { qreal(1), QPoint() };
771QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(
const QWindow *, QPoint *)
773 return { qreal(1), QPoint() };
780#include "moc_qhighdpiscaling_p.cpp"
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
static const char screenFactorsEnvVar[]
static const char dpiAdjustmentPolicyEnvVar[]
static const ScaleFactorRoundingPolicyLookup scaleFactorRoundingPolicyLookup[]
static bool operator==(const EnumLookup< EnumType > &e1, const EnumLookup< EnumType > &e2)
static const char scaleFactorRoundingPolicyEnvVar[]
static std::optional< qreal > qEnvironmentVariableOptionalReal(const char *name)
static Qt::HighDpiScaleFactorRoundingPolicy lookupScaleFactorRoundingPolicy(const QByteArray &v)
static const char enableHighDpiScalingEnvVar[]
static const DpiAdjustmentPolicyLookup dpiAdjustmentPolicyLookup[]
static const char usePhysicalDpiEnvVar[]
static std::optional< QString > qEnvironmentVariableOptionalString(const char *name)
static std::optional< QByteArray > qEnvironmentVariableOptionalByteArray(const char *name)
static QByteArray joinEnumValues(const EnumLookup< EnumType > *i1, const EnumLookup< EnumType > *i2)
static const char scaleFactorEnvVar[]
static const char scaleFactorProperty[]
static QHighDpiScaling::DpiAdjustmentPolicy lookupDpiAdjustmentPolicy(const QByteArray &v)