From 17058c5e8e6328cb27b87b7056642d9cbfac307d Mon Sep 17 00:00:00 2001 From: yeshanshan Date: Mon, 1 Jun 2026 13:33:20 +0800 Subject: [PATCH] feat: add key state notification applet for Wayland MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Create a new dde-key-notify applet to monitor Caps Lock and Num Lock states on Wayland 2. Implement a Wayland client extension to listen for keyboard modifier state changes via the treeland-keyboard-state-notify-protocol 3. Integrate with DConfig to control Caps Lock toggle OSD display 4. Send OSD notifications through DBus (org.deepin.dde.shell) when lock states change 5. Add build system integration and installation rules for the new applet Log: Added keyboard state notification support for Wayland to display OSD when Caps Lock and Num Lock states change Influence: 1. Test on Wayland session: toggle Caps Lock and verify OSD appears/ disappears correctly 2. Test on X11 session: verify no OSD is shown (feature is Wayland-only) 3. Test Num Lock toggle and verify OSD notification 4. Verify Caps Lock toggle OSD can be disabled via DConfig (org.deepin.dde.daemon/org.deepin.dde.daemon.keyboard/capslockToggle) 5. Verify package installation completes successfully 6. Test multiple rapid toggles to ensure no crashes or missed notifications feat: 添加 Wayland 下的键盘状态通知小程序 1. 创建新的 dde-key-notify 小程序,用于监控 Wayland 下的大写锁定和数字锁 定键状态 2. 实现 Wayland 客户端扩展,通过 treeland-keyboard-state-notify 协议监听 键盘修饰键状态变化 3. 集成 DConfig 以控制大写锁定键 OSD 显示 4. 当锁定状态变化时通过 DBus(org.deepin.dde.shell)发送 OSD 通知 5. 添加构建系统集成和安装规则 Log: 添加 Wayland 键盘状态通知功能,大写锁定和数字锁定状态变化时显示 OSD Influence: 1. 在 Wayland 会话中测试:切换大写锁定键,验证 OSD 正确显示/消失 2. 在 X11 会话中测试:验证不显示 OSD(该功能仅支持 Wayland) 3. 测试数字锁定键切换,验证 OSD 通知 4. 通过 DConfig(org.deepin.dde.daemon/org.deepin.dde.daemon.keyboard/ capslockToggle)验证大写锁定 OSD 可被禁用 5. 验证软件包安装完成 6. 测试快速多次切换,确保无崩溃或丢失通知 PMS: TASK-390457 --- applets/CMakeLists.txt | 1 + applets/dde-key-notify/CMakeLists.txt | 30 ++++++ applets/dde-key-notify/keynotifyapplet.cpp | 75 +++++++++++++++ applets/dde-key-notify/keynotifyapplet.h | 44 +++++++++ applets/dde-key-notify/package/metadata.json | 7 ++ applets/dde-key-notify/treelandkeynotify.cpp | 99 ++++++++++++++++++++ applets/dde-key-notify/treelandkeynotify.h | 62 ++++++++++++ debian/control | 2 +- debian/dde-shell.install | 2 + 9 files changed, 321 insertions(+), 1 deletion(-) create mode 100644 applets/dde-key-notify/CMakeLists.txt create mode 100644 applets/dde-key-notify/keynotifyapplet.cpp create mode 100644 applets/dde-key-notify/keynotifyapplet.h create mode 100644 applets/dde-key-notify/package/metadata.json create mode 100644 applets/dde-key-notify/treelandkeynotify.cpp create mode 100644 applets/dde-key-notify/treelandkeynotify.h diff --git a/applets/CMakeLists.txt b/applets/CMakeLists.txt index c93043d84..0486c8430 100644 --- a/applets/CMakeLists.txt +++ b/applets/CMakeLists.txt @@ -5,4 +5,5 @@ add_subdirectory(dde-am) add_subdirectory(dde-appearance) add_subdirectory(dde-apps) +add_subdirectory(dde-key-notify) add_subdirectory(dde-shutdown) diff --git a/applets/dde-key-notify/CMakeLists.txt b/applets/dde-key-notify/CMakeLists.txt new file mode 100644 index 000000000..bf02afea6 --- /dev/null +++ b/applets/dde-key-notify/CMakeLists.txt @@ -0,0 +1,30 @@ +# SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +# +# SPDX-License-Identifier: GPL-3.0-or-later + +find_package(TreelandProtocols REQUIRED) + +set(TREELAND_KEYBOARD_STATE_NOTIFY_PROTOCOL + ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-keyboard-state-notify-unstable-v1.xml +) + +add_library(dde-key-notify SHARED + keynotifyapplet.cpp + keynotifyapplet.h + treelandkeynotify.cpp + treelandkeynotify.h +) + +qt_generate_wayland_protocol_client_sources(dde-key-notify + NO_INCLUDE_CORE_ONLY + FILES + ${TREELAND_KEYBOARD_STATE_NOTIFY_PROTOCOL} +) + +target_link_libraries(dde-key-notify PRIVATE + dde-shell-frame + Qt${QT_VERSION_MAJOR}::DBus + Qt${QT_VERSION_MAJOR}::WaylandClient +) + +ds_install_package(PACKAGE org.deepin.ds.dde-key-notify TARGET dde-key-notify) diff --git a/applets/dde-key-notify/keynotifyapplet.cpp b/applets/dde-key-notify/keynotifyapplet.cpp new file mode 100644 index 000000000..e069e4415 --- /dev/null +++ b/applets/dde-key-notify/keynotifyapplet.cpp @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "keynotifyapplet.h" + +#include "pluginfactory.h" +#include "treelandkeynotify.h" + +#include +#include + +DCORE_USE_NAMESPACE + +DS_BEGIN_NAMESPACE +namespace keynotify +{ + +KeyNotifyApplet::KeyNotifyApplet(QObject *parent) + : DApplet(parent) +{ +} + +KeyNotifyApplet::~KeyNotifyApplet() = default; + +bool KeyNotifyApplet::load() +{ + if (!Dtk::Gui::DGuiApplicationHelper::testAttribute(Dtk::Gui::DGuiApplicationHelper::IsWaylandPlatform)) { + return DApplet::load(); + } + + m_keyNotify = new TreelandKeyNotify(this); + connect(m_keyNotify, &TreelandKeyNotify::capsLockChanged, this, &KeyNotifyApplet::showCapsLockOsd); + connect(m_keyNotify, &TreelandKeyNotify::numLockChanged, this, &KeyNotifyApplet::showNumLockOsd); + initConfig(); + m_keyNotify->setCapsLockEnabled(m_config->value(QStringLiteral("capslockToggle")).toBool()); + return DApplet::load(); +} + +void KeyNotifyApplet::showCapsLockOsd(bool locked) +{ + sendOsd(locked ? QStringLiteral("CapsLockOn") : QStringLiteral("CapsLockOff")); +} + +void KeyNotifyApplet::showNumLockOsd(bool locked) +{ + sendOsd(locked ? QStringLiteral("NumLockOn") : QStringLiteral("NumLockOff")); +} + +void KeyNotifyApplet::updateCapsLockToggle(const QString &key) +{ + if (key != QStringLiteral("capslockToggle")) { + return; + } + + m_keyNotify->setCapsLockEnabled(m_config->value(key).toBool()); +} + +void KeyNotifyApplet::sendOsd(const QString &osdType) +{ + DDBusSender().service("org.deepin.dde.shell").path("/org/deepin/dde/shell/osd").interface("org.deepin.dde.shell.osd").method("ShowOSD").arg(osdType).call(); +} + +void KeyNotifyApplet::initConfig() +{ + m_config = Dtk::Core::DConfig::create(QStringLiteral("org.deepin.dde.daemon"), QStringLiteral("org.deepin.dde.daemon.keyboard"), QString(), this); + connect(m_config, &Dtk::Core::DConfig::valueChanged, this, &KeyNotifyApplet::updateCapsLockToggle); +} + +D_APPLET_CLASS(KeyNotifyApplet) + +} +DS_END_NAMESPACE + +#include "keynotifyapplet.moc" diff --git a/applets/dde-key-notify/keynotifyapplet.h b/applets/dde-key-notify/keynotifyapplet.h new file mode 100644 index 000000000..1f664e622 --- /dev/null +++ b/applets/dde-key-notify/keynotifyapplet.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "applet.h" + +#include + +#include + +DS_BEGIN_NAMESPACE +namespace keynotify +{ + +class TreelandKeyNotify; + +class KeyNotifyApplet : public DApplet +{ + Q_OBJECT + +public: + explicit KeyNotifyApplet(QObject *parent = nullptr); + ~KeyNotifyApplet() override; + + bool load() override; + +private Q_SLOTS: + void showCapsLockOsd(bool locked); + void showNumLockOsd(bool locked); + void updateCapsLockToggle(const QString &key); + +private: + void sendOsd(const QString &osdType); + void initConfig(); + +private: + QPointer m_keyNotify; + Dtk::Core::DConfig *m_config = nullptr; +}; + +} +DS_END_NAMESPACE diff --git a/applets/dde-key-notify/package/metadata.json b/applets/dde-key-notify/package/metadata.json new file mode 100644 index 000000000..5fd63c575 --- /dev/null +++ b/applets/dde-key-notify/package/metadata.json @@ -0,0 +1,7 @@ +{ + "Plugin": { + "Version": "1.0", + "Id": "org.deepin.ds.dde-key-notify", + "Category": "DDE" + } +} diff --git a/applets/dde-key-notify/treelandkeynotify.cpp b/applets/dde-key-notify/treelandkeynotify.cpp new file mode 100644 index 000000000..1fda99bb8 --- /dev/null +++ b/applets/dde-key-notify/treelandkeynotify.cpp @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "treelandkeynotify.h" + +#include "wayland-treeland-keyboard-state-notify-unstable-v1-client-protocol.h" + +DS_BEGIN_NAMESPACE +namespace keynotify +{ + +TreelandKeyNotify::TreelandKeyNotify(QObject *parent) + : QWaylandClientExtensionTemplate(treeland_keyboard_state_notify_manager_v1_interface.version) +{ + setParent(parent); + connect(this, &TreelandKeyNotify::activeChanged, this, &TreelandKeyNotify::updateWatcher); +} + +void TreelandKeyNotify::setCapsLockEnabled(bool enabled) +{ + if (m_capsLockEnabled == enabled) { + return; + } + + m_capsLockEnabled = enabled; + updateWatcher(); +} + +void TreelandKeyNotify::updateWatcher() +{ + if (!isActive()) { + if (m_watcher) { + m_watcher->deleteLater(); + m_watcher = nullptr; + } + return; + } + + if (!m_watcher) { + m_watcher = createWatcher(this); + if (!m_watcher) { + return; + } + + connect(m_watcher, &TreelandKeyWatcher::stateChanged, this, [this](uint32_t modifier, uint32_t state) { + if (modifier == TreelandKeyWatcher::modifier_caps_lock) { + if (state == TreelandKeyWatcher::modifier_state_locked) { + Q_EMIT capsLockChanged(true); + } else if (state == TreelandKeyWatcher::modifier_state_unlocked) { + Q_EMIT capsLockChanged(false); + } + } else if (modifier == TreelandKeyWatcher::modifier_num_lock) { + if (state == TreelandKeyWatcher::modifier_state_locked) { + Q_EMIT numLockChanged(true); + } else if (state == TreelandKeyWatcher::modifier_state_unlocked) { + Q_EMIT numLockChanged(false); + } + } + }); + } + + m_watcher->watchLocks(m_capsLockEnabled); +} + +TreelandKeyWatcher *TreelandKeyNotify::createWatcher(QObject *parent) +{ + if (!isActive()) { + return nullptr; + } + + auto *watcher = get_keyboard_state_watcher(nullptr); + if (!watcher) { + return nullptr; + } + + return new TreelandKeyWatcher(watcher, parent); +} + +TreelandKeyWatcher::TreelandKeyWatcher(struct ::treeland_keyboard_state_watcher_v1 *object, QObject *parent) + : QObject(parent) + , QtWayland::treeland_keyboard_state_watcher_v1(object) +{ +} + +void TreelandKeyWatcher::watchLocks(bool watchCapsLock) +{ + set_modifiers(watchCapsLock ? modifier_caps_lock | modifier_num_lock : modifier_num_lock); + set_flags(watch_flag_locked | watch_flag_unlocked); + apply(); +} + +void TreelandKeyWatcher::treeland_keyboard_state_watcher_v1_state_changed(uint32_t modifier, uint32_t state) +{ + Q_EMIT stateChanged(modifier, state); +} + +} +DS_END_NAMESPACE diff --git a/applets/dde-key-notify/treelandkeynotify.h b/applets/dde-key-notify/treelandkeynotify.h new file mode 100644 index 000000000..7ddd78fa7 --- /dev/null +++ b/applets/dde-key-notify/treelandkeynotify.h @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "dsglobal.h" +#include "qwayland-treeland-keyboard-state-notify-unstable-v1.h" + +#include +#include + +struct treeland_keyboard_state_watcher_v1; + +DS_BEGIN_NAMESPACE +namespace keynotify +{ + +class TreelandKeyWatcher; + +class TreelandKeyNotify : public QWaylandClientExtensionTemplate, public QtWayland::treeland_keyboard_state_notify_manager_v1 +{ + Q_OBJECT + +public: + explicit TreelandKeyNotify(QObject *parent = nullptr); + + void setCapsLockEnabled(bool enabled); + +Q_SIGNALS: + void capsLockChanged(bool locked); + void numLockChanged(bool locked); + +private Q_SLOTS: + void updateWatcher(); + +private: + TreelandKeyWatcher *createWatcher(QObject *parent = nullptr); + +private: + bool m_capsLockEnabled = false; + QPointer m_watcher; +}; + +class TreelandKeyWatcher : public QObject, public QtWayland::treeland_keyboard_state_watcher_v1 +{ + Q_OBJECT + +public: + explicit TreelandKeyWatcher(struct ::treeland_keyboard_state_watcher_v1 *object, QObject *parent = nullptr); + + void watchLocks(bool watchCapsLock); + +Q_SIGNALS: + void stateChanged(uint32_t modifier, uint32_t state); + +protected: + void treeland_keyboard_state_watcher_v1_state_changed(uint32_t modifier, uint32_t state) override; +}; + +} +DS_END_NAMESPACE diff --git a/debian/control b/debian/control index b98ab6b1c..80383de34 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.5.7), + treeland-protocols (>> 0.5.7), wayland-protocols, Standards-Version: 4.6.2 Homepage: https://github.com/linuxdeepin/dde-shell diff --git a/debian/dde-shell.install b/debian/dde-shell.install index 228149a31..c85ddeb6b 100644 --- a/debian/dde-shell.install +++ b/debian/dde-shell.install @@ -2,6 +2,7 @@ usr/bin/* usr/lib/*/dde-shell/org.deepin.ds.dde-am* usr/lib/*/dde-shell/org.deepin.ds.dde-appearance* usr/lib/*/dde-shell/org.deepin.ds.dde-apps* +usr/lib/*/dde-shell/org.deepin.ds.dde-key-notify* usr/lib/*/dde-shell/org.deepin.ds.dde-shutdown* usr/lib/*/dde-shell/org.deepin.ds.dock* usr/lib/*/dde-shell/org.deepin.ds.notification* @@ -19,6 +20,7 @@ usr/share/dde-shell/*/translations usr/share/dde-shell/org.deepin.ds.dde-am*/ usr/share/dde-shell/org.deepin.ds.dde-appearance*/ usr/share/dde-shell/org.deepin.ds.dde-apps*/ +usr/share/dde-shell/org.deepin.ds.dde-key-notify*/ usr/share/dde-shell/org.deepin.ds.dde-shutdown*/ usr/share/dde-shell/org.deepin.ds.dock*/ usr/share/dde-shell/org.deepin.ds.notification*/