1
/****************************************************************************
3
** Copyright (C) 2016 The Qt Company Ltd.
4
** Contact: https://www.qt.io/licensing/
6
** This file is part of the QtGui module of the Qt Toolkit.
8
** $QT_BEGIN_LICENSE:LGPL$
9
** Commercial License Usage
10
** Licensees holding valid commercial Qt licenses may use this file in
11
** accordance with the commercial license agreement provided with the
12
** Software or, alternatively, in accordance with the terms contained in
13
** a written agreement between you and The Qt Company. For licensing terms
14
** and conditions see https://www.qt.io/terms-conditions. For further
15
** information use the contact form at https://www.qt.io/contact-us.
17
** GNU Lesser General Public License Usage
18
** Alternatively, this file may be used under the terms of the GNU Lesser
19
** General Public License version 3 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL3 included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 3 requirements
23
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25
** GNU General Public License Usage
26
** Alternatively, this file may be used under the terms of the GNU
27
** General Public License version 2.0 or (at your option) the GNU General
28
** Public license version 3 or any later version approved by the KDE Free
29
** Qt Foundation. The licenses are as published by the Free Software
30
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31
** included in the packaging of this file. Please review the following
32
** information to ensure the GNU General Public License requirements will
33
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34
** https://www.gnu.org/licenses/gpl-3.0.html.
38
****************************************************************************/
40
#include "qhighdpiscaling_p.h"
41
#include "qguiapplication.h"
43
#include "qplatformintegration.h"
44
#include "private/qscreen_p.h"
46
#include <QtCore/qdebug.h>
50
Q_LOGGING_CATEGORY(lcScaling, "qt.scaling");
52
#ifndef QT_NO_HIGHDPISCALING
53
static const char legacyDevicePixelEnvVar[] = "QT_DEVICE_PIXEL_RATIO";
54
static const char scaleFactorEnvVar[] = "QT_SCALE_FACTOR";
55
static const char autoScreenEnvVar[] = "QT_AUTO_SCREEN_SCALE_FACTOR";
56
static const char screenFactorsEnvVar[] = "QT_SCREEN_SCALE_FACTORS";
58
static inline qreal initialGlobalScaleFactor()
62
if (qEnvironmentVariableIsSet(scaleFactorEnvVar)) {
64
const qreal f = qgetenv(scaleFactorEnvVar).toDouble(&ok);
66
qCDebug(lcScaling) << "Apply " << scaleFactorEnvVar << f;
70
if (qEnvironmentVariableIsSet(legacyDevicePixelEnvVar)) {
71
qWarning() << "Warning:" << legacyDevicePixelEnvVar << "is deprecated. Instead use:" << endl
72
<< " " << autoScreenEnvVar << "to enable platform plugin controlled per-screen factors." << endl
73
<< " " << screenFactorsEnvVar << "to set per-screen factors." << endl
74
<< " " << scaleFactorEnvVar << "to set the application global scale factor.";
76
int dpr = qEnvironmentVariableIntValue(legacyDevicePixelEnvVar);
85
\class QHighDpiScaling
91
\brief Collection of utility functions for UI scaling.
93
QHighDpiScaling implements utility functions for high-dpi scaling for use
94
on operating systems that provide limited support for native scaling. In
95
addition this functionality can be used for simulation and testing purposes.
97
The functions support scaling between the device independent coordinate
98
system used by Qt applications and the native coordinate system used by
99
the platform plugins. Intended usage locations are the low level / platform
100
plugin interfacing parts of QtGui, for example the QWindow, QScreen and
101
QWindowSystemInterface implementation.
103
There are now up to three active coordinate systems in Qt:
105
---------------------------------------------------
106
| Application Device Independent Pixels | devicePixelRatio
109
|---------------------------------------------------| Qt Scale Factor
110
| Qt Gui QPlatform* Native Pixels | *
111
| Qt platform plugin |
112
|---------------------------------------------------| OS Scale Factor
113
| Display Device Pixels |
114
| (Graphics Buffers) |
115
-----------------------------------------------------
117
This is an simplification and shows the main coordinate system. All layers
118
may work with device pixels in specific cases: OpenGL, creating the backing
119
store, and QPixmap management. The "Native Pixels" coordinate system is
120
internal to Qt and should not be exposed to Qt users: Seen from the outside
121
there are only two coordinate systems: device independent pixels and device
124
The devicePixelRatio seen by applications is the product of the Qt scale
125
factor and the OS scale factor. The value of the scale factors may be 1,
126
in which case two or more of the coordinate systems are equivalent. Platforms
127
that (may) have an OS scale factor include \macos, iOS and Wayland.
129
Note that the functions in this file do not work with the OS scale factor
130
directly and are limited to converting between device independent and native
131
pixels. The OS scale factor is accunted for by QWindow::devicePixelRatio()
132
and similar functions.
134
Configuration Examples:
136
'Classic': Device Independent Pixels = Native Pixels = Device Pixels
137
--------------------------------------------------- devicePixelRatio: 1
138
| Application / Qt Gui 100 x 100 |
139
| | Qt Scale Factor: 1
140
| Qt Platform / OS 100 x 100 |
141
| | OS Scale Factor: 1
142
| Display 100 x 100 |
143
-----------------------------------------------------
145
'Retina Device': Device Independent Pixels = Native Pixels
146
--------------------------------------------------- devicePixelRatio: 2
147
| Application / Qt Gui 100 x 100 |
148
| | Qt Scale Factor: 1
149
| Qt Platform / OS 100 x 100 |
150
|---------------------------------------------------| OS Scale Factor: 2
151
| Display 200 x 200 |
152
-----------------------------------------------------
154
'2x Qt Scaling': Native Pixels = Device Pixels
155
--------------------------------------------------- devicePixelRatio: 2
156
| Application / Qt Gui 100 x 100 |
157
|---------------------------------------------------| Qt Scale Factor: 2
158
| Qt Platform / OS 200 x 200 |
159
| | OS Scale Factor: 1
160
| Display 200 x 200 |
161
-----------------------------------------------------
163
The Qt Scale Factor is the product of two sub-scale factors, which
164
are independently either set or determined by the platform plugin.
165
Several APIs are offered for this, targeting both developers and
166
end users. All scale factors are of type qreal.
168
1) A global scale factor
169
The QT_SCALE_FACTOR environment variable can be used to set
170
a global scale factor for all windows in the processs. This
171
is useful for testing and debugging (you can simulate any
172
devicePixelRatio without needing access to special hardware),
173
and perhaps also for targeting a specific application to
174
a specific display type (embedded use cases).
176
2) Per-screen scale factors
177
Some platform plugins support providing a per-screen scale
178
factor based on display density information. These platforms
179
include X11, Windows, and Android.
181
There are two APIs for enabling or disabling this behavior:
182
- The QT_AUTO_SCREEN_SCALE_FACTOR environment variable.
183
- The AA_EnableHighDpiScaling and AA_DisableHighDpiScaling
184
application attributes
186
Enabling either will make QHighDpiScaling call QPlatformScreen::pixelDensity()
187
and use the value provided as the scale factor for the screen in
188
question. Disabling is done on a 'veto' basis where either the
189
environment or the application can disable the scaling. The intended use
190
cases are 'My system is not providing correct display density
191
information' and 'My application needs to work in display pixels',
194
The QT_SCREEN_SCALE_FACTORS environment variable can be used to set the screen
195
scale factors manually. Set this to a semicolon-separated
196
list of scale factors (matching the order of QGuiApplications::screens()),
197
or to a list of name=value pairs (where name matches QScreen::name()).
199
Coordinate conversion functions must be used when writing code that passes
200
geometry across the Qt Gui / Platform plugin boundary. The main conversion
202
T toNativePixels(T, QWindow *)
203
T fromNativePixels(T, QWindow*)
205
The following classes in QtGui use native pixels, for the convenience of the
209
QWindowSystemInterface (API only - Events are in device independent pixels)
211
As a special consideration platform plugin code should be careful about
212
calling QtGui geometry accessor functions:
213
QRect r = window->geometry();
214
Here the returned geometry is in device independent pixels. Add a conversion call:
215
QRect r = QHighDpi::toNativePixels(window->geometry());
216
(Avoiding calling QWindow and instead using the QPlatformWindow geometry
217
might be a better course of action in this case.)
220
qreal QHighDpiScaling::m_factor = 1.0;
221
bool QHighDpiScaling::m_active = false; //"overall active" - is there any scale factor set.
222
bool QHighDpiScaling::m_usePixelDensity = false; // use scale factor from platform plugin
223
bool QHighDpiScaling::m_pixelDensityScalingActive = false; // pixel density scale factor > 1
224
bool QHighDpiScaling::m_globalScalingActive = false; // global scale factor is active
225
bool QHighDpiScaling::m_screenFactorSet = false; // QHighDpiScaling::setScreenFactor has been used
226
QDpi QHighDpiScaling::m_logicalDpi = QDpi(-1,-1); // The scaled logical DPI of the primary screen
229
Initializes the QHighDpiScaling global variables. Called before the
230
platform plugin is created.
233
static inline bool usePixelDensity()
235
// Determine if we should set a scale factor based on the pixel density
236
// reported by the platform plugin. There are several enablers and several
237
// disablers. A single disable may veto all other enablers.
238
if (QCoreApplication::testAttribute(Qt::AA_DisableHighDpiScaling))
240
bool screenEnvValueOk;
241
const int screenEnvValue = qEnvironmentVariableIntValue(autoScreenEnvVar, &screenEnvValueOk);
242
if (screenEnvValueOk && screenEnvValue < 1)
244
return QCoreApplication::testAttribute(Qt::AA_EnableHighDpiScaling)
245
|| (screenEnvValueOk && screenEnvValue > 0)
246
|| (qEnvironmentVariableIsSet(legacyDevicePixelEnvVar) && qgetenv(legacyDevicePixelEnvVar).toLower() == "auto");
249
void QHighDpiScaling::initHighDpiScaling()
251
// Determine if there is a global scale factor set.
252
m_factor = initialGlobalScaleFactor();
253
m_globalScalingActive = !qFuzzyCompare(m_factor, qreal(1));
255
m_usePixelDensity = usePixelDensity();
257
m_pixelDensityScalingActive = false; //set in updateHighDpiScaling below
259
// we update m_active in updateHighDpiScaling, but while we create the
260
// screens, we have to assume that m_usePixelDensity implies scaling
261
m_active = m_globalScalingActive || m_usePixelDensity;
264
void QHighDpiScaling::updateHighDpiScaling()
266
if (QCoreApplication::testAttribute(Qt::AA_DisableHighDpiScaling))
269
if (m_usePixelDensity && !m_pixelDensityScalingActive) {
270
const auto screens = QGuiApplication::screens();
271
for (QScreen *screen : screens) {
272
if (!qFuzzyCompare(screenSubfactor(screen->handle()), qreal(1))) {
273
m_pixelDensityScalingActive = true;
278
if (qEnvironmentVariableIsSet(screenFactorsEnvVar)) {
280
const auto specs = qgetenv(screenFactorsEnvVar).split(';');
281
for (const QByteArray &spec : specs) {
283
int equalsPos = spec.lastIndexOf('=');
286
// support "name=factor"
287
QByteArray name = spec.mid(0, equalsPos);
288
QByteArray f = spec.mid(equalsPos + 1);
290
factor = f.toDouble(&ok);
292
const auto screens = QGuiApplication::screens();
293
for (QScreen *s : screens) {
294
if (s->name() == QString::fromLocal8Bit(name)) {
301
// listing screens in order
303
factor = spec.toDouble(&ok);
304
if (ok && i < QGuiApplication::screens().count())
305
screen = QGuiApplication::screens().at(i);
308
setScreenFactor(screen, factor);
312
m_active = m_globalScalingActive || m_screenFactorSet || m_pixelDensityScalingActive;
314
QPlatformScreen *primaryScreen = QGuiApplication::primaryScreen()->handle();
315
qreal sf = screenSubfactor(primaryScreen);
316
QDpi primaryDpi = primaryScreen->logicalDpi();
317
m_logicalDpi = QDpi(primaryDpi.first / sf, primaryDpi.second / sf);
321
Sets the global scale factor which is applied to all windows.
323
void QHighDpiScaling::setGlobalFactor(qreal factor)
325
if (qFuzzyCompare(factor, m_factor))
327
if (!QGuiApplication::allWindows().isEmpty())
328
qWarning("QHighDpiScaling::setFactor: Should only be called when no windows exist.");
330
m_globalScalingActive = !qFuzzyCompare(factor, qreal(1));
331
m_factor = m_globalScalingActive ? factor : qreal(1);
332
m_active = m_globalScalingActive || m_screenFactorSet || m_pixelDensityScalingActive;
333
const auto screens = QGuiApplication::screens();
334
for (QScreen *screen : screens)
335
screen->d_func()->updateHighDpi();
338
static const char scaleFactorProperty[] = "_q_scaleFactor";
341
Sets a per-screen scale factor.
343
void QHighDpiScaling::setScreenFactor(QScreen *screen, qreal factor)
345
m_screenFactorSet = true;
347
screen->setProperty(scaleFactorProperty, QVariant(factor));
349
// hack to force re-evaluation of screen geometry
350
if (screen->handle())
351
screen->d_func()->setPlatformScreen(screen->handle()); // updates geometries based on scale factor
354
QPoint QHighDpiScaling::mapPositionToNative(const QPoint &pos, const QPlatformScreen *platformScreen)
358
const qreal scaleFactor = factor(platformScreen);
359
const QPoint topLeft = platformScreen->geometry().topLeft();
360
return (pos - topLeft) * scaleFactor + topLeft;
363
QPoint QHighDpiScaling::mapPositionFromNative(const QPoint &pos, const QPlatformScreen *platformScreen)
367
const qreal scaleFactor = factor(platformScreen);
368
const QPoint topLeft = platformScreen->geometry().topLeft();
369
return (pos - topLeft) / scaleFactor + topLeft;
372
qreal QHighDpiScaling::screenSubfactor(const QPlatformScreen *screen)
374
qreal factor = qreal(1.0);
376
if (m_usePixelDensity)
377
factor *= screen->pixelDensity();
378
if (m_screenFactorSet) {
379
QVariant screenFactor = screen->screen()->property(scaleFactorProperty);
380
if (screenFactor.isValid())
381
factor *= screenFactor.toReal();
387
QDpi QHighDpiScaling::logicalDpi()
392
qreal QHighDpiScaling::factor(const QScreen *screen)
394
// Fast path for when scaling in Qt is not used at all.
398
// The effective factor for a given screen is the product of the
399
// screen and global sub-factors
400
qreal factor = m_factor;
402
factor *= screenSubfactor(screen->handle());
406
qreal QHighDpiScaling::factor(const QPlatformScreen *platformScreen)
411
return m_factor * screenSubfactor(platformScreen);
414
qreal QHighDpiScaling::factor(const QWindow *window)
419
return factor(window ? window->screen() : QGuiApplication::primaryScreen());
422
QPoint QHighDpiScaling::origin(const QScreen *screen)
424
return screen->geometry().topLeft();
427
QPoint QHighDpiScaling::origin(const QPlatformScreen *platformScreen)
429
return platformScreen->geometry().topLeft();
432
#endif //QT_NO_HIGHDPISCALING