From 801e9981a883499b3138fcab86da1d46619afef8 Mon Sep 17 00:00:00 2001 From: luhongxu Date: Mon, 12 Oct 2026 18:32:49 +0800 Subject: [PATCH 1/3] feat: migrate showdesktop and multitaskview to compositor-action protocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Rewrite showdesktop wrapper to use treeland-compositor-action-unstable-v1 2. Rewrite multitaskview wrapper to use treeland-compositor-action-unstable-v1 3. Remove old state tracking logic from showdesktop wrapper 4. Remove two-step worker pattern from multitaskview wrapper 5. Update CMakeLists.txt to use new protocol XML and treeland-protocols 0.7.0 6. Update debian/control build dependency to treeland-protocols (>= 0.7.0) Log: Migrated show desktop and multitask view to compositor action protocol Influence: 1. Test show desktop toggle on Wayland session 2. Test multitask view toggle on Wayland session 3. Verify X11 fallback paths remain unchanged 4. Verify build with treeland-protocols 0.7.0 installed feat: 迁移显示桌面和多任务视图至 compositor-action 协议 1. 重写 showdesktop 包装类,使用 treeland-compositor-action-unstable-v1 协议 2. 重写 multitaskview 包装类,使用 treeland-compositor-action-unstable-v1 协议 3. 移除 showdesktop 包装类中旧的状态跟踪逻辑 4. 移除 multitaskview 包装类中的两步式 worker 模式 5. 更新 CMakeLists.txt 使用新协议 XML 和 treeland-protocols 0.7.0 6. 更新 debian/control 构建依赖至 treeland-protocols (>= 0.7.0) Log: 迁移显示桌面和多任务视图功能至 compositor action 协议 Influence: 1. 在 Wayland 会话下测试显示桌面切换功能 2. 在 Wayland 会话下测试多任务视图切换功能 3. 验证 X11 回退路径保持不变 4. 验证安装 treeland-protocols 0.7.0 后可正常编译 --- applets/dde-shutdown/CMakeLists.txt | 7 ++- applets/dde-shutdown/treelandlockscreen.cpp | 43 ++++++++----------- applets/dde-shutdown/treelandlockscreen.h | 22 ++-------- debian/control | 2 +- panels/dock/CMakeLists.txt | 1 - panels/dock/multitaskview/CMakeLists.txt | 9 ++-- panels/dock/multitaskview/multitaskview.cpp | 6 +-- panels/dock/multitaskview/multitaskview.h | 4 +- .../treelandcompositoraction.cpp | 29 +++++++++++++ .../multitaskview/treelandcompositoraction.h | 20 +++++++++ .../multitaskview/treelandmultitaskview.cpp | 31 ------------- .../multitaskview/treelandmultitaskview.h | 33 -------------- panels/dock/showdesktop/CMakeLists.txt | 8 ++-- panels/dock/showdesktop/showdesktop.cpp | 10 ++--- panels/dock/showdesktop/showdesktop.h | 4 +- .../showdesktop/treelandcompositoraction.cpp | 29 +++++++++++++ .../showdesktop/treelandcompositoraction.h | 21 +++++++++ .../dock/showdesktop/treelandshowdesktop.cpp | 30 ------------- panels/dock/showdesktop/treelandshowdesktop.h | 27 ------------ panels/dock/waylanddockhelper.cpp | 8 ---- panels/dock/waylanddockhelper.h | 11 ----- 21 files changed, 145 insertions(+), 210 deletions(-) create mode 100644 panels/dock/multitaskview/treelandcompositoraction.cpp create mode 100644 panels/dock/multitaskview/treelandcompositoraction.h delete mode 100644 panels/dock/multitaskview/treelandmultitaskview.cpp delete mode 100644 panels/dock/multitaskview/treelandmultitaskview.h create mode 100644 panels/dock/showdesktop/treelandcompositoraction.cpp create mode 100644 panels/dock/showdesktop/treelandcompositoraction.h delete mode 100644 panels/dock/showdesktop/treelandshowdesktop.cpp delete mode 100644 panels/dock/showdesktop/treelandshowdesktop.h diff --git a/applets/dde-shutdown/CMakeLists.txt b/applets/dde-shutdown/CMakeLists.txt index e0c327445..18fb906e5 100644 --- a/applets/dde-shutdown/CMakeLists.txt +++ b/applets/dde-shutdown/CMakeLists.txt @@ -1,7 +1,7 @@ -# SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +# SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. # # SPDX-License-Identifier: GPL-3.0-or-later -find_package(TreelandProtocols REQUIRED) +find_package(TreelandProtocols 0.7.0 REQUIRED) add_library(dde-shutdown SHARED shutdownapplet.cpp @@ -11,9 +11,8 @@ add_library(dde-shutdown SHARED ) qt_generate_wayland_protocol_client_sources(dde-shutdown - NO_INCLUDE_CORE_ONLY FILES - ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-dde-shell-v1.xml + ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-compositor-action-unstable-v1.xml ) target_link_libraries(dde-shutdown PRIVATE diff --git a/applets/dde-shutdown/treelandlockscreen.cpp b/applets/dde-shutdown/treelandlockscreen.cpp index 35ac41293..77776be63 100644 --- a/applets/dde-shutdown/treelandlockscreen.cpp +++ b/applets/dde-shutdown/treelandlockscreen.cpp @@ -1,51 +1,44 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "treelandlockscreen.h" +#include "wayland-treeland-compositor-action-unstable-v1-client-protocol.h" DS_BEGIN_NAMESPACE namespace shutdown { TreeLandLockScreen::TreeLandLockScreen() - : QWaylandClientExtensionTemplate(treeland_dde_shell_manager_v1_interface.version) + : QWaylandClientExtensionTemplate(treeland_compositor_action_v1_interface.version) { } -void TreeLandLockScreen::lock() +TreeLandLockScreen::~TreeLandLockScreen() { - if (auto impl = workder()) - impl->lock(); -} - -void TreeLandLockScreen::shutdown() -{ - if (auto impl = workder()) - impl->shutdown(); + if (isActive()) { + destroy(); + } } -void TreeLandLockScreen::switchUser() +void TreeLandLockScreen::lock() { - if (auto impl = workder()) - impl->switch_user(); + if (isActive()) { + trigger(action_lockscreen); + } } -TreeLandLockScreenWorker *TreeLandLockScreen::workder() +void TreeLandLockScreen::shutdown() { - if (!isActive()) { - return nullptr; + if (isActive()) { + trigger(action_shutdown_menu); } - - if (m_worker.isNull()) { - m_worker.reset(new TreeLandLockScreenWorker(get_treeland_lockscreen())); - } - return m_worker.get(); } -TreeLandLockScreenWorker::TreeLandLockScreenWorker(struct ::treeland_lockscreen_v1 *object) - : QWaylandClientExtensionTemplate(treeland_lockscreen_v1_interface.version) - , QtWayland::treeland_lockscreen_v1(object) +void TreeLandLockScreen::switchUser() { + if (isActive()) { + trigger(action_show_user_switch); + } } } DS_END_NAMESPACE diff --git a/applets/dde-shutdown/treelandlockscreen.h b/applets/dde-shutdown/treelandlockscreen.h index 452913166..1cd74cf35 100644 --- a/applets/dde-shutdown/treelandlockscreen.h +++ b/applets/dde-shutdown/treelandlockscreen.h @@ -1,41 +1,27 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #pragma once #include "dsglobal.h" -#include "qwayland-treeland-dde-shell-v1.h" +#include "qwayland-treeland-compositor-action-unstable-v1.h" #include DS_BEGIN_NAMESPACE namespace shutdown { -class TreeLandLockScreenWorker; -class TreeLandLockScreen : public QWaylandClientExtensionTemplate, public QtWayland::treeland_dde_shell_manager_v1 +class TreeLandLockScreen : public QWaylandClientExtensionTemplate, public QtWayland::treeland_compositor_action_v1 { Q_OBJECT public: explicit TreeLandLockScreen(); + ~TreeLandLockScreen(); void lock(); void shutdown(); void switchUser(); - -private: - TreeLandLockScreenWorker *workder(); - -private: - QScopedPointer m_worker; -}; - -class TreeLandLockScreenWorker : public QWaylandClientExtensionTemplate, public QtWayland::treeland_lockscreen_v1 -{ - Q_OBJECT - -public: - explicit TreeLandLockScreenWorker(struct ::treeland_lockscreen_v1 *object); }; } DS_END_NAMESPACE diff --git a/debian/control b/debian/control index 27a16a495..be6dfa2e3 100644 --- a/debian/control +++ b/debian/control @@ -40,7 +40,7 @@ Build-Depends: qt6-wayland-dev-tools, qt6-wayland-private-dev, systemd, - treeland-protocols (>= 0.6.0), + treeland-protocols (>= 0.7.0), wayland-protocols, Standards-Version: 4.6.2 Homepage: https://github.com/linuxdeepin/dde-shell diff --git a/panels/dock/CMakeLists.txt b/panels/dock/CMakeLists.txt index 58deda7db..70ba6f2b1 100644 --- a/panels/dock/CMakeLists.txt +++ b/panels/dock/CMakeLists.txt @@ -61,7 +61,6 @@ qt_generate_wayland_protocol_client_sources(dockpanel NO_INCLUDE_CORE_ONLY FILES ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-wallpaper-color-v1.xml - ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-dde-shell-v1.xml ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-xwindow-control-unstable-v1.xml ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-region-watch-unstable-v1.xml ) diff --git a/panels/dock/multitaskview/CMakeLists.txt b/panels/dock/multitaskview/CMakeLists.txt index 158cf4810..d742320e7 100644 --- a/panels/dock/multitaskview/CMakeLists.txt +++ b/panels/dock/multitaskview/CMakeLists.txt @@ -4,14 +4,14 @@ find_package(PkgConfig REQUIRED) find_package(Qt${QT_VERSION_MAJOR} ${REQUIRED_QT_VERSION} COMPONENTS WaylandClient) -find_package(TreelandProtocols REQUIRED) +find_package(TreelandProtocols 0.7.0 REQUIRED) pkg_check_modules(WaylandClient REQUIRED IMPORTED_TARGET wayland-client) add_library(dock-multitaskview SHARED multitaskview.cpp multitaskview.h - treelandmultitaskview.cpp - treelandmultitaskview.h + treelandcompositoraction.cpp + treelandcompositoraction.h ) target_include_directories(dock-multitaskview PRIVATE @@ -20,9 +20,8 @@ target_include_directories(dock-multitaskview PRIVATE ) qt_generate_wayland_protocol_client_sources(dock-multitaskview - NO_INCLUDE_CORE_ONLY FILES - ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-dde-shell-v1.xml + ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-compositor-action-unstable-v1.xml ) target_link_libraries(dock-multitaskview PRIVATE diff --git a/panels/dock/multitaskview/multitaskview.cpp b/panels/dock/multitaskview/multitaskview.cpp index 750f93a5b..66517ce4e 100644 --- a/panels/dock/multitaskview/multitaskview.cpp +++ b/panels/dock/multitaskview/multitaskview.cpp @@ -41,7 +41,7 @@ MultiTaskView::MultiTaskView(QObject *parent) }); auto platformName = QGuiApplication::platformName(); if (QStringLiteral("wayland") == platformName) { - m_multitaskview.reset(new TreeLandMultitaskview); + m_compositorAction.reset(new TreelandCompositorAction); } else { m_kWinCompositingConfig = DConfig::create("org.kde.kwin", "org.kde.kwin.compositing", QString(), this); m_kWinEffect = m_kWinCompositingConfig->value(windowEffectTypeKey).toInt() != KWinOptimalPerformance; @@ -87,8 +87,8 @@ void MultiTaskView::openWorkspace() {QStringLiteral("kwin_version"), m_kwinVersion} })); #endif - if (m_multitaskview) { - m_multitaskview->toggle(); + if (m_compositorAction) { + m_compositorAction->toggle(); return; } diff --git a/panels/dock/multitaskview/multitaskview.h b/panels/dock/multitaskview/multitaskview.h index 14ab24e0d..2403d3023 100644 --- a/panels/dock/multitaskview/multitaskview.h +++ b/panels/dock/multitaskview/multitaskview.h @@ -8,7 +8,7 @@ #include "../frame/dockiteminfo.h" #include "applet.h" #include "dsglobal.h" -#include "treelandmultitaskview.h" +#include "treelandcompositoraction.h" #include @@ -38,7 +38,7 @@ class MultiTaskView : public DS_NAMESPACE::DAppletDock bool m_kWinEffect = true; QString m_kwinVersion; QString m_iconName; - QScopedPointer m_multitaskview; + QScopedPointer m_compositorAction; Dtk::Core::DConfig *m_kWinCompositingConfig = nullptr; }; diff --git a/panels/dock/multitaskview/treelandcompositoraction.cpp b/panels/dock/multitaskview/treelandcompositoraction.cpp new file mode 100644 index 000000000..091dd8a8c --- /dev/null +++ b/panels/dock/multitaskview/treelandcompositoraction.cpp @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "treelandcompositoraction.h" +#include "wayland-treeland-compositor-action-unstable-v1-client-protocol.h" + +namespace dock +{ +TreelandCompositorAction::TreelandCompositorAction(QObject *parent) + : QWaylandClientExtensionTemplate(treeland_compositor_action_v1_interface.version) +{ + setParent(parent); +} + +TreelandCompositorAction::~TreelandCompositorAction() +{ + if (isActive()) { + destroy(); + } +} + +void TreelandCompositorAction::toggle() +{ + if (isActive()) { + trigger(action_toggle_multitask_view); + } +} +} diff --git a/panels/dock/multitaskview/treelandcompositoraction.h b/panels/dock/multitaskview/treelandcompositoraction.h new file mode 100644 index 000000000..b2b4ed6ea --- /dev/null +++ b/panels/dock/multitaskview/treelandcompositoraction.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "qwayland-treeland-compositor-action-unstable-v1.h" +#include + +namespace dock +{ + +class TreelandCompositorAction : public QWaylandClientExtensionTemplate, public QtWayland::treeland_compositor_action_v1 +{ +public: + explicit TreelandCompositorAction(QObject *parent = nullptr); + ~TreelandCompositorAction(); + void toggle(); +}; +} diff --git a/panels/dock/multitaskview/treelandmultitaskview.cpp b/panels/dock/multitaskview/treelandmultitaskview.cpp deleted file mode 100644 index 371438ed6..000000000 --- a/panels/dock/multitaskview/treelandmultitaskview.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "treelandmultitaskview.h" - -namespace dock -{ -TreeLandMultitaskview::TreeLandMultitaskview() - : QWaylandClientExtensionTemplate(treeland_dde_shell_manager_v1_interface.version) -{ -} - -void TreeLandMultitaskview::toggle() -{ - if (!isActive()) { - return; - } - - if (m_worker.isNull()) { - m_worker.reset(new TreeLandMultitaskviewWorker(get_treeland_multitaskview())); - } - m_worker->toggle(); -} - -TreeLandMultitaskviewWorker::TreeLandMultitaskviewWorker(struct ::treeland_multitaskview_v1 *object) - : QWaylandClientExtensionTemplate(treeland_multitaskview_v1_interface.version) - , QtWayland::treeland_multitaskview_v1(object) -{ -} -} diff --git a/panels/dock/multitaskview/treelandmultitaskview.h b/panels/dock/multitaskview/treelandmultitaskview.h deleted file mode 100644 index 4a3e2d85a..000000000 --- a/panels/dock/multitaskview/treelandmultitaskview.h +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "qwayland-treeland-dde-shell-v1.h" - -#include - -namespace dock -{ -class TreeLandMultitaskviewWorker; -class TreeLandMultitaskview : public QWaylandClientExtensionTemplate, public QtWayland::treeland_dde_shell_manager_v1 -{ - Q_OBJECT - -public: - explicit TreeLandMultitaskview(); - void toggle(); - -private: - QScopedPointer m_worker; -}; - -class TreeLandMultitaskviewWorker : public QWaylandClientExtensionTemplate, public QtWayland::treeland_multitaskview_v1 -{ - Q_OBJECT - -public: - explicit TreeLandMultitaskviewWorker(struct ::treeland_multitaskview_v1 *object); -}; -} diff --git a/panels/dock/showdesktop/CMakeLists.txt b/panels/dock/showdesktop/CMakeLists.txt index 2b8f400c5..ee89c4556 100644 --- a/panels/dock/showdesktop/CMakeLists.txt +++ b/panels/dock/showdesktop/CMakeLists.txt @@ -4,19 +4,19 @@ find_package(PkgConfig REQUIRED) find_package(Qt${QT_VERSION_MAJOR} ${REQUIRED_QT_VERSION} COMPONENTS WaylandClient) -find_package(TreelandProtocols 0.6.0 REQUIRED) +find_package(TreelandProtocols 0.7.0 REQUIRED) pkg_check_modules(WaylandClient REQUIRED IMPORTED_TARGET wayland-client) add_library(dock-showdesktop SHARED showdesktop.cpp showdesktop.h - treelandshowdesktop.cpp - treelandshowdesktop.h + treelandcompositoraction.cpp + treelandcompositoraction.h ) qt_generate_wayland_protocol_client_sources(dock-showdesktop FILES - ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-show-desktop-unstable-v1.xml + ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-compositor-action-unstable-v1.xml ) target_link_libraries(dock-showdesktop PRIVATE diff --git a/panels/dock/showdesktop/showdesktop.cpp b/panels/dock/showdesktop/showdesktop.cpp index 409a7d5dc..f273e1212 100644 --- a/panels/dock/showdesktop/showdesktop.cpp +++ b/panels/dock/showdesktop/showdesktop.cpp @@ -5,7 +5,7 @@ #include "showdesktop.h" #include "applet.h" #include "pluginfactory.h" -#include "treelandshowdesktop.h" +#include "treelandcompositoraction.h" #include #include @@ -22,7 +22,7 @@ namespace dock { ShowDesktop::ShowDesktop(QObject *parent) : DApplet(parent) - , m_showDesktop(nullptr) + , m_compositorAction(nullptr) , m_dockConfig(nullptr) , m_visible(true) { @@ -47,7 +47,7 @@ bool ShowDesktop::load() bool ShowDesktop::init() { if (QStringLiteral("wayland") == QGuiApplication::platformName()) { - m_showDesktop = new TreelandShowDesktop(this); + m_compositorAction = new TreelandCompositorAction(this); } // 从配置中读取初始的可见性状态 @@ -61,8 +61,8 @@ bool ShowDesktop::init() void ShowDesktop::toggleShowDesktop() { - if (m_showDesktop) { - m_showDesktop->desktopToggle(); + if (m_compositorAction) { + m_compositorAction->desktopToggle(); return; } diff --git a/panels/dock/showdesktop/showdesktop.h b/panels/dock/showdesktop/showdesktop.h index da2d00e46..30c0c0303 100644 --- a/panels/dock/showdesktop/showdesktop.h +++ b/panels/dock/showdesktop/showdesktop.h @@ -6,7 +6,7 @@ #include "applet.h" #include "dsglobal.h" -#include "treelandshowdesktop.h" +#include "treelandcompositoraction.h" #include @@ -35,7 +35,7 @@ private slots: void onEnableShowDesktopChanged(); private: - TreelandShowDesktop *m_showDesktop; + TreelandCompositorAction *m_compositorAction; Dtk::Core::DConfig *m_dockConfig; bool m_visible; }; diff --git a/panels/dock/showdesktop/treelandcompositoraction.cpp b/panels/dock/showdesktop/treelandcompositoraction.cpp new file mode 100644 index 000000000..d454ece09 --- /dev/null +++ b/panels/dock/showdesktop/treelandcompositoraction.cpp @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "treelandcompositoraction.h" +#include "wayland-treeland-compositor-action-unstable-v1-client-protocol.h" + +namespace dock +{ +TreelandCompositorAction::TreelandCompositorAction(QObject *parent) + : QWaylandClientExtensionTemplate(treeland_compositor_action_v1_interface.version) +{ + setParent(parent); +} + +TreelandCompositorAction::~TreelandCompositorAction() +{ + if (isActive()) { + destroy(); + } +} + +void TreelandCompositorAction::desktopToggle() +{ + if (isActive()) { + trigger(action_show_desktop); + } +} +} diff --git a/panels/dock/showdesktop/treelandcompositoraction.h b/panels/dock/showdesktop/treelandcompositoraction.h new file mode 100644 index 000000000..ec6f2c9cb --- /dev/null +++ b/panels/dock/showdesktop/treelandcompositoraction.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "qwayland-treeland-compositor-action-unstable-v1.h" +#include + +namespace dock +{ + +class TreelandCompositorAction : public QWaylandClientExtensionTemplate, public QtWayland::treeland_compositor_action_v1 +{ +public: + explicit TreelandCompositorAction(QObject *parent); + ~TreelandCompositorAction(); + + void desktopToggle(); +}; +} diff --git a/panels/dock/showdesktop/treelandshowdesktop.cpp b/panels/dock/showdesktop/treelandshowdesktop.cpp deleted file mode 100644 index ff1beffe9..000000000 --- a/panels/dock/showdesktop/treelandshowdesktop.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "treelandshowdesktop.h" -#include "wayland-treeland-show-desktop-unstable-v1-client-protocol.h" - -namespace dock -{ -TreelandShowDesktop::TreelandShowDesktop(QObject *parent) - : QWaylandClientExtensionTemplate(treeland_show_desktop_v1_interface.version) - , m_desktopState(state_normal) -{ - setParent(parent); -} - -void TreelandShowDesktop::desktopToggle() -{ - if (isActive()) { - set_show_desktop_state(m_desktopState == state_show ? state_normal : state_show); - } -} - -void TreelandShowDesktop::treeland_show_desktop_v1_show_desktop_state(uint32_t state) -{ - if (state != m_desktopState) { - m_desktopState = state; - } -} -} diff --git a/panels/dock/showdesktop/treelandshowdesktop.h b/panels/dock/showdesktop/treelandshowdesktop.h deleted file mode 100644 index 311208ccb..000000000 --- a/panels/dock/showdesktop/treelandshowdesktop.h +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "qwayland-treeland-show-desktop-unstable-v1.h" -#include -#include - -namespace dock -{ - -class TreelandShowDesktop : public QWaylandClientExtensionTemplate, public QtWayland::treeland_show_desktop_v1 -{ -public: - explicit TreelandShowDesktop(QObject *parent); - - void desktopToggle(); - -protected: - void treeland_show_desktop_v1_show_desktop_state(uint32_t state) override; - -private: - uint32_t m_desktopState; -}; -} diff --git a/panels/dock/waylanddockhelper.cpp b/panels/dock/waylanddockhelper.cpp index ce7f37c86..26c6d02c0 100644 --- a/panels/dock/waylanddockhelper.cpp +++ b/panels/dock/waylanddockhelper.cpp @@ -8,9 +8,7 @@ #include "dockhelper.h" #include "dockpanel.h" #include "layershell/dlayershellwindow.h" -#include "qwayland-treeland-dde-shell-v1.h" #include "qwayland-treeland-xwindow-control-unstable-v1.h" -#include "wayland-treeland-dde-shell-v1-client-protocol.h" #include "wayland-treeland-xwindow-control-unstable-v1-client-protocol.h" #include "qwayland-treeland-region-watch-unstable-v1.h" #include "wayland-treeland-region-watch-unstable-v1-client-protocol.h" @@ -26,7 +24,6 @@ WaylandDockHelper::WaylandDockHelper(DockPanel *panel) , m_isCurrentActiveWindowFullscreened(false) , m_panel(panel) { - m_ddeShellManager.reset(new TreeLandDDEShellManager()); m_xwindowControl.reset(new TreeLandXWindowControl()); m_regionWatchManager.reset(new TreeLandRegionWatchManager()); DS_NAMESPACE::DAppletBridge bridge("org.deepin.ds.dock.taskmanager"); @@ -227,11 +224,6 @@ void WallpaperColorManager::watchScreen(const QString &screeName) } } -TreeLandDDEShellManager::TreeLandDDEShellManager() - : QWaylandClientExtensionTemplate(treeland_dde_shell_manager_v1_interface.version) -{ -} - TreeLandXWindowControl::TreeLandXWindowControl() : QWaylandClientExtensionTemplate(treeland_xwindow_control_v1_interface.version) { diff --git a/panels/dock/waylanddockhelper.h b/panels/dock/waylanddockhelper.h index 592cec8ad..e34d8da0a 100644 --- a/panels/dock/waylanddockhelper.h +++ b/panels/dock/waylanddockhelper.h @@ -7,7 +7,6 @@ #include "dockhelper.h" #include "dockpanel.h" -#include "qwayland-treeland-dde-shell-v1.h" #include "qwayland-treeland-wallpaper-color-v1.h" #include "qwayland-treeland-xwindow-control-unstable-v1.h" #include "qwayland-treeland-region-watch-unstable-v1.h" @@ -20,7 +19,6 @@ namespace dock { class WallpaperColorManager; class TreeLandDockTriggerArea; -class TreeLandDDEShellManager; class TreeLandXWindowControl; class TreeLandRegionWatchManager; class TreeLandRegionWatch; @@ -65,7 +63,6 @@ protected Q_SLOTS: QScopedPointer m_wallpaperColorManager; QScopedPointer m_regionWatch; QScopedPointer m_regionWatchManager; - QScopedPointer m_ddeShellManager; QScopedPointer m_xwindowControl; struct ::wl_surface *m_dockWlSurface = nullptr; // Dock's wl_surface for XEmbed positioning }; @@ -86,14 +83,6 @@ class WallpaperColorManager : public QWaylandClientExtensionTemplate, public QtWayland::treeland_dde_shell_manager_v1 -{ - Q_OBJECT - -public: - explicit TreeLandDDEShellManager(); -}; - class TreeLandXWindowControl : public QWaylandClientExtensionTemplate, public QtWayland::treeland_xwindow_control_v1 { Q_OBJECT From ecde4f0e3ea7eacaa7ace609c4df9e6df3c23d8e Mon Sep 17 00:00:00 2001 From: rewine Date: Tue, 13 Oct 2026 11:29:39 +0800 Subject: [PATCH 2/3] fix: guard Wayland protocol destruction with isInitialized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Add null-safe destroy() guards to avoid crash on uninitialized proxy 2. Replace isActive() with isInitialized() to prevent proxy leak on global removal 3. Add missing destructors for WallpaperColorManager and keyboard notify objects Log: No user-facing changes Influence: 1. Run dock panel on a non-treeland Wayland compositor and quit normally 2. Toggle SmartHide mode repeatedly and verify region watch is released 3. Switch CapsLock/NumLock and shutdown/lockscreen on Wayland to confirm no crash fix: 使用 isInitialized 保护 Wayland 协议析构销毁 1. 为 destroy() 增加空指针保护,避免未初始化代理崩溃 2. 将 isActive() 改为 isInitialized(),防止 global 移除后代理泄漏 3. 补齐 WallpaperColorManager 与键盘通知对象的析构销毁 Log: 无用户可见变化 Influence: 1. 在非 treeland 的 Wayland 合成器上运行 dock 面板并正常退出 2. 反复切换智能隐藏模式,确认区域监听已释放 3. 在 Wayland 下切换大小写/数字锁定与关机/锁屏,确认无崩溃 --- applets/dde-key-notify/treelandkeynotify.cpp | 16 ++++++++++++++++ applets/dde-key-notify/treelandkeynotify.h | 2 ++ applets/dde-shutdown/treelandlockscreen.cpp | 2 +- frame/wayland/xdgactivation.cpp | 3 ++- .../multitaskview/treelandcompositoraction.cpp | 2 +- .../showdesktop/treelandcompositoraction.cpp | 2 +- .../dock/taskmanager/treelandwindowmonitor.cpp | 3 ++- panels/dock/waylanddockhelper.cpp | 9 ++++++++- panels/dock/waylanddockhelper.h | 1 + shell/treelandoutputwatcher.cpp | 3 ++- 10 files changed, 36 insertions(+), 7 deletions(-) diff --git a/applets/dde-key-notify/treelandkeynotify.cpp b/applets/dde-key-notify/treelandkeynotify.cpp index 1fda99bb8..9f74cd06a 100644 --- a/applets/dde-key-notify/treelandkeynotify.cpp +++ b/applets/dde-key-notify/treelandkeynotify.cpp @@ -17,6 +17,16 @@ TreelandKeyNotify::TreelandKeyNotify(QObject *parent) connect(this, &TreelandKeyNotify::activeChanged, this, &TreelandKeyNotify::updateWatcher); } +TreelandKeyNotify::~TreelandKeyNotify() +{ + if (m_watcher) { + delete m_watcher; + m_watcher = nullptr; + } + if (isInitialized()) + destroy(); +} + void TreelandKeyNotify::setCapsLockEnabled(bool enabled) { if (m_capsLockEnabled == enabled) { @@ -83,6 +93,12 @@ TreelandKeyWatcher::TreelandKeyWatcher(struct ::treeland_keyboard_state_watcher_ { } +TreelandKeyWatcher::~TreelandKeyWatcher() +{ + if (isInitialized()) + destroy(); +} + void TreelandKeyWatcher::watchLocks(bool watchCapsLock) { set_modifiers(watchCapsLock ? modifier_caps_lock | modifier_num_lock : modifier_num_lock); diff --git a/applets/dde-key-notify/treelandkeynotify.h b/applets/dde-key-notify/treelandkeynotify.h index 7ddd78fa7..cbac83f3d 100644 --- a/applets/dde-key-notify/treelandkeynotify.h +++ b/applets/dde-key-notify/treelandkeynotify.h @@ -24,6 +24,7 @@ class TreelandKeyNotify : public QWaylandClientExtensionTemplatedockScreenName()) { @@ -265,7 +271,8 @@ TreeLandRegionWatch::TreeLandRegionWatch(WaylandDockHelper *helper, struct ::tre TreeLandRegionWatch::~TreeLandRegionWatch() { - destroy(); + if (isInitialized()) + destroy(); } void TreeLandRegionWatch::treeland_region_watch_v1_enter() diff --git a/panels/dock/waylanddockhelper.h b/panels/dock/waylanddockhelper.h index e34d8da0a..d135ea389 100644 --- a/panels/dock/waylanddockhelper.h +++ b/panels/dock/waylanddockhelper.h @@ -72,6 +72,7 @@ class WallpaperColorManager : public QWaylandClientExtensionTemplate Date: Thu, 15 Oct 2026 11:28:42 +0800 Subject: [PATCH 3/3] feat: add OSD Wayland adaptation, Bluetooth types, unit tests and AT-SPI tests --- .../osd/brightness/CMakeLists.txt | 2 +- .../osd/brightness/treelandbrightness.cpp | 22 +-- .../osd/brightness/treelandbrightness.h | 18 +- .../package/icons/osd_bluetooth_off.dci | Bin 0 -> 1903 bytes .../package/icons/osd_bluetooth_on.dci | Bin 0 -> 1617 bytes .../notification/osd/default/package/main.qml | 2 + .../osd/displaymode/displaymodeapplet.cpp | 26 +++ .../osd/displaymode/displaymodeapplet.h | 5 +- panels/notification/osd/osdpanel.cpp | 8 +- tests/at/spi/expected_names.yaml | 112 +++++++++++ tests/panels/notification/CMakeLists.txt | 1 + tests/panels/notification/osd/CMakeLists.txt | 183 ++++++++++++++++++ .../notification/osd/audioapplet_test.cpp | 112 +++++++++++ .../osd/brightnessapplet_test.cpp | 85 ++++++++ .../osd/displaymodeapplet_test.cpp | 119 ++++++++++++ .../panels/notification/osd/osdpanel_test.cpp | 171 ++++++++++++++++ 16 files changed, 839 insertions(+), 27 deletions(-) create mode 100644 panels/notification/osd/default/package/icons/osd_bluetooth_off.dci create mode 100644 panels/notification/osd/default/package/icons/osd_bluetooth_on.dci create mode 100644 tests/panels/notification/osd/CMakeLists.txt create mode 100644 tests/panels/notification/osd/audioapplet_test.cpp create mode 100644 tests/panels/notification/osd/brightnessapplet_test.cpp create mode 100644 tests/panels/notification/osd/displaymodeapplet_test.cpp create mode 100644 tests/panels/notification/osd/osdpanel_test.cpp diff --git a/panels/notification/osd/brightness/CMakeLists.txt b/panels/notification/osd/brightness/CMakeLists.txt index 9a1e2b0b8..53bb4b18d 100644 --- a/panels/notification/osd/brightness/CMakeLists.txt +++ b/panels/notification/osd/brightness/CMakeLists.txt @@ -16,7 +16,7 @@ add_library(osd-brightness SHARED qt_generate_wayland_protocol_client_sources(osd-brightness NO_INCLUDE_CORE_ONLY FILES - ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-output-manager-v1.xml + ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-output-manager-unstable-v2.xml ) target_link_libraries(osd-brightness PRIVATE diff --git a/panels/notification/osd/brightness/treelandbrightness.cpp b/panels/notification/osd/brightness/treelandbrightness.cpp index dd6b99983..83575d120 100644 --- a/panels/notification/osd/brightness/treelandbrightness.cpp +++ b/panels/notification/osd/brightness/treelandbrightness.cpp @@ -4,7 +4,7 @@ #include "treelandbrightness.h" -#include "wayland-treeland-output-manager-v1-client-protocol.h" +#include "wayland-treeland-output-manager-unstable-v2-client-protocol.h" #include #include @@ -13,32 +13,32 @@ namespace osd { -TreelandColorControl::TreelandColorControl(struct ::treeland_output_color_control_v1 *object, QObject *parent) +TreelandPictureControl::TreelandPictureControl(struct ::treeland_output_picture_control_v2 *object, QObject *parent) : QObject(parent) - , QtWayland::treeland_output_color_control_v1(object) + , QtWayland::treeland_output_picture_control_v2(object) { } -TreelandColorControl::~TreelandColorControl() +TreelandPictureControl::~TreelandPictureControl() { if (isInitialized()) { destroy(); } } -double TreelandColorControl::brightness() const +double TreelandPictureControl::brightness() const { return m_brightness; } -void TreelandColorControl::treeland_output_color_control_v1_brightness(wl_fixed_t brightness) +void TreelandPictureControl::treeland_output_picture_control_v2_brightness(wl_fixed_t brightness) { m_brightness = wl_fixed_to_double(brightness); Q_EMIT brightnessChanged(m_brightness); } TreelandBrightness::TreelandBrightness(QObject *parent) - : QWaylandClientExtensionTemplate(treeland_output_manager_v1_interface.version) + : QWaylandClientExtensionTemplate(treeland_output_manager_v2_interface.version) { setParent(parent); connect(this, &TreelandBrightness::activeChanged, this, &TreelandBrightness::refresh); @@ -47,8 +47,6 @@ TreelandBrightness::TreelandBrightness(QObject *parent) TreelandBrightness::~TreelandBrightness() { delete m_control; - m_control = nullptr; - m_output = nullptr; if (isInitialized()) { destroy(); } @@ -76,13 +74,13 @@ void TreelandBrightness::refresh() return; } - auto *raw = get_color_control(output); + auto *raw = get_picture_control(output); if (!raw) { return; } - m_control = new TreelandColorControl(raw, this); - connect(m_control, &TreelandColorControl::brightnessChanged, + m_control = new TreelandPictureControl(raw, this); + connect(m_control, &TreelandPictureControl::brightnessChanged, this, &TreelandBrightness::brightnessChanged); } diff --git a/panels/notification/osd/brightness/treelandbrightness.h b/panels/notification/osd/brightness/treelandbrightness.h index ea87d8a92..f308b727c 100644 --- a/panels/notification/osd/brightness/treelandbrightness.h +++ b/panels/notification/osd/brightness/treelandbrightness.h @@ -4,24 +4,24 @@ #pragma once -#include "qwayland-treeland-output-manager-v1.h" +#include "qwayland-treeland-output-manager-unstable-v2.h" #include #include #include struct wl_output; -struct treeland_output_color_control_v1; +struct treeland_output_picture_control_v2; namespace osd { -class TreelandColorControl : public QObject, public QtWayland::treeland_output_color_control_v1 +class TreelandPictureControl : public QObject, public QtWayland::treeland_output_picture_control_v2 { Q_OBJECT public: - explicit TreelandColorControl(struct ::treeland_output_color_control_v1 *object, QObject *parent = nullptr); - ~TreelandColorControl() override; + explicit TreelandPictureControl(struct ::treeland_output_picture_control_v2 *object, QObject *parent = nullptr); + ~TreelandPictureControl() override; double brightness() const; @@ -29,18 +29,18 @@ class TreelandColorControl : public QObject, public QtWayland::treeland_output_c void brightnessChanged(double brightness); protected: - void treeland_output_color_control_v1_brightness(wl_fixed_t brightness) override; + void treeland_output_picture_control_v2_brightness(wl_fixed_t brightness) override; private: double m_brightness = 0.0; }; // Read-only Treeland brightness provider for the OSD. It binds a single -// treeland_output_color_control_v1 to the primary wl_output and caches the +// treeland_output_picture_control_v2 to the primary wl_output and caches the // brightness reported by the compositor. It never commits brightness changes; // dde-shortcut-tool is responsible for adjusting brightness, and this provider // only reflects the resulting value. -class TreelandBrightness : public QWaylandClientExtensionTemplate, public QtWayland::treeland_output_manager_v1 +class TreelandBrightness : public QWaylandClientExtensionTemplate, public QtWayland::treeland_output_manager_v2 { Q_OBJECT @@ -58,7 +58,7 @@ class TreelandBrightness : public QWaylandClientExtensionTemplate m_control; + QPointer m_control; struct wl_output *m_output = nullptr; }; diff --git a/panels/notification/osd/default/package/icons/osd_bluetooth_off.dci b/panels/notification/osd/default/package/icons/osd_bluetooth_off.dci new file mode 100644 index 0000000000000000000000000000000000000000..7aacb0b3e75facd3280a91fcb6cb9eb6b4402c3c GIT binary patch literal 1903 zcmbu4c{CJy6vu~}Fwr1OcE;F}!RXm}_GU(wC`&V$$56&Lcw#gjN%pdgEGgMWXbi_N zo^9|f!>DHo&zfE&Ns%=|y?W{NclA5>oZt7{dq3ZEzZT|500##Ez^MfX{LGW#`a*LC z5Q6>P{a~K%!M;D@N85)uwSI*D#(3?^1DY@mSeTbb;J49tDXIN!7)vOEL)^8ak;+EuRsGeIEy;l@;ofcD6LLce2+#ZTv~A zW7|ael}ZczC0M)oWw7bNXo^}hNj$1ZG{Zz_5X7GW5HJIn335Z}2s4P@IoU%&2mO(C zHj_>Ub^epz>P^k2cvSF3iQ-h_(Mcv)U@u-488BrPb9TGWMUjKst}TiU6{m+rpgJ{H zQjX!4=1gBd9@w1?itIeWJP;r5#9fUVL}<7PnODHIi8;;+{M6GFB3itV4>!@1LbVP}}iiRvS@!Xd7C%8`~^w}PCja0WK zt1Hp)?$`w1MqZQLgv|L)LS$bk4r?3l=!m}9bnALphZ0DC{ojB$mCq=u+%^j)@qM)~>}y+~D0!Ma7U z7&$=M3hDsXD_*^>!|Ro(xdKe-aw8A(nylG$)MptYK(hTuQGzxWDY5zKGm_p8@hQ%+ zF-ui1quAOJ9B#^o3r-0ihb~(Odu*F9D(F3Mb)`H7pV*d1-39a6Ng|mjtaod+VEBth zyK0O|QcEFL&Yswy{eio;83uv>9Te|zIYK#YS&a7B>PQ}|HAdfVEeTUIO>xNvOg|?g zcv>?Z%lOXC?u=X3-PU{Rw+Tbv4mfk|CB*&}s$s#(DxDDbP-s%{ujJZ_QM-SF(}%iF z(%%!KD?*jxxK#RyioJtV(iDDViC%};uPU8TBb1M0mxm*J(O%H?nZBtLS4N`x;wE`4 zqQ|vI30oK8ASq8RrmrO(M2C~w<1?SvA?rHGG7##liX}UQ4@t{bK_8#8qNBy^HanJ- zs6*vITWbWqbh!^%e`i;uw}KoP7HCWx^48{&LhxeAZ`t`UjDZ1h%`db`LV zvSc@R|Dp)Jk<9kd%JGCz;tt_q6hP35V+j_3#<38kWoqS{$=hkzP&nOwfGcWm@J=0Q&GS zM8syr$?K^tsz)xMn58opkY&GQSK&Yz2rwfVPICg};M--$!|&bk_jZGK7y09%Hf6#@ zh}7kqU>pKM0Mb>!$g}Dzb2Py6p@(S}T+2>v1cU=$p=R6+<)qBJ#NxO*5i?vaRuvF3 zFNZYRDw+m$&TrC>(wO--`9)Uhg~z;U^D9nF6$LT!b<16wDNtLx2W~oBK0eD3isu2> zo{t8mP}bA6Jha5gyZ&6C8+g;ypjwGu{ZnD-Dg4Ewpc{v-nqGro`$>qRaS$)y%2;yq z?;)b267K2guzST9CiRk=S!otc!dp0u<+KrWfro2?b~QHJa7vUH*-~QZA{6ewLsCC} z(aU?lMauoR2K_%VXOfjy^xtMla;KuOC!B}Pl8iK2k2v{5Ro_m^$KIFLOqF10praAK Uop681U}b`T<3{(#!yJJB0L&MuYybcN literal 0 HcmV?d00001 diff --git a/panels/notification/osd/default/package/icons/osd_bluetooth_on.dci b/panels/notification/osd/default/package/icons/osd_bluetooth_on.dci new file mode 100644 index 0000000000000000000000000000000000000000..0e28bcf6565488ff07b04fbc72294630e78a04a1 GIT binary patch literal 1617 zcmZ>9_GDmWWME)2He#RzFtR~(Gv(zMF^;}M7dPa%9u|CXfn5V(wBpDA;e1ru;Q|jp&s5y)0^H~%cncCG%nHIQ!T|V$e1ML zD)LdxS46y( zFaEP!_}}tu_2u$co8G$X$LueBe5-Y_b(o+0jWsQ4f|j%Vtv_60Ubs-aJ#EXW%yl|z zr{C*$`ZjrL?&cXP-!Bw9YDn!eGMZl`$sWh){PM)r#K@3Ud(0%&eC`JLPMsF6c;Wx2 zFRL^cvzKMZ?vGoY$L!`GS@~(_d4Y!6Z*xE8*vcKqxYK#Em;1Kog0%sMJC7&vF!=wn zP`SE?_urD(=F>Mk@>Vw97T(ctoHymkT`|`DKi9751qEeq)SSkCBidn#nVsaEhlh3N zEZMO5zVO#<@Uo9BzBr_p2M-IExt7ARKP7GLxy)EYK=#q+qE|7 zi%$`A_?ge(vBu}uRN;-5vXa}jTFJI9y7>9+n>LT=<||Q0)|s~OWGlxzzx@9AlBD9R z>m4e$e(!C*_~3Q%YjxhYcNcn2{FQ)oi=mCZgg%b{UTnY0(@IUkZj#pBWWnt2L zpEHYpShQHH@>|pIk8`?IHvT#D+vdQ;KVDzDnbYq)nxwM#Mbmp351FLN)Bajmy#MC! z@3pJyeiGyJBF|Ny#NHg2^>`TeJ@Q9H@Tmm(8SkraduB#ozs|$rb~1wZ?}tl0OKN_x zI@;!J_1utqb!YrkpU8*P&egG>ERkW@;G{g+O3bw}SnA|eKlO6Ojpw@N9$SB}@W-2- z%GTF?Uc^KSJ6%!xS5!4=?mi9960^9XyWGwDq)*SvW?E5@7IgnwO*j*a`up5m^OQq5 zlkYjbYJX{Yc4}#w(vsz8PUkI}R~TSa*brGH&$R#fr@qw5>|1k<3oc!HJN-_~fs=oh zy%1tPcVO$yuQ5l~UY%Qc^L%5=J+%n_bLPoLlP~lr1Ji`;tE*+k>q@swE{LA(%@Z&D zb;U;gvzvFR?EH1u3wgFrdwF_8}9g=$W8Z(HrjZGp6FVww((Bs qiCM3AeEs8lV%6+lmsxI4EZxq}I8iR;>0YIajwx@Qk6o(gWd;Bf@i!X) literal 0 HcmV?d00001 diff --git a/panels/notification/osd/default/package/main.qml b/panels/notification/osd/default/package/main.qml index 20d727fe3..3637712ad 100644 --- a/panels/notification/osd/default/package/main.qml +++ b/panels/notification/osd/default/package/main.qml @@ -64,6 +64,8 @@ AppletItem { ListElement { type: "FnToggle"; iconName: "osd_fn"; text: qsTr("Fn toggle")} // Fn切换 ListElement { type: "AirplaneModeOn"; iconName: "osd_airplane_on"; text: qsTr("Airplane mode on")} ListElement { type: "AirplaneModeOff"; iconName: "osd_airplane_off"; text: qsTr("Airplane mode off")} + ListElement { type: "BluetoothOn"; iconName: "osd_bluetooth_on"; text: qsTr("Bluetooth on")} + ListElement { type: "BluetoothOff"; iconName: "osd_bluetooth_off"; text: qsTr("Bluetooth off")} ListElement { type: "AudioMicMuteOn"; iconName: "osd_mic_off"; text: qsTr("Microphone off")} ListElement { type: "AudioMicMuteOff"; iconName: "osd_mic_on"; text: qsTr("Microphone on")} ListElement { type: "balance"; iconName: "osd_power_balance"; text: qsTr("Balanced power")} diff --git a/panels/notification/osd/displaymode/displaymodeapplet.cpp b/panels/notification/osd/displaymode/displaymodeapplet.cpp index 847ec77e8..e9707af0d 100644 --- a/panels/notification/osd/displaymode/displaymodeapplet.cpp +++ b/panels/notification/osd/displaymode/displaymodeapplet.cpp @@ -13,6 +13,9 @@ #include #include +#include + +DGUI_USE_NAMESPACE Q_LOGGING_CATEGORY(osdDPLog, "org.deepin.dde.shell.osd.display") @@ -77,6 +80,12 @@ DisPlayModeApplet::DisPlayModeApplet(QObject *parent) QObject::connect(m_actionTimer, &QTimer::timeout, this, &DisPlayModeApplet::doAction); } +bool DisPlayModeApplet::load() +{ + m_isWayland = DGuiApplicationHelper::testAttribute(DGuiApplicationHelper::IsWaylandPlatform); + return DApplet::load(); +} + int DisPlayModeApplet::state() const { return m_state; @@ -102,6 +111,13 @@ QQmlListProperty DisPlayModeApplet::planItems() void DisPlayModeApplet::sync() { + // On Wayland there is no Treeland display-mode protocol yet; fall back to + // a disabled state so the OSD shows "unavailable" until a protocol is ready. + if (m_isWayland) { + setState(2); + return; + } + if (!m_actionTimer->isActive()) { fetchPlanItems(); auto current = fetchCurrentPlanItem(); @@ -115,6 +131,11 @@ void DisPlayModeApplet::sync() void DisPlayModeApplet::next() { + // Display mode switching is not available on Wayland yet. + if (m_isWayland) { + return; + } + auto it = std::find_if(m_planItems.begin(), m_planItems.end(), [this](const DPItem *item) { return m_currentPlanItem && item->key() == m_currentPlanItem->key(); }); @@ -131,6 +152,11 @@ void DisPlayModeApplet::next() void DisPlayModeApplet::doAction() { + // Display mode switching is not available on Wayland yet. + if (m_isWayland) { + return; + } + const auto item = currentPlanItem(); if (!item) return; diff --git a/panels/notification/osd/displaymode/displaymodeapplet.h b/panels/notification/osd/displaymode/displaymodeapplet.h index 2957fb9b9..0e3cf05b8 100644 --- a/panels/notification/osd/displaymode/displaymodeapplet.h +++ b/panels/notification/osd/displaymode/displaymodeapplet.h @@ -56,6 +56,8 @@ class DisPlayModeApplet : public DS_NAMESPACE::DApplet public: explicit DisPlayModeApplet(QObject *parent = nullptr); + bool load() override; + int state() const; DPItem *currentPlanItem() const; QQmlListProperty planItems(); @@ -79,8 +81,9 @@ class DisPlayModeApplet : public DS_NAMESPACE::DApplet private: QList m_planItems; DPItem *m_currentPlanItem = nullptr; - int m_state; + int m_state = 0; QTimer *m_actionTimer = nullptr; + bool m_isWayland = false; }; } diff --git a/panels/notification/osd/osdpanel.cpp b/panels/notification/osd/osdpanel.cpp index 6d47be03d..4597abc0c 100644 --- a/panels/notification/osd/osdpanel.cpp +++ b/panels/notification/osd/osdpanel.cpp @@ -19,6 +19,10 @@ namespace osd { OsdPanel::OsdPanel(QObject *parent) : DPanel(parent) { + m_osdTimer = new QTimer(this); + m_osdTimer->setInterval(m_interval); + m_osdTimer->setSingleShot(true); + QObject::connect(m_osdTimer, &QTimer::timeout, this, &OsdPanel::doneSetting); } bool OsdPanel::load() @@ -44,10 +48,6 @@ bool OsdPanel::init() } new OsdDBusAdaptor(this); - m_osdTimer = new QTimer(this); - m_osdTimer->setInterval(m_interval); - m_osdTimer->setSingleShot(true); - QObject::connect(m_osdTimer, &QTimer::timeout, this, &OsdPanel::doneSetting); return DPanel::init(); } diff --git a/tests/at/spi/expected_names.yaml b/tests/at/spi/expected_names.yaml index 4565179cc..3f4222929 100644 --- a/tests/at/spi/expected_names.yaml +++ b/tests/at/spi/expected_names.yaml @@ -997,4 +997,116 @@ qml_elements: source_file: shell/SceneWindow.qml parent_type: ApplicationWindow line: 24 +- element_type: Window + id: root + accessible_id: Root + accessible_role: '' + source_file: panels/notification/osd/package/main.qml + parent_type: '' + line: 13 +- element_type: Control + id: '' + accessible_id: Control + accessible_role: '' + source_file: panels/notification/osd/package/main.qml + parent_type: Window + line: 75 +- element_type: Repeater + id: '' + accessible_id: Repeater + accessible_role: '' + source_file: panels/notification/osd/package/main.qml + parent_type: Control + line: 82 +- element_type: AppletItem + id: control + accessible_id: Control + accessible_role: '' + source_file: panels/notification/osd/default/package/main.qml + parent_type: '' + line: 12 +- element_type: DciIcon + id: '' + accessible_id: DciIcon + accessible_role: '' + source_file: panels/notification/osd/default/package/main.qml + parent_type: RowLayout + line: 86 +- element_type: Text + id: '' + accessible_id: Text + accessible_role: '' + source_file: panels/notification/osd/default/package/main.qml + parent_type: RowLayout + line: 99 +- element_type: AppletItem + id: control + accessible_id: Control + accessible_role: '' + source_file: panels/notification/osd/audio/package/main.qml + parent_type: '' + line: 12 +- element_type: DciIcon + id: '' + accessible_id: DciIcon + accessible_role: '' + source_file: panels/notification/osd/audio/package/main.qml + parent_type: RowLayout + line: 48 +- element_type: ProgressBar + id: progressBar + accessible_id: ProgressBar + accessible_role: '' + source_file: panels/notification/osd/audio/package/main.qml + parent_type: ColumnLayout + line: 76 +- element_type: Text + id: '' + accessible_id: Text + accessible_role: '' + source_file: panels/notification/osd/audio/package/main.qml + parent_type: RowLayout + line: 123 +- element_type: Text + id: '' + accessible_id: Text + accessible_role: '' + source_file: panels/notification/osd/audio/package/main.qml + parent_type: RowLayout + line: 129 +- element_type: AppletItem + id: control + accessible_id: Control + accessible_role: '' + source_file: panels/notification/osd/brightness/package/main.qml + parent_type: '' + line: 12 +- element_type: DciIcon + id: '' + accessible_id: DciIcon + accessible_role: '' + source_file: panels/notification/osd/brightness/package/main.qml + parent_type: RowLayout + line: 46 +- element_type: ProgressBar + id: progressBar + accessible_id: ProgressBar + accessible_role: '' + source_file: panels/notification/osd/brightness/package/main.qml + parent_type: RowLayout + line: 58 +- element_type: Text + id: '' + accessible_id: Text + accessible_role: '' + source_file: panels/notification/osd/brightness/package/main.qml + parent_type: RowLayout + line: 98 +- element_type: Text + id: '' + accessible_id: Text + accessible_role: '' + source_file: panels/notification/osd/brightness/package/main.qml + parent_type: RowLayout + line: 104 transient_elements: [] diff --git a/tests/panels/notification/CMakeLists.txt b/tests/panels/notification/CMakeLists.txt index e72550161..095cbc348 100644 --- a/tests/panels/notification/CMakeLists.txt +++ b/tests/panels/notification/CMakeLists.txt @@ -4,3 +4,4 @@ add_subdirectory(common) add_subdirectory(server) +add_subdirectory(osd) diff --git a/tests/panels/notification/osd/CMakeLists.txt b/tests/panels/notification/osd/CMakeLists.txt new file mode 100644 index 000000000..f2203944c --- /dev/null +++ b/tests/panels/notification/osd/CMakeLists.txt @@ -0,0 +1,183 @@ +# SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +# +# SPDX-License-Identifier: GPL-3.0-or-later + +find_package(GTest REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS + Core + Test + DBus +) + +# ---- osdpanel_test ---- +add_executable(osdpanel_tests + ${CMAKE_SOURCE_DIR}/panels/notification/osd/osdpanel.h + ${CMAKE_SOURCE_DIR}/panels/notification/osd/osdpanel.cpp + ${CMAKE_SOURCE_DIR}/panels/notification/osd/osddbusadaptor.h + ${CMAKE_SOURCE_DIR}/panels/notification/osd/osddbusadaptor.cpp + osdpanel_test.cpp +) + +set_property(TARGET osdpanel_tests APPEND PROPERTY AUTOMOC_MACRO_NAMES "D_APPLET_CLASS") + +target_compile_options(osdpanel_tests PRIVATE + -fvisibility=hidden + -fvisibility-inlines-hidden +) + +target_include_directories(osdpanel_tests PRIVATE + ${CMAKE_SOURCE_DIR}/panels/notification/osd + ${CMAKE_SOURCE_DIR}/frame +) + +target_link_libraries(osdpanel_tests PRIVATE + GTest::GTest + GTest::gmock + GTest::gmock_main + GTest::Main + + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Test + Qt${QT_VERSION_MAJOR}::DBus + + dde-shell-frame +) + +add_test( + NAME osdpanel_tests + COMMAND ${CMAKE_COMMAND} -E env + LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/frame + $ +) + +# ---- audioapplet_test ---- +add_executable(audioapplet_tests + ${CMAKE_SOURCE_DIR}/panels/notification/osd/audio/audioapplet.h + ${CMAKE_SOURCE_DIR}/panels/notification/osd/audio/audioapplet.cpp + audioapplet_test.cpp +) + +set_property(TARGET audioapplet_tests APPEND PROPERTY AUTOMOC_MACRO_NAMES "D_APPLET_CLASS") + +target_compile_options(audioapplet_tests PRIVATE + -fvisibility=hidden + -fvisibility-inlines-hidden +) + +target_include_directories(audioapplet_tests PRIVATE + ${CMAKE_SOURCE_DIR}/panels/notification/osd/audio + ${CMAKE_SOURCE_DIR}/frame +) + +target_link_libraries(audioapplet_tests PRIVATE + GTest::GTest + GTest::gmock + GTest::gmock_main + GTest::Main + + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Test + Qt${QT_VERSION_MAJOR}::DBus + + dde-shell-frame +) + +add_test( + NAME audioapplet_tests + COMMAND ${CMAKE_COMMAND} -E env + LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/frame + $ +) + +# ---- brightnessapplet_test ---- +find_package(TreelandProtocols REQUIRED) +find_package(PkgConfig REQUIRED) +pkg_check_modules(WaylandClient REQUIRED IMPORTED_TARGET wayland-client) + +add_executable(brightnessapplet_tests + ${CMAKE_SOURCE_DIR}/panels/notification/osd/brightness/brightnessapplet.h + ${CMAKE_SOURCE_DIR}/panels/notification/osd/brightness/brightnessapplet.cpp + ${CMAKE_SOURCE_DIR}/panels/notification/osd/brightness/treelandbrightness.h + ${CMAKE_SOURCE_DIR}/panels/notification/osd/brightness/treelandbrightness.cpp + brightnessapplet_test.cpp +) + +qt_generate_wayland_protocol_client_sources(brightnessapplet_tests + NO_INCLUDE_CORE_ONLY + FILES + ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-output-manager-unstable-v2.xml +) + +set_property(TARGET brightnessapplet_tests APPEND PROPERTY AUTOMOC_MACRO_NAMES "D_APPLET_CLASS") + +target_compile_options(brightnessapplet_tests PRIVATE + -fvisibility=hidden + -fvisibility-inlines-hidden +) + +target_include_directories(brightnessapplet_tests PRIVATE + ${CMAKE_SOURCE_DIR}/panels/notification/osd/brightness + ${CMAKE_SOURCE_DIR}/frame +) + +target_link_libraries(brightnessapplet_tests PRIVATE + GTest::GTest + GTest::gmock + GTest::gmock_main + GTest::Main + + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Test + Qt${QT_VERSION_MAJOR}::DBus + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::WaylandClient + + dde-shell-frame + PkgConfig::WaylandClient +) + +add_test( + NAME brightnessapplet_tests + COMMAND ${CMAKE_COMMAND} -E env + LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/frame + $ +) + +# ---- displaymodeapplet_test ---- +add_executable(displaymodeapplet_tests + ${CMAKE_SOURCE_DIR}/panels/notification/osd/displaymode/displaymodeapplet.h + ${CMAKE_SOURCE_DIR}/panels/notification/osd/displaymode/displaymodeapplet.cpp + displaymodeapplet_test.cpp +) + +set_property(TARGET displaymodeapplet_tests APPEND PROPERTY AUTOMOC_MACRO_NAMES "D_APPLET_CLASS") + +target_compile_options(displaymodeapplet_tests PRIVATE + -fvisibility=hidden + -fvisibility-inlines-hidden +) + +target_include_directories(displaymodeapplet_tests PRIVATE + ${CMAKE_SOURCE_DIR}/panels/notification/osd/displaymode + ${CMAKE_SOURCE_DIR}/frame +) + +target_link_libraries(displaymodeapplet_tests PRIVATE + GTest::GTest + GTest::gmock + GTest::gmock_main + GTest::Main + + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Test + Qt${QT_VERSION_MAJOR}::DBus + + dde-shell-frame +) + +add_test( + NAME displaymodeapplet_tests + COMMAND ${CMAKE_COMMAND} -E env + LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/frame + $ +) diff --git a/tests/panels/notification/osd/audioapplet_test.cpp b/tests/panels/notification/osd/audioapplet_test.cpp new file mode 100644 index 000000000..26d4a1c65 --- /dev/null +++ b/tests/panels/notification/osd/audioapplet_test.cpp @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include + +#include +#include +#include + +#include "audioapplet.h" + +using namespace osd; +using ::testing::Return; + +// Mock interface for audio DBus operations. +// The real AudioApplet uses DDBusSender to call org.deepin.dde.Audio1. +// For unit testing we cannot rely on a live DBus session, so we test +// the icon name computation logic directly by setting the internal +// volume value and observing the result of sync/fetchIconName. +class AudioAppletTest : public ::testing::Test { +protected: + void SetUp() override { + if (!QCoreApplication::instance()) { + int argc = 0; + char *argv[] = {nullptr}; + app = new QCoreApplication(argc, argv); + } + applet = new AudioApplet(); + } + + void TearDown() override { + delete applet; + applet = nullptr; + } + + QCoreApplication *app = nullptr; + AudioApplet *applet = nullptr; +}; + +// Test initial state +TEST_F(AudioAppletTest, InitialState) { + EXPECT_EQ(applet->iconName().toStdString(), ""); + EXPECT_DOUBLE_EQ(applet->volumeValue(), 1.0); +} + +// Test setIconName emits signal +TEST_F(AudioAppletTest, SetIconNameEmitsSignal) { + QSignalSpy spy(applet, &AudioApplet::iconNameChanged); + applet->setIconName("osd_volume_33"); + EXPECT_EQ(applet->iconName().toStdString(), "osd_volume_33"); + EXPECT_EQ(spy.count(), 1); +} + +// Test setIconName does not emit when unchanged +TEST_F(AudioAppletTest, SetIconNameNoSignalWhenUnchanged) { + applet->setIconName("osd_volume_33"); + QSignalSpy spy(applet, &AudioApplet::iconNameChanged); + applet->setIconName("osd_volume_33"); + EXPECT_EQ(spy.count(), 0); +} + +// Test setVolumeValue emits signal +TEST_F(AudioAppletTest, SetVolumeValueEmitsSignal) { + QSignalSpy spy(applet, &AudioApplet::volumeValueChanged); + applet->setVolumeValue(0.5); + EXPECT_DOUBLE_EQ(applet->volumeValue(), 0.5); + EXPECT_EQ(spy.count(), 1); +} + +// Test setVolumeValue does not emit when unchanged (fuzzy compare) +TEST_F(AudioAppletTest, SetVolumeValueNoSignalWhenUnchanged) { + applet->setVolumeValue(0.5); + QSignalSpy spy(applet, &AudioApplet::volumeValueChanged); + applet->setVolumeValue(0.5); + EXPECT_EQ(spy.count(), 0); +} + +// Test increaseVolume property +TEST_F(AudioAppletTest, IncreaseVolumeProperty) { + EXPECT_TRUE(applet->increaseVolume()); + + QSignalSpy spy(applet, &AudioApplet::increaseVolumeChanged); + applet->setIncreaseVolume(false); + EXPECT_FALSE(applet->increaseVolume()); + EXPECT_EQ(spy.count(), 1); +} + +// Test increaseVolume no signal when unchanged +TEST_F(AudioAppletTest, IncreaseVolumeNoSignalWhenUnchanged) { + applet->setIncreaseVolume(true); + QSignalSpy spy(applet, &AudioApplet::increaseVolumeChanged); + applet->setIncreaseVolume(true); + EXPECT_EQ(spy.count(), 0); +} + +// Test sync does not crash without DBus (graceful degradation) +TEST_F(AudioAppletTest, SyncWithoutDBus) { + EXPECT_NO_THROW(applet->sync()); +} + +// Main function for tests +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + + QCoreApplication app(argc, argv); + + return RUN_ALL_TESTS(); +} + +#include "audioapplet_test.moc" diff --git a/tests/panels/notification/osd/brightnessapplet_test.cpp b/tests/panels/notification/osd/brightnessapplet_test.cpp new file mode 100644 index 000000000..48ae123c2 --- /dev/null +++ b/tests/panels/notification/osd/brightnessapplet_test.cpp @@ -0,0 +1,85 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include + +#include +#include +#include + +#include "brightnessapplet.h" + +using namespace osd; + +// Test fixture for BrightnessApplet +class BrightnessAppletTest : public ::testing::Test { +protected: + void SetUp() override { + applet = new BrightnessApplet(); + } + + void TearDown() override { + delete applet; + applet = nullptr; + } + + BrightnessApplet *applet = nullptr; +}; + +// Test initial state +TEST_F(BrightnessAppletTest, InitialState) { + EXPECT_DOUBLE_EQ(applet->brightness(), 0.0); + EXPECT_EQ(applet->iconName().toStdString(), ""); +} + +// Test sync does not crash without DBus (graceful degradation) +TEST_F(BrightnessAppletTest, SyncWithoutDBus) { + EXPECT_NO_THROW(applet->sync()); +} + +// Test sync sets icon name for zero brightness +TEST_F(BrightnessAppletTest, SyncSetsIconForZeroBrightness) { + applet->sync(); + // Without DBus, fetchBrightness returns 0, so brightness stays 0 + // fetchIconName for brightness <= 0 returns "osd_brightness0" + EXPECT_EQ(applet->iconName().toStdString(), "osd_brightness0"); +} + +// Test brightness property signal +TEST_F(BrightnessAppletTest, BrightnessSignalEmission) { + QSignalSpy spy(applet, &BrightnessApplet::brightnessChanged); + // sync() with no DBus will call setBrightness(0) which equals initial 0, + // so no signal is emitted + applet->sync(); + EXPECT_EQ(spy.count(), 0); +} + +// Test iconName signal is emitted on first sync +TEST_F(BrightnessAppletTest, IconNameSignalEmission) { + QSignalSpy spy(applet, &BrightnessApplet::iconNameChanged); + applet->sync(); + // iconName changes from "" to "osd_brightness0" + EXPECT_EQ(spy.count(), 1); +} + +// Test iconName does not change when sync called again with same brightness +TEST_F(BrightnessAppletTest, IconNameNoChangeOnRepeatedSync) { + applet->sync(); + QSignalSpy spy(applet, &BrightnessApplet::iconNameChanged); + applet->sync(); + // iconName is already "osd_brightness0", no new signal + EXPECT_EQ(spy.count(), 0); +} + +// Main function for tests +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + + QCoreApplication app(argc, argv); + + return RUN_ALL_TESTS(); +} + +#include "brightnessapplet_test.moc" diff --git a/tests/panels/notification/osd/displaymodeapplet_test.cpp b/tests/panels/notification/osd/displaymodeapplet_test.cpp new file mode 100644 index 000000000..ca039ce86 --- /dev/null +++ b/tests/panels/notification/osd/displaymodeapplet_test.cpp @@ -0,0 +1,119 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include + +#include +#include +#include + +#include "displaymodeapplet.h" + +using namespace osd; + +// Test fixture for DisPlayModeApplet +class DisplayModeAppletTest : public ::testing::Test { +protected: + void SetUp() override { + applet = new DisPlayModeApplet(); + } + + void TearDown() override { + delete applet; + applet = nullptr; + } + + DisPlayModeApplet *applet = nullptr; +}; + +// Test DPItem construction with 4-arg constructor +TEST(DisplayModeItemTest, ConstructWithFourArgs) { + DPItem item("Duplicate", "osd_multi_screen_copy", DPItem::Merge); + EXPECT_EQ(item.text().toStdString(), "Duplicate"); + EXPECT_EQ(item.name().toStdString(), ""); + EXPECT_EQ(item.iconName().toStdString(), "osd_multi_screen_copy"); + EXPECT_EQ(item.mode(), DPItem::Merge); +} + +// Test DPItem construction with 5-arg constructor +TEST(DisplayModeItemTest, ConstructWithFiveArgs) { + DPItem item("Only on HDMI-1", "HDMI-1", "osd_multi_screen_only1", DPItem::Single); + EXPECT_EQ(item.text().toStdString(), "Only on HDMI-1"); + EXPECT_EQ(item.name().toStdString(), "HDMI-1"); + EXPECT_EQ(item.iconName().toStdString(), "osd_multi_screen_only1"); + EXPECT_EQ(item.mode(), DPItem::Single); +} + +// Test DPItem key format +TEST(DisplayModeItemTest, KeyFormat) { + DPItem item("Extend", "osd_multi_screen_extension", DPItem::Extend); + QString expectedKey = QString("%1-%2-%3").arg("Extend").arg("").arg(DPItem::Extend); + EXPECT_EQ(item.key().toStdString(), expectedKey.toStdString()); +} + +// Test DPItem key format with name +TEST(DisplayModeItemTest, KeyFormatWithName) { + DPItem item("Only on VGA", "VGA-1", "osd_multi_screen_only2", DPItem::Single); + QString expectedKey = QString("%1-%2-%3").arg("Only on VGA").arg("VGA-1").arg(DPItem::Single); + EXPECT_EQ(item.key().toStdString(), expectedKey.toStdString()); +} + +// Test DPItem mode enum values +TEST(DisplayModeItemTest, ModeEnumValues) { + EXPECT_EQ(DPItem::Merge, 1); + EXPECT_EQ(DPItem::Extend, 2); + EXPECT_EQ(DPItem::Single, 3); +} + +// Test initial state +TEST_F(DisplayModeAppletTest, InitialState) { + EXPECT_EQ(applet->state(), 0); + EXPECT_EQ(applet->currentPlanItem(), nullptr); +} + +// Test sync does not crash without DBus (graceful degradation) +TEST_F(DisplayModeAppletTest, SyncWithoutDBus) { + EXPECT_NO_THROW(applet->sync()); +} + +// Test next() does not crash on empty plan list +TEST_F(DisplayModeAppletTest, NextOnEmptyPlanList) { + EXPECT_NO_THROW(applet->next()); +} + +// Test doAction does not crash without current item +TEST_F(DisplayModeAppletTest, DoActionWithoutCurrentItem) { + EXPECT_NO_THROW(applet->doAction()); +} + +// Test state signal is emitted when changed +TEST_F(DisplayModeAppletTest, StateSignalEmission) { + // sync() without DBus will not change state from initial 0, + // since fetchState() depends on DConfig which may not be available. + // Just verify no crash. + QSignalSpy spy(applet, &DisPlayModeApplet::stateChanged); + applet->sync(); + // State may or may not change depending on DConfig availability. + // The key point is no crash. + SUCCEED(); +} + +// Test planItems is initially empty +TEST_F(DisplayModeAppletTest, PlanItemsInitiallyEmpty) { + // planItems() returns QQmlListProperty; check via the list + auto items = applet->planItems(); + EXPECT_EQ(items.count(&items), 0); +} + +// Main function for tests +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + + QCoreApplication app(argc, argv); + + return RUN_ALL_TESTS(); +} + +#include "displaymodeapplet_test.moc" diff --git a/tests/panels/notification/osd/osdpanel_test.cpp b/tests/panels/notification/osd/osdpanel_test.cpp new file mode 100644 index 000000000..8ff9c01ac --- /dev/null +++ b/tests/panels/notification/osd/osdpanel_test.cpp @@ -0,0 +1,171 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "osdpanel.h" + +using namespace osd; + +// Test fixture for OsdPanel +class OsdPanelTest : public ::testing::Test { +protected: + void SetUp() override { + panel = new OsdPanel(); + } + + void TearDown() override { + delete panel; + panel = nullptr; + } + + OsdPanel *panel = nullptr; +}; + +// Test initial state +TEST_F(OsdPanelTest, InitialState) { + EXPECT_FALSE(panel->visible()); + EXPECT_TRUE(panel->osdType().isEmpty()); + EXPECT_TRUE(panel->lastOsdType().isEmpty()); +} + +// Test ShowOSD sets osdType via signal +TEST_F(OsdPanelTest, ShowOSDSetsOsdType) { + QSignalSpy spy(panel, &OsdPanel::osdTypeChanged); + + panel->ShowOSD("AudioUp"); + + // ShowOSD uses a 100ms timer to set osdType; wait for it + QTest::qWait(200); + + EXPECT_EQ(panel->osdType().toStdString(), "AudioUp"); + EXPECT_EQ(spy.count(), 1); + EXPECT_EQ(spy.takeFirst().at(0).toString().toStdString(), "AudioUp"); +} + +// Test ShowOSD makes panel visible +TEST_F(OsdPanelTest, ShowOSDMakesVisible) { + QSignalSpy spy(panel, &OsdPanel::visibleChanged); + + EXPECT_FALSE(panel->visible()); + + panel->ShowOSD("AudioUp"); + QTest::qWait(200); + + EXPECT_TRUE(panel->visible()); + EXPECT_EQ(spy.count(), 1); +} + +// Test ShowOSD with different type updates osdType +TEST_F(OsdPanelTest, ShowOSDDifferentType) { + panel->ShowOSD("AudioUp"); + QTest::qWait(200); + EXPECT_EQ(panel->osdType().toStdString(), "AudioUp"); + + panel->ShowOSD("AudioDown"); + QTest::qWait(200); + EXPECT_EQ(panel->osdType().toStdString(), "AudioDown"); +} + +// Test lastOsdType is updated after show +TEST_F(OsdPanelTest, LastOsdTypeUpdated) { + panel->ShowOSD("BrightnessUp"); + QTest::qWait(200); + EXPECT_EQ(panel->lastOsdType().toStdString(), "BrightnessUp"); +} + +// Test hideOsd sets visible false after timer expires +TEST_F(OsdPanelTest, HideOsdSetsInvisible) { + panel->ShowOSD("AudioUp"); + QTest::qWait(200); + EXPECT_TRUE(panel->visible()); + + // Non-SwitchWM3D interval is 1000ms, plus doneSetting delay. + // Wait long enough for the full hide cycle. + QTest::qWait(3000); + EXPECT_FALSE(panel->visible()); +} + +// Test ShowOSD called multiple times resets timer +TEST_F(OsdPanelTest, ShowOSDMultipleCallsResetTimer) { + panel->ShowOSD("AudioUp"); + QTest::qWait(200); + + panel->ShowOSD("AudioDown"); + QTest::qWait(200); + + EXPECT_EQ(panel->osdType().toStdString(), "AudioDown"); + EXPECT_TRUE(panel->visible()); +} + +// Test empty osd type +TEST_F(OsdPanelTest, ShowOSDEmptyType) { + panel->ShowOSD(""); + QTest::qWait(200); + EXPECT_TRUE(panel->osdType().isEmpty()); +} + +// Test SwitchWM3D uses 2000ms interval +TEST_F(OsdPanelTest, ShowOSDSwitchWM3D) { + panel->ShowOSD("SwitchWM3D"); + QTest::qWait(200); + EXPECT_EQ(panel->osdType().toStdString(), "SwitchWM3D"); + EXPECT_TRUE(panel->visible()); +} + +// D-Bus integration test for org.deepin.dde.Osd1 ShowOSD +class OsdDBusTest : public ::testing::Test { +protected: + void SetUp() override { + panel = new OsdPanel(); + // Try to initialize (registers DBus service). May fail if service + // is already taken; in that case skip the DBus tests. + dbusReady = panel->init(); + } + + void TearDown() override { + delete panel; + panel = nullptr; + } + + OsdPanel *panel = nullptr; + bool dbusReady = false; +}; + +// Test ShowOSD via DBus interface +TEST_F(OsdDBusTest, ShowOSDViaDBus) { + if (!dbusReady) { + GTEST_SKIP() << "DBus service registration failed; skipping DBus integration test"; + } + + QDBusInterface iface("org.deepin.dde.Osd1", "/", "org.deepin.dde.Osd1", + QDBusConnection::sessionBus()); + ASSERT_TRUE(iface.isValid()); + + QDBusReply reply = iface.call("ShowOSD", QString("AudioUp")); + ASSERT_TRUE(reply.isValid()); + + QTest::qWait(200); + EXPECT_EQ(panel->osdType().toStdString(), "AudioUp"); + EXPECT_TRUE(panel->visible()); +} + +// Main function for tests +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + + QCoreApplication app(argc, argv); + + return RUN_ALL_TESTS(); +} + +#include "osdpanel_test.moc"