diff --git a/src/blackgui/blackgui.pro b/src/blackgui/blackgui.pro index 43ee47b90..9b454e1ea 100644 --- a/src/blackgui/blackgui.pro +++ b/src/blackgui/blackgui.pro @@ -25,6 +25,9 @@ else { QMAKE_CXXFLAGS += -idirafter $$EXTERNALSROOT/common/include/qwt } +# needed for "ShellScalingApi.h" only +# win32 { LIBS *= -lSHCore } + DEPENDPATH += . .. PRECOMPILED_HEADER = pch/pch.h diff --git a/src/blackgui/guiapplication.cpp b/src/blackgui/guiapplication.cpp index 16460e005..dae3b97f8 100644 --- a/src/blackgui/guiapplication.cpp +++ b/src/blackgui/guiapplication.cpp @@ -66,6 +66,7 @@ #include #include #include +#include using namespace BlackConfig; using namespace BlackMisc; @@ -351,21 +352,27 @@ namespace BlackGui CApplication::exit(retcode); } - void CGuiApplication::highDpiScreenSupport(double scaleFactor) + void CGuiApplication::highDpiScreenSupport(const QString &scaleFactor) { // https://lists.qt-project.org/pipermail/development/2019-September/037434.html - if (scaleFactor < 0) + // QSize s = CGuiUtility::physicalScreenSizeOs(); + QString sf = scaleFactor.trimmed().isEmpty() ? defaultScaleFactorString() : scaleFactor; + if (sf.contains('/')) { - // qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1"); // until 5.14 - qputenv("QT_ENABLE_HIGHDPI_SCALING", "1"); - } - else - { - QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); // DPI support - QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); // HiDPI pixmaps - const QString sf = QString::number(scaleFactor, 'f', 2); - qputenv("QT_SCALE_FACTOR", sf.toLatin1()); + const double sfd = parseFraction(scaleFactor, -1); + sf = sfd < 0 ? "1.0" : QString::number(sfd, 'f', 8); } + + sf = cleanNumber(sf); + + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); // DPI support + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); // HiDPI pixmaps + + // qputenv("QT_ENABLE_HIGHDPI_SCALING", "1"); + QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Floor); + + const QByteArray sfa = sf.toLatin1(); + qputenv("QT_SCALE_FACTOR", sfa); } bool CGuiApplication::isUsingHighDpiScreenSupport() @@ -375,7 +382,7 @@ namespace BlackGui QScreen *CGuiApplication::currentScreen() { - QWidget *w = CGuiApplication::mainApplicationWidget(); + const QWidget *w = CGuiApplication::mainApplicationWidget(); const int s = QApplication::desktop()->screenNumber(w); if (s < QGuiApplication::screens().size()) { return QGuiApplication::screens()[s]; } return QGuiApplication::primaryScreen(); @@ -552,6 +559,37 @@ namespace BlackGui return -1.0; } + QString CGuiApplication::scaleFactor(int argc, char *argv[]) + { + for (int i = 1; i < argc; ++i) + { + if (qstrcmp(argv[i], "--scale") == 0 || qstrcmp(argv[i], "-scale") == 0) + { + if (i + 1 >= argc) { return QString(); } // no value + const QString factor(argv[i + 1]); + return factor.trimmed(); + } + } + return QString(); + } + + QString CGuiApplication::defaultScaleFactorString() + { + if (!CBuildConfig::isRunningOnWindowsNtPlatform()) { return "1.0"; } + + // On windows + // Qt 5.14.1 default is device ratio 3 + // Qt 5.14.0 default device ratio was 2 + + // 2/3 (0.66667) => device ratio 3 + // 0.75 => device ratio 2.25 + // 0.8 => device ratio 2.4 + // 1.00 => device ratio 3 + + // currently NOT used + return "1.0"; + } + bool CGuiApplication::cmdLineErrorMessage(const QString &errorMessage, bool retry) const { const QString helpText(beautifyHelpMessage(m_parser.helpText())); diff --git a/src/blackgui/guiapplication.h b/src/blackgui/guiapplication.h index d47f80adc..c6caa9f4d 100644 --- a/src/blackgui/guiapplication.h +++ b/src/blackgui/guiapplication.h @@ -284,7 +284,7 @@ namespace BlackGui //! Support for high DPI screens //! \note Needs to be at the beginning of main - static void highDpiScreenSupport(double scaleFactor = -1); + static void highDpiScreenSupport(const QString &scaleFactor = {}); //! Uses the high DPI support? static bool isUsingHighDpiScreenSupport(); @@ -300,8 +300,15 @@ namespace BlackGui static void modalWindowToFront(); //! Parse scale factor if any + //! \deprecated using scaleFactor now static double parseScaleFactor(int argc, char *argv[]); + //! Get the scale factor + static QString scaleFactor(int argc, char *argv[]); + + //! Get a default scale factor + static QString defaultScaleFactorString(); + signals: //! Style sheet changed void styleSheetsChanged(); diff --git a/src/blackgui/guiutility.cpp b/src/blackgui/guiutility.cpp index b82fce791..7615f52a8 100644 --- a/src/blackgui/guiutility.cpp +++ b/src/blackgui/guiutility.cpp @@ -43,6 +43,14 @@ #include #include +// for the screen size + +#ifdef Q_OS_WINDOWS +#include "wtypes.h" +#include "ShellScalingApi.h" +#include +#endif + using namespace BlackMisc; namespace BlackGui @@ -114,6 +122,79 @@ namespace BlackGui return QApplication::desktop()->screenGeometry(QApplication::desktop()).size(); } + QSizeF CGuiUtility::physicalScreenSize(QWidget *currentWidget) + { + QScreen *s = QGuiApplication::screens().front(); + if (currentWidget) + { + const int sn = QApplication::desktop()->screenNumber(currentWidget); + s = QGuiApplication::screens().at(sn); + } + + if (!s) { return QSizeF(); } + const QSizeF ps1 = s->physicalSize(); + return ps1; + } + + namespace Private + { +#ifdef Q_OS_WINDOWS + QSize windowsGetDesktopResolution() + { + /** + // https://stackoverflow.com/questions/8690619/how-to-get-screen-resolution-in-c + RECT desktop; + // Get a handle to the desktop window + const HWND hDesktop = GetDesktopWindow(); + // Get the size of screen to the variable desktop + GetWindowRect(hDesktop, &desktop); + // The top left corner will have coordinates (0,0) + // and the bottom right corner will have coordinates + // (horizontal, vertical) + const int horizontal = desktop.right; + const int vertical = desktop.bottom; + return QSize(vertical, horizontal); + **/ + + // https://stackoverflow.com/questions/2156212/how-to-get-the-monitor-screen-resolution-from-a-hwnd + const HWND hDesktop = GetDesktopWindow(); + HMONITOR monitor = MonitorFromWindow(hDesktop, MONITOR_DEFAULTTONEAREST); + MONITORINFO info; + info.cbSize = sizeof(MONITORINFO); + GetMonitorInfo(monitor, &info); + + /** + DEVICE_SCALE_FACTOR pScale; + GetScaleFactorForMonitor(monitor, &pScale); + **/ + + const int monitor_width = info.rcMonitor.right - info.rcMonitor.left; + const int monitor_height = info.rcMonitor.bottom - info.rcMonitor.top; + return QSize(monitor_height, monitor_width); + + /** + // https://stackoverflow.com/a/50205813/356726 + const int width = static_cast(GetSystemMetrics(SM_CXSCREEN)); + const int height = static_cast(GetSystemMetrics(SM_CYSCREEN)); + return QSize(width, height); + **/ + } +#endif + } // namespace Private + + QSize CGuiUtility::physicalScreenSizeOs() + { +#ifdef Q_OS_WINDOWS + return Private::windowsGetDesktopResolution(); +#elif defined(Q_OS_MAC) + return QSize(); +#elif defined(Q_OS_LINUX) + return QSize(); +#else + return QSize(); +#endif + } + bool CGuiUtility::isMainWindowFrameless() { const CEnableForFramelessWindow *mw = CGuiUtility::mainFramelessEnabledWindow(); @@ -122,8 +203,15 @@ namespace BlackGui QString CGuiUtility::screenInformation(const QString &separator) { + const QSize ps = physicalScreenSizeOs(); QString i = u"Number of screens: " % QString::number(QGuiApplication::screens().size()) % separator % - u"Primary screen: " % QGuiApplication::primaryScreen()->name(); + u"Primary screen: " % QGuiApplication::primaryScreen()->name() % separator % + u"QT_AUTO_SCREEN_SCALE_FACTOR: " % qgetenv("QT_AUTO_SCREEN_SCALE_FACTOR") % separator % + u"QT_SCALE_FACTOR: " % qgetenv("QT_SCALE_FACTOR") % separator % + u"QT_ENABLE_HIGHDPI_SCALING: " % qgetenv("QT_ENABLE_HIGHDPI_SCALING") % separator % + u"QT_SCALE_FACTOR_ROUNDING_POLICY: " % qgetenv("QT_SCALE_FACTOR_ROUNDING_POLICY") % separator % + u"QT_SCREEN_SCALE_FACTORS: " % qgetenv("QT_SCREEN_SCALE_FACTORS") % separator % + u"OS screen res." % QString::number(ps.width()) % u"/" % QString::number(ps.height()) % separator; for (const QScreen *screen : QGuiApplication::screens()) { @@ -509,7 +597,7 @@ namespace BlackGui else { flags &= ~Qt::WindowStaysOnBottomHint; - flags |= Qt::WindowStaysOnTopHint; + flags |= Qt::WindowStaysOnTopHint; } widget->setWindowFlags(flags); widget->show(); // without that the window sometimes just disappears @@ -523,7 +611,7 @@ namespace BlackGui if (onTop) { flags &= ~Qt::WindowStaysOnBottomHint; - flags |= Qt::WindowStaysOnTopHint; + flags |= Qt::WindowStaysOnTopHint; } else { diff --git a/src/blackgui/guiutility.h b/src/blackgui/guiutility.h index be1dd2227..41a7cdb2b 100644 --- a/src/blackgui/guiutility.h +++ b/src/blackgui/guiutility.h @@ -66,6 +66,14 @@ namespace BlackGui //! \remark might be scaled depending on CGuiUtility::mainApplicationWidgetPixelRatio static QSize desktopSize(); + //! Pysical screen resolution + //! \remark unscaled + static QSizeF physicalScreenSize(QWidget *currentWidget = nullptr); + + //! Qt independent implementation + //! \remark allows to return values before QGuiApplication is initialized + static QSize physicalScreenSizeOs(); + //! Is main window frameless? static bool isMainWindowFrameless(); diff --git a/src/blackmisc/stringutils.cpp b/src/blackmisc/stringutils.cpp index 33a490231..3c7496023 100644 --- a/src/blackmisc/stringutils.cpp +++ b/src/blackmisc/stringutils.cpp @@ -491,6 +491,64 @@ namespace BlackMisc return (c % 2) == 0; } + double parseFraction(const QString &fraction, double failDefault) + { + if (fraction.isEmpty()) { return failDefault; } + bool ok; + + double r = failDefault; + if (fraction.contains('/')) + { + const QStringList parts = fraction.split('/'); + if (parts.size() != 2) { return failDefault; } + const double c = parts.front().trimmed().toDouble(&ok); + if (!ok) { return failDefault; } + + const double d = parts.last().trimmed().toDouble(&ok); + if (!ok) { return failDefault; } + if (qFuzzyCompare(0.0, d)) { return failDefault; } + r = c / d; + } + else + { + r = fraction.trimmed().toDouble(&ok); + if (!ok) { return failDefault; } + } + return r; + } + + QString cleanNumber(const QString &number) + { + QString n = number.trimmed(); + if (n.isEmpty()) { return QString(); } + + int dp = n.indexOf('.'); + if (dp < 0) { dp = n.indexOf(','); } + + // clean all trailing stuff + while (dp >= 0 && !n.isEmpty()) + { + const QChar l = n.at(n.size() - 1); + if (l == '0') + { + n.chop(1); + continue; + } + else if (l == '.' || l == ',') + { + n.chop(1); + } + break; + } + + while (n.startsWith("00")) + { + n.remove(0, 1); + } + + return n; + } + } // ns //! \endcond diff --git a/src/blackmisc/stringutils.h b/src/blackmisc/stringutils.h index a5e44e419..c47dd0b0d 100644 --- a/src/blackmisc/stringutils.h +++ b/src/blackmisc/stringutils.h @@ -308,6 +308,12 @@ namespace BlackMisc //! Contains any string of the list? BLACKMISC_EXPORT bool containsAny(const QString &testString, const QStringList &any, Qt::CaseSensitivity cs); + //! Parse a fraction like 2/3 + BLACKMISC_EXPORT double parseFraction(const QString &fraction, double failDefault = std::numeric_limits::quiet_NaN()); + + //! Remove leading 0, trailing 0, " ", and "." from a number + BLACKMISC_EXPORT QString cleanNumber(const QString &number); + namespace Mixin { /*! diff --git a/src/swiftcore/main.cpp b/src/swiftcore/main.cpp index f66caa69c..44bda373d 100644 --- a/src/swiftcore/main.cpp +++ b/src/swiftcore/main.cpp @@ -27,7 +27,7 @@ using namespace BlackGui; int main(int argc, char *argv[]) { //! [SwiftApplicationDemo] - CGuiApplication::highDpiScreenSupport(CGuiApplication::parseScaleFactor(argc, argv)); + CGuiApplication::highDpiScreenSupport(CGuiApplication::scaleFactor(argc, argv)); QApplication qa(argc, argv); Q_UNUSED(qa) // init of qa is required, but qa not used diff --git a/src/swiftdata/main.cpp b/src/swiftdata/main.cpp index 9174d80eb..9d1471286 100644 --- a/src/swiftdata/main.cpp +++ b/src/swiftdata/main.cpp @@ -25,9 +25,9 @@ using namespace BlackGui; int main(int argc, char *argv[]) { - CGuiApplication::highDpiScreenSupport(CGuiApplication::parseScaleFactor(argc, argv)); + CGuiApplication::highDpiScreenSupport(CGuiApplication::scaleFactor(argc, argv)); QApplication qa(argc, argv); - Q_UNUSED(qa); + Q_UNUSED(qa) CCrashHandler::instance()->init(); CGuiApplication a(CApplicationInfo::swiftMappingTool(), CApplicationInfo::MappingTool, CIcons::swiftDatabase48()); diff --git a/src/swiftguistandard/main.cpp b/src/swiftguistandard/main.cpp index 8bcf6d39c..6533bd548 100644 --- a/src/swiftguistandard/main.cpp +++ b/src/swiftguistandard/main.cpp @@ -24,7 +24,7 @@ using namespace BlackCore; int main(int argc, char *argv[]) { //! [SwiftApplicationDemo] - CGuiApplication::highDpiScreenSupport(CGuiApplication::parseScaleFactor(argc, argv)); + CGuiApplication::highDpiScreenSupport(CGuiApplication::scaleFactor(argc, argv)); QApplication qa(argc, argv); Q_UNUSED(qa) // application init needed diff --git a/src/swiftguistandard/swiftguistdinit.cpp b/src/swiftguistandard/swiftguistdinit.cpp index 5d69695f5..ed61fec99 100644 --- a/src/swiftguistandard/swiftguistdinit.cpp +++ b/src/swiftguistandard/swiftguistdinit.cpp @@ -76,7 +76,8 @@ void SwiftGuiStd::init() sGui->initMainApplicationWidget(this); // experimental: avoid mic flickering - if (CBuildConfig::isLocalDeveloperDebugBuild()) { BlackSound::occupyAudioInputDevice(); } + // disabled for 5.14.1 + // if (CBuildConfig::isLocalDeveloperDebugBuild()) { BlackSound::occupyAudioInputDevice(); } // log messages m_logSubscriber.changeSubscription(CLogPattern().withSeverityAtOrAbove(CStatusMessage::SeverityInfo)); diff --git a/src/swiftlauncher/main.cpp b/src/swiftlauncher/main.cpp index c0e1a7b04..cef5fa0cc 100644 --- a/src/swiftlauncher/main.cpp +++ b/src/swiftlauncher/main.cpp @@ -29,7 +29,7 @@ using namespace BlackCore::Db; int main(int argc, char *argv[]) { //! [SwiftApplicationDemo] - CGuiApplication::highDpiScreenSupport(CGuiApplication::parseScaleFactor(argc, argv)); + CGuiApplication::highDpiScreenSupport(CGuiApplication::scaleFactor(argc, argv)); QApplication qa(argc, argv); // needed Q_UNUSED(qa) CGuiApplication a(CApplicationInfo::swiftLauncher(), CApplicationInfo::Laucher, CIcons::swiftLauncher1024()); diff --git a/src/swiftlauncher/swiftlauncher.cpp b/src/swiftlauncher/swiftlauncher.cpp index 1e0daba8b..627131b77 100644 --- a/src/swiftlauncher/swiftlauncher.cpp +++ b/src/swiftlauncher/swiftlauncher.cpp @@ -483,7 +483,7 @@ void CSwiftLauncher::startButtonPressed() { const QObject *sender = QObject::sender(); const qreal scaleFactor = ui->comp_Scale->getScaleFactor(); - CGuiApplication::highDpiScreenSupport(scaleFactor); + CGuiApplication::highDpiScreenSupport(QString::number(scaleFactor, 'f', 4)); const Qt::KeyboardModifiers km = QGuiApplication::queryKeyboardModifiers(); const bool shift = km.testFlag(Qt::ShiftModifier);