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); }