diff options
author | Morten Johan Sørvig <morten.sorvig@theqtcompany.com> | 2015-06-22 12:32:11 +0200 |
---|---|---|
committer | Paul Olav Tvete <paul.tvete@theqtcompany.com> | 2015-07-30 12:44:41 +0000 |
commit | a705b4ec1f6f7133390054f8b6b8077ef0550311 (patch) | |
tree | d9df0f8650bd62690c8489e4491bc5c2407c9d07 /src/gui/kernel/qhighdpiscaling.cpp | |
parent | dcd2debe625841ee8cba6d13a56cde7613c40358 (diff) |
Introduce cross platform high-dpi scaling
Add a coordinate scaling layer to QtGui, which supports 'devicePixelRatio'
type high-dpi on all platforms, in production and also for
development and testing purposes.
High-DPI scaling is opt-in, by setting environment variables:
QT_SCALE_FACTOR - sets a global scale factor
QT_AUTO_SCREEN_SCALE_FACTOR - sets per-screen scale factors,
where the scale factors are provided by the platform plugin.
This QtGui scaling can be used instead of or in addition to scaling
done by the window system. This distinction is not visible to
applications [when they use Qt API], which will see a change in
the value returned by the devicePixelRatio() accessors as usual.
Introduce a new (private to Qt) coordinate system: native pixels.
The coordinate system stack now looks like:
device-independent pixels (app, upper parts of Qt)
native pixels (lower parts of Qt Gui, platform plugins)
device pixels (backing stores and OpenGL)
Add private QHighDpi namespace with scaling functions that convert
between device-independent pixels and native pixels:
T toNativePixels(T, QWindow *);
T fromNativePixels(T, QWindow *);
Add scaling calls the QWindow (and friends) cross-platform implementation,
around the calls to QPlatformWindow functions. QPlatformWindow now uses
native coordinates - platform code remains largely unchanged since native
coordinates are window system coordinates.
QWindow now uses (possibly) scaled coordinates. This means that
platform plugins no longer can rely on QWindow::geometry() and
related functions. QPlatformWindow::windowGeometry() and other
convenience functions have been added for use when the platform
plugin needs to convert scaled geometry to native geometry.
Add Qt::AA_NoHighDpiScaling, which can be use to disable any
scaling in QtGui, effectively ignoring the environment variables.
(Note that this does not disable any scaling done by the window
system.)
Contributions from Friedemann and Paul.
Task-number: QTBUG-46615
Change-Id: I673bbd69c130e73b13cce83be11bfb28f580bf60
Reviewed-by: Lars Knoll <lars.knoll@theqtcompany.com>
Diffstat (limited to 'src/gui/kernel/qhighdpiscaling.cpp')
-rw-r--r-- | src/gui/kernel/qhighdpiscaling.cpp | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp new file mode 100644 index 0000000000..19e3045fc9 --- /dev/null +++ b/src/gui/kernel/qhighdpiscaling.cpp @@ -0,0 +1,320 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qhighdpiscaling_p.h" +#include "qguiapplication.h" +#include "qscreen.h" +#include "qplatformintegration.h" +#include "private/qscreen_p.h" + +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcScaling, "qt.scaling"); + +static const char legacyDevicePixelEnvVar[] = "QT_DEVICE_PIXEL_RATIO"; +static const char scaleFactorEnvVar[] = "QT_SCALE_FACTOR"; +static const char autoScreenEnvVar[] = "QT_AUTO_SCREEN_SCALE_FACTOR"; +static const char screenFactorsEnvVar[] = "QT_SCREEN_SCALE_FACTORS"; + +static inline qreal initialScaleFactor() +{ + + qreal result = 1; + if (qEnvironmentVariableIsSet(scaleFactorEnvVar)) { + bool ok; + const qreal f = qgetenv(scaleFactorEnvVar).toDouble(&ok); + if (ok && f > 0) { + qCDebug(lcScaling) << "Apply " << scaleFactorEnvVar << f; + result = f; + } + } else { + if (qEnvironmentVariableIsSet(legacyDevicePixelEnvVar)) { + qWarning() << "Warning:" << legacyDevicePixelEnvVar << "is deprecated. Instead use:"; + qWarning() << " " << autoScreenEnvVar << "to enable platform plugin controlled per-screen factors."; + qWarning() << " " << screenFactorsEnvVar << "to set per-screen factors."; + qWarning() << " " << scaleFactorEnvVar << "to set the application global scale factor."; + + int dpr = qEnvironmentVariableIntValue(legacyDevicePixelEnvVar); + if (dpr > 0) + result = dpr; + } + } + return result; +} + +/*! + \class QHighDpiScaling + \since 5.6 + \internal + \preliminary + \ingroup qpa + + \brief Collection of utility functions for UI scaling. + + QHighDpiScaling implements utility functions for high-dpi scaling for use + on operating systems that provide limited support for native scaling. In + addition this functionality can be used for simulation and testing purposes. + + The functions support scaling between the device independent coordinate + system used by Qt applications and the native coordinate system used by + the platform plugins. Intended usage locations are the low level / platform + plugin interfacing parts of QtGui, for example the QWindow, QScreen and + QWindowSystemInterface implementation. + + The coordinate system scaling is enabled by setting one or more scale + factors. These will then be factored into the value returned by the + devicePixelRatio() accessors (any native scale factor will also be + included in this value). Several setters are available: + + - A process-global scale factor + - QT_SCALE_FACTOR (environment variable) + - QHighDpiScaling::setGlobalFactor() + + - A per-screen scale factor + - QT_AUTO_SCALE_FACTOR (environment variable) + Setting this to a true-ish value will make QHighDpiScaling + call QPlatformScreen::pixelDensity() + - QHighDpiScaling::setScreenFactor(screen, factor); + - QT_SCREEN_SCALE_FACTORS (environment variable) + Set this to a semicolon-separated list of scale factors + (matching the order of QGuiApplications::screens()), + or to a list of name=value pairs (where name matches + QScreen::name()). + + All scale factors are of type qreal. + + The main scaling functions for use in QtGui are: + T toNativePixels(T, QWindow *) + T fromNativePixels(T, QWindow*) + Where T is QPoint, QSize, QRect etc. +*/ + +qreal QHighDpiScaling::m_factor = 1.0; +bool QHighDpiScaling::m_active = false; //"overall active" - is there any scale factor set. +bool QHighDpiScaling::m_usePixelDensity = false; // use scale factor from platform plugin +bool QHighDpiScaling::m_pixelDensityScalingActive = false; // pixel density scale factor > 1 +bool QHighDpiScaling::m_globalScalingActive = false; // global scale factor is active +bool QHighDpiScaling::m_screenFactorSet = false; // QHighDpiScaling::setScreenFactor has been used +QDpi QHighDpiScaling::m_logicalDpi = QDpi(-1,-1); // The scaled logical DPI of the primary screen + +/* + Initializes the QHighDpiScaling global variables. Called before the + platform plugin is created. +*/ +void QHighDpiScaling::initHighDpiScaling() +{ + if (QCoreApplication::testAttribute(Qt::AA_NoHighDpiScaling)) { + m_factor = 1; + m_active = false; + return; + } + m_factor = initialScaleFactor(); + bool usePlatformPluginPixelDensity = qEnvironmentVariableIsSet(autoScreenEnvVar) + || qgetenv(legacyDevicePixelEnvVar).toLower() == "auto"; + + m_globalScalingActive = !qFuzzyCompare(m_factor, qreal(1)); + m_usePixelDensity = usePlatformPluginPixelDensity; + m_pixelDensityScalingActive = false; //set in updateHighDpiScaling below + + // we update m_active in updateHighDpiScaling, but while we create the + // screens, we have to assume that m_usePixelDensity implies scaling + m_active = m_globalScalingActive || m_usePixelDensity; +} + +void QHighDpiScaling::updateHighDpiScaling() +{ + if (QCoreApplication::testAttribute(Qt::AA_NoHighDpiScaling)) + return; + + if (m_usePixelDensity && !m_pixelDensityScalingActive) { + Q_FOREACH (QScreen *screen, QGuiApplication::screens()) { + if (!qFuzzyCompare(screenSubfactor(screen->handle()), qreal(1))) { + m_pixelDensityScalingActive = true; + break; + } + } + } + if (qEnvironmentVariableIsSet(screenFactorsEnvVar)) { + int i = 0; + Q_FOREACH (const QByteArray &spec, qgetenv(screenFactorsEnvVar).split(';')) { + QScreen *screen = 0; + int equalsPos = spec.lastIndexOf('='); + double factor = 0; + if (equalsPos > 0) { + // support "name=factor" + QByteArray name = spec.mid(0, equalsPos); + QByteArray f = spec.mid(equalsPos + 1); + bool ok; + factor = f.toDouble(&ok); + if (ok) { + Q_FOREACH (QScreen *s, QGuiApplication::screens()) { + if (s->name() == QString::fromLocal8Bit(name)) { + screen = s; + break; + } + } + } + } else { + // listing screens in order + bool ok; + factor = spec.toDouble(&ok); + if (ok && i < QGuiApplication::screens().count()) + screen = QGuiApplication::screens().at(i); + } + if (screen) + setScreenFactor(screen, factor); + ++i; + } + } + m_active = m_globalScalingActive || m_screenFactorSet || m_pixelDensityScalingActive; + + QPlatformScreen *primaryScreen = QGuiApplication::primaryScreen()->handle(); + qreal sf = screenSubfactor(primaryScreen); + QDpi primaryDpi = primaryScreen->logicalDpi(); + m_logicalDpi = QDpi(primaryDpi.first / sf, primaryDpi.second / sf); +} + +/* + Sets the global scale factor which is applied to all windows. +*/ +void QHighDpiScaling::setGlobalFactor(qreal factor) +{ + if (qFuzzyCompare(factor, m_factor)) + return; + if (!QGuiApplication::allWindows().isEmpty()) + qWarning() << Q_FUNC_INFO << "QHighDpiScaling::setFactor: Should only be called when no windows exist."; + + m_globalScalingActive = !qFuzzyCompare(factor, qreal(1)); + m_factor = m_globalScalingActive ? factor : qreal(1); + m_active = m_globalScalingActive || m_screenFactorSet || m_pixelDensityScalingActive; + Q_FOREACH (QScreen *screen, QGuiApplication::screens()) + screen->d_func()->updateHighDpi(); +} + +static const char scaleFactorProperty[] = "_q_scaleFactor"; + +/* + Sets a per-screen scale factor. +*/ +void QHighDpiScaling::setScreenFactor(QScreen *screen, qreal factor) +{ + m_screenFactorSet = true; + m_active = true; + screen->setProperty(scaleFactorProperty, QVariant(factor)); + + // hack to force re-evaluation of screen geometry + if (screen->handle()) + screen->d_func()->setPlatformScreen(screen->handle()); // updates geometries based on scale factor +} + +QPoint QHighDpiScaling::mapPositionToNative(const QPoint &pos, const QPlatformScreen *platformScreen) +{ + if (!platformScreen) + return pos; + const qreal scaleFactor = factor(platformScreen); + const QPoint topLeft = platformScreen->geometry().topLeft(); + return (pos - topLeft) * scaleFactor + topLeft; +} + +QPoint QHighDpiScaling::mapPositionFromNative(const QPoint &pos, const QPlatformScreen *platformScreen) +{ + if (!platformScreen) + return pos; + const qreal scaleFactor = factor(platformScreen); + const QPoint topLeft = platformScreen->geometry().topLeft(); + return (pos - topLeft) / scaleFactor + topLeft; +} + +qreal QHighDpiScaling::screenSubfactor(const QPlatformScreen *screen) +{ + qreal factor = qreal(1.0); + if (screen) { + if (m_usePixelDensity) + factor *= screen->pixelDensity(); + if (m_screenFactorSet) { + QVariant screenFactor = screen->screen()->property(scaleFactorProperty); + if (screenFactor.isValid()) + factor *= screenFactor.toReal(); + } + } + return factor; +} + +QDpi QHighDpiScaling::logicalDpi() +{ + return m_logicalDpi; +} + +qreal QHighDpiScaling::factor(const QScreen *screen) +{ + // Fast path for when scaling in Qt is not used at all. + if (!m_active) + return qreal(1.0); + + // The effective factor for a given screen is the product of the + // screen and global sub-factors + qreal factor = m_factor; + if (screen) + factor *= screenSubfactor(screen->handle()); + return factor; +} + +qreal QHighDpiScaling::factor(const QPlatformScreen *platformScreen) +{ + if (!m_active) + return qreal(1.0); + + return m_factor * screenSubfactor(platformScreen); +} + +qreal QHighDpiScaling::factor(const QWindow *window) +{ + if (!m_active) + return qreal(1.0); + + return factor(window ? window->screen() : QGuiApplication::primaryScreen()); +} + +QPoint QHighDpiScaling::origin(const QScreen *screen) +{ + return screen->geometry().topLeft(); +} + +QPoint QHighDpiScaling::origin(const QPlatformScreen *platformScreen) +{ + return platformScreen->geometry().topLeft(); +} + +QT_END_NAMESPACE |