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