From e3adb917a642e5b9b6809dda962ef70fcae0a3b3 Mon Sep 17 00:00:00 2001 From: Yuhang Zhao <2546789017@qq.com> Date: Thu, 6 Jan 2022 11:18:33 +0800 Subject: [PATCH] Fix build on Qt before 5.14 1. The QWinRegistryKey class was introduced in 5.14.0. The implementation is quite clean, so just copy the sources files from Qt repo in case the user is linking against a version older than 5.14. 2. Fixes the rounding error of the self-painted frame border of the examples. 3. Other minor tweaks. Fixes: #95 Task-number: #94 Signed-off-by: Yuhang Zhao <2546789017@qq.com> --- CMakeLists.txt | 12 +-- examples/common.pri | 2 +- examples/mainwindow/main.cpp | 4 +- examples/mainwindow/mainwindow.cpp | 7 +- examples/quick/main.cpp | 4 +- examples/widget/main.cpp | 4 +- examples/widget/widget.cpp | 6 +- lib.pro | 10 ++- qwinregistry.cpp | 122 +++++++++++++++++++++++++++++ qwinregistry_p.h | 92 ++++++++++++++++++++++ utilities_win32.cpp | 16 ++-- 11 files changed, 249 insertions(+), 30 deletions(-) create mode 100644 qwinregistry.cpp create mode 100644 qwinregistry_p.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d346455..5832689 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,16 +42,16 @@ endif() if(WIN32) list(APPEND SOURCES framelesshelper_windows.h + qwinregistry_p.h + qwinregistry.cpp utilities_win32.cpp framelesshelper_win32.h framelesshelper_win32.cpp ) -else() - if(MACOS) - list(APPEND SOURCES utilities_macos.mm) - else() - list(APPEND SOURCES utilities_linux.cpp) - endif() +elseif(APPLE) + list(APPEND SOURCES utilities_macos.mm) +elseif(UNIX) + list(APPEND SOURCES utilities_linux.cpp) endif() if(WIN32 AND BUILD_SHARED_LIBS) diff --git a/examples/common.pri b/examples/common.pri index 9cb63f4..8d0a609 100644 --- a/examples/common.pri +++ b/examples/common.pri @@ -17,6 +17,6 @@ win32 { win32 { CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../debug -lFramelessHelperd else: CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../release -lFramelessHelper -} else: unix { +} else { LIBS += -L$$OUT_PWD/../../bin -lFramelessHelper } diff --git a/examples/mainwindow/main.cpp b/examples/mainwindow/main.cpp index 68c5421..24c9fe4 100644 --- a/examples/mainwindow/main.cpp +++ b/examples/mainwindow/main.cpp @@ -31,9 +31,9 @@ int main(int argc, char *argv[]) #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); #endif +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round); #endif QApplication application(argc, argv); diff --git a/examples/mainwindow/mainwindow.cpp b/examples/mainwindow/mainwindow.cpp index 53c6298..493a8c4 100644 --- a/examples/mainwindow/mainwindow.cpp +++ b/examples/mainwindow/mainwindow.cpp @@ -52,14 +52,14 @@ MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) : QMainWindow(par connect(this, &MainWindow::windowTitleChanged, titleBarWidget->titleLabel, &QLabel::setText); connect(titleBarWidget->closeButton, &QPushButton::clicked, this, &MainWindow::close); connect(titleBarWidget->minimizeButton, &QPushButton::clicked, this, &MainWindow::showMinimized); - connect(titleBarWidget->maximizeButton, &QPushButton::clicked, [this](){ + connect(titleBarWidget->maximizeButton, &QPushButton::clicked, this, [this](){ if (isMaximized() || isFullScreen()) { showNormal(); } else { showMaximized(); } }); - connect(this, &MainWindow::windowStateChanged, [this](){ + connect(this, &MainWindow::windowStateChanged, this, [this](){ titleBarWidget->maximizeButton->setChecked(isMaximized()); titleBarWidget->maximizeButton->setToolTip(isMaximized() ? tr("Restore") : tr("Maximize")); }); @@ -122,7 +122,6 @@ void MainWindow::paintEvent(QPaintEvent *event) { QMainWindow::paintEvent(event); if (windowState() == Qt::WindowNoState) { - QPainter painter(this); const int w = width(); const int h = height(); #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) @@ -136,7 +135,9 @@ void MainWindow::paintEvent(QPaintEvent *event) {w, h - 1, 0, h - 1}, {0, h, 0, 0} }; + QPainter painter(this); painter.save(); + painter.setRenderHint(QPainter::Antialiasing, false); const ColorizationArea area = Utilities::getColorizationArea(); const bool colorizedBorder = ((area == ColorizationArea::TitleBar_WindowBorder) || (area == ColorizationArea::All)); diff --git a/examples/quick/main.cpp b/examples/quick/main.cpp index e941fe7..4cf6fd4 100644 --- a/examples/quick/main.cpp +++ b/examples/quick/main.cpp @@ -33,9 +33,9 @@ int main(int argc, char *argv[]) #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); #endif +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round); #endif QGuiApplication application(argc, argv); diff --git a/examples/widget/main.cpp b/examples/widget/main.cpp index e1bf118..15e652d 100644 --- a/examples/widget/main.cpp +++ b/examples/widget/main.cpp @@ -31,9 +31,9 @@ int main(int argc, char *argv[]) #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); #endif +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round); #endif QApplication application(argc, argv); diff --git a/examples/widget/widget.cpp b/examples/widget/widget.cpp index bd6cb83..78df163 100644 --- a/examples/widget/widget.cpp +++ b/examples/widget/widget.cpp @@ -137,9 +137,6 @@ void Widget::paintEvent(QPaintEvent *event) { QWidget::paintEvent(event); if (!isMaximized() && !isFullScreen()) { - QPainter painter(this); - painter.save(); - painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); const auto w = static_cast(width()); const auto h = static_cast(height()); #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) @@ -158,6 +155,9 @@ void Widget::paintEvent(QPaintEvent *event) || (area == ColorizationArea::All)); const QColor borderColor = (isActiveWindow() ? (colorizedBorder ? Utilities::getColorizationColor() : Qt::black) : Qt::darkGray); const auto borderThickness = static_cast(Utilities::getWindowVisibleFrameBorderThickness(winId())); + QPainter painter(this); + painter.save(); + painter.setRenderHint(QPainter::Antialiasing, false); painter.setPen({borderColor, borderThickness}); painter.drawLines(lines); painter.restore(); diff --git a/lib.pro b/lib.pro index 3503849..745b69d 100644 --- a/lib.pro +++ b/lib.pro @@ -1,7 +1,7 @@ TARGET = $$qtLibraryTarget(FramelessHelper) TEMPLATE = lib win32: DLLDESTDIR = $$OUT_PWD/bin -else: unix: DESTDIR = $$OUT_PWD/bin +else: DESTDIR = $$OUT_PWD/bin QT += core-private gui-private CONFIG += c++17 strict_c++ utf8_source warn_on DEFINES += \ @@ -28,10 +28,14 @@ qtHaveModule(quick) { win32 { HEADERS += \ framelesshelper_windows.h \ - framelesshelper_win32.h + framelesshelper_win32.h \ + qwinregistry_p.h SOURCES += \ utilities_win32.cpp \ - framelesshelper_win32.cpp + framelesshelper_win32.cpp \ + qwinregistry.cpp LIBS += -luser32 -lshell32 RC_FILE = framelesshelper.rc } +linux*: SOURCES += utilities_linux.cpp +macx: SOURCES += utilities_macos.mm diff --git a/qwinregistry.cpp b/qwinregistry.cpp new file mode 100644 index 0000000..cc3c0c9 --- /dev/null +++ b/qwinregistry.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwinregistry_p.h" + +#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) + +#include + +#include + +QT_BEGIN_NAMESPACE + +QWinRegistryKey::QWinRegistryKey() : + m_key(nullptr) +{ +} + +// Open a key with the specified permissions (KEY_READ/KEY_WRITE). +// "access" is to explicitly use the 32- or 64-bit branch. +QWinRegistryKey::QWinRegistryKey(HKEY parentHandle, QStringView subKey, + REGSAM permissions, REGSAM access) +{ + if (RegOpenKeyEx(parentHandle, reinterpret_cast(subKey.utf16()), + 0, permissions | access, &m_key) != ERROR_SUCCESS) { + m_key = nullptr; + } +} + +QWinRegistryKey::~QWinRegistryKey() +{ + close(); +} + +void QWinRegistryKey::close() +{ + if (isValid()) { + RegCloseKey(m_key); + m_key = nullptr; + } +} + +QString QWinRegistryKey::stringValue(QStringView subKey) const +{ + QString result; + if (!isValid()) + return result; + DWORD type; + DWORD size; + auto subKeyC = reinterpret_cast(subKey.utf16()); + if (RegQueryValueEx(m_key, subKeyC, nullptr, &type, nullptr, &size) != ERROR_SUCCESS + || (type != REG_SZ && type != REG_EXPAND_SZ) || size <= 2) { + return result; + } + // Reserve more for rare cases where trailing '\0' are missing in registry. + // Rely on 0-termination since strings of size 256 padded with 0 have been + // observed (QTBUG-84455). + size += 2; + QVarLengthArray buffer(static_cast(size)); + std::fill(buffer.data(), buffer.data() + size, 0u); + if (RegQueryValueEx(m_key, subKeyC, nullptr, &type, buffer.data(), &size) == ERROR_SUCCESS) + result = QString::fromWCharArray(reinterpret_cast(buffer.constData())); + return result; +} + +QPair QWinRegistryKey::dwordValue(QStringView subKey) const +{ + if (!isValid()) + return qMakePair(0, false); + DWORD type; + auto subKeyC = reinterpret_cast(subKey.utf16()); + if (RegQueryValueEx(m_key, subKeyC, nullptr, &type, nullptr, nullptr) != ERROR_SUCCESS + || type != REG_DWORD) { + return qMakePair(0, false); + } + DWORD value = 0; + DWORD size = sizeof(value); + const bool ok = + RegQueryValueEx(m_key, subKeyC, nullptr, nullptr, + reinterpret_cast(&value), &size) == ERROR_SUCCESS; + return qMakePair(value, ok); +} + +QT_END_NAMESPACE + +#endif // QT_VERSION < QT_VERSION_CHECK(5, 14, 0) diff --git a/qwinregistry_p.h b/qwinregistry_p.h new file mode 100644 index 0000000..2201a58 --- /dev/null +++ b/qwinregistry_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) +#include +#else // QT_VERSION < QT_VERSION_CHECK(5, 14, 0) +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QWinRegistryKey +{ +public: + Q_DISABLE_COPY(QWinRegistryKey) + + QWinRegistryKey(); + explicit QWinRegistryKey(HKEY parentHandle, QStringView subKey, + REGSAM permissions = KEY_READ, REGSAM access = 0); + ~QWinRegistryKey(); + + QWinRegistryKey(QWinRegistryKey &&other) noexcept + : m_key(qExchange(other.m_key, nullptr)) {} + QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QWinRegistryKey) + void swap(QWinRegistryKey &other) noexcept { qSwap(m_key, other.m_key); } + + bool isValid() const { return m_key != nullptr; } + operator HKEY() const { return m_key; } + void close(); + + QString stringValue(QStringView subKey) const; + QPair dwordValue(QStringView subKey) const; + +private: + HKEY m_key; +}; + +QT_END_NAMESPACE + +#endif // QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) diff --git a/utilities_win32.cpp b/utilities_win32.cpp index f38bc25..e5f6f9f 100644 --- a/utilities_win32.cpp +++ b/utilities_win32.cpp @@ -24,7 +24,6 @@ #include "utilities.h" #include -#include #include #if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) #include @@ -38,6 +37,7 @@ #else #include #endif +#include "qwinregistry_p.h" #include "framelesshelper_windows.h" Q_DECLARE_METATYPE(QMargins) @@ -319,22 +319,22 @@ QString Utilities::getSystemErrorMessage(const QString &function) QColor Utilities::getColorizationColor() { + const auto resultFromRegistry = []() -> QColor { + QWinRegistryKey registry(HKEY_CURRENT_USER, QString::fromUtf8(kDwmRegistryKey)); + const auto result = registry.dwordValue(QStringLiteral("ColorizationColor")); + return (result.second ? QColor::fromRgba(result.first) : Qt::darkGray); + }; static const auto pDwmGetColorizationColor = reinterpret_cast(QSystemLibrary::resolve(QStringLiteral("dwmapi"), "DwmGetColorizationColor")); if (!pDwmGetColorizationColor) { - return Qt::darkGray; + return resultFromRegistry(); } DWORD color = 0; BOOL opaque = FALSE; const HRESULT hr = pDwmGetColorizationColor(&color, &opaque); if (FAILED(hr)) { qWarning() << __getSystemErrorMessage(QStringLiteral("DwmGetColorizationColor"), hr); - QWinRegistryKey registry(HKEY_CURRENT_USER, QString::fromUtf8(kDwmRegistryKey)); - const auto result = registry.dwordValue(QStringLiteral("ColorizationColor")); - if (!result.second) { - return Qt::darkGray; - } - return QColor::fromRgba(result.first); + return resultFromRegistry(); } return QColor::fromRgba(color); }