9#include "private/qscreen_p.h"
10#include <private/qguiapplication_p.h>
12#include <QtCore/qdebug.h>
13#include <QtCore/qmetaobject.h>
22#ifndef QT_NO_HIGHDPISCALING
34 QString value = qEnvironmentVariable(name);
35 return value.isNull() ?
std::nullopt :
std::optional(
std::move(value));
40 QByteArray value = qgetenv(name);
41 return value.isNull() ?
std::nullopt :
std::optional(
std::move(value));
46 const QByteArray val = qgetenv(name);
51 const qreal value = val.toDouble(&ok);
52 return ok ?
std::optional(value) :
std::nullopt;
56
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
230qreal QHighDpiScaling::m_factor = 1.0;
231bool QHighDpiScaling::m_active =
false;
232bool QHighDpiScaling::m_usePlatformPluginDpi =
false;
233bool QHighDpiScaling::m_platformPluginDpiScalingActive =
false;
234bool QHighDpiScaling::m_globalScalingActive =
false;
235bool QHighDpiScaling::m_screenFactorSet =
false;
236bool QHighDpiScaling::m_usePhysicalDpi =
false;
237QVector<QHighDpiScaling::ScreenFactor> QHighDpiScaling::m_screenFactors;
238QHighDpiScaling::DpiAdjustmentPolicy QHighDpiScaling::m_dpiAdjustmentPolicy = QHighDpiScaling::DpiAdjustmentPolicy::Unset;
239QHash<QString, qreal> QHighDpiScaling::m_namedScreenScaleFactors;
241qreal QHighDpiScaling::rawScaleFactor(
const QPlatformScreen *screen)
245 QDpi platformBaseDpi = screen->logicalBaseDpi();
246 if (QHighDpiScaling::m_usePhysicalDpi) {
247 QSize sz = screen->geometry().size();
248 QSizeF psz = screen->physicalSize();
249 qreal platformPhysicalDpi = ((sz.height() / psz.height()) + (sz.width() / psz.width())) * qreal(25.4 * 0.5);
250 factor = qRound(platformPhysicalDpi) / qreal(platformBaseDpi.first);
252 const QDpi platformLogicalDpi = QPlatformScreen::overrideDpi(screen->logicalDpi());
253 factor = qreal(platformLogicalDpi.first) / qreal(platformBaseDpi.first);
259template <
class EnumType>
266template <
class EnumType>
269 return qstricmp(e1.name, e2.name) == 0;
272template <
class EnumType>
276 for (; i1 < i2; ++i1) {
277 if (!result.isEmpty())
278 result += QByteArrayLiteral(
", ");
284using ScaleFactorRoundingPolicyLookup = EnumLookup<Qt::HighDpiScaleFactorRoundingPolicy>;
288 {
"Round", Qt::HighDpiScaleFactorRoundingPolicy::Round},
289 {
"Ceil", Qt::HighDpiScaleFactorRoundingPolicy::Ceil},
290 {
"Floor", Qt::HighDpiScaleFactorRoundingPolicy::Floor},
291 {
"RoundPreferFloor", Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor},
292 {
"PassThrough", Qt::HighDpiScaleFactorRoundingPolicy::PassThrough}
298 auto end =
std::end(scaleFactorRoundingPolicyLookup);
299 auto it = std::find(std::begin(scaleFactorRoundingPolicyLookup), end,
300 ScaleFactorRoundingPolicyLookup{v.constData(), Qt::HighDpiScaleFactorRoundingPolicy::Unset});
301 return it != end ? it->value : Qt::HighDpiScaleFactorRoundingPolicy::Unset;
304using DpiAdjustmentPolicyLookup = EnumLookup<QHighDpiScaling::DpiAdjustmentPolicy>;
308 {
"AdjustDpi", QHighDpiScaling::DpiAdjustmentPolicy::Enabled},
309 {
"DontAdjustDpi", QHighDpiScaling::DpiAdjustmentPolicy::Disabled},
310 {
"AdjustUpOnly", QHighDpiScaling::DpiAdjustmentPolicy::UpOnly}
316 auto end =
std::end(dpiAdjustmentPolicyLookup);
317 auto it = std::find(std::begin(dpiAdjustmentPolicyLookup), end,
318 DpiAdjustmentPolicyLookup{v.constData(), QHighDpiScaling::DpiAdjustmentPolicy::Unset});
319 return it != end ? it->value : QHighDpiScaling::DpiAdjustmentPolicy::Unset;
322qreal QHighDpiScaling::roundScaleFactor(qreal rawFactor)
331 Qt::HighDpiScaleFactorRoundingPolicy scaleFactorRoundingPolicy =
332 QGuiApplication::highDpiScaleFactorRoundingPolicy();
335 qreal roundedFactor = rawFactor;
336 switch (scaleFactorRoundingPolicy) {
337 case Qt::HighDpiScaleFactorRoundingPolicy::Round:
338 roundedFactor = qRound(rawFactor);
340 case Qt::HighDpiScaleFactorRoundingPolicy::Ceil:
341 roundedFactor = qCeil(rawFactor);
343 case Qt::HighDpiScaleFactorRoundingPolicy::Floor:
344 roundedFactor = qFloor(rawFactor);
346 case Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor:
348 roundedFactor = rawFactor - qFloor(rawFactor) < 0.75
349 ? qFloor(rawFactor) : qCeil(rawFactor);
351 case Qt::HighDpiScaleFactorRoundingPolicy::PassThrough:
352 case Qt::HighDpiScaleFactorRoundingPolicy::Unset:
358 roundedFactor = qMax(roundedFactor, qreal(1));
360 return roundedFactor;
363QDpi QHighDpiScaling::effectiveLogicalDpi(
const QPlatformScreen *screen, qreal rawFactor, qreal roundedFactor)
374 const QDpi baseDpi = screen->logicalBaseDpi();
375 const qreal dpiAdjustmentFactor = rawFactor / roundedFactor;
378 if (QHighDpiScaling::m_dpiAdjustmentPolicy == DpiAdjustmentPolicy::Disabled)
380 if (QHighDpiScaling::m_dpiAdjustmentPolicy == DpiAdjustmentPolicy::UpOnly && dpiAdjustmentFactor < 1)
383 return QDpi(baseDpi.first * dpiAdjustmentFactor, baseDpi.second * dpiAdjustmentFactor);
387
388
389
390
391
392void QHighDpiScaling::initHighDpiScaling()
394 qCDebug(lcHighDpi) <<
"Initializing high-DPI scaling";
397 static const char* envDebugStr =
"environment variable set:";
398 std::optional envEnableHighDpiScaling = qEnvironmentVariableIntegerValue(enableHighDpiScalingEnvVar);
399 if (envEnableHighDpiScaling.has_value())
400 qCDebug(lcHighDpi) << envDebugStr << enableHighDpiScalingEnvVar << envEnableHighDpiScaling.value();
402 std::optional<qreal> envScaleFactor = qEnvironmentVariableOptionalReal(scaleFactorEnvVar);
403 if (envScaleFactor.has_value())
404 qCDebug(lcHighDpi) << envDebugStr << scaleFactorEnvVar << envScaleFactor.value();
406 const QString envScreenFactors = qEnvironmentVariable(screenFactorsEnvVar);
407 if (envScreenFactors.isNull())
408 qCDebug(lcHighDpi) << envDebugStr << screenFactorsEnvVar << envScreenFactors;
410 std::optional envUsePhysicalDpi = qEnvironmentVariableIntegerValue(usePhysicalDpiEnvVar);
411 if (envUsePhysicalDpi.has_value())
412 qCDebug(lcHighDpi) << envDebugStr << usePhysicalDpiEnvVar << envUsePhysicalDpi.value();
414 std::optional<QByteArray> envScaleFactorRoundingPolicy = qEnvironmentVariableOptionalByteArray(scaleFactorRoundingPolicyEnvVar);
415 if (envScaleFactorRoundingPolicy.has_value())
416 qCDebug(lcHighDpi) << envDebugStr << scaleFactorRoundingPolicyEnvVar << envScaleFactorRoundingPolicy.value();
418 std::optional<QByteArray> envDpiAdjustmentPolicy = qEnvironmentVariableOptionalByteArray(dpiAdjustmentPolicyEnvVar);
419 if (envDpiAdjustmentPolicy.has_value())
420 qCDebug(lcHighDpi) << envDebugStr << dpiAdjustmentPolicyEnvVar << envDpiAdjustmentPolicy.value();
423 m_usePlatformPluginDpi = envEnableHighDpiScaling.value_or(1) > 0;
424 m_platformPluginDpiScalingActive =
false;
427 m_factor = envScaleFactor.value_or(qreal(1));
428 m_globalScalingActive = !qFuzzyCompare(m_factor, qreal(1));
433 m_screenFactors = parseScreenScaleFactorsSpec(envScreenFactors);
434 m_namedScreenScaleFactors.clear();
436 m_usePhysicalDpi = envUsePhysicalDpi.value_or(0) > 0;
439 if (envScaleFactorRoundingPolicy.has_value()) {
440 QByteArray policyText = envScaleFactorRoundingPolicy.value();
441 auto policyEnumValue = lookupScaleFactorRoundingPolicy(policyText);
442 if (policyEnumValue != Qt::HighDpiScaleFactorRoundingPolicy::Unset) {
444 QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy = policyEnumValue;
446 auto values = joinEnumValues(std::begin(scaleFactorRoundingPolicyLookup),
447 std::end(scaleFactorRoundingPolicyLookup));
448 qWarning(
"Unknown scale factor rounding policy: %s. Supported values are: %s.",
449 policyText.constData(), values.constData());
454 if (envDpiAdjustmentPolicy.has_value()) {
455 QByteArray policyText = envDpiAdjustmentPolicy.value();
456 auto policyEnumValue = lookupDpiAdjustmentPolicy(policyText);
457 if (policyEnumValue != DpiAdjustmentPolicy::Unset) {
458 QHighDpiScaling::m_dpiAdjustmentPolicy = policyEnumValue;
460 auto values = joinEnumValues(std::begin(dpiAdjustmentPolicyLookup),
461 std::end(dpiAdjustmentPolicyLookup));
462 qWarning(
"Unknown DPI adjustment policy: %s. Supported values are: %s.",
463 policyText.constData(), values.constData());
468 m_active = m_globalScalingActive || m_usePlatformPluginDpi;
470 qCDebug(lcHighDpi) <<
"Initialization done, high-DPI scaling is"
471 << (m_active ?
"active" :
"inactive");
475
476
477
478void QHighDpiScaling::updateHighDpiScaling()
480 qCDebug(lcHighDpi) <<
"Updating high-DPI scaling";
483 if (m_screenFactors.size() > 0) {
484 qCDebug(lcHighDpi) <<
"Applying screen factors" << m_screenFactors;
486 const auto screens = QGuiApplication::screens();
487 for (
const auto &[name, rawFactor]: m_screenFactors) {
488 const qreal factor = roundScaleFactor(rawFactor);
491 if (i < screens.size())
492 setScreenFactor(screens.at(i), factor);
494 for (QScreen *screen : screens) {
495 if (screen->name() == name) {
496 setScreenFactor(screen, factor);
506 if (m_usePlatformPluginDpi && !m_platformPluginDpiScalingActive ) {
507 const auto screens = QGuiApplication::screens();
508 for (QScreen *screen : screens) {
509 if (!qFuzzyCompare(screenSubfactor(screen->handle()), qreal(1))) {
510 m_platformPluginDpiScalingActive =
true;
516 m_active = m_globalScalingActive || m_screenFactorSet || m_platformPluginDpiScalingActive;
518 qCDebug(lcHighDpi) <<
"Update done, high-DPI scaling is"
519 << (m_active ?
"active" :
"inactive");
523
524
525void QHighDpiScaling::setGlobalFactor(qreal factor)
527 qCDebug(lcHighDpi) <<
"Setting global scale factor to" << factor;
529 if (qFuzzyCompare(factor, m_factor))
531 if (!QGuiApplication::allWindows().isEmpty())
532 qWarning(
"QHighDpiScaling::setFactor: Should only be called when no windows exist.");
534 const auto screens = QGuiApplication::screens();
536 std::vector<QScreenPrivate::UpdateEmitter> updateEmitters;
537 for (QScreen *screen : screens)
538 updateEmitters.emplace_back(screen);
540 m_globalScalingActive = !qFuzzyCompare(factor, qreal(1));
541 m_factor = m_globalScalingActive ? factor : qreal(1);
542 m_active = m_globalScalingActive || m_screenFactorSet || m_platformPluginDpiScalingActive ;
543 for (QScreen *screen : screens)
544 screen->d_func()->updateGeometry();
550
551
552void QHighDpiScaling::setScreenFactor(QScreen *screen, qreal factor)
554 qCDebug(lcHighDpi) <<
"Setting screen scale factor for" << screen <<
"to" << factor;
556 if (!qFuzzyCompare(factor, qreal(1))) {
557 m_screenFactorSet =
true;
561 QScreenPrivate::UpdateEmitter updateEmitter(screen);
565 const QString name = screen->name();
567 screen->setProperty(scaleFactorProperty, QVariant(factor));
569 QHighDpiScaling::m_namedScreenScaleFactors.insert(name, factor);
571 screen->d_func()->updateGeometry();
574QPoint QHighDpiScaling::mapPositionToNative(
const QPoint &pos,
const QPlatformScreen *platformScreen)
578 const qreal scaleFactor = factor(platformScreen);
579 const QPoint topLeft = platformScreen->geometry().topLeft();
580 return (pos - topLeft) * scaleFactor + topLeft;
583QPoint QHighDpiScaling::mapPositionFromNative(
const QPoint &pos,
const QPlatformScreen *platformScreen)
587 const qreal scaleFactor = factor(platformScreen);
588 const QPoint topLeft = platformScreen->geometry().topLeft();
589 return (pos - topLeft) / scaleFactor + topLeft;
592qreal QHighDpiScaling::screenSubfactor(
const QPlatformScreen *screen)
594 auto factor = qreal(1.0);
602 bool screenPropertyUsed =
false;
603 if (m_screenFactorSet) {
607 if (
auto qScreen = screen->screen()) {
608 auto screenFactor = qScreen->property(scaleFactorProperty).toReal(&screenPropertyUsed);
609 if (screenPropertyUsed)
610 factor = screenFactor;
613 if (!screenPropertyUsed) {
614 auto byNameIt = QHighDpiScaling::m_namedScreenScaleFactors.constFind(screen->name());
615 if ((screenPropertyUsed = byNameIt != QHighDpiScaling::m_namedScreenScaleFactors.cend()))
620 if (!screenPropertyUsed && m_usePlatformPluginDpi)
621 factor = roundScaleFactor(rawScaleFactor(screen));
626QDpi QHighDpiScaling::logicalDpi(
const QScreen *screen)
629 if (!screen || !screen->handle())
632 if (!m_usePlatformPluginDpi) {
633 const qreal screenScaleFactor = screenSubfactor(screen->handle());
634 const QDpi dpi = QPlatformScreen::overrideDpi(screen->handle()->logicalDpi());
635 return QDpi{ dpi.first / screenScaleFactor, dpi.second / screenScaleFactor };
638 const qreal scaleFactor = rawScaleFactor(screen->handle());
639 const qreal roundedScaleFactor = roundScaleFactor(scaleFactor);
640 return effectiveLogicalDpi(screen->handle(), scaleFactor, roundedScaleFactor);
646QScreen *QHighDpiScaling::screenForPosition(QHighDpiScaling::Point position, QScreen *guess)
648 if (position.kind == QHighDpiScaling::Point::Invalid)
651 auto getPlatformScreenGuess = [](QScreen *maybeScreen) -> QPlatformScreen * {
653 return maybeScreen->handle();
654 if (QScreen *primary = QGuiApplication::primaryScreen())
655 return primary->handle();
659 QPlatformScreen *platformGuess = getPlatformScreenGuess(guess);
663 auto onScreen = [](QHighDpiScaling::Point position,
const QPlatformScreen *platformScreen) ->
bool {
664 return position.kind == Point::Native
665 ? platformScreen->geometry().contains(position.point)
666 : platformScreen->screen()->geometry().contains(position.point);
670 if (onScreen(position, platformGuess))
671 return platformGuess->screen();
674 const auto screens = platformGuess->virtualSiblings();
675 for (
const QPlatformScreen *screen : screens) {
676 if (onScreen(position, screen))
677 return screen->screen();
683QList<QHighDpiScaling::ScreenFactor> QHighDpiScaling::parseScreenScaleFactorsSpec(QStringView screenScaleFactors)
685 QVector<QHighDpiScaling::ScreenFactor> screenFactors;
690 const auto specs = screenScaleFactors.split(u';');
691 for (
const auto &spec : specs) {
692 const qsizetype equalsPos = spec.lastIndexOf(u'=');
693 if (equalsPos == -1) {
696 const qreal factor = spec.toDouble(&ok);
697 if (ok && factor > 0) {
698 screenFactors.append(QHighDpiScaling::ScreenFactor(QString(), factor));
703 const qreal factor = spec.mid(equalsPos + 1).toDouble(&ok);
704 if (ok && factor > 0) {
705 screenFactors.append(QHighDpiScaling::ScreenFactor(spec.left(equalsPos).toString(), factor));
710 return screenFactors;
713QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(
const QPlatformScreen *platformScreen, QHighDpiScaling::Point position)
717 return { qreal(1), QPoint() };
719 return { m_factor, QPoint() };
720 return { m_factor * screenSubfactor(platformScreen), platformScreen->geometry().topLeft() };
723QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(
const QScreen *screen, QHighDpiScaling::Point position)
727 return { qreal(1), QPoint() };
729 return { m_factor, QPoint() };
730 return scaleAndOrigin(screen->handle(), position);
733QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(
const QWindow *window, QHighDpiScaling::Point position)
736 return { qreal(1), QPoint() };
740 QScreen *screen = window ? window->screen() : QGuiApplication::primaryScreen();
741 QScreen *overrideScreen = QHighDpiScaling::screenForPosition(position, screen);
742 QScreen *targetScreen = overrideScreen ? overrideScreen : screen;
743 return scaleAndOrigin(targetScreen, position);
746#ifndef QT_NO_DEBUG_STREAM
747QDebug operator<<(QDebug debug,
const QHighDpiScaling::ScreenFactor &factor)
749 const QDebugStateSaver saver(debug);
751 if (!factor.name.isEmpty())
752 debug << factor.name <<
"=";
753 debug << factor.factor;
760QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(
const QPlatformScreen *, QPoint *)
762 return { qreal(1), QPoint() };
765QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(
const QScreen *, QPoint *)
767 return { qreal(1), QPoint() };
770QHighDpiScaling::ScaleAndOrigin QHighDpiScaling::scaleAndOrigin(
const QWindow *, QPoint *)
772 return { qreal(1), QPoint() };
779#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)