From b7b4e7ff01605727d51da9e4d1158a27d2920fa2 Mon Sep 17 00:00:00 2001 From: JiDe Zhang Date: Tue, 26 May 2026 18:37:11 +0800 Subject: [PATCH 1/2] ci: add GitHub Actions CI workflows for Arch Linux and Deepin builds Add CI workflows to verify the project builds correctly on both Arch Linux (latest) and Deepin crimson. This helps catch any platform-specific compilation issues introduced by the accessor pattern refactor. --- .../qt5integration-archlinux-build.yml | 73 +++++++++++ .../workflows/qt5integration-deepin-build.yml | 119 ++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 .github/workflows/qt5integration-archlinux-build.yml create mode 100644 .github/workflows/qt5integration-deepin-build.yml diff --git a/.github/workflows/qt5integration-archlinux-build.yml b/.github/workflows/qt5integration-archlinux-build.yml new file mode 100644 index 00000000..5f69e66d --- /dev/null +++ b/.github/workflows/qt5integration-archlinux-build.yml @@ -0,0 +1,73 @@ +# SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +# +# SPDX-License-Identifier: LGPL-3.0-or-later + +name: Build qt5integration on Arch Linux + +on: + push: + + pull_request: + +jobs: + container: + runs-on: ubuntu-latest + container: archlinux:latest + steps: + - name: Initialize pacman and system update + run: | + pacman-key --init + pacman --noconfirm --noprogressbar -Syu + + - name: Install base build tools + run: | + pacman -S --noconfirm --noprogressbar base-devel git cmake ninja pkgconf + + - name: Install qt5integration system dependencies + run: | + pacman -S --noconfirm --noprogressbar \ + qt5-base qt5-tools qt5-svg qt5-x11extras \ + qt5-wayland qt5-imageformats \ + extra-cmake-modules \ + gtest \ + libqtxdg xcb-util-renderutil \ + fontconfig libglvnd mtdev libxrender \ + icu uchardet dbus spdlog gsettings-qt \ + libxext libxi cups startup-notification systemd xcb-util \ + librsvg libraw kwayland \ + dtkcommon dtklog \ + treeland-protocols dtkcore dtkgui dtkwidget + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Configure and build qt5integration + run: | + cmake -B build \ + -GNinja \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_INSTALL_LIBDIR=lib \ + -DCMAKE_BUILD_TYPE=Release \ + -DDTK5=ON + cmake --build build -j$(nproc) + echo "✅ qt5integration built successfully!" + + - name: Install qt5integration to staging directory + run: | + DESTDIR=/tmp/qt5integration-install cmake --install build + echo "Total: $(find /tmp/qt5integration-install -type f | wc -l) files" + + - name: Create installation package + run: | + tar -czf /tmp/qt5integration-archlinux-$(date +%Y%m%d-%H%M%S).tar.gz -C /tmp/qt5integration-install . + ls -la /tmp/qt5integration-archlinux-*.tar.gz + + - name: Upload qt5integration Arch Linux build artifacts + if: ${{ !env.ACT }} + uses: actions/upload-artifact@v4 + with: + name: qt5integration-archlinux-build + path: "/tmp/qt5integration-archlinux-*.tar.gz" + if-no-files-found: error + retention-days: 30 diff --git a/.github/workflows/qt5integration-deepin-build.yml b/.github/workflows/qt5integration-deepin-build.yml new file mode 100644 index 00000000..d2c601a0 --- /dev/null +++ b/.github/workflows/qt5integration-deepin-build.yml @@ -0,0 +1,119 @@ +# SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +# +# SPDX-License-Identifier: LGPL-3.0-or-later + +name: Build qt5integration on Deepin crimson + +on: + push: + + pull_request: + +jobs: + container: + runs-on: ubuntu-latest + container: linuxdeepin/deepin:crimson + steps: + - uses: actions/checkout@v4 + + - name: Setup apt sources and build tools + run: | + set -euxo pipefail + echo "deb [trusted=yes] http://mirrors.kernel.org/deepin/beige/ crimson main commercial community" > /etc/apt/sources.list + echo "deb-src [trusted=yes] http://mirrors.kernel.org/deepin/beige/ crimson main commercial community" >> /etc/apt/sources.list + rm -rf /etc/apt/sources.list.d + apt-get update + apt-get install -y devscripts equivs git + + - name: Build and install dtkcommon from source + run: | + set -euxo pipefail + cd /tmp + git clone --depth=1 https://github.com/linuxdeepin/dtkcommon.git + cd dtkcommon + mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control + dpkg-buildpackage -uc -us -b + dpkg -i ../*.deb 2>/dev/null || apt-get install -f -y + echo "✅ dtkcommon installed" + + - name: Build and install dtklog from source + run: | + set -euxo pipefail + cd /tmp + git clone --depth=1 https://github.com/linuxdeepin/dtklog.git + cd dtklog + mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control + dpkg-buildpackage -uc -us -b -Pnodtk6 + dpkg -i ../*.deb 2>/dev/null || apt-get install -f -y + echo "✅ dtklog installed" + + - name: Build and install treeland-protocols from source + run: | + set -euxo pipefail + apt-get install -y --allow-unauthenticated wlr-protocols || true + cd /tmp + git clone --depth=1 https://github.com/linuxdeepin/treeland-protocols.git + cd treeland-protocols + mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control + dpkg-buildpackage -uc -us -b + dpkg -i ../*.deb 2>/dev/null || apt-get install -f -y + echo "✅ treeland-protocols installed" + + - name: Build and install dtkcore from source + run: | + set -euxo pipefail + cd /tmp + git clone --depth=1 https://github.com/linuxdeepin/dtkcore.git + cd dtkcore + mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control + dpkg-buildpackage -uc -us -b -Pnodtk6 + dpkg -i ../*.deb 2>/dev/null || apt-get install -f -y + echo "✅ dtkcore installed" + + - name: Build and install dtkgui from source + run: | + set -euxo pipefail + cd /tmp + git clone --depth=1 https://github.com/linuxdeepin/dtkgui.git + cd dtkgui + mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control + dpkg-buildpackage -uc -us -b -Pnodtk6 + dpkg -i ../*.deb 2>/dev/null || apt-get install -f -y + echo "✅ dtkgui installed" + + - name: Build and install dtkwidget from source + run: | + set -euxo pipefail + cd /tmp + git clone --depth=1 https://github.com/linuxdeepin/dtkwidget.git + cd dtkwidget + mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control + dpkg-buildpackage -uc -us -b -Pnodtk6 + dpkg -i ../*.deb 2>/dev/null || apt-get install -f -y + echo "✅ dtkwidget installed" + + - name: Install qt5integration build dependencies + run: | + set -euxo pipefail + mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control + + - name: Build qt5integration deb packages + run: | + set -euxo pipefail + dpkg-buildpackage -uc -us -b -Pnodtk6 + echo "✅ qt5integration deb packages built successfully!" + ls -la ../ + + - name: Collect deb artifacts + run: | + mkdir -p dist + mv ../*.deb dist/ + ls -la dist + + - name: Upload qt5integration deb packages as artifacts + uses: actions/upload-artifact@v4 + with: + name: qt5integration-deepin-deb-packages + path: dist/*.deb + if-no-files-found: error + retention-days: 30 From 3df2e8f852281803c4c3795924fd204033cade33 Mon Sep 17 00:00:00 2001 From: JiDe Zhang Date: Tue, 26 May 2026 18:37:11 +0800 Subject: [PATCH 2/2] refactor: replace private access hacks with accessor pattern Remove all '#define private/protected public' hacks and replace them with a proper C++ template-based private accessor pattern using explicit template instantiation (friend injection trick), mirroring the approach used in linuxdeepin/treeland#875. Adds platformthemeplugin/dprivateaccessor_p.h with Accessor/AccessorImpl templates and D_DECLARE_PRIVATE_MEMBER, D_PRIVATE_MEMBER macros. All helpers live at global scope so ADL correctly resolves the friend-injected get() function. --- platformthemeplugin/CMakeLists.txt | 3 +- platformthemeplugin/dprivateaccessor_p.h | 79 ++++++++++++++++++++++++ platformthemeplugin/qdeepintheme.cpp | 32 ++++++---- 3 files changed, 102 insertions(+), 12 deletions(-) create mode 100644 platformthemeplugin/dprivateaccessor_p.h diff --git a/platformthemeplugin/CMakeLists.txt b/platformthemeplugin/CMakeLists.txt index cb8bc62f..823a6154 100644 --- a/platformthemeplugin/CMakeLists.txt +++ b/platformthemeplugin/CMakeLists.txt @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +# SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. # # SPDX-License-Identifier: LGPL-3.0-or-later @@ -55,6 +55,7 @@ dtk_add_plugin( main.cpp ${DBUS_INTERFACES} HEADERS + dprivateaccessor_p.h dthemesettings.h qdeepinfiledialoghelper.h qdeepintheme.h diff --git a/platformthemeplugin/dprivateaccessor_p.h b/platformthemeplugin/dprivateaccessor_p.h new file mode 100644 index 00000000..367d325e --- /dev/null +++ b/platformthemeplugin/dprivateaccessor_p.h @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include + +// Private member accessor using the explicit template instantiation technique. +// +// C++ Standard [temp.explicit]/12 states: +// "The usual access checking rules do not apply to names used to +// specify explicit instantiation definitions." +// +// This allows passing pointers to private/protected data members and +// member functions as template arguments in explicit instantiations, +// bypassing normal access control — without modifying the class definition +// and without the UB caused by "#define private public". +// +// NOTE: The friend declaration must live inside the Tag struct so the friend +// function is findable via ADL when calling get(TagName{}). + +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wnon-template-friend") + +template +struct Qt5IntegrationPrivateAccessor +{ + using MemberPtr = typename Tag::MemberPtr; + friend MemberPtr get(Tag) noexcept; +}; + +template +struct Qt5IntegrationPrivateAccessorImpl : Qt5IntegrationPrivateAccessor +{ + friend typename Tag::MemberPtr get(Tag) noexcept { return Ptr; } +}; + +QT_WARNING_POP + +// Non-static data member access +// Usage: D_PRIVATE_MEMBER(obj, TagName{}) → gives the member value (copy/ref) +// Usage: &D_PRIVATE_MEMBER(obj, TagName{}) → gives address of member +#define D_DECLARE_PRIVATE_MEMBER(TagName, ClassName, MemberName, MemberType) \ + struct TagName { \ + using MemberPtr = MemberType ClassName::*; \ + friend MemberPtr get(TagName) noexcept; \ + }; \ + template struct Qt5IntegrationPrivateAccessorImpl + +// Trampoline: ensures get(tag) is called from a context with no class-scope +// get() member that might suppress ADL (C++ [basic.lookup.argdep] para 3). +namespace dtk_private_detail { + template + inline typename Tag::MemberPtr access(Tag t) noexcept { return get(t); } +} + +#define D_PRIVATE_MEMBER(obj, tag) ((obj).*dtk_private_detail::access(tag)) + +// Non-static member function call +// Usage: D_PRIVATE_CALL(obj, TagName{}, arg1, arg2) +#define D_DECLARE_PRIVATE_FUNCTION(TagName, ClassName, FuncName, RetType, ...) \ + struct TagName { \ + using MemberPtr = RetType (ClassName::*)(__VA_ARGS__); \ + friend MemberPtr get(TagName) noexcept; \ + }; \ + template struct Qt5IntegrationPrivateAccessorImpl + +#define D_PRIVATE_CALL(obj, tag, ...) ((obj).*dtk_private_detail::access(tag))(__VA_ARGS__) + +// Static data member access +// get(TagName{}) returns MemberType*, so D_PRIVATE_STATIC_MEMBER dereferences it +#define D_DECLARE_PRIVATE_STATIC_MEMBER(TagName, ClassName, MemberName, MemberType) \ + struct TagName { \ + using MemberPtr = MemberType*; \ + friend MemberPtr get(TagName) noexcept; \ + }; \ + template struct Qt5IntegrationPrivateAccessorImpl + +#define D_PRIVATE_STATIC_MEMBER(tag) (*dtk_private_detail::access(tag)) diff --git a/platformthemeplugin/qdeepintheme.cpp b/platformthemeplugin/qdeepintheme.cpp index 8044c46d..aed4af22 100644 --- a/platformthemeplugin/qdeepintheme.cpp +++ b/platformthemeplugin/qdeepintheme.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2017 - 2023 UnionTech Software Technology Co., Ltd. + * SPDX-FileCopyrightText: 2017 - 2026 UnionTech Software Technology Co., Ltd. * SPDX-License-Identifier: LGPL-3.0-or-later */ #include "qdeepintheme.h" @@ -18,21 +18,31 @@ #include #include -#define private public #include -#undef private #include #include #include #include #include #include +#include "dprivateaccessor_p.h" #undef signals #include DGUI_USE_NAMESPACE +#if QT_VERSION < QT_VERSION_CHECK(5,14,0) +using QHighDpiScaling_m_logicalDpi_type = QPair; +D_DECLARE_PRIVATE_STATIC_MEMBER(QHighDpiScaling_m_logicalDpi_tag, QHighDpiScaling, m_logicalDpi, QHighDpiScaling_m_logicalDpi_type); +#elif QT_VERSION < QT_VERSION_CHECK(6,0,0) +D_DECLARE_PRIVATE_STATIC_MEMBER(QHighDpiScaling_m_usePixelDensity_tag, QHighDpiScaling, m_usePixelDensity, bool); +#else +D_DECLARE_PRIVATE_STATIC_MEMBER(QHighDpiScaling_m_usePlatformPluginDpi_tag, QHighDpiScaling, m_usePlatformPluginDpi, bool); +#endif +D_DECLARE_PRIVATE_STATIC_MEMBER(QHighDpiScaling_m_factor_tag, QHighDpiScaling, m_factor, qreal); +D_DECLARE_PRIVATE_STATIC_MEMBER(QHighDpiScaling_m_screenFactorSet_tag, QHighDpiScaling, m_screenFactorSet, bool); + #ifdef XDG_ICON_VERSION_MAR #include extern void updateXdgIconSystemTheme(); @@ -275,7 +285,7 @@ static bool updateScaleFactor(qreal value) value = 1.0; } - if (qFuzzyCompare(QHighDpiScaling::m_factor, value)) { + if (qFuzzyCompare(D_PRIVATE_STATIC_MEMBER(QHighDpiScaling_m_factor_tag{}), value)) { return false; } @@ -386,20 +396,20 @@ static bool updateScaleLogcailDpi(const QPair &dpi) bool ok = dpi.first >= 0 && dpi.second >= 0; #if QT_VERSION < QT_VERSION_CHECK(5,14,0) if (dpi.first > 0) { - QHighDpiScaling::m_logicalDpi.first = dpi.first; + D_PRIVATE_STATIC_MEMBER(QHighDpiScaling_m_logicalDpi_tag{}).first = dpi.first; } else if (qIsNull(dpi.first)) { - QHighDpiScaling::m_logicalDpi.first = qGuiApp->primaryScreen()->handle()->logicalDpi().first; + D_PRIVATE_STATIC_MEMBER(QHighDpiScaling_m_logicalDpi_tag{}).first = qGuiApp->primaryScreen()->handle()->logicalDpi().first; } if (dpi.second > 0) { - QHighDpiScaling::m_logicalDpi.second = dpi.second; + D_PRIVATE_STATIC_MEMBER(QHighDpiScaling_m_logicalDpi_tag{}).second = dpi.second; } else if (qIsNull(dpi.second)) { - QHighDpiScaling::m_logicalDpi.second = qGuiApp->primaryScreen()->handle()->logicalDpi().second; + D_PRIVATE_STATIC_MEMBER(QHighDpiScaling_m_logicalDpi_tag{}).second = qGuiApp->primaryScreen()->handle()->logicalDpi().second; } #elif QT_VERSION < QT_VERSION_CHECK(6,0,0) - QHighDpiScaling::m_usePixelDensity = false; // Do not use dpi from platform plugin + D_PRIVATE_STATIC_MEMBER(QHighDpiScaling_m_usePixelDensity_tag{}) = false; // Do not use dpi from platform plugin #else - QHighDpiScaling::m_usePlatformPluginDpi = false; // Do not use dpi from platform plugin + D_PRIVATE_STATIC_MEMBER(QHighDpiScaling_m_usePlatformPluginDpi_tag{}) = false; // Do not use dpi from platform plugin #endif return ok; } @@ -753,7 +763,7 @@ static void compelledUpdateScaleLogcailDpi() { } static void onScreenAdded(QScreen *s) { - if (QHighDpiScaling::m_screenFactorSet) { + if (D_PRIVATE_STATIC_MEMBER(QHighDpiScaling_m_screenFactorSet_tag{})) { auto setting = QDeepinTheme::getSettings(); auto value = setting->screenScaleFactors();