diff --git a/3rdparty/ext_qt/0031-Compute-logical-DPI-on-a-per-screen-basis.patch b/3rdparty/ext_qt/0031-Compute-logical-DPI-on-a-per-screen-basis.patch new file mode 100644 index 0000000000..a8d63f0d3f --- /dev/null +++ b/3rdparty/ext_qt/0031-Compute-logical-DPI-on-a-per-screen-basis.patch @@ -0,0 +1,115 @@ +From 676320297d7e5654cbe66fe4bd86125824e05840 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= +Date: Mon, 25 Apr 2016 09:27:48 +0200 +Subject: [PATCH 31/36] Compute logical DPI on a per-screen basis + +The logical DPI reported to applications is the platform screen +logical DPI divided by the platform screen scale factor. + +Use the screen in question when calculating the DPI instead of +the values from the main screen. + +QHighDpiScaling::logicalDpi now takes a QScreen pointer. + +Done-with: Friedemann Kleint +Task-number: QTBUG-53022 +Change-Id: I0f62b5878c37e3488e9a8cc48aef183ff822d0c4 +--- + src/gui/kernel/qhighdpiscaling.cpp | 20 +++++++++----------- + src/gui/kernel/qhighdpiscaling_p.h | 2 +- + src/gui/kernel/qscreen.cpp | 6 +++--- + 3 files changed, 13 insertions(+), 15 deletions(-) + +diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp +index 22e46e0851..541d4f12af 100644 +--- a/src/gui/kernel/qhighdpiscaling.cpp ++++ b/src/gui/kernel/qhighdpiscaling.cpp +@@ -224,7 +224,6 @@ bool QHighDpiScaling::m_usePixelDensity = false; // use scale factor from platfo + 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 +@@ -312,14 +311,6 @@ void QHighDpiScaling::updateHighDpiScaling() + } + } + m_active = m_globalScalingActive || m_screenFactorSet || m_pixelDensityScalingActive; +- +- QScreen *primaryScreen = QGuiApplication::primaryScreen(); +- if (!primaryScreen) +- return; +- QPlatformScreen *platformScreen = primaryScreen->handle(); +- qreal sf = screenSubfactor(platformScreen); +- QDpi primaryDpi = platformScreen->logicalDpi(); +- m_logicalDpi = QDpi(primaryDpi.first / sf, primaryDpi.second / sf); + } + + /* +@@ -405,9 +396,16 @@ qreal QHighDpiScaling::screenSubfactor(const QPlatformScreen *screen) + return factor; + } + +-QDpi QHighDpiScaling::logicalDpi() ++QDpi QHighDpiScaling::logicalDpi(const QScreen *screen) + { +- return m_logicalDpi; ++ // (Note: m_active test is performed at call site.) ++ if (!screen) ++ return QDpi(96, 96); ++ ++ qreal platformScreenfactor = screenSubfactor(screen->handle()); ++ QDpi platformScreenDpi = screen->handle()->logicalDpi(); ++ return QDpi(platformScreenDpi.first / platformScreenfactor, ++ platformScreenDpi.second / platformScreenfactor); + } + + qreal QHighDpiScaling::factor(const QScreen *screen) +diff --git a/src/gui/kernel/qhighdpiscaling_p.h b/src/gui/kernel/qhighdpiscaling_p.h +index 83fc9452c5..ecd9ed6515 100644 +--- a/src/gui/kernel/qhighdpiscaling_p.h ++++ b/src/gui/kernel/qhighdpiscaling_p.h +@@ -85,7 +85,7 @@ public: + static QPoint origin(const QPlatformScreen *platformScreen); + static QPoint mapPositionFromNative(const QPoint &pos, const QPlatformScreen *platformScreen); + static QPoint mapPositionToNative(const QPoint &pos, const QPlatformScreen *platformScreen); +- static QDpi logicalDpi(); ++ static QDpi logicalDpi(const QScreen *screen); + + private: + static qreal screenSubfactor(const QPlatformScreen *screen); +diff --git a/src/gui/kernel/qscreen.cpp b/src/gui/kernel/qscreen.cpp +index f208eb02be..82ee62e6b4 100644 +--- a/src/gui/kernel/qscreen.cpp ++++ b/src/gui/kernel/qscreen.cpp +@@ -279,7 +279,7 @@ qreal QScreen::logicalDotsPerInchX() const + { + Q_D(const QScreen); + if (QHighDpiScaling::isActive()) +- return QHighDpiScaling::logicalDpi().first; ++ return QHighDpiScaling::logicalDpi(this).first; + return d->logicalDpi.first; + } + +@@ -295,7 +295,7 @@ qreal QScreen::logicalDotsPerInchY() const + { + Q_D(const QScreen); + if (QHighDpiScaling::isActive()) +- return QHighDpiScaling::logicalDpi().second; ++ return QHighDpiScaling::logicalDpi(this).second; + return d->logicalDpi.second; + } + +@@ -314,7 +314,7 @@ qreal QScreen::logicalDotsPerInchY() const + qreal QScreen::logicalDotsPerInch() const + { + Q_D(const QScreen); +- QDpi dpi = QHighDpiScaling::isActive() ? QHighDpiScaling::logicalDpi() : d->logicalDpi; ++ QDpi dpi = QHighDpiScaling::isActive() ? QHighDpiScaling::logicalDpi(this) : d->logicalDpi; + return (dpi.first + dpi.second) * qreal(0.5); + } + +-- +2.18.0.windows.1 + diff --git a/3rdparty/ext_qt/0032-Update-Dpi-and-scale-factor-computation.patch b/3rdparty/ext_qt/0032-Update-Dpi-and-scale-factor-computation.patch new file mode 100644 index 0000000000..f01ee778af --- /dev/null +++ b/3rdparty/ext_qt/0032-Update-Dpi-and-scale-factor-computation.patch @@ -0,0 +1,554 @@ +From 3ef20bcb114c316efb74a0a704e918731847620c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= +Date: Mon, 25 Apr 2016 11:31:34 +0200 +Subject: [PATCH 32/36] Update Dpi and scale factor computation + +Remove pixelScale() in favor of logicalBaseDpi(). Compute scale factor +based on logical DPI and logical base DPI, or optionally based on the +physical DPI. + +Add policies for running the scale factor and adjusting the logical +DPI reported to the application. The policies are set via environment +variables: + + QT_SCALE_FACTOR_ROUNDING_POLICY=Round|Ceil|Floor|RoundPreferFloor|PassThrough + QT_DPI_ADJUSTMENT_POLICY=AdjustDpi|DontAdjustDpi|AdjustUpOnly + QT_USE_PHYSICAL_DPI=0|1 + +Done-with: Friedemann Kleint +Task-number: QTBUG-53022 +Change-Id: I4846f223186df665eb0a9c827eaef0a96d1f458f +--- + src/gui/kernel/qhighdpiscaling.cpp | 234 ++++++++++++++++-- + src/gui/kernel/qhighdpiscaling_p.h | 29 +++ + src/gui/kernel/qplatformscreen.cpp | 14 ++ + src/gui/kernel/qplatformscreen.h | 1 + + .../android/qandroidplatformscreen.cpp | 8 +- + .../android/qandroidplatformscreen.h | 2 +- + src/plugins/platforms/cocoa/qcocoascreen.h | 1 + + .../platforms/windows/qwindowsscreen.cpp | 9 - + .../platforms/windows/qwindowsscreen.h | 2 +- + src/plugins/platforms/xcb/qxcbscreen.cpp | 11 - + src/plugins/platforms/xcb/qxcbscreen.h | 3 +- + tests/manual/highdpi/highdpi.pro | 1 + + 12 files changed, 264 insertions(+), 51 deletions(-) + +diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp +index 541d4f12af..ae531569ce 100644 +--- a/src/gui/kernel/qhighdpiscaling.cpp ++++ b/src/gui/kernel/qhighdpiscaling.cpp +@@ -44,6 +44,9 @@ + #include "private/qscreen_p.h" + + #include ++#include ++ ++#include + + QT_BEGIN_NAMESPACE + +@@ -54,6 +57,18 @@ 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 const char scaleFactorRoundingPolicyEnvVar[] = "QT_SCALE_FACTOR_ROUNDING_POLICY"; ++static const char dpiAdjustmentPolicyEnvVar[] = "QT_DPI_ADJUSTMENT_POLICY"; ++static const char usePhysicalDpiEnvVar[] = "QT_USE_PHYSICAL_DPI"; ++ ++// 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) ++{ ++ bool ok = false; ++ int value = qEnvironmentVariableIntValue(name, &ok); ++ return ok ? value > 0 : defaultValue; ++} + + static inline qreal initialGlobalScaleFactor() + { +@@ -247,6 +262,191 @@ static inline bool usePixelDensity() + qgetenv(legacyDevicePixelEnvVar).compare("auto", Qt::CaseInsensitive) == 0); + } + ++qreal QHighDpiScaling::rawScaleFactor(const QPlatformScreen *screen) ++{ ++ // Determine if physical DPI should be used ++ static bool usePhysicalDpi = qEnvironmentVariableAsBool(usePhysicalDpiEnvVar, false); ++ ++ // Calculate scale factor beased on platform screen DPI values ++ qreal factor; ++ QDpi platformBaseDpi = screen->logicalBaseDpi(); ++ if (usePhysicalDpi) { ++ qreal platformPhysicalDpi = screen->screen()->physicalDotsPerInch(); ++ factor = qreal(platformPhysicalDpi) / qreal(platformBaseDpi.first); ++ } else { ++ QDpi platformLogicalDpi = screen->logicalDpi(); ++ factor = qreal(platformLogicalDpi.first) / qreal(platformBaseDpi.first); ++ } ++ ++ return factor; ++} ++ ++template ++struct EnumLookup ++{ ++ const char *name; ++ EnumType value; ++}; ++ ++template ++static bool operator==(const EnumLookup &e1, const EnumLookup &e2) ++{ ++ return qstricmp(e1.name, e2.name) == 0; ++} ++ ++template ++static QByteArray joinEnumValues(const EnumLookup *i1, const EnumLookup *i2) ++{ ++ QByteArray result; ++ for (; i1 < i2; ++i1) { ++ if (!result.isEmpty()) ++ result += QByteArrayLiteral(", "); ++ result += i1->name; ++ } ++ return result; ++} ++ ++using ScaleFactorRoundingPolicyLookup = EnumLookup; ++ ++static const ScaleFactorRoundingPolicyLookup scaleFactorRoundingPolicyLookup[] = ++{ ++ {"Round", QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::Round}, ++ {"Ceil", QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::Ceil}, ++ {"Floor", QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::Floor}, ++ {"RoundPreferFloor", QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor}, ++ {"PassThrough", QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::PassThrough} ++}; ++ ++static QHighDpiScaling::HighDpiScaleFactorRoundingPolicy ++ lookupScaleFactorRoundingPolicy(const QByteArray &v) ++{ ++ auto end = std::end(scaleFactorRoundingPolicyLookup); ++ auto it = std::find(std::begin(scaleFactorRoundingPolicyLookup), end, ++ ScaleFactorRoundingPolicyLookup{v.constData(), QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::NotSet}); ++ return it != end ? it->value : QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::NotSet; ++} ++ ++using DpiAdjustmentPolicyLookup = EnumLookup; ++ ++static const DpiAdjustmentPolicyLookup dpiAdjustmentPolicyLookup[] = ++{ ++ {"AdjustDpi", QHighDpiScaling::DpiAdjustmentPolicy::Enabled}, ++ {"DontAdjustDpi", QHighDpiScaling::DpiAdjustmentPolicy::Disabled}, ++ {"AdjustUpOnly", QHighDpiScaling::DpiAdjustmentPolicy::UpOnly} ++}; ++ ++static QHighDpiScaling::DpiAdjustmentPolicy ++ lookupDpiAdjustmentPolicy(const QByteArray &v) ++{ ++ auto end = std::end(dpiAdjustmentPolicyLookup); ++ auto it = std::find(std::begin(dpiAdjustmentPolicyLookup), end, ++ DpiAdjustmentPolicyLookup{v.constData(), QHighDpiScaling::DpiAdjustmentPolicy::NotSet}); ++ return it != end ? it->value : QHighDpiScaling::DpiAdjustmentPolicy::NotSet; ++} ++ ++qreal QHighDpiScaling::roundScaleFactor(qreal rawFactor) ++{ ++ // Apply scale factor rounding policy. Using mathematically correct rounding ++ // may not give the most desirable visual results, especially for ++ // critical fractions like .5. In general, rounding down results in visual ++ // 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 = HighDpiScaleFactorRoundingPolicy::NotSet; ++ ++ // Determine rounding policy ++ if (scaleFactorRoundingPolicy == HighDpiScaleFactorRoundingPolicy::NotSet) { ++ // Check environment ++ if (qEnvironmentVariableIsSet(scaleFactorRoundingPolicyEnvVar)) { ++ QByteArray policyText = qgetenv(scaleFactorRoundingPolicyEnvVar); ++ auto policyEnumValue = lookupScaleFactorRoundingPolicy(policyText); ++ if (policyEnumValue != HighDpiScaleFactorRoundingPolicy::NotSet) { ++ 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()); ++ } ++ } else { ++ // Set default policy if no environment variable is set. ++ scaleFactorRoundingPolicy = HighDpiScaleFactorRoundingPolicy::RoundPreferFloor; ++ } ++ } ++ ++ // Apply rounding policy. ++ qreal roundedFactor = rawFactor; ++ switch (scaleFactorRoundingPolicy) { ++ case HighDpiScaleFactorRoundingPolicy::Round: ++ roundedFactor = qRound(rawFactor); ++ break; ++ case HighDpiScaleFactorRoundingPolicy::Ceil: ++ roundedFactor = qCeil(rawFactor); ++ break; ++ case HighDpiScaleFactorRoundingPolicy::Floor: ++ roundedFactor = qFloor(rawFactor); ++ break; ++ case HighDpiScaleFactorRoundingPolicy::RoundPreferFloor: ++ // Round up for .75 and higher. This favors "small UI" over "large UI". ++ roundedFactor = rawFactor - qFloor(rawFactor) < 0.75 ++ ? qFloor(rawFactor) : qCeil(rawFactor); ++ break; ++ case HighDpiScaleFactorRoundingPolicy::PassThrough: ++ case HighDpiScaleFactorRoundingPolicy::NotSet: ++ break; ++ } ++ ++ // Don't round down to to zero; clamp the minimum (rounded) factor to 1. ++ // This is not a common case but can happen if a display reports a very ++ // low DPI. ++ if (scaleFactorRoundingPolicy != HighDpiScaleFactorRoundingPolicy::PassThrough) ++ roundedFactor = qMax(roundedFactor, qreal(1)); ++ ++ return roundedFactor; ++} ++ ++QDpi QHighDpiScaling::effectiveLogicalDpi(const QPlatformScreen *screen, qreal rawFactor, qreal roundedFactor) ++{ ++ // Apply DPI adjustment policy, if needed. If enabled this will change ++ // the reported logical DPI to account for the difference between the ++ // rounded scale factor and the actual scale factor. The effect ++ // is that text size will be correct for the screen dpi, but may be (slightly) ++ // out of sync 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::NotSet; ++ ++ // Determine adjustment policy. ++ if (dpiAdjustmentPolicy == DpiAdjustmentPolicy::NotSet) { ++ if (qEnvironmentVariableIsSet(dpiAdjustmentPolicyEnvVar)) { ++ QByteArray policyText = qgetenv(dpiAdjustmentPolicyEnvVar); ++ auto policyEnumValue = lookupDpiAdjustmentPolicy(policyText); ++ if (policyEnumValue != DpiAdjustmentPolicy::NotSet) { ++ 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::NotSet) ++ 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) ++ return baseDpi; ++ if (dpiAdjustmentPolicy == DpiAdjustmentPolicy::UpOnly && dpiAdjustmentFactor < 1) ++ return baseDpi; ++ ++ return QDpi(baseDpi.first * dpiAdjustmentFactor, baseDpi.second * dpiAdjustmentFactor); ++} ++ + void QHighDpiScaling::initHighDpiScaling() + { + // Determine if there is a global scale factor set. +@@ -257,8 +457,6 @@ void QHighDpiScaling::initHighDpiScaling() + + 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; + } + +@@ -310,7 +508,7 @@ void QHighDpiScaling::updateHighDpiScaling() + ++i; + } + } +- m_active = m_globalScalingActive || m_screenFactorSet || m_pixelDensityScalingActive; ++ m_active = m_globalScalingActive || m_usePixelDensity; + } + + /* +@@ -371,22 +569,8 @@ qreal QHighDpiScaling::screenSubfactor(const QPlatformScreen *screen) + { + qreal factor = qreal(1.0); + if (screen) { +- if (m_usePixelDensity) { +- qreal pixelDensity = screen->pixelDensity(); +- +- // Pixel density reported by the screen is sometimes not precise enough, +- // so recalculate it: divide px (physical pixels) by dp (device-independent pixels) +- // for both width and height, and then use the average if it is different from +- // the one initially reported by the screen +- QRect screenGeometry = screen->geometry(); +- qreal wFactor = qreal(screenGeometry.width()) / qRound(screenGeometry.width() / pixelDensity); +- qreal hFactor = qreal(screenGeometry.height()) / qRound(screenGeometry.height() / pixelDensity); +- qreal averageDensity = (wFactor + hFactor) / 2; +- if (!qFuzzyCompare(pixelDensity, averageDensity)) +- pixelDensity = averageDensity; +- +- factor *= pixelDensity; +- } ++ if (m_usePixelDensity) ++ factor *= roundScaleFactor(rawScaleFactor(screen)); + if (m_screenFactorSet) { + QVariant screenFactor = screen->screen()->property(scaleFactorProperty); + if (screenFactor.isValid()) +@@ -399,13 +583,15 @@ qreal QHighDpiScaling::screenSubfactor(const QPlatformScreen *screen) + QDpi QHighDpiScaling::logicalDpi(const QScreen *screen) + { + // (Note: m_active test is performed at call site.) +- if (!screen) ++ if (!screen || !screen->handle()) + return QDpi(96, 96); + +- qreal platformScreenfactor = screenSubfactor(screen->handle()); +- QDpi platformScreenDpi = screen->handle()->logicalDpi(); +- return QDpi(platformScreenDpi.first / platformScreenfactor, +- platformScreenDpi.second / platformScreenfactor); ++ if (!m_usePixelDensity) ++ return screen->handle()->logicalDpi(); ++ ++ const qreal scaleFactor = rawScaleFactor(screen->handle()); ++ const qreal roundedScaleFactor = roundScaleFactor(scaleFactor); ++ return effectiveLogicalDpi(screen->handle(), scaleFactor, roundedScaleFactor); + } + + qreal QHighDpiScaling::factor(const QScreen *screen) +diff --git a/src/gui/kernel/qhighdpiscaling_p.h b/src/gui/kernel/qhighdpiscaling_p.h +index ecd9ed6515..55bddfeb88 100644 +--- a/src/gui/kernel/qhighdpiscaling_p.h ++++ b/src/gui/kernel/qhighdpiscaling_p.h +@@ -71,7 +71,33 @@ typedef QPair QDpi; + + #ifndef QT_NO_HIGHDPISCALING + class Q_GUI_EXPORT QHighDpiScaling { ++ Q_GADGET + public: ++ enum class HighDpiScaleFactorRoundingPolicy { ++ NotSet, ++ Round, ++ Ceil, ++ Floor, ++ RoundPreferFloor, ++ PassThrough ++ }; ++ Q_ENUM(HighDpiScaleFactorRoundingPolicy) ++ ++ enum class DpiAdjustmentPolicy { ++ NotSet, ++ Enabled, ++ Disabled, ++ UpOnly ++ }; ++ Q_ENUM(DpiAdjustmentPolicy) ++ ++ QHighDpiScaling() = delete; ++ ~QHighDpiScaling() = delete; ++ QHighDpiScaling(const QHighDpiScaling &) = delete; ++ QHighDpiScaling &operator=(const QHighDpiScaling &) = delete; ++ QHighDpiScaling(QHighDpiScaling &&) = delete; ++ QHighDpiScaling &operator=(QHighDpiScaling &&) = delete; ++ + static void initHighDpiScaling(); + static void updateHighDpiScaling(); + static void setGlobalFactor(qreal factor); +@@ -88,6 +114,9 @@ public: + static QDpi logicalDpi(const QScreen *screen); + + private: ++ static qreal rawScaleFactor(const QPlatformScreen *screen); ++ static qreal roundScaleFactor(qreal rawFactor); ++ static QDpi effectiveLogicalDpi(const QPlatformScreen *screen, qreal rawFactor, qreal roundedFactor); + static qreal screenSubfactor(const QPlatformScreen *screen); + + static qreal m_factor; +diff --git a/src/gui/kernel/qplatformscreen.cpp b/src/gui/kernel/qplatformscreen.cpp +index b7b312e89e..07a2231228 100644 +--- a/src/gui/kernel/qplatformscreen.cpp ++++ b/src/gui/kernel/qplatformscreen.cpp +@@ -194,6 +194,20 @@ QDpi QPlatformScreen::logicalDpi() const + 25.4 * s.height() / ps.height()); + } + ++/*! ++ Reimplement to return the base logical DPI for the platform. This ++ DPI value should correspond to a standard-DPI (1x) display. The ++ default implementation returns 96. ++ ++ QtGui will use this value (together with logicalDpi) to compute ++ the scale factor when high-DPI scaling is enabled: ++ factor = logicalDPI / baseDPI ++*/ ++QDpi QPlatformScreen::logicalBaseDpi() const ++{ ++ return QDpi(96, 96); ++} ++ + /*! + Reimplement this function in subclass to return the device pixel ratio + for the screen. This is the ratio between physical pixels and the +diff --git a/src/gui/kernel/qplatformscreen.h b/src/gui/kernel/qplatformscreen.h +index e9d64c8a29..63b5d5a4a7 100644 +--- a/src/gui/kernel/qplatformscreen.h ++++ b/src/gui/kernel/qplatformscreen.h +@@ -113,6 +113,7 @@ public: + + virtual QSizeF physicalSize() const; + virtual QDpi logicalDpi() const; ++ virtual QDpi logicalBaseDpi() const; + virtual qreal devicePixelRatio() const; + virtual qreal pixelDensity() const; + +diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp +index 7dc8bb8080..80757c2135 100644 +--- a/src/plugins/platforms/android/qandroidplatformscreen.cpp ++++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp +@@ -401,15 +401,17 @@ void QAndroidPlatformScreen::doRedraw() + m_dirtyRect = QRect(); + } + ++static const int androidLogicalDpi = 72; ++ + QDpi QAndroidPlatformScreen::logicalDpi() const + { +- qreal lDpi = QtAndroid::scaledDensity() * 72; ++ qreal lDpi = QtAndroid::scaledDensity() * androidLogicalDpi; + return QDpi(lDpi, lDpi); + } + +-qreal QAndroidPlatformScreen::pixelDensity() const ++QDpi QAndroidPlatformScreen::logicalBaseDpi() const + { +- return QtAndroid::pixelDensity(); ++ return QDpi(androidLogicalDpi, androidLogicalDpi); + } + + Qt::ScreenOrientation QAndroidPlatformScreen::orientation() const +diff --git a/src/plugins/platforms/android/qandroidplatformscreen.h b/src/plugins/platforms/android/qandroidplatformscreen.h +index f15aeae3fd..5dc158e351 100644 +--- a/src/plugins/platforms/android/qandroidplatformscreen.h ++++ b/src/plugins/platforms/android/qandroidplatformscreen.h +@@ -103,7 +103,7 @@ protected: + + private: + QDpi logicalDpi() const override; +- qreal pixelDensity() const override; ++ QDpi logicalBaseDpi() const override; + Qt::ScreenOrientation orientation() const override; + Qt::ScreenOrientation nativeOrientation() const override; + void surfaceChanged(JNIEnv *env, jobject surface, int w, int h) override; +diff --git a/src/plugins/platforms/cocoa/qcocoascreen.h b/src/plugins/platforms/cocoa/qcocoascreen.h +index 9ded98df32..a73b97c771 100644 +--- a/src/plugins/platforms/cocoa/qcocoascreen.h ++++ b/src/plugins/platforms/cocoa/qcocoascreen.h +@@ -64,6 +64,7 @@ public: + qreal devicePixelRatio() const override; + QSizeF physicalSize() const override { return m_physicalSize; } + QDpi logicalDpi() const override { return m_logicalDpi; } ++ QDpi logicalBaseDpi() const override { return m_logicalDpi; } + qreal refreshRate() const override { return m_refreshRate; } + QString name() const override { return m_name; } + QPlatformCursor *cursor() const override { return m_cursor; } +diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp +index 8a5f0e6577..ebde684038 100644 +--- a/src/plugins/platforms/windows/qwindowsscreen.cpp ++++ b/src/plugins/platforms/windows/qwindowsscreen.cpp +@@ -254,15 +254,6 @@ QWindow *QWindowsScreen::windowAt(const QPoint &screenPoint, unsigned flags) + return result; + } + +-qreal QWindowsScreen::pixelDensity() const +-{ +- // QTBUG-49195: Use logical DPI instead of physical DPI to calculate +- // the pixel density since it is reflects the Windows UI scaling. +- // High DPI auto scaling should be disabled when the user chooses +- // small fonts on a High DPI monitor, resulting in lower logical DPI. +- return qMax(1, qRound(logicalDpi().first / 96)); +-} +- + /*! + \brief Determine siblings in a virtual desktop system. + +diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h +index 33c9effa2a..a7b1c64e29 100644 +--- a/src/plugins/platforms/windows/qwindowsscreen.h ++++ b/src/plugins/platforms/windows/qwindowsscreen.h +@@ -87,7 +87,7 @@ public: + QImage::Format format() const override { return m_data.format; } + QSizeF physicalSize() const override { return m_data.physicalSizeMM; } + QDpi logicalDpi() const override { return m_data.dpi; } +- qreal pixelDensity() const override; ++ QDpi logicalBaseDpi() const override { return QDpi(96, 96); }; + qreal devicePixelRatio() const override { return 1.0; } + qreal refreshRate() const override { return m_data.refreshRateHz; } + QString name() const override { return m_data.name; } +diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp +index 57dbdc9bec..9af0794d29 100644 +--- a/src/plugins/platforms/xcb/qxcbscreen.cpp ++++ b/src/plugins/platforms/xcb/qxcbscreen.cpp +@@ -671,11 +671,6 @@ QDpi QXcbScreen::logicalDpi() const + return m_virtualDesktop->dpi(); + } + +-qreal QXcbScreen::pixelDensity() const +-{ +- return m_pixelDensity; +-} +- + QPlatformCursor *QXcbScreen::cursor() const + { + return m_cursor; +@@ -739,12 +734,6 @@ void QXcbScreen::updateGeometry(const QRect &geometry, uint8_t rotation) + if (m_sizeMillimeters.isEmpty()) + m_sizeMillimeters = sizeInMillimeters(geometry.size(), m_virtualDesktop->dpi()); + +- qreal dpi = geometry.width() / physicalSize().width() * qreal(25.4); +- +- // Use 128 as a reference DPI on small screens. This favors "small UI" over "large UI". +- qreal referenceDpi = physicalSize().width() <= 320 ? 128 : 96; +- +- m_pixelDensity = qMax(1, qRound(dpi/referenceDpi)); + m_geometry = geometry; + m_availableGeometry = geometry & m_virtualDesktop->workArea(); + QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), m_geometry, m_availableGeometry); +diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h +index be6c45e415..3f619d71c2 100644 +--- a/src/plugins/platforms/xcb/qxcbscreen.h ++++ b/src/plugins/platforms/xcb/qxcbscreen.h +@@ -161,7 +161,7 @@ public: + QImage::Format format() const override; + QSizeF physicalSize() const override { return m_sizeMillimeters; } + QDpi logicalDpi() const override; +- qreal pixelDensity() const override; ++ QDpi logicalBaseDpi() const override { return QDpi(96, 96); }; + QPlatformCursor *cursor() const override; + qreal refreshRate() const override { return m_refreshRate; } + Qt::ScreenOrientation orientation() const override { return m_orientation; } +@@ -226,7 +226,6 @@ private: + Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation; + QXcbCursor *m_cursor; + int m_refreshRate = 60; +- int m_pixelDensity = 1; + QEdidParser m_edid; + }; + +diff --git a/tests/manual/highdpi/highdpi.pro b/tests/manual/highdpi/highdpi.pro +index 9db083cd82..2de8ed3bb5 100644 +--- a/tests/manual/highdpi/highdpi.pro ++++ b/tests/manual/highdpi/highdpi.pro +@@ -15,3 +15,4 @@ HEADERS += \ + RESOURCES += \ + highdpi.qrc + ++DEFINES += HAVE_SCREEN_BASE_DPI +-- +2.18.0.windows.1 + diff --git a/3rdparty/ext_qt/0033-Move-QT_FONT_DPI-to-cross-platform-code.patch b/3rdparty/ext_qt/0033-Move-QT_FONT_DPI-to-cross-platform-code.patch new file mode 100644 index 0000000000..ef703408b1 --- /dev/null +++ b/3rdparty/ext_qt/0033-Move-QT_FONT_DPI-to-cross-platform-code.patch @@ -0,0 +1,133 @@ +From 3f00c3cddb35c4f17100b2becbb99dec4e48b351 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= +Date: Thu, 2 Jun 2016 09:52:21 +0200 +Subject: [PATCH 33/36] Move QT_FONT_DPI to cross-platform code + +This makes it possible to test the effects of setting +Qt::AA_HighDpiScaling/QT_AUTO_SCREEN_SCALE_FACTOR, with different DPI +values on all platforms. + +This also makes it possible to access the actual DPI values reported +by the OS/WS via the QPlatformScreen API. + +A drawback is that there is no single place to check the environment +variable; currently done in three places. This may be +further simplified later on. + +Done-with: Friedemann Kleint +Task-number: QTBUG-53022 +Change-Id: Idd6463219d3ae58fe0ab72c17686cce2eb9dbadd +--- + src/gui/kernel/qhighdpiscaling.cpp | 4 ++-- + src/gui/kernel/qplatformscreen.cpp | 8 ++++++++ + src/gui/kernel/qplatformscreen.h | 2 ++ + src/gui/kernel/qscreen.cpp | 7 +++++-- + src/gui/kernel/qwindowsysteminterface.cpp | 4 ++-- + src/plugins/platforms/xcb/qxcbscreen.cpp | 4 ---- + 6 files changed, 19 insertions(+), 10 deletions(-) + +diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp +index ae531569ce..fc8084c45a 100644 +--- a/src/gui/kernel/qhighdpiscaling.cpp ++++ b/src/gui/kernel/qhighdpiscaling.cpp +@@ -274,7 +274,7 @@ qreal QHighDpiScaling::rawScaleFactor(const QPlatformScreen *screen) + qreal platformPhysicalDpi = screen->screen()->physicalDotsPerInch(); + factor = qreal(platformPhysicalDpi) / qreal(platformBaseDpi.first); + } else { +- QDpi platformLogicalDpi = screen->logicalDpi(); ++ const QDpi platformLogicalDpi = QPlatformScreen::overrideDpi(screen->logicalDpi()); + factor = qreal(platformLogicalDpi.first) / qreal(platformBaseDpi.first); + } + +@@ -587,7 +587,7 @@ QDpi QHighDpiScaling::logicalDpi(const QScreen *screen) + return QDpi(96, 96); + + if (!m_usePixelDensity) +- return screen->handle()->logicalDpi(); ++ return QPlatformScreen::overrideDpi(screen->handle()->logicalDpi()); + + const qreal scaleFactor = rawScaleFactor(screen->handle()); + const qreal roundedScaleFactor = roundScaleFactor(scaleFactor); +diff --git a/src/gui/kernel/qplatformscreen.cpp b/src/gui/kernel/qplatformscreen.cpp +index 07a2231228..54ec211f2c 100644 +--- a/src/gui/kernel/qplatformscreen.cpp ++++ b/src/gui/kernel/qplatformscreen.cpp +@@ -194,6 +194,14 @@ QDpi QPlatformScreen::logicalDpi() const + 25.4 * s.height() / ps.height()); + } + ++// Helper function for accessing the platform screen logical dpi ++// which accounts for QT_FONT_DPI. ++QPair QPlatformScreen::overrideDpi(const QPair &in) ++{ ++ static const int overrideDpi = qEnvironmentVariableIntValue("QT_FONT_DPI"); ++ return overrideDpi > 0 ? QDpi(overrideDpi, overrideDpi) : in; ++} ++ + /*! + Reimplement to return the base logical DPI for the platform. This + DPI value should correspond to a standard-DPI (1x) display. The +diff --git a/src/gui/kernel/qplatformscreen.h b/src/gui/kernel/qplatformscreen.h +index 63b5d5a4a7..32e6bf7ec7 100644 +--- a/src/gui/kernel/qplatformscreen.h ++++ b/src/gui/kernel/qplatformscreen.h +@@ -159,6 +159,8 @@ public: + // The platform screen's geometry in device independent coordinates + QRect deviceIndependentGeometry() const; + ++ static QDpi overrideDpi(const QDpi &in); ++ + protected: + void resizeMaximizedWindows(); + +diff --git a/src/gui/kernel/qscreen.cpp b/src/gui/kernel/qscreen.cpp +index 82ee62e6b4..b856435f67 100644 +--- a/src/gui/kernel/qscreen.cpp ++++ b/src/gui/kernel/qscreen.cpp +@@ -84,8 +84,11 @@ void QScreenPrivate::setPlatformScreen(QPlatformScreen *screen) + platformScreen->d_func()->screen = q; + orientation = platformScreen->orientation(); + geometry = platformScreen->deviceIndependentGeometry(); +- availableGeometry = QHighDpi::fromNative(platformScreen->availableGeometry(), QHighDpiScaling::factor(platformScreen), geometry.topLeft()); +- logicalDpi = platformScreen->logicalDpi(); ++ availableGeometry = QHighDpi::fromNative(platformScreen->availableGeometry(), ++ QHighDpiScaling::factor(platformScreen), geometry.topLeft()); ++ ++ logicalDpi = QPlatformScreen::overrideDpi(platformScreen->logicalDpi()); ++ + refreshRate = platformScreen->refreshRate(); + // safeguard ourselves against buggy platform behavior... + if (refreshRate < 1.0) +diff --git a/src/gui/kernel/qwindowsysteminterface.cpp b/src/gui/kernel/qwindowsysteminterface.cpp +index 5b32405f5e..0bedae1bb4 100644 +--- a/src/gui/kernel/qwindowsysteminterface.cpp ++++ b/src/gui/kernel/qwindowsysteminterface.cpp +@@ -780,8 +780,8 @@ void QWindowSystemInterface::handleScreenGeometryChange(QScreen *screen, const Q + + void QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(QScreen *screen, qreal dpiX, qreal dpiY) + { +- QWindowSystemInterfacePrivate::ScreenLogicalDotsPerInchEvent *e = +- new QWindowSystemInterfacePrivate::ScreenLogicalDotsPerInchEvent(screen, dpiX, dpiY); // ### tja ++ const QDpi effectiveDpi = QPlatformScreen::overrideDpi(QDpi{dpiX, dpiY}); ++ auto e = new QWindowSystemInterfacePrivate::ScreenLogicalDotsPerInchEvent(screen, effectiveDpi.first, effectiveDpi.second); + QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); + } + +diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp +index 9af0794d29..27ffcad902 100644 +--- a/src/plugins/platforms/xcb/qxcbscreen.cpp ++++ b/src/plugins/platforms/xcb/qxcbscreen.cpp +@@ -660,10 +660,6 @@ QImage::Format QXcbScreen::format() const + + QDpi QXcbScreen::logicalDpi() const + { +- static const int overrideDpi = qEnvironmentVariableIntValue("QT_FONT_DPI"); +- if (overrideDpi) +- return QDpi(overrideDpi, overrideDpi); +- + const int forcedDpi = m_virtualDesktop->forcedDpi(); + if (forcedDpi > 0) { + return QDpi(forcedDpi, forcedDpi); +-- +2.18.0.windows.1 + diff --git a/3rdparty/ext_qt/0034-Update-QT_SCREEN_SCALE_FACTORS.patch b/3rdparty/ext_qt/0034-Update-QT_SCREEN_SCALE_FACTORS.patch new file mode 100644 index 0000000000..b78e67977c --- /dev/null +++ b/3rdparty/ext_qt/0034-Update-QT_SCREEN_SCALE_FACTORS.patch @@ -0,0 +1,155 @@ +From f58ea52d89d9230ab9d441d96f6884044c4c686d Mon Sep 17 00:00:00 2001 +From: Friedemann Kleint +Date: Wed, 27 Feb 2019 08:46:47 +0100 +Subject: [PATCH 34/36] Update QT_SCREEN_SCALE_FACTORS +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Store name->factor associations in a global QHash instead of on the +QScreen object, making them survive QScreen object deletion, for +example on disconnect/ connect cycles. + +Make factors set with QT_SCREEN_SCALE_FACTORS override the screen +factors computed from platform plugin DPI values. This matches the use +case for QT_SCREEN_SCALE_FACTORS (but not the general scale factors +combine by multiplication”principle) + +Done-with: Friedemann Kleint +Task-number: QTBUG-53022 +Change-Id: I12771249314ab0c073e609d62f57ac0d18d3b6ce +--- + src/gui/kernel/qhighdpiscaling.cpp | 62 ++++++++++++++++++++++-------- + src/gui/kernel/qhighdpiscaling_p.h | 2 +- + 2 files changed, 47 insertions(+), 17 deletions(-) + +diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp +index fc8084c45a..b1350599b7 100644 +--- a/src/gui/kernel/qhighdpiscaling.cpp ++++ b/src/gui/kernel/qhighdpiscaling.cpp +@@ -61,6 +61,12 @@ 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 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) +@@ -478,20 +484,19 @@ void QHighDpiScaling::updateHighDpiScaling() + int i = 0; + const auto specs = qgetenv(screenFactorsEnvVar).split(';'); + for (const QByteArray &spec : specs) { +- QScreen *screen = 0; + int equalsPos = spec.lastIndexOf('='); +- double factor = 0; ++ qreal 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) { ++ if (ok && factor > 0 ) { + const auto screens = QGuiApplication::screens(); + for (QScreen *s : screens) { + if (s->name() == QString::fromLocal8Bit(name)) { +- screen = s; ++ setScreenFactor(s, factor); + break; + } + } +@@ -500,11 +505,11 @@ void QHighDpiScaling::updateHighDpiScaling() + // listing screens in order + bool ok; + factor = spec.toDouble(&ok); +- if (ok && i < QGuiApplication::screens().count()) +- screen = QGuiApplication::screens().at(i); ++ if (ok && factor > 0 && i < QGuiApplication::screens().count()) { ++ QScreen *screen = QGuiApplication::screens().at(i); ++ setScreenFactor(screen, factor); ++ } + } +- if (screen) +- setScreenFactor(screen, factor); + ++i; + } + } +@@ -540,7 +545,14 @@ void QHighDpiScaling::setScreenFactor(QScreen *screen, qreal factor) + m_screenFactorSet = true; + m_active = true; + } +- screen->setProperty(scaleFactorProperty, QVariant(factor)); ++ ++ // Prefer associating the factor with screen name over the object ++ // since the screen object may be deleted on screen disconnects. ++ const QString name = screen->name(); ++ if (!name.isEmpty()) ++ qNamedScreenScaleFactors()->insert(name, factor); ++ else ++ screen->setProperty(scaleFactorProperty, QVariant(factor)); + + // hack to force re-evaluation of screen geometry + if (screen->handle()) +@@ -568,15 +580,33 @@ QPoint QHighDpiScaling::mapPositionFromNative(const QPoint &pos, const QPlatform + qreal QHighDpiScaling::screenSubfactor(const QPlatformScreen *screen) + { + qreal factor = qreal(1.0); +- if (screen) { +- if (m_usePixelDensity) +- factor *= roundScaleFactor(rawScaleFactor(screen)); +- if (m_screenFactorSet) { +- QVariant screenFactor = screen->screen()->property(scaleFactorProperty); +- if (screenFactor.isValid()) +- factor *= screenFactor.toReal(); ++ if (!screen) ++ return factor; ++ ++ // Unlike the other code where factors are combined ++ // by multiplication, factors from QT_SCREEN_SCALE_FACTORS takes ++ // precedence over the factor computed from platform plugin ++ // DPI. The rationale is that the user is setting the factor ++ // to override erroneous DPI values. ++ bool screenPropertyUsed = false; ++ if (m_screenFactorSet) { ++ // Check if there is a factor set on the screen object or ++ // associated with the screen name. These are mutually ++ // exclusive, so checking order is not significant. ++ QVariant byIndex = screen->screen()->property(scaleFactorProperty); ++ auto byName = qNamedScreenScaleFactors()->find(screen->name()); ++ if (byIndex.isValid()) { ++ screenPropertyUsed = true; ++ factor = byIndex.toReal(); ++ } else if (byName != qNamedScreenScaleFactors()->end()) { ++ screenPropertyUsed = true; ++ factor = *byName; + } + } ++ ++ if (!screenPropertyUsed && m_usePixelDensity) ++ factor = roundScaleFactor(rawScaleFactor(screen)); ++ + return factor; + } + +diff --git a/src/gui/kernel/qhighdpiscaling_p.h b/src/gui/kernel/qhighdpiscaling_p.h +index 55bddfeb88..d3f71854a8 100644 +--- a/src/gui/kernel/qhighdpiscaling_p.h ++++ b/src/gui/kernel/qhighdpiscaling_p.h +@@ -101,7 +101,7 @@ public: + static void initHighDpiScaling(); + static void updateHighDpiScaling(); + static void setGlobalFactor(qreal factor); +- static void setScreenFactor(QScreen *window, qreal factor); ++ static void setScreenFactor(QScreen *screen, qreal factor); + + static bool isActive() { return m_active; } + static qreal factor(const QWindow *window); +-- +2.18.0.windows.1 + diff --git a/3rdparty/ext_qt/0035-Deprecate-QT_AUTO_SCREEN_SCALE_FACTOR.patch b/3rdparty/ext_qt/0035-Deprecate-QT_AUTO_SCREEN_SCALE_FACTOR.patch new file mode 100644 index 0000000000..980f3f1670 --- /dev/null +++ b/3rdparty/ext_qt/0035-Deprecate-QT_AUTO_SCREEN_SCALE_FACTOR.patch @@ -0,0 +1,101 @@ +From 4fdc698603e5ac3bc0cf107fdf3880ba68dbfb02 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= +Date: Thu, 10 Nov 2016 14:17:53 +0100 +Subject: [PATCH 35/36] Deprecate QT_AUTO_SCREEN_SCALE_FACTOR +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Replace by QT_ENABLE_HIGHDPI_SCALING. + +QT_AUTO_SCREEN_SCALE_FACTOR has the usability problem that it mixes +enabling the high-DPI scaling mode with the method of getting screen +scale factors (“auto”). Due to this, it ends up with a slightly +strange name. + +QT_ENABLE_HIGHDPI_SCALING matches the C++ option +(Qt::AA_EnableHighDPiScaling), and leaves the scale factor acquisition +method unspecified, possibly to be set by some other means (like +QT_SCREEN_SCALE_FACTORS). + +Done-with: Friedemann Kleint +Task-number: QTBUG-53022 +Change-Id: I30033d91175a00db7837efc9c48c33396f5f0449 +--- + src/gui/kernel/qhighdpiscaling.cpp | 29 +++++++++++++++++++++++------ + 1 file changed, 23 insertions(+), 6 deletions(-) + +diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp +index b1350599b7..52c0665b5b 100644 +--- a/src/gui/kernel/qhighdpiscaling.cpp ++++ b/src/gui/kernel/qhighdpiscaling.cpp +@@ -54,8 +54,10 @@ Q_LOGGING_CATEGORY(lcScaling, "qt.scaling"); + + #ifndef QT_NO_HIGHDPISCALING + static const char legacyDevicePixelEnvVar[] = "QT_DEVICE_PIXEL_RATIO"; ++static const char legacyAutoScreenEnvVar[] = "QT_AUTO_SCREEN_SCALE_FACTOR"; ++ ++static const char enableHighDpiScalingEnvVar[] = "QT_ENABLE_HIGHDPI_SCALING"; + 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 const char scaleFactorRoundingPolicyEnvVar[] = "QT_SCALE_FACTOR_ROUNDING_POLICY"; + static const char dpiAdjustmentPolicyEnvVar[] = "QT_DPI_ADJUSTMENT_POLICY"; +@@ -88,17 +90,24 @@ static inline qreal initialGlobalScaleFactor() + result = f; + } + } else { ++ // Check for deprecated environment variables. + if (qEnvironmentVariableIsSet(legacyDevicePixelEnvVar)) { + qWarning("Warning: %s is deprecated. Instead use:\n" + " %s to enable platform plugin controlled per-screen factors.\n" +- " %s to set per-screen factors.\n" ++ " %s to set per-screen DPI.\n" + " %s to set the application global scale factor.", +- legacyDevicePixelEnvVar, autoScreenEnvVar, screenFactorsEnvVar, scaleFactorEnvVar); ++ legacyDevicePixelEnvVar, legacyAutoScreenEnvVar, screenFactorsEnvVar, scaleFactorEnvVar); + + int dpr = qEnvironmentVariableIntValue(legacyDevicePixelEnvVar); + if (dpr > 0) + result = dpr; + } ++ ++ if (qEnvironmentVariableIsSet(legacyAutoScreenEnvVar)) { ++ qWarning("Warning: %s is deprecated. Instead use:\n" ++ " %s to enable platform plugin controlled per-screen factors.", ++ legacyAutoScreenEnvVar, enableHighDpiScalingEnvVar); ++ } + } + return result; + } +@@ -256,16 +265,24 @@ static inline bool usePixelDensity() + // Determine if we should set a scale factor based on the pixel density + // reported by the platform plugin. There are several enablers and several + // disablers. A single disable may veto all other enablers. ++ ++ // First, check of there is an explicit disable. + if (QCoreApplication::testAttribute(Qt::AA_DisableHighDpiScaling)) + return false; + bool screenEnvValueOk; +- const int screenEnvValue = qEnvironmentVariableIntValue(autoScreenEnvVar, &screenEnvValueOk); ++ const int screenEnvValue = qEnvironmentVariableIntValue(legacyAutoScreenEnvVar, &screenEnvValueOk); + if (screenEnvValueOk && screenEnvValue < 1) + return false; ++ bool enableEnvValueOk; ++ const int enableEnvValue = qEnvironmentVariableIntValue(enableHighDpiScalingEnvVar, &enableEnvValueOk); ++ if (enableEnvValueOk && enableEnvValue < 1) ++ return false; ++ ++ // Then return if there was an enable. + return QCoreApplication::testAttribute(Qt::AA_EnableHighDpiScaling) + || (screenEnvValueOk && screenEnvValue > 0) +- || (qEnvironmentVariableIsSet(legacyDevicePixelEnvVar) && +- qgetenv(legacyDevicePixelEnvVar).compare("auto", Qt::CaseInsensitive) == 0); ++ || (enableEnvValueOk && enableEnvValue > 0) ++ || (qEnvironmentVariableIsSet(legacyDevicePixelEnvVar) && qgetenv(legacyDevicePixelEnvVar).toLower() == "auto"); + } + + qreal QHighDpiScaling::rawScaleFactor(const QPlatformScreen *screen) +-- +2.18.0.windows.1 + diff --git a/3rdparty/ext_qt/0036-Add-high-DPI-scale-factor-rounding-policy-C-API.patch b/3rdparty/ext_qt/0036-Add-high-DPI-scale-factor-rounding-policy-C-API.patch new file mode 100644 index 0000000000..74f2b666e7 --- /dev/null +++ b/3rdparty/ext_qt/0036-Add-high-DPI-scale-factor-rounding-policy-C-API.patch @@ -0,0 +1,314 @@ +From 8e4d9b59af6b64345dd2a23656cf157ad76614eb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= +Date: Sat, 7 Oct 2017 01:35:29 +0200 +Subject: [PATCH 36/36] Add high-DPI scale factor rounding policy C++ API + +This API enables tuning of how Qt rounds fractional scale factors, and +corresponds to the QT_SCALE_FACTOR_ROUNDING_POLICY environment +variable + +New API: + Qt::HighDPiScaleFactorRoundingPolicy + QGuiApplication::setHighDpiScaleFactorRoundingPolicy() + QGuiApplication::highDpiScaleFactorRoundingPolicy() + +Done-with: Friedemann Kleint +Task-number: QTBUG-53022 +Change-Id: Ic360f26a173caa757e4ebde35ce08a6b74290b7d +--- + src/corelib/global/qnamespace.h | 10 +++++++ + src/corelib/global/qnamespace.qdoc | 22 ++++++++++++++ + src/gui/kernel/qguiapplication.cpp | 44 ++++++++++++++++++++++++++++ + src/gui/kernel/qguiapplication.h | 3 ++ + src/gui/kernel/qguiapplication_p.h | 1 + + src/gui/kernel/qhighdpiscaling.cpp | 47 +++++++++++++++++------------- + src/gui/kernel/qhighdpiscaling_p.h | 10 ------- + 7 files changed, 106 insertions(+), 31 deletions(-) + +diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h +index 3ab9921986..fea3d4d8da 100644 +--- a/src/corelib/global/qnamespace.h ++++ b/src/corelib/global/qnamespace.h +@@ -1728,6 +1728,15 @@ public: + ChecksumItuV41 + }; + ++ enum class HighDpiScaleFactorRoundingPolicy { ++ NotSet, ++ Round, ++ Ceil, ++ Floor, ++ RoundPreferFloor, ++ PassThrough ++ }; ++ + #ifndef Q_QDOC + // NOTE: Generally, do not add QT_Q_ENUM if a corresponding Q_Q_FLAG exists. + QT_Q_ENUM(ScrollBarPolicy) +@@ -1813,6 +1822,7 @@ public: + QT_Q_ENUM(MouseEventSource) + QT_Q_FLAG(MouseEventFlag) + QT_Q_ENUM(ChecksumType) ++ QT_Q_ENUM(HighDpiScaleFactorRoundingPolicy) + QT_Q_ENUM(TabFocusBehavior) + #endif // Q_DOC + +diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc +index 5bba8c5fe5..a3ca70a74d 100644 +--- a/src/corelib/global/qnamespace.qdoc ++++ b/src/corelib/global/qnamespace.qdoc +@@ -3252,3 +3252,25 @@ + + \value ChecksumItuV41 Checksum calculation based on ITU-V.41. + */ ++ ++/*! ++ \enum Qt::HighDpiScaleFactorRoundingPolicy ++ \since 5.14 ++ ++ This enum describes the possible High-DPI scale factor rounding policies, which ++ decide how non-integer scale factors (such as Windows 150%) are handled. ++ ++ The active policy is set by calling QGuiApplication::setHighDdpiScaleFactorRoundingPolicy() before ++ the application object is created, or by setting the QT_SCALE_FACTOR_ROUNDING_POLICY ++ environment variable. ++ ++ \sa QGuiApplication::setHighDdpiScaleFactorRoundingPolicy() ++ \sa AA_EnableHighDpiScaling. ++ ++ \omitvalue NotSet ++ \value Round Round up for .5 and above. ++ \value Ceil Always round up. ++ \value Floor Always round down. ++ \value RoundPreferFloor Round up for .75 and above. ++ \value PassThrough Don't round. ++*/ +diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp +index fd01f8bb7b..3541c1ae59 100644 +--- a/src/gui/kernel/qguiapplication.cpp ++++ b/src/gui/kernel/qguiapplication.cpp +@@ -146,6 +146,8 @@ QString QGuiApplicationPrivate::styleOverride; + + Qt::ApplicationState QGuiApplicationPrivate::applicationState = Qt::ApplicationInactive; + ++Qt::HighDpiScaleFactorRoundingPolicy QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy = ++ Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor; + bool QGuiApplicationPrivate::highDpiScalingUpdated = false; + + QPointer QGuiApplicationPrivate::currentDragWindow; +@@ -677,6 +679,8 @@ QGuiApplication::~QGuiApplication() + QGuiApplicationPrivate::lastCursorPosition = {qInf(), qInf()}; + QGuiApplicationPrivate::currentMousePressWindow = QGuiApplicationPrivate::currentMouseWindow = nullptr; + QGuiApplicationPrivate::applicationState = Qt::ApplicationInactive; ++ QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy = ++ Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor; + QGuiApplicationPrivate::highDpiScalingUpdated = false; + QGuiApplicationPrivate::currentDragWindow = nullptr; + QGuiApplicationPrivate::tabletDevicePoints.clear(); +@@ -3448,6 +3452,46 @@ Qt::ApplicationState QGuiApplication::applicationState() + return QGuiApplicationPrivate::applicationState; + } + ++/*! ++ \since 5.14 ++ ++ Sets the high-DPI scale factor rounding policy for the application. The ++ policy decides how non-integer scale factors (such as Windows 150%) are ++ handled, for applications that have AA_EnableHighDpiScaling enabled. ++ ++ The two principal options are whether fractional scale factors should ++ be rounded to an integer or not. Keeping the scale factor as-is will ++ make the user interface size match the OS setting exactly, but may cause ++ painting errors, for example with the Windows style. ++ ++ If rounding is wanted, then which type of rounding should be decided ++ next. Mathematically correct rounding is supported but may not give ++ the best visual results: Consider if you want to render 1.5x as 1x ++ ("small UI") or as 2x ("large UI"). See the Qt::HighDpiScaleFactorRoundingPolicy ++ enum for a complete list of all options. ++ ++ This function must be called before creating the application object, ++ and can be overridden by setting the QT_SCALE_FACTOR_ROUNDING_POLICY ++ environment variable. The QGuiApplication::highDpiScaleFactorRoundingPolicy() ++ accessor will reflect the environment, if set. ++ ++ The default value is Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor. ++*/ ++void QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy policy) ++{ ++ QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy = policy; ++} ++ ++/*! ++ \since 5.14 ++ ++ Returns the high-DPI scale factor rounding policy. ++*/ ++Qt::HighDpiScaleFactorRoundingPolicy QGuiApplication::highDpiScaleFactorRoundingPolicy() ++{ ++ return QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy; ++} ++ + /*! + \since 5.2 + \fn void QGuiApplication::applicationStateChanged(Qt::ApplicationState state) +diff --git a/src/gui/kernel/qguiapplication.h b/src/gui/kernel/qguiapplication.h +index 02dffef0fe..2814ba1d1b 100644 +--- a/src/gui/kernel/qguiapplication.h ++++ b/src/gui/kernel/qguiapplication.h +@@ -156,6 +156,9 @@ public: + + static Qt::ApplicationState applicationState(); + ++ static void setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy policy); ++ static Qt::HighDpiScaleFactorRoundingPolicy highDpiScaleFactorRoundingPolicy(); ++ + static int exec(); + bool notify(QObject *, QEvent *) override; + +diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h +index 042a36c31f..7962bb0177 100644 +--- a/src/gui/kernel/qguiapplication_p.h ++++ b/src/gui/kernel/qguiapplication_p.h +@@ -216,6 +216,7 @@ public: + static QWindow *currentMouseWindow; + static QWindow *currentMousePressWindow; + static Qt::ApplicationState applicationState; ++ static Qt::HighDpiScaleFactorRoundingPolicy highDpiScaleFactorRoundingPolicy; + static bool highDpiScalingUpdated; + static QPointer currentDragWindow; + +diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp +index 52c0665b5b..29b6d7e7c2 100644 +--- a/src/gui/kernel/qhighdpiscaling.cpp ++++ b/src/gui/kernel/qhighdpiscaling.cpp +@@ -329,24 +329,24 @@ static QByteArray joinEnumValues(const EnumLookup *i1, const EnumLooku + return result; + } + +-using ScaleFactorRoundingPolicyLookup = EnumLookup; ++using ScaleFactorRoundingPolicyLookup = EnumLookup; + + static const ScaleFactorRoundingPolicyLookup scaleFactorRoundingPolicyLookup[] = + { +- {"Round", QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::Round}, +- {"Ceil", QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::Ceil}, +- {"Floor", QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::Floor}, +- {"RoundPreferFloor", QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor}, +- {"PassThrough", QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::PassThrough} ++ {"Round", Qt::HighDpiScaleFactorRoundingPolicy::Round}, ++ {"Ceil", Qt::HighDpiScaleFactorRoundingPolicy::Ceil}, ++ {"Floor", Qt::HighDpiScaleFactorRoundingPolicy::Floor}, ++ {"RoundPreferFloor", Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor}, ++ {"PassThrough", Qt::HighDpiScaleFactorRoundingPolicy::PassThrough} + }; + +-static QHighDpiScaling::HighDpiScaleFactorRoundingPolicy ++static Qt::HighDpiScaleFactorRoundingPolicy + lookupScaleFactorRoundingPolicy(const QByteArray &v) + { + auto end = std::end(scaleFactorRoundingPolicyLookup); + auto it = std::find(std::begin(scaleFactorRoundingPolicyLookup), end, +- ScaleFactorRoundingPolicyLookup{v.constData(), QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::NotSet}); +- return it != end ? it->value : QHighDpiScaling::HighDpiScaleFactorRoundingPolicy::NotSet; ++ ScaleFactorRoundingPolicyLookup{v.constData(), Qt::HighDpiScaleFactorRoundingPolicy::NotSet}); ++ return it != end ? it->value : Qt::HighDpiScaleFactorRoundingPolicy::NotSet; + } + + using DpiAdjustmentPolicyLookup = EnumLookup; +@@ -375,15 +375,15 @@ 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 = HighDpiScaleFactorRoundingPolicy::NotSet; ++ static auto scaleFactorRoundingPolicy = Qt::HighDpiScaleFactorRoundingPolicy::NotSet; + + // Determine rounding policy +- if (scaleFactorRoundingPolicy == HighDpiScaleFactorRoundingPolicy::NotSet) { ++ if (scaleFactorRoundingPolicy == Qt::HighDpiScaleFactorRoundingPolicy::NotSet) { + // Check environment + if (qEnvironmentVariableIsSet(scaleFactorRoundingPolicyEnvVar)) { + QByteArray policyText = qgetenv(scaleFactorRoundingPolicyEnvVar); + auto policyEnumValue = lookupScaleFactorRoundingPolicy(policyText); +- if (policyEnumValue != HighDpiScaleFactorRoundingPolicy::NotSet) { ++ if (policyEnumValue != Qt::HighDpiScaleFactorRoundingPolicy::NotSet) { + scaleFactorRoundingPolicy = policyEnumValue; + } else { + auto values = joinEnumValues(std::begin(scaleFactorRoundingPolicyLookup), +@@ -391,38 +391,43 @@ qreal QHighDpiScaling::roundScaleFactor(qreal rawFactor) + 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::NotSet) { ++ scaleFactorRoundingPolicy = QGuiApplication::highDpiScaleFactorRoundingPolicy(); + } else { +- // Set default policy if no environment variable is set. +- scaleFactorRoundingPolicy = HighDpiScaleFactorRoundingPolicy::RoundPreferFloor; ++ // Make application setting reflect environment ++ QGuiApplication::setHighDpiScaleFactorRoundingPolicy(scaleFactorRoundingPolicy); + } + } + + // Apply rounding policy. + qreal roundedFactor = rawFactor; + switch (scaleFactorRoundingPolicy) { +- case HighDpiScaleFactorRoundingPolicy::Round: ++ case Qt::HighDpiScaleFactorRoundingPolicy::Round: + roundedFactor = qRound(rawFactor); + break; +- case HighDpiScaleFactorRoundingPolicy::Ceil: ++ case Qt::HighDpiScaleFactorRoundingPolicy::Ceil: + roundedFactor = qCeil(rawFactor); + break; +- case HighDpiScaleFactorRoundingPolicy::Floor: ++ case Qt::HighDpiScaleFactorRoundingPolicy::Floor: + roundedFactor = qFloor(rawFactor); + break; +- case HighDpiScaleFactorRoundingPolicy::RoundPreferFloor: ++ case Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor: + // Round up for .75 and higher. This favors "small UI" over "large UI". + roundedFactor = rawFactor - qFloor(rawFactor) < 0.75 + ? qFloor(rawFactor) : qCeil(rawFactor); + break; +- case HighDpiScaleFactorRoundingPolicy::PassThrough: +- case HighDpiScaleFactorRoundingPolicy::NotSet: ++ case Qt::HighDpiScaleFactorRoundingPolicy::PassThrough: ++ case Qt::HighDpiScaleFactorRoundingPolicy::NotSet: + break; + } + + // Don't round down to to zero; clamp the minimum (rounded) factor to 1. + // This is not a common case but can happen if a display reports a very + // low DPI. +- if (scaleFactorRoundingPolicy != HighDpiScaleFactorRoundingPolicy::PassThrough) ++ if (scaleFactorRoundingPolicy != Qt::HighDpiScaleFactorRoundingPolicy::PassThrough) + roundedFactor = qMax(roundedFactor, qreal(1)); + + return roundedFactor; +diff --git a/src/gui/kernel/qhighdpiscaling_p.h b/src/gui/kernel/qhighdpiscaling_p.h +index d3f71854a8..ae361a9ddb 100644 +--- a/src/gui/kernel/qhighdpiscaling_p.h ++++ b/src/gui/kernel/qhighdpiscaling_p.h +@@ -73,16 +73,6 @@ typedef QPair QDpi; + class Q_GUI_EXPORT QHighDpiScaling { + Q_GADGET + public: +- enum class HighDpiScaleFactorRoundingPolicy { +- NotSet, +- Round, +- Ceil, +- Floor, +- RoundPreferFloor, +- PassThrough +- }; +- Q_ENUM(HighDpiScaleFactorRoundingPolicy) +- + enum class DpiAdjustmentPolicy { + NotSet, + Enabled, +-- +2.18.0.windows.1 + diff --git a/3rdparty/ext_qt/CMakeLists.txt b/3rdparty/ext_qt/CMakeLists.txt index 085a91f51b..4b353e8ef8 100644 --- a/3rdparty/ext_qt/CMakeLists.txt +++ b/3rdparty/ext_qt/CMakeLists.txt @@ -1,258 +1,264 @@ SET(EXTPREFIX_qt "${EXTPREFIX}") if (WIN32) list(APPEND _QT_conf -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtdoc -skip qtgraphicaleffects -skip qtlocation -skip qtsensors -skip qtserialport -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -no-sql-sqlite -nomake examples -nomake tools -no-compile-examples -no-dbus -no-iconv -no-qml-debug -no-libproxy -no-system-proxies -no-icu -no-mtdev -skip qtcharts -skip qtdatavis3d -skip qtgamepad -skip qtnetworkauth -skip qtpurchasing -skip qtremoteobjects -skip qtscxml -skip qtserialbus -skip qtspeech -skip qtvirtualkeyboard # -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -openssl-linked -I ${EXTPREFIX_qt}/include -L ${EXTPREFIX_qt}/lib # -opensource -confirm-license # -release -platform win32-g++ -prefix ${EXTPREFIX_qt} QMAKE_LFLAGS_APP+=${SECURITY_EXE_LINKER_FLAGS} QMAKE_LFLAGS_SHLIB+=${SECURITY_SHARED_LINKER_FLAGS} QMAKE_LFLAGS_SONAME+=${SECURITY_SHARED_LINKER_FLAGS} ) if (QT_ENABLE_DEBUG_INFO) # Set the option to build Qt with debugging info enabled list(APPEND _QT_conf -force-debug-info) endif(QT_ENABLE_DEBUG_INFO) if (QT_ENABLE_DYNAMIC_OPENGL) list(APPEND _QT_conf -opengl dynamic -angle) else (QT_ENABLE_DYNAMIC_OPENGL) list(APPEND _QT_conf -opengl desktop -no-angle) endif (QT_ENABLE_DYNAMIC_OPENGL) if (NOT USE_QT_TABLET_WINDOWS) set(ext_qt_PATCH_COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0001-disable-wintab.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/disable-winink.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0002-Don-t-request-the-MIME-image-every-time-Windows-asks.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0003-Hack-always-return-we-support-DIBV5.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0004-Fix-debug-on-openGL-ES.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0005-cumulative-patch-for-hdr.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0006-Fix-swizzling-when-rendering-QPainter-on-QOpenGLWidg.patch ) else() set(ext_qt_PATCH_COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0002-Don-t-request-the-MIME-image-every-time-Windows-asks.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0003-Hack-always-return-we-support-DIBV5.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0004-Fix-debug-on-openGL-ES.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0005-cumulative-patch-for-hdr.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0006-Fix-swizzling-when-rendering-QPainter-on-QOpenGLWidg.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0020-Synthesize-Enter-LeaveEvent-for-accepted-QTabletEven.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0021-Fix-QTabletEvent-uniqueId-when-Qt-uses-WinInk.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0022-Fix-generation-of-Leave-events-when-using-tablet-dev.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0023-Implement-a-switch-for-tablet-API-on-Windows.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0024-Fetch-stylus-button-remapping-from-WinTab-driver.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0025-Disable-tablet-relative-mode-in-Qt.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0026-Fetch-mapped-screen-size-from-the-Wintab-driver.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0027-Switch-stylus-pointer-type-when-the-tablet-is-in-the.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0028-Fix-updating-tablet-pressure-resolution-on-every-pro.patch ) endif() set(ext_qt_PATCH_COMMAND ${ext_qt_PATCH_COMMAND} COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/set-has-border-in-full-screen-default.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/remove-fullscreen-border-hack.patch COMMAND ${PATCH_COMMAND} -p1 -d qttools -i ${CMAKE_CURRENT_SOURCE_DIR}/windeployqt-force-allow-debug-info.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0030-Windows-QPA-Make-the-expected-screen-be-in-sync-with.patch + COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0031-Compute-logical-DPI-on-a-per-screen-basis.patch + COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0032-Update-Dpi-and-scale-factor-computation.patch + COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0033-Move-QT_FONT_DPI-to-cross-platform-code.patch + COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0034-Update-QT_SCREEN_SCALE_FACTORS.patch + COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0035-Deprecate-QT_AUTO_SCREEN_SCALE_FACTOR.patch + COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0036-Add-high-DPI-scale-factor-rounding-policy-C-API.patch ) ExternalProject_Add( ext_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL https://download.qt.io/archive/qt/5.12/5.12.2/single/qt-everywhere-src-5.12.2.tar.xz URL_MD5 99c2eb46e533371798b4ca2d1458e065 PATCH_COMMAND ${ext_qt_PATCH_COMMAND} INSTALL_DIR ${EXTPREFIX_qt} CONFIGURE_COMMAND /configure.bat ${_QT_conf} BUILD_COMMAND mingw32-make -j${SUBMAKE_JOBS} INSTALL_COMMAND mingw32-make -j${SUBMAKE_JOBS} install UPDATE_COMMAND "" # Use a short name to reduce the chance of exceeding path length limit SOURCE_DIR s BINARY_DIR b DEPENDS ext_patch ext_openssl ) elseif (NOT APPLE) ExternalProject_Add( ext_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL https://download.qt.io/archive/qt/5.12/5.12.2/single/qt-everywhere-src-5.12.2.tar.xz URL_MD5 99c2eb46e533371798b4ca2d1458e065 PATCH_COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0010-Fix-tablet-jitter-on-X11.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0011-Add-an-ID-for-recognition-of-UGEE-tablets.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0012-Synthesize-Enter-LeaveEvent-for-accepted-QTabletEven.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0013-Poison-Qt-s-headers-with-a-mark-about-presence-of-En.patch CMAKE_ARGS -DOPENSSL_LIBS='-L${EXTPREFIX_qt}/lib -lssl -lcrypto' CONFIGURE_COMMAND /configure -prefix ${EXTPREFIX_qt} -opensource -confirm-license -openssl-linked -verbose -nomake examples -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtgraphicaleffects -skip qtlocation -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtandroidextras -skip qtserialport -skip qtdatavis3d -skip qtvirtualkeyboard -skip qtspeech -skip qtsensors -skip qtgamepad -skip qtscxml -skip qtremoteobjects -skip qtxmlpatterns -skip qtnetworkauth -skip qtcharts -skip qtdatavis3d -skip qtgamepad -skip qtpurchasing -skip qtscxml -skip qtserialbus -skip qtspeech -skip qtvirtualkeyboard -skip qtmultimedia INSTALL_DIR ${EXTPREFIX_qt} BUILD_COMMAND $(MAKE) INSTALL_COMMAND $(MAKE) install UPDATE_COMMAND "" BUILD_IN_SOURCE 1 ) else( APPLE ) # XCODE_VERSION is set by CMake when using the Xcode generator, otherwise we need # to detect it manually here. if (NOT XCODE_VERSION) execute_process( COMMAND xcodebuild -version OUTPUT_VARIABLE xcodebuild_version OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_FILE /dev/null ) string(REGEX MATCH "Xcode ([0-9]+([.][0-9]+)*)" version_match ${xcodebuild_version}) if (version_match) message(STATUS "${EXTPREFIX_qt}:Identified Xcode Version: ${CMAKE_MATCH_1}") set(XCODE_VERSION ${CMAKE_MATCH_1}) else() # If detecting Xcode version failed, set a crazy high version so we default # to the newest. set(XCODE_VERSION 99) message(WARNING "${EXTPREFIX_qt}:Failed to detect the version of an installed copy of Xcode, falling back to highest supported version. Set XCODE_VERSION to override.") endif(version_match) endif(NOT XCODE_VERSION) # ------------------------------------------------------------------------------- # Verify the Xcode installation on Mac OS like Qt5.7 does/will # If not stop now, the system isn't configured correctly for Qt. # No reason to even proceed. # ------------------------------------------------------------------------------- set(XCSELECT_OUTPUT) find_program(XCSELECT_PROGRAM "xcode-select") if(XCSELECT_PROGRAM) message(STATUS "${EXTPREFIX_qt}:Found XCSELECT_PROGRAM as ${XCSELECT_PROGRAM}") set(XCSELECT_COMMAND ${XCSELECT_PROGRAM} "--print-path") execute_process( COMMAND ${XCSELECT_COMMAND} RESULT_VARIABLE XCSELECT_COMMAND_RESULT OUTPUT_VARIABLE XCSELECT_COMMAND_OUTPUT ERROR_FILE /dev/null ) if(NOT XCSELECT_COMMAND_RESULT) # returned 0, we're ok. string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" XCSELECT_COMMAND_OUTPUT ${XCSELECT_COMMAND_OUTPUT}) else() string(REPLACE ";" " " XCSELECT_COMMAND_STR "${XCSELECT_COMMAND}") # message(STATUS "${XCSELECT_COMMAND_STR}") message(FATAL_ERROR "${EXTPREFIX_qt}:${XCSELECT_PROGRAM} test failed with status ${XCSELECT_COMMAND_RESULT}") endif() else() message(FATAL_ERROR "${EXTPREFIX_qt}:${XCSELECT_PROGRAM} not found. No Xcode is selected. Use xcode-select -switch to choose an Xcode version") endif() # Belts and suspenders # Beyond all the Xcode and Qt version checking, the proof of the pudding # lies in the success/failure of this command: xcrun --find xcrun. # On failure a patch is necessary, otherwise we're ok # So hard check xcrun now... set(XCRUN_OUTPUT) find_program(XCRUN_PROGRAM "xcrun") if(XCRUN_PROGRAM) message(STATUS "${EXTPREFIX_qt}:Found XCRUN_PROGRAM as ${XCRUN_PROGRAM}") set(XCRUN_COMMAND ${XCRUN_PROGRAM} "--find xcrun") execute_process( COMMAND ${XCRUN_COMMAND} RESULT_VARIABLE XCRUN_COMMAND_RESULT OUTPUT_VARIABLE XCRUN_COMMAND_OUTPUT ERROR_FILE /dev/null ) if(NOT XCRUN_COMMAND_RESULT) # returned 0, we're ok. string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" XCRUN_COMMAND_OUTPUT ${XCRUN_COMMAND_OUTPUT}) else() string(REPLACE ";" " " XCRUN_COMMAND_STR "${XCRUN_COMMAND}") # message(STATUS "${XCRUN_COMMAND_STR}") message(STATUS "${EXTPREFIX_qt}:xcrun test failed with status ${XCRUN_COMMAND_RESULT}") endif() else() message(STATUS "${EXTPREFIX_qt}:xcrun not found -- ${XCRUN_PROGRAM}") endif() # # Now configure ext_qt accordingly # if ((XCRUN_COMMAND_RESULT) AND (NOT (XCODE_VERSION VERSION_LESS 8.0.0))) # Fix Xcode xcrun related issue. # NOTE: This should be fixed by Qt 5.7.1 see here: http://code.qt.io/cgit/qt/qtbase.git/commit/?h=dev&id=77a71c32c9d19b87f79b208929e71282e8d8b5d9 # NOTE: but no one's holding their breath. set(ext_qt_PATCH_COMMAND ${PATCH_COMMAND}} -p1 -b -d /qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/mac_standardpaths_qtbug-61159.diff COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0012-Synthesize-Enter-LeaveEvent-for-accepted-QTabletEven.patch COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0013-Poison-Qt-s-headers-with-a-mark-about-presence-of-En.patch #COMMAND ${PATCH_COMMAND} -p1 -b -d /qtbase/mkspecs/features/mac -i ${CMAKE_CURRENT_SOURCE_DIR}/mac-default.patch ) message(STATUS "${EXTPREFIX_qt}:Additional patches injected.") else() # No extra patches will be applied # NOTE: defaults for some untested scenarios like xcrun fails and xcode_version < 8. # NOTE: that is uncharted territory and (hopefully) a very unlikely scenario... set(ext_qt_PATCH_COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0012-Synthesize-Enter-LeaveEvent-for-accepted-QTabletEven.patch ) endif() # Qt is big - try and parallelize if at all possible include(ProcessorCount) ProcessorCount(NUM_CORES) if(NOT NUM_CORES EQUAL 0) if (NUM_CORES GREATER 2) # be nice... MATH( EXPR NUM_CORES "${NUM_CORES} - 2" ) endif() set(PARALLEL_MAKE "make;-j${NUM_CORES}") message(STATUS "${EXTPREFIX_qt}:Parallelized make: ${PARALLEL_MAKE}") else() set(PARALLEL_MAKE "make") endif() ExternalProject_Add(ext_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} LOG_DOWNLOAD ON LOG_UPDATE ON LOG_CONFIGURE ON LOG_BUILD ON LOG_TEST ON LOG_INSTALL ON BUILD_IN_SOURCE ON URL https://download.qt.io/archive/qt/5.12/5.12.2/single/qt-everywhere-src-5.12.2.tar.xz URL_MD5 99c2eb46e533371798b4ca2d1458e065 CMAKE_ARGS -DOPENSSL_LIBS='-L${EXTPREFIX_qt}/lib -lssl -lcrypto' INSTALL_DIR ${EXTPREFIX_qt} CONFIGURE_COMMAND /configure -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtdoc -skip qtgraphicaleffects -skip qtlocation -skip qtsensors -skip qtserialport -skip qtwayland -skip qtwebchannel -skip qtwebsockets -skip qtwebview -skip qtwebengine -skip qtxmlpatterns -no-sql-sqlite -skip qtcharts -skip qtdatavis3d -skip qtgamepad -skip qtnetworkauth -skip qtpurchasing -skip qtremoteobjects -skip qtscxml -skip qtserialbus -skip qtspeech -skip qtvirtualkeyboard -nomake examples -nomake tools -no-compile-examples -no-dbus -no-iconv -no-qml-debug -no-libproxy -no-system-proxies -no-icu -no-mtdev -system-zlib -qt-pcre -opensource -confirm-license -openssl-linked -prefix ${EXTPREFIX_qt} BUILD_COMMAND ${PARALLEL_MAKE} INSTALL_COMMAND make install UPDATE_COMMAND "" BUILD_IN_SOURCE 1 ) endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 703ba7fbd5..ed705eeadd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,826 +1,838 @@ project(krita) message(STATUS "Using CMake version: ${CMAKE_VERSION}") cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) set(MIN_QT_VERSION 5.6.0) set(MIN_FRAMEWORKS_VERSION 5.18.0) if (POLICY CMP0002) cmake_policy(SET CMP0002 OLD) endif() if (POLICY CMP0017) cmake_policy(SET CMP0017 NEW) endif () if (POLICY CMP0022) cmake_policy(SET CMP0022 OLD) endif () if (POLICY CMP0026) cmake_policy(SET CMP0026 OLD) endif() if (POLICY CMP0042) cmake_policy(SET CMP0042 NEW) endif() if (POLICY CMP0046) cmake_policy(SET CMP0046 OLD) endif () if (POLICY CMP0059) cmake_policy(SET CMP0059 OLD) endif() if (POLICY CMP0063) cmake_policy(SET CMP0063 OLD) endif() if (POLICY CMP0054) cmake_policy(SET CMP0054 OLD) endif() if (POLICY CMP0064) cmake_policy(SET CMP0064 OLD) endif() if (POLICY CMP0071) cmake_policy(SET CMP0071 OLD) endif() if (APPLE) set(APPLE_SUPPRESS_X11_WARNING TRUE) set(KDE_SKIP_RPATH_SETTINGS TRUE) set(CMAKE_MACOSX_RPATH 1) set(BUILD_WITH_INSTALL_RPATH 1) add_definitions(-mmacosx-version-min=10.11 -Wno-macro-redefined -Wno-deprecated-register) endif() if (CMAKE_COMPILER_IS_GNUCXX AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9 AND NOT WIN32) add_definitions(-Wno-suggest-override) endif() ###################### ####################### ## Constants defines ## ####################### ###################### # define common versions of Krita applications, used to generate kritaversion.h # update these version for every release: set(KRITA_VERSION_STRING "4.2.0-pre-alpha") # Major version: 3 for 3.x, 4 for 4.x, etc. set(KRITA_STABLE_VERSION_MAJOR 4) # Minor version: 0 for 4.0, 1 for 4.1, etc. set(KRITA_STABLE_VERSION_MINOR 2) # Bugfix release version, or 0 for before the first stable release set(KRITA_VERSION_RELEASE 0) # the 4th digit, really only used for the Windows installer: # - [Pre-]Alpha: Starts from 0, increment 1 per release # - Beta: Starts from 50, increment 1 per release # - Stable: Set to 100, bump to 101 if emergency update is needed set(KRITA_VERSION_REVISION 0) set(KRITA_ALPHA 1) # uncomment only for Alpha #set(KRITA_BETA 1) # uncomment only for Beta #set(KRITA_RC 1) # uncomment only for RC set(KRITA_YEAR 2018) # update every year if(NOT DEFINED KRITA_ALPHA AND NOT DEFINED KRITA_BETA AND NOT DEFINED KRITA_RC) set(KRITA_STABLE 1) # do not edit endif() message(STATUS "Krita version: ${KRITA_VERSION_STRING}") # Define the generic version of the Krita libraries here # This makes it easy to advance it when the next Krita release comes. # 14 was the last GENERIC_KRITA_LIB_VERSION_MAJOR of the previous Krita series # (2.x) so we're starting with 15 in 3.x series, 16 in 4.x series if(KRITA_STABLE_VERSION_MAJOR EQUAL 4) math(EXPR GENERIC_KRITA_LIB_VERSION_MAJOR "${KRITA_STABLE_VERSION_MINOR} + 16") else() # let's make sure we won't forget to update the "16" message(FATAL_ERROR "Reminder: please update offset == 16 used to compute GENERIC_KRITA_LIB_VERSION_MAJOR to something bigger") endif() set(GENERIC_KRITA_LIB_VERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}.0.0") set(GENERIC_KRITA_LIB_SOVERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/kde_macro") # fetch git revision for the current build set(KRITA_GIT_SHA1_STRING "") set(KRITA_GIT_BRANCH_STRING "") include(GetGitRevisionDescription) get_git_head_hash(GIT_SHA1) get_git_branch(GIT_BRANCH) if(GIT_SHA1) string(SUBSTRING ${GIT_SHA1} 0 7 GIT_SHA1) set(KRITA_GIT_SHA1_STRING ${GIT_SHA1}) if(GIT_BRANCH) set(KRITA_GIT_BRANCH_STRING ${GIT_BRANCH}) else() set(KRITA_GIT_BRANCH_STRING "(detached HEAD)") endif() endif() # create test make targets enable_testing() # collect list of broken tests, empty here to start fresh with each cmake run set(KRITA_BROKEN_TESTS "" CACHE INTERNAL "KRITA_BROKEN_TESTS") ############ ############# ## Options ## ############# ############ include(FeatureSummary) if (WIN32) option(USE_DRMINGW "Support the Dr. Mingw crash handler (only on windows)" ON) add_feature_info("Dr. Mingw" USE_DRMINGW "Enable the Dr. Mingw crash handler") if (MINGW) option(USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags (mingw-w64)" ON) add_feature_info("Linker Security Flags" USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags") if (USE_MINGW_HARDENING_LINKER) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") # Enable high-entropy ASLR for 64-bit # The image base has to be >4GB for HEASLR to be enabled. # The values used here are kind of arbitrary. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x140000000") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") endif ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") else (USE_MINGW_HARDENING_LINKER) message(WARNING "Linker Security Flags not enabled!") endif (USE_MINGW_HARDENING_LINKER) endif (MINGW) endif () option(HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal." ON) configure_file(config-hide-safe-asserts.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hide-safe-asserts.h) add_feature_info("Hide Safe Asserts" HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal.") option(USE_LOCK_FREE_HASH_TABLE "Use lock free hash table instead of blocking." ON) configure_file(config-hash-table-implementaion.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hash-table-implementaion.h) add_feature_info("Lock free hash table" USE_LOCK_FREE_HASH_TABLE "Use lock free hash table instead of blocking.") option(FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true." OFF) add_feature_info("Foundation Build" FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true.") option(KRITA_ENABLE_BROKEN_TESTS "Enable tests that are marked as broken" OFF) add_feature_info("Enable Broken Tests" KRITA_ENABLE_BROKEN_TESTS "Runs broken test when \"make test\" is invoked (use -DKRITA_ENABLE_BROKEN_TESTS=ON to enable).") option(LIMIT_LONG_TESTS "Run long running unittests in a limited quick mode" ON) configure_file(config-limit-long-tests.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-limit-long-tests.h) add_feature_info("Limit long tests" LIMIT_LONG_TESTS "Run long running unittests in a limited quick mode") option(ENABLE_PYTHON_2 "Enables the compiler to look for Python 2.7 instead of Python 3. Some packaged scripts are not compatible with Python 2 and this should only be used if you really have to use 2.7." OFF) option(BUILD_KRITA_QT_DESIGNER_PLUGINS "Build Qt Designer plugins for Krita widgets" OFF) add_feature_info("Build Qt Designer plugins" BUILD_KRITA_QT_DESIGNER_PLUGINS "Builds Qt Designer plugins for Krita widgets (use -DBUILD_KRITA_QT_DESIGNER_PLUGINS=ON to enable).") include(MacroJPEG) ######################################################### ## Look for Python3 It is also searched by KF5, ## ## so we should request the correct version in advance ## ######################################################### function(TestCompileLinkPythonLibs OUTPUT_VARNAME) include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_INCLUDES ${PYTHON_INCLUDE_PATH}) set(CMAKE_REQUIRED_LIBRARIES ${PYTHON_LIBRARIES}) if (MINGW) set(CMAKE_REQUIRED_DEFINITIONS -D_hypot=hypot) endif (MINGW) unset(${OUTPUT_VARNAME} CACHE) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { Py_InitializeEx(0); }" ${OUTPUT_VARNAME}) endfunction() if(MINGW) if(ENABLE_PYTHON_2) message(FATAL_ERROR "Python 2.7 is not supported on Windows at the moment.") else(ENABLE_PYTHON_2) find_package(PythonInterp 3.6 EXACT) find_package(PythonLibs 3.6 EXACT) endif(ENABLE_PYTHON_2) if (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) if(ENABLE_PYTHON_2) find_package(PythonLibrary 2.7) else(ENABLE_PYTHON_2) find_package(PythonLibrary 3.6) endif(ENABLE_PYTHON_2) TestCompileLinkPythonLibs(CAN_USE_PYTHON_LIBS) if (NOT CAN_USE_PYTHON_LIBS) message(FATAL_ERROR "Compiling with Python library failed, please check whether the architecture is correct. Python will be disabled.") endif (NOT CAN_USE_PYTHON_LIBS) endif (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) else(MINGW) if(ENABLE_PYTHON_2) find_package(PythonInterp 2.7) find_package(PythonLibrary 2.7) else(ENABLE_PYTHON_2) find_package(PythonInterp 3.0) find_package(PythonLibrary 3.0) endif(ENABLE_PYTHON_2) endif(MINGW) ######################## ######################### ## Look for KDE and Qt ## ######################### ######################## find_package(ECM 5.22 REQUIRED NOMODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(ECMOptionalAddSubdirectory) include(ECMAddAppIcon) include(ECMSetupVersion) include(ECMMarkNonGuiExecutable) include(ECMGenerateHeaders) include(GenerateExportHeader) include(ECMMarkAsTest) include(ECMInstallIcons) include(CMakePackageConfigHelpers) include(WriteBasicConfigVersionFile) include(CheckFunctionExists) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings) # do not reorder to be alphabetical: this is the order in which the frameworks # depend on each other. find_package(KF5 ${MIN_FRAMEWORKS_VERSION} REQUIRED COMPONENTS Config WidgetsAddons Completion CoreAddons GuiAddons I18n ItemModels ItemViews WindowSystem ) # KConfig deprecated authorizeKAction. In order to be warning free, # compile with the updated function when the dependency is new enough. # Remove this (and the uses of the define) when the minimum KF5 # version is >= 5.24.0. if (${KF5Config_VERSION} VERSION_LESS "5.24.0" ) message("Old KConfig (< 5.24.0) found.") add_definitions(-DKCONFIG_BEFORE_5_24) endif() find_package(Qt5 ${MIN_QT_VERSION} REQUIRED COMPONENTS Core Gui Widgets Xml Network PrintSupport Svg Test Concurrent ) if (WIN32) set(CMAKE_REQUIRED_INCLUDES ${Qt5Core_INCLUDE_DIRS}) set(CMAKE_REQUIRED_LIBRARIES ${Qt5Core_LIBRARIES}) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_MSWindowsUseWinTabAPI); } " QT_HAS_WINTAB_SWITCH ) unset(CMAKE_REQUIRED_INCLUDES) unset(CMAKE_REQUIRED_LIBRARIES) option(USE_QT_TABLET_WINDOWS "Do not use Krita's forked Wintab and Windows Ink support on Windows, but leave everything to Qt." ON) add_feature_info("Use Qt's Windows Tablet Support" USE_QT_TABLET_WINDOWS "Do not use Krita's forked Wintab and Windows Ink support on Windows, but leave everything to Qt.") configure_file(config_use_qt_tablet_windows.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_use_qt_tablet_windows.h) endif () set(CMAKE_REQUIRED_INCLUDES ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS}) set(CMAKE_REQUIRED_LIBRARIES ${Qt5Core_LIBRARIES} ${Qt5Gui_LIBRARIES}) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QSurfaceFormat fmt; fmt.setColorSpace(QSurfaceFormat::scRGBColorSpace); fmt.setColorSpace(QSurfaceFormat::bt2020PQColorSpace); } " HAVE_HDR ) configure_file(config-hdr.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hdr.h) +CHECK_CXX_SOURCE_COMPILES(" +#include +int main(int argc, char *argv[]) { +QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round); +QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor); +QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); +} +" +HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY +) +configure_file(config-high-dpi-scale-factor-rounding-policy.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-high-dpi-scale-factor-rounding-policy.h) + if (WIN32) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QWindowsWindowFunctions::setHasBorderInFullScreenDefault(true); } " HAVE_SET_HAS_BORDER_IN_FULL_SCREEN_DEFAULT ) configure_file(config-set-has-border-in-full-screen-default.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-set-has-border-in-full-screen-default.h) endif (WIN32) unset(CMAKE_REQUIRED_INCLUDES) unset(CMAKE_REQUIRED_LIBRARIES) include (MacroAddFileDependencies) include (MacroBoolTo01) include (MacroEnsureOutOfSourceBuild) macro_ensure_out_of_source_build("Compiling Krita inside the source directory is not possible. Please refer to the build instruction https://community.kde.org/Krita#Build_Instructions") # Note: OPTIONAL_COMPONENTS does not seem to be reliable # (as of ECM 5.15.0, CMake 3.2) find_package(Qt5Multimedia ${MIN_QT_VERSION}) set_package_properties(Qt5Multimedia PROPERTIES DESCRIPTION "Qt multimedia integration" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used to provide sound support for animations") macro_bool_to_01(Qt5Multimedia_FOUND HAVE_QT_MULTIMEDIA) configure_file(config-qtmultimedia.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-qtmultimedia.h ) if (NOT APPLE) find_package(Qt5Quick ${MIN_QT_VERSION}) set_package_properties(Qt5Quick PROPERTIES DESCRIPTION "QtQuick" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used for the touch gui for Krita") macro_bool_to_01(Qt5Quick_FOUND HAVE_QT_QUICK) find_package(Qt5QuickWidgets ${MIN_QT_VERSION}) set_package_properties(Qt5QuickWidgets PROPERTIES DESCRIPTION "QtQuickWidgets" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used for the touch gui for Krita") endif() if (NOT WIN32 AND NOT APPLE) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED X11Extras) find_package(Qt5DBus ${MIN_QT_VERSION}) set(HAVE_DBUS ${Qt5DBus_FOUND}) set_package_properties(Qt5DBus PROPERTIES DESCRIPTION "Qt DBUS integration" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used to provide a dbus api on Linux") find_package(KF5Crash ${MIN_FRAMEWORKS_VERSION}) macro_bool_to_01(KF5Crash_FOUND HAVE_KCRASH) set_package_properties(KF5Crash PROPERTIES DESCRIPTION "KDE's Crash Handler" URL "http://api.kde.org/frameworks-api/frameworks5-apidocs/kcrash/html/index.html" TYPE OPTIONAL PURPOSE "Optionally used to provide crash reporting on Linux") find_package(X11 REQUIRED COMPONENTS Xinput) set(HAVE_X11 TRUE) add_definitions(-DHAVE_X11) find_package(XCB COMPONENTS XCB ATOM) set(HAVE_XCB ${XCB_FOUND}) else() set(HAVE_DBUS FALSE) set(HAVE_X11 FALSE) set(HAVE_XCB FALSE) endif() add_definitions( -DQT_USE_QSTRINGBUILDER -DQT_STRICT_ITERATORS -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_NO_URL_CAST_FROM_STRING ) if (${Qt5_VERSION} VERSION_GREATER "5.8.0" ) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50900) elseif(${Qt5_VERSION} VERSION_GREATER "5.7.0" ) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50800) elseif(${Qt5_VERSION} VERSION_GREATER "5.6.0" ) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50700) else() add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50600) endif() add_definitions(-DTRANSLATION_DOMAIN=\"krita\") # # The reason for this mode is that the Debug mode disable inlining # if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals") endif() option(KRITA_DEVS "For Krita developers. This modifies the DEBUG build type to use -O3 -g, while still enabling Q_ASSERT. This is necessary because the Qt5 cmake modules normally append QT_NO_DEBUG to any build type that is not labeled Debug") if (KRITA_DEVS) set(CMAKE_CXX_FLAGS_DEBUG "-O3 -g" CACHE STRING "" FORCE) endif() if(UNIX) set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};m") endif() if(WIN32) if(MSVC) # C4522: 'class' : multiple assignment operators specified set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4522") endif() endif() # KDECompilerSettings adds the `--export-all-symbols` linker flag. # We don't really need it. if(MINGW) string(REPLACE "-Wl,--export-all-symbols" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") string(REPLACE "-Wl,--export-all-symbols" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}") endif(MINGW) if(MINGW) # Hack CMake's variables to tell AR to create thin archives to reduce unnecessary writes. # Source of definition: https://github.com/Kitware/CMake/blob/v3.14.1/Modules/Platform/Windows-GNU.cmake#L128 # Thin archives: https://sourceware.org/binutils/docs/binutils/ar.html#index-thin-archives macro(mingw_use_thin_archive lang) foreach(rule CREATE_SHARED_MODULE CREATE_SHARED_LIBRARY LINK_EXECUTABLE) string(REGEX REPLACE "( [^ T]+) " "\\1T " CMAKE_${lang}_${rule} "${CMAKE_${lang}_${rule}}") endforeach() endmacro() mingw_use_thin_archive(CXX) endif(MINGW) # enable exceptions globally kde_enable_exceptions() set(KRITA_DEFAULT_TEST_DATA_DIR ${CMAKE_SOURCE_DIR}/sdk/tests/data/) macro(macro_add_unittest_definitions) add_definitions(-DFILES_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/") add_definitions(-DFILES_OUTPUT_DIR="${CMAKE_CURRENT_BINARY_DIR}") add_definitions(-DFILES_DEFAULT_DATA_DIR="${KRITA_DEFAULT_TEST_DATA_DIR}") add_definitions(-DSYSTEM_RESOURCES_DATA_DIR="${CMAKE_SOURCE_DIR}/krita/data/") endmacro() # overcome some platform incompatibilities if(WIN32) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/winquirks) add_definitions(-D_USE_MATH_DEFINES) add_definitions(-DNOMINMAX) set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib) endif() # set custom krita plugin installdir set(KRITA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR}/kritaplugins) ########################### ############################ ## Required dependencies ## ############################ ########################### find_package(PNG REQUIRED) if (APPLE) # this is not added correctly on OSX -- see http://forum.kde.org/viewtopic.php?f=139&t=101867&p=221242#p221242 include_directories(SYSTEM ${PNG_INCLUDE_DIR}) endif() add_definitions(-DBOOST_ALL_NO_LIB) find_package(Boost 1.55 REQUIRED COMPONENTS system) include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) ## ## Test for GNU Scientific Library ## find_package(GSL) set_package_properties(GSL PROPERTIES URL "http://www.gnu.org/software/gsl" TYPE RECOMMENDED PURPOSE "Required by Krita's Transform tool.") macro_bool_to_01(GSL_FOUND HAVE_GSL) configure_file(config-gsl.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gsl.h ) ########################### ############################ ## Optional dependencies ## ############################ ########################### find_package(ZLIB) set_package_properties(ZLIB PROPERTIES DESCRIPTION "Compression library" URL "http://www.zlib.net/" TYPE OPTIONAL PURPOSE "Optionally used by the G'Mic and the PSD plugins") macro_bool_to_01(ZLIB_FOUND HAVE_ZLIB) find_package(OpenEXR) set_package_properties(OpenEXR PROPERTIES DESCRIPTION "High dynamic-range (HDR) image file format" URL "http://www.openexr.com" TYPE OPTIONAL PURPOSE "Required by the Krita OpenEXR filter") macro_bool_to_01(OPENEXR_FOUND HAVE_OPENEXR) set(LINK_OPENEXR_LIB) if(OPENEXR_FOUND) include_directories(SYSTEM ${OPENEXR_INCLUDE_DIR}) set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES}) add_definitions(${OPENEXR_DEFINITIONS}) endif() find_package(TIFF) set_package_properties(TIFF PROPERTIES DESCRIPTION "TIFF Library and Utilities" URL "http://www.remotesensing.org/libtiff" TYPE OPTIONAL PURPOSE "Required by the Krita TIFF filter") find_package(JPEG) set_package_properties(JPEG PROPERTIES DESCRIPTION "Free library for JPEG image compression. Note: libjpeg8 is NOT supported." URL "http://www.libjpeg-turbo.org" TYPE OPTIONAL PURPOSE "Required by the Krita JPEG filter") find_package(GIF) set_package_properties(GIF PROPERTIES DESCRIPTION "Library for loading and saving gif files." URL "http://giflib.sourceforge.net/" TYPE OPTIONAL PURPOSE "Required by the Krita GIF filter") find_package(HEIF "1.3.0") set_package_properties(HEIF PROPERTIES DESCRIPTION "Library for loading and saving heif files." URL "https://github.com/strukturag/libheif" TYPE OPTIONAL PURPOSE "Required by the Krita HEIF filter") set(LIBRAW_MIN_VERSION "0.16") find_package(LibRaw ${LIBRAW_MIN_VERSION}) set_package_properties(LibRaw PROPERTIES DESCRIPTION "Library to decode RAW images" URL "http://www.libraw.org" TYPE OPTIONAL PURPOSE "Required to build the raw import plugin") find_package(FFTW3) set_package_properties(FFTW3 PROPERTIES DESCRIPTION "A fast, free C FFT library" URL "http://www.fftw.org/" TYPE OPTIONAL PURPOSE "Required by the Krita for fast convolution operators and some G'Mic features") macro_bool_to_01(FFTW3_FOUND HAVE_FFTW3) find_package(OCIO) set_package_properties(OCIO PROPERTIES DESCRIPTION "The OpenColorIO Library" URL "http://www.opencolorio.org" TYPE OPTIONAL PURPOSE "Required by the Krita LUT docker") macro_bool_to_01(OCIO_FOUND HAVE_OCIO) set_package_properties(PythonLibrary PROPERTIES DESCRIPTION "Python Library" URL "http://www.python.org" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(PYTHONLIBS_FOUND HAVE_PYTHONLIBS) find_package(SIP "4.19.13") set_package_properties(SIP PROPERTIES DESCRIPTION "Support for generating SIP Python bindings" URL "https://www.riverbankcomputing.com/software/sip/download" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(SIP_FOUND HAVE_SIP) find_package(PyQt5 "5.6.0") set_package_properties(PyQt5 PROPERTIES DESCRIPTION "Python bindings for Qt5." URL "https://www.riverbankcomputing.com/software/pyqt/download5" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(PYQT5_FOUND HAVE_PYQT5) ## ## Look for OpenGL ## # TODO: see if there is a better check for QtGui being built with opengl support (and thus the QOpenGL* classes) if(Qt5Gui_OPENGL_IMPLEMENTATION) message(STATUS "Found QtGui OpenGL support") else() message(FATAL_ERROR "Did NOT find QtGui OpenGL support. Check your Qt configuration. You cannot build Krita without Qt OpenGL support.") endif() ## ## Test for eigen3 ## find_package(Eigen3 3.0 REQUIRED) set_package_properties(Eigen3 PROPERTIES DESCRIPTION "C++ template library for linear algebra" URL "http://eigen.tuxfamily.org" TYPE REQUIRED) ## ## Test for exiv2 ## find_package(LibExiv2 0.16 REQUIRED) ## ## Test for lcms ## find_package(LCMS2 2.4 REQUIRED) set_package_properties(LCMS2 PROPERTIES DESCRIPTION "LittleCMS Color management engine" URL "http://www.littlecms.com" TYPE REQUIRED PURPOSE "Will be used for color management and is necessary for Krita") if(LCMS2_FOUND) if(NOT ${LCMS2_VERSION} VERSION_LESS 2040 ) set(HAVE_LCMS24 TRUE) endif() set(HAVE_REQUIRED_LCMS_VERSION TRUE) set(HAVE_LCMS2 TRUE) endif() ## ## Test for Vc ## set(OLD_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ) set(HAVE_VC FALSE) if (NOT ${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") if(NOT MSVC) find_package(Vc 1.1.0) set_package_properties(Vc PROPERTIES DESCRIPTION "Portable, zero-overhead SIMD library for C++" URL "https://github.com/VcDevel/Vc" TYPE OPTIONAL PURPOSE "Required by the Krita for vectorization") macro_bool_to_01(Vc_FOUND HAVE_VC) endif() endif() configure_file(config-vc.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-vc.h ) if(HAVE_VC) message(STATUS "Vc found!") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/vc") include (VcMacros) if(Vc_COMPILER_IS_CLANG) set(ADDITIONAL_VC_FLAGS "-Wabi -ffp-contract=fast") if(NOT WIN32) set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC") endif() elseif (NOT MSVC) set(ADDITIONAL_VC_FLAGS "-Wabi -fabi-version=0 -ffp-contract=fast") if(NOT WIN32) set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC") endif() endif() #Handle Vc master if(Vc_COMPILER_IS_GCC OR Vc_COMPILER_IS_CLANG) AddCompilerFlag("-std=c++11" _ok) if(NOT _ok) AddCompilerFlag("-std=c++0x" _ok) endif() endif() macro(ko_compile_for_all_implementations_no_scalar _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() macro(ko_compile_for_all_implementations _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY Scalar SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() endif() set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} ) add_definitions(${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS}) ## ## Test endianness ## include (TestBigEndian) test_big_endian(CMAKE_WORDS_BIGENDIAN) ## ## Test for qt-poppler ## find_package(Poppler COMPONENTS Qt5) set_package_properties(Poppler PROPERTIES DESCRIPTION "A PDF rendering library" URL "http://poppler.freedesktop.org" TYPE OPTIONAL PURPOSE "Required by the Krita PDF filter.") ## ## Test for quazip ## find_package(QuaZip 0.6) set_package_properties(QuaZip PROPERTIES DESCRIPTION "A library for reading and writing zip files" URL "https://stachenov.github.io/quazip/" TYPE REQUIRED PURPOSE "Needed for reading and writing KRA and ORA files" ) ## ## Test for Atomics ## include(CheckAtomic) ############################ ############################# ## Add Krita helper macros ## ############################# ############################ include(MacroKritaAddBenchmark) #################### ##################### ## Define includes ## ##################### #################### # for config.h and includes (if any?) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/interfaces ) add_subdirectory(libs) add_subdirectory(plugins) add_subdirectory(benchmarks) add_subdirectory(krita) configure_file(KoConfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/KoConfig.h ) configure_file(config_convolution.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_convolution.h) configure_file(config-ocio.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-ocio.h ) check_function_exists(powf HAVE_POWF) configure_file(config-powf.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-powf.h) if(WIN32) include(${CMAKE_CURRENT_LIST_DIR}/packaging/windows/installer/ConfigureInstallerNsis.cmake) endif() message("\nBroken tests:") foreach(tst ${KRITA_BROKEN_TESTS}) message(" * ${tst}") endforeach() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/po OR EXISTS ${CMAKE_CURRENT_BINARY_DIR}/po ) find_package(KF5I18n CONFIG REQUIRED) ki18n_install(po) endif() diff --git a/config-high-dpi-scale-factor-rounding-policy.h.in b/config-high-dpi-scale-factor-rounding-policy.h.in new file mode 100644 index 0000000000..8fd464ef13 --- /dev/null +++ b/config-high-dpi-scale-factor-rounding-policy.h.in @@ -0,0 +1,4 @@ +/* config-hdr.h. Generated by cmake from config-high-dpi-scale-factor-rounding-policy.h.in */ + +/* Define if QGuiApplication::setHighDpiScaleFactorRoundingPolicy is available */ +#cmakedefine HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY 1 diff --git a/krita/main.cc b/krita/main.cc index 374000623a..ec834e8afb 100644 --- a/krita/main.cc +++ b/krita/main.cc @@ -1,528 +1,547 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2002 Patrick Julien * Copyright (c) 2015 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= 0x050900 #include #endif #include #include #include #include #include #include "data/splash/splash_screen.xpm" #include "data/splash/splash_holidays.xpm" #include "data/splash/splash_screen_x2.xpm" #include "data/splash/splash_holidays_x2.xpm" #include "KisDocument.h" #include "kis_splash_screen.h" #include "KisPart.h" #include "KisApplicationArguments.h" #include #include "input/KisQtWidgetsTweaker.h" #include #include #if defined Q_OS_WIN #include "config_use_qt_tablet_windows.h" #include #ifndef USE_QT_TABLET_WINDOWS #include #include #else #include #endif +#include "config-high-dpi-scale-factor-rounding-policy.h" #include "config-set-has-border-in-full-screen-default.h" #ifdef HAVE_SET_HAS_BORDER_IN_FULL_SCREEN_DEFAULT #include #endif #include #endif #if defined HAVE_KCRASH #include #elif defined USE_DRMINGW namespace { void tryInitDrMingw() { wchar_t path[MAX_PATH]; QString pathStr = QCoreApplication::applicationDirPath().replace(L'/', L'\\') + QStringLiteral("\\exchndl.dll"); if (pathStr.size() > MAX_PATH - 1) { return; } int pathLen = pathStr.toWCharArray(path); path[pathLen] = L'\0'; // toWCharArray doesn't add NULL terminator HMODULE hMod = LoadLibraryW(path); if (!hMod) { return; } // No need to call ExcHndlInit since the crash handler is installed on DllMain auto myExcHndlSetLogFileNameA = reinterpret_cast(GetProcAddress(hMod, "ExcHndlSetLogFileNameA")); if (!myExcHndlSetLogFileNameA) { return; } // Set the log file path to %LocalAppData%\kritacrash.log QString logFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation).replace(L'/', L'\\') + QStringLiteral("\\kritacrash.log"); myExcHndlSetLogFileNameA(logFile.toLocal8Bit()); } typedef enum ORIENTATION_PREFERENCE { ORIENTATION_PREFERENCE_NONE = 0x0, ORIENTATION_PREFERENCE_LANDSCAPE = 0x1, ORIENTATION_PREFERENCE_PORTRAIT = 0x2, ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED = 0x4, ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED = 0x8 } ORIENTATION_PREFERENCE; typedef BOOL WINAPI (*pSetDisplayAutoRotationPreferences_t)( ORIENTATION_PREFERENCE orientation ); void resetRotation() { QLibrary user32Lib("user32"); if (!user32Lib.load()) { qWarning() << "Failed to load user32.dll! This really should not happen."; return; } pSetDisplayAutoRotationPreferences_t pSetDisplayAutoRotationPreferences = reinterpret_cast(user32Lib.resolve("SetDisplayAutoRotationPreferences")); if (!pSetDisplayAutoRotationPreferences) { dbgKrita << "Failed to load function SetDisplayAutoRotationPreferences"; return; } bool result = pSetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE_NONE); dbgKrita << "SetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE_NONE) returned" << result; } } // namespace #endif extern "C" int main(int argc, char **argv) { // The global initialization of the random generator qsrand(time(0)); bool runningInKDE = !qgetenv("KDE_FULL_SESSION").isEmpty(); #if defined HAVE_X11 qputenv("QT_QPA_PLATFORM", "xcb"); #endif // Workaround a bug in QNetworkManager qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1)); // A per-user unique string, without /, because QLocalServer cannot use names with a / in it QString key = "Krita4" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation).replace("/", "_"); key = key.replace(":", "_").replace("\\","_"); QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); #if QT_VERSION >= 0x050900 QCoreApplication::setAttribute(Qt::AA_DisableShaderDiskCache, true); #endif +#ifdef HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY + // This rounding policy depends on a series of patches to Qt related to + // https://bugreports.qt.io/browse/QTBUG-53022. These patches are applied + // in ext_qt for WIndows (patches 0031-0036). + // + // The rounding policy can be set externally by setting the environment + // variable `QT_SCALE_FACTOR_ROUNDING_POLICY` to one of the following: + // Round: Round up for .5 and above. + // Ceil: Always round up. + // Floor: Always round down. + // RoundPreferFloor: Round up for .75 and above. + // PassThrough: Don't round. + // + // The default is set to Round to obtain the same behaviour as in the past, + // but can be overridden by the above environment variable. + QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round); +#endif + const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); bool singleApplication = true; bool enableOpenGLDebug = false; bool openGLDebugSynchronous = false; bool logUsage = true; { singleApplication = kritarc.value("EnableSingleApplication", true).toBool(); if (kritarc.value("EnableHiDPI", true).toBool()) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); } if (!qgetenv("KRITA_HIDPI").isEmpty()) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); } if (!qgetenv("KRITA_OPENGL_DEBUG").isEmpty()) { enableOpenGLDebug = true; } else { enableOpenGLDebug = kritarc.value("EnableOpenGLDebug", false).toBool(); } if (enableOpenGLDebug && (qgetenv("KRITA_OPENGL_DEBUG") == "sync" || kritarc.value("OpenGLDebugSynchronous", false).toBool())) { openGLDebugSynchronous = true; } KisConfig::RootSurfaceFormat rootSurfaceFormat = KisConfig::rootSurfaceFormat(&kritarc); KisOpenGL::OpenGLRenderer preferredRenderer = KisOpenGL::RendererAuto; logUsage = kritarc.value("LogUsage", true).toBool(); const QString preferredRendererString = kritarc.value("OpenGLRenderer", "auto").toString(); preferredRenderer = KisOpenGL::convertConfigToOpenGLRenderer(preferredRendererString); #ifdef Q_OS_WIN // Force ANGLE to use Direct3D11. D3D9 doesn't support OpenGL ES 3 and WARP // might get weird crashes atm. qputenv("QT_ANGLE_PLATFORM", "d3d11"); #endif const QSurfaceFormat format = KisOpenGL::selectSurfaceFormat(preferredRenderer, rootSurfaceFormat, enableOpenGLDebug); if (format.renderableType() == QSurfaceFormat::OpenGLES) { QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true); } else { QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL, true); } KisOpenGL::setDefaultSurfaceFormat(format); KisOpenGL::setDebugSynchronous(openGLDebugSynchronous); #ifdef Q_OS_WIN // HACK: https://bugs.kde.org/show_bug.cgi?id=390651 resetRotation(); #endif } if (logUsage) { KisUsageLogger::initialize(); } QString root; QString language; { // Create a temporary application to get the root QCoreApplication app(argc, argv); Q_UNUSED(app); root = KoResourcePaths::getApplicationRoot(); QSettings languageoverride(configPath + QStringLiteral("/klanguageoverridesrc"), QSettings::IniFormat); languageoverride.beginGroup(QStringLiteral("Language")); language = languageoverride.value(qAppName(), "").toString(); } #ifdef Q_OS_LINUX { QByteArray originalXdgDataDirs = qgetenv("XDG_DATA_DIRS"); if (originalXdgDataDirs.isEmpty()) { // We don't want to completely override the default originalXdgDataDirs = "/usr/local/share/:/usr/share/"; } qputenv("XDG_DATA_DIRS", QFile::encodeName(root + "share") + ":" + originalXdgDataDirs); } #else qputenv("XDG_DATA_DIRS", QFile::encodeName(root + "share")); #endif dbgKrita << "Setting XDG_DATA_DIRS" << qgetenv("XDG_DATA_DIRS"); // Now that the paths are set, set the language. First check the override from the language // selection dialog. dbgKrita << "Override language:" << language; bool rightToLeft = false; if (!language.isEmpty()) { KLocalizedString::setLanguages(language.split(":")); // And override Qt's locale, too qputenv("LANG", language.split(":").first().toLocal8Bit()); QLocale locale(language.split(":").first()); QLocale::setDefault(locale); const QStringList rtlLanguages = QStringList() << "ar" << "dv" << "he" << "ha" << "ku" << "fa" << "ps" << "ur" << "yi"; if (rtlLanguages.contains(language.split(':').first())) { rightToLeft = true; } } else { dbgKrita << "Qt UI languages:" << QLocale::system().uiLanguages() << qgetenv("LANG"); // And if there isn't one, check the one set by the system. QLocale locale = QLocale::system(); if (locale.name() != QStringLiteral("en")) { QStringList uiLanguages = locale.uiLanguages(); for (QString &uiLanguage : uiLanguages) { // This list of language codes that can have a specifier should // be extended whenever we have translations that need it; right // now, only en, pt, zh are in this situation. if (uiLanguage.startsWith("en") || uiLanguage.startsWith("pt")) { uiLanguage.replace(QChar('-'), QChar('_')); } else if (uiLanguage.startsWith("zh-Hant") || uiLanguage.startsWith("zh-TW")) { uiLanguage = "zh_TW"; } else if (uiLanguage.startsWith("zh-Hans") || uiLanguage.startsWith("zh-CN")) { uiLanguage = "zh_CN"; } } for (int i = 0; i < uiLanguages.size(); i++) { QString uiLanguage = uiLanguages[i]; // Strip the country code int idx = uiLanguage.indexOf(QChar('-')); if (idx != -1) { uiLanguage = uiLanguage.left(idx); uiLanguages.replace(i, uiLanguage); } } dbgKrita << "Converted ui languages:" << uiLanguages; qputenv("LANG", uiLanguages.first().toLocal8Bit()); #ifdef Q_OS_MAC // See https://bugs.kde.org/show_bug.cgi?id=396370 KLocalizedString::setLanguages(QStringList() << uiLanguages.first()); #else KLocalizedString::setLanguages(QStringList() << uiLanguages); #endif } } #if defined Q_OS_WIN && defined USE_QT_TABLET_WINDOWS && defined QT_HAS_WINTAB_SWITCH const bool forceWinTab = !KisConfig::useWin8PointerInputNoApp(&kritarc); QCoreApplication::setAttribute(Qt::AA_MSWindowsUseWinTabAPI, forceWinTab); if (qEnvironmentVariableIsEmpty("QT_WINTAB_DESKTOP_RECT") && qEnvironmentVariableIsEmpty("QT_IGNORE_WINTAB_MAPPING")) { QRect customTabletRect; KisDlgCustomTabletResolution::Mode tabletMode = KisDlgCustomTabletResolution::getTabletMode(&customTabletRect); KisDlgCustomTabletResolution::applyConfiguration(tabletMode, customTabletRect); } #endif // first create the application so we can create a pixmap KisApplication app(key, argc, argv); #ifdef HAVE_SET_HAS_BORDER_IN_FULL_SCREEN_DEFAULT if (QCoreApplication::testAttribute(Qt::AA_UseDesktopOpenGL)) { QWindowsWindowFunctions::setHasBorderInFullScreenDefault(true); } #endif KisUsageLogger::writeHeader(); if (!language.isEmpty()) { if (rightToLeft) { app.setLayoutDirection(Qt::RightToLeft); } else { app.setLayoutDirection(Qt::LeftToRight); } } KLocalizedString::setApplicationDomain("krita"); dbgKrita << "Available translations" << KLocalizedString::availableApplicationTranslations(); dbgKrita << "Available domain translations" << KLocalizedString::availableDomainTranslations("krita"); #ifdef Q_OS_WIN QDir appdir(KoResourcePaths::getApplicationRoot()); QString path = qgetenv("PATH"); qputenv("PATH", QFile::encodeName(appdir.absolutePath() + "/bin" + ";" + appdir.absolutePath() + "/lib" + ";" + appdir.absolutePath() + "/Frameworks" + ";" + appdir.absolutePath() + ";" + path)); dbgKrita << "PATH" << qgetenv("PATH"); #endif if (qApp->applicationDirPath().contains(KRITA_BUILD_DIR)) { qFatal("FATAL: You're trying to run krita from the build location. You can only run Krita from the installation location."); } #if defined HAVE_KCRASH KCrash::initialize(); #elif defined USE_DRMINGW tryInitDrMingw(); #endif // If we should clear the config, it has to be done as soon as possible after // KisApplication has been created. Otherwise the config file may have been read // and stored in a KConfig object we have no control over. app.askClearConfig(); KisApplicationArguments args(app); if (singleApplication && app.isRunning()) { // only pass arguments to main instance if they are not for batch processing // any batch processing would be done in this separate instance const bool batchRun = args.exportAs(); if (!batchRun) { QByteArray ba = args.serialize(); if (app.sendMessage(ba)) { return 0; } } } if (!runningInKDE) { // Icons in menus are ugly and distracting app.setAttribute(Qt::AA_DontShowIconsInMenus); } app.installEventFilter(KisQtWidgetsTweaker::instance()); if (!args.noSplash()) { // then create the pixmap from an xpm: we cannot get the // location of our datadir before we've started our components, // so use an xpm. QDate currentDate = QDate::currentDate(); QWidget *splash = 0; if (currentDate > QDate(currentDate.year(), 12, 4) || currentDate < QDate(currentDate.year(), 1, 9)) { splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_holidays_xpm), QPixmap(splash_holidays_x2_xpm)); } else { splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_screen_xpm), QPixmap(splash_screen_x2_xpm)); } app.setSplashScreen(splash); } #if defined Q_OS_WIN KisConfig cfg(false); bool supportedWindowsVersion = true; #if QT_VERSION >= 0x050900 QOperatingSystemVersion osVersion = QOperatingSystemVersion::current(); if (osVersion.type() == QOperatingSystemVersion::Windows) { if (osVersion.majorVersion() >= QOperatingSystemVersion::Windows7.majorVersion()) { supportedWindowsVersion = true; } else { supportedWindowsVersion = false; if (cfg.readEntry("WarnedAboutUnsupportedWindows", false)) { QMessageBox::information(0, i18nc("@title:window", "Krita: Warning"), i18n("You are running an unsupported version of Windows: %1.\n" "This is not recommended. Do not report any bugs.\n" "Please update to a supported version of Windows: Windows 7, 8, 8.1 or 10.", osVersion.name())); cfg.writeEntry("WarnedAboutUnsupportedWindows", true); } } } #endif #ifndef USE_QT_TABLET_WINDOWS { if (cfg.useWin8PointerInput() && !KisTabletSupportWin8::isAvailable()) { cfg.setUseWin8PointerInput(false); } if (!cfg.useWin8PointerInput()) { bool hasWinTab = KisTabletSupportWin::init(); if (!hasWinTab && supportedWindowsVersion) { if (KisTabletSupportWin8::isPenDeviceAvailable()) { // Use WinInk automatically cfg.setUseWin8PointerInput(true); } else if (!cfg.readEntry("WarnedAboutMissingWinTab", false)) { if (KisTabletSupportWin8::isAvailable()) { QMessageBox::information(nullptr, i18n("Krita Tablet Support"), i18n("Cannot load WinTab driver and no Windows Ink pen devices are found. If you have a drawing tablet, please make sure the tablet driver is properly installed."), QMessageBox::Ok, QMessageBox::Ok); } else { QMessageBox::information(nullptr, i18n("Krita Tablet Support"), i18n("Cannot load WinTab driver. If you have a drawing tablet, please make sure the tablet driver is properly installed."), QMessageBox::Ok, QMessageBox::Ok); } cfg.writeEntry("WarnedAboutMissingWinTab", true); } } } if (cfg.useWin8PointerInput()) { KisTabletSupportWin8 *penFilter = new KisTabletSupportWin8(); if (penFilter->init()) { // penFilter.registerPointerDeviceNotifications(); app.installNativeEventFilter(penFilter); dbgKrita << "Using Win8 Pointer Input for tablet support"; } else { dbgKrita << "No Win8 Pointer Input available"; delete penFilter; } } } #elif defined QT_HAS_WINTAB_SWITCH // Check if WinTab/WinInk has actually activated const bool useWinTabAPI = app.testAttribute(Qt::AA_MSWindowsUseWinTabAPI); if (useWinTabAPI != !cfg.useWin8PointerInput()) { cfg.setUseWin8PointerInput(useWinTabAPI); } #endif #endif if (!app.start(args)) { return 1; } #if QT_VERSION >= 0x050700 app.setAttribute(Qt::AA_CompressHighFrequencyEvents, false); #endif // Set up remote arguments. QObject::connect(&app, SIGNAL(messageReceived(QByteArray,QObject*)), &app, SLOT(remoteArguments(QByteArray,QObject*))); QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), &app, SLOT(fileOpenRequested(QString))); // Hardware information KisUsageLogger::write("\nHardware Information\n"); KisUsageLogger::write(QString(" GPU Acceleration: %1").arg(kritarc.value("OpenGLRenderer", "auto").toString())); KisUsageLogger::write(QString(" Memory: %1 Mb").arg(KisImageConfig(true).totalRAM())); KisUsageLogger::write(QString(" Number of Cores: %1").arg(QThread::idealThreadCount())); KisUsageLogger::write(QString(" Swap Location: %1\n").arg(KisImageConfig(true).swapDir())); int state = app.exec(); { QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); kritarc.setValue("canvasState", "OPENGL_SUCCESS"); } if (logUsage) { KisUsageLogger::close(); } return state; }