Skip to content

Commit 6041439

Browse files
committed
feat: add key state notification applet for Wayland
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
1 parent 2ea0136 commit 6041439

8 files changed

Lines changed: 320 additions & 0 deletions

File tree

applets/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@
55
add_subdirectory(dde-am)
66
add_subdirectory(dde-appearance)
77
add_subdirectory(dde-apps)
8+
add_subdirectory(dde-key-notify)
89
add_subdirectory(dde-shutdown)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
2+
#
3+
# SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
find_package(TreelandProtocols REQUIRED)
6+
7+
set(TREELAND_KEYBOARD_STATE_NOTIFY_PROTOCOL
8+
${TREELAND_PROTOCOLS_DATA_DIR}/treeland-keyboard-state-notify-unstable-v1.xml
9+
)
10+
11+
add_library(dde-key-notify SHARED
12+
keynotifyapplet.cpp
13+
keynotifyapplet.h
14+
treelandkeynotify.cpp
15+
treelandkeynotify.h
16+
)
17+
18+
qt_generate_wayland_protocol_client_sources(dde-key-notify
19+
NO_INCLUDE_CORE_ONLY
20+
FILES
21+
${TREELAND_KEYBOARD_STATE_NOTIFY_PROTOCOL}
22+
)
23+
24+
target_link_libraries(dde-key-notify PRIVATE
25+
dde-shell-frame
26+
Qt${QT_VERSION_MAJOR}::DBus
27+
Qt${QT_VERSION_MAJOR}::WaylandClient
28+
)
29+
30+
ds_install_package(PACKAGE org.deepin.ds.dde-key-notify TARGET dde-key-notify)
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
2+
//
3+
// SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
#include "keynotifyapplet.h"
6+
7+
#include "pluginfactory.h"
8+
#include "treelandkeynotify.h"
9+
10+
#include <DDBusSender>
11+
#include <DGuiApplicationHelper>
12+
13+
DCORE_USE_NAMESPACE
14+
15+
DS_BEGIN_NAMESPACE
16+
namespace keynotify
17+
{
18+
19+
KeyNotifyApplet::KeyNotifyApplet(QObject *parent)
20+
: DApplet(parent)
21+
{
22+
}
23+
24+
KeyNotifyApplet::~KeyNotifyApplet() = default;
25+
26+
bool KeyNotifyApplet::load()
27+
{
28+
if (!Dtk::Gui::DGuiApplicationHelper::testAttribute(Dtk::Gui::DGuiApplicationHelper::IsWaylandPlatform)) {
29+
return DApplet::load();
30+
}
31+
32+
m_keyNotify = new TreelandKeyNotify(this);
33+
connect(m_keyNotify, &TreelandKeyNotify::capsLockChanged, this, &KeyNotifyApplet::showCapsLockOsd);
34+
connect(m_keyNotify, &TreelandKeyNotify::numLockChanged, this, &KeyNotifyApplet::showNumLockOsd);
35+
initConfig();
36+
m_keyNotify->setCapsLockEnabled(m_config->value(QStringLiteral("capslockToggle")).toBool());
37+
return DApplet::load();
38+
}
39+
40+
void KeyNotifyApplet::showCapsLockOsd(bool locked)
41+
{
42+
sendOsd(locked ? QStringLiteral("CapsLockOn") : QStringLiteral("CapsLockOff"));
43+
}
44+
45+
void KeyNotifyApplet::showNumLockOsd(bool locked)
46+
{
47+
sendOsd(locked ? QStringLiteral("NumLockOn") : QStringLiteral("NumLockOff"));
48+
}
49+
50+
void KeyNotifyApplet::updateCapsLockToggle(const QString &key)
51+
{
52+
if (key != QStringLiteral("capslockToggle")) {
53+
return;
54+
}
55+
56+
m_keyNotify->setCapsLockEnabled(m_config->value(key).toBool());
57+
}
58+
59+
void KeyNotifyApplet::sendOsd(const QString &osdType)
60+
{
61+
DDBusSender().service("org.deepin.dde.shell").path("/org/deepin/dde/shell/osd").interface("org.deepin.dde.shell.osd").method("ShowOSD").arg(osdType).call();
62+
}
63+
64+
void KeyNotifyApplet::initConfig()
65+
{
66+
m_config = Dtk::Core::DConfig::create(QStringLiteral("org.deepin.dde.daemon"), QStringLiteral("org.deepin.dde.daemon.keyboard"), QString(), this);
67+
connect(m_config, &Dtk::Core::DConfig::valueChanged, this, &KeyNotifyApplet::updateCapsLockToggle);
68+
}
69+
70+
D_APPLET_CLASS(KeyNotifyApplet)
71+
72+
}
73+
DS_END_NAMESPACE
74+
75+
#include "keynotifyapplet.moc"
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
2+
//
3+
// SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
#pragma once
6+
7+
#include "applet.h"
8+
9+
#include <DConfig>
10+
11+
#include <QPointer>
12+
13+
DS_BEGIN_NAMESPACE
14+
namespace keynotify
15+
{
16+
17+
class TreelandKeyNotify;
18+
19+
class KeyNotifyApplet : public DApplet
20+
{
21+
Q_OBJECT
22+
23+
public:
24+
explicit KeyNotifyApplet(QObject *parent = nullptr);
25+
~KeyNotifyApplet() override;
26+
27+
bool load() override;
28+
29+
private Q_SLOTS:
30+
void showCapsLockOsd(bool locked);
31+
void showNumLockOsd(bool locked);
32+
void updateCapsLockToggle(const QString &key);
33+
34+
private:
35+
void sendOsd(const QString &osdType);
36+
void initConfig();
37+
38+
private:
39+
QPointer<TreelandKeyNotify> m_keyNotify;
40+
Dtk::Core::DConfig *m_config = nullptr;
41+
};
42+
43+
}
44+
DS_END_NAMESPACE
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"Plugin": {
3+
"Version": "1.0",
4+
"Id": "org.deepin.ds.dde-key-notify",
5+
"Category": "DDE"
6+
}
7+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
2+
//
3+
// SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
#include "treelandkeynotify.h"
6+
7+
#include "wayland-treeland-keyboard-state-notify-unstable-v1-client-protocol.h"
8+
9+
DS_BEGIN_NAMESPACE
10+
namespace keynotify
11+
{
12+
13+
TreelandKeyNotify::TreelandKeyNotify(QObject *parent)
14+
: QWaylandClientExtensionTemplate<TreelandKeyNotify>(treeland_keyboard_state_notify_manager_v1_interface.version)
15+
{
16+
setParent(parent);
17+
connect(this, &TreelandKeyNotify::activeChanged, this, &TreelandKeyNotify::updateWatcher);
18+
}
19+
20+
void TreelandKeyNotify::setCapsLockEnabled(bool enabled)
21+
{
22+
if (m_capsLockEnabled == enabled) {
23+
return;
24+
}
25+
26+
m_capsLockEnabled = enabled;
27+
updateWatcher();
28+
}
29+
30+
void TreelandKeyNotify::updateWatcher()
31+
{
32+
if (!isActive()) {
33+
if (m_watcher) {
34+
m_watcher->deleteLater();
35+
m_watcher = nullptr;
36+
}
37+
return;
38+
}
39+
40+
if (!m_watcher) {
41+
m_watcher = createWatcher(this);
42+
if (!m_watcher) {
43+
return;
44+
}
45+
46+
connect(m_watcher, &TreelandKeyWatcher::stateChanged, this, [this](uint32_t modifier, uint32_t state) {
47+
if (modifier == TreelandKeyWatcher::modifier_caps_lock) {
48+
if (state == TreelandKeyWatcher::modifier_state_locked) {
49+
Q_EMIT capsLockChanged(true);
50+
} else if (state == TreelandKeyWatcher::modifier_state_unlocked) {
51+
Q_EMIT capsLockChanged(false);
52+
}
53+
} else if (modifier == TreelandKeyWatcher::modifier_num_lock) {
54+
if (state == TreelandKeyWatcher::modifier_state_locked) {
55+
Q_EMIT numLockChanged(true);
56+
} else if (state == TreelandKeyWatcher::modifier_state_unlocked) {
57+
Q_EMIT numLockChanged(false);
58+
}
59+
}
60+
});
61+
}
62+
63+
m_watcher->watchLocks(m_capsLockEnabled);
64+
}
65+
66+
TreelandKeyWatcher *TreelandKeyNotify::createWatcher(QObject *parent)
67+
{
68+
if (!isActive()) {
69+
return nullptr;
70+
}
71+
72+
auto *watcher = get_keyboard_state_watcher(nullptr);
73+
if (!watcher) {
74+
return nullptr;
75+
}
76+
77+
return new TreelandKeyWatcher(watcher, parent);
78+
}
79+
80+
TreelandKeyWatcher::TreelandKeyWatcher(struct ::treeland_keyboard_state_watcher_v1 *object, QObject *parent)
81+
: QObject(parent)
82+
, QtWayland::treeland_keyboard_state_watcher_v1(object)
83+
{
84+
}
85+
86+
void TreelandKeyWatcher::watchLocks(bool watchCapsLock)
87+
{
88+
set_modifiers(watchCapsLock ? modifier_caps_lock | modifier_num_lock : modifier_num_lock);
89+
set_flags(watch_flag_locked | watch_flag_unlocked);
90+
apply();
91+
}
92+
93+
void TreelandKeyWatcher::treeland_keyboard_state_watcher_v1_state_changed(uint32_t modifier, uint32_t state)
94+
{
95+
Q_EMIT stateChanged(modifier, state);
96+
}
97+
98+
}
99+
DS_END_NAMESPACE
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
2+
//
3+
// SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
#pragma once
6+
7+
#include "dsglobal.h"
8+
#include "qwayland-treeland-keyboard-state-notify-unstable-v1.h"
9+
10+
#include <QPointer>
11+
#include <QtWaylandClient/QWaylandClientExtension>
12+
13+
struct treeland_keyboard_state_watcher_v1;
14+
15+
DS_BEGIN_NAMESPACE
16+
namespace keynotify
17+
{
18+
19+
class TreelandKeyWatcher;
20+
21+
class TreelandKeyNotify : public QWaylandClientExtensionTemplate<TreelandKeyNotify>, public QtWayland::treeland_keyboard_state_notify_manager_v1
22+
{
23+
Q_OBJECT
24+
25+
public:
26+
explicit TreelandKeyNotify(QObject *parent = nullptr);
27+
28+
void setCapsLockEnabled(bool enabled);
29+
30+
Q_SIGNALS:
31+
void capsLockChanged(bool locked);
32+
void numLockChanged(bool locked);
33+
34+
private Q_SLOTS:
35+
void updateWatcher();
36+
37+
private:
38+
TreelandKeyWatcher *createWatcher(QObject *parent = nullptr);
39+
40+
private:
41+
bool m_capsLockEnabled = false;
42+
QPointer<TreelandKeyWatcher> m_watcher;
43+
};
44+
45+
class TreelandKeyWatcher : public QObject, public QtWayland::treeland_keyboard_state_watcher_v1
46+
{
47+
Q_OBJECT
48+
49+
public:
50+
explicit TreelandKeyWatcher(struct ::treeland_keyboard_state_watcher_v1 *object, QObject *parent = nullptr);
51+
52+
void watchLocks(bool watchCapsLock);
53+
54+
Q_SIGNALS:
55+
void stateChanged(uint32_t modifier, uint32_t state);
56+
57+
protected:
58+
void treeland_keyboard_state_watcher_v1_state_changed(uint32_t modifier, uint32_t state) override;
59+
};
60+
61+
}
62+
DS_END_NAMESPACE

debian/dde-shell.install

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ usr/bin/*
22
usr/lib/*/dde-shell/org.deepin.ds.dde-am*
33
usr/lib/*/dde-shell/org.deepin.ds.dde-appearance*
44
usr/lib/*/dde-shell/org.deepin.ds.dde-apps*
5+
usr/lib/*/dde-shell/org.deepin.ds.dde-key-notify*
56
usr/lib/*/dde-shell/org.deepin.ds.dde-shutdown*
67
usr/lib/*/dde-shell/org.deepin.ds.dock*
78
usr/lib/*/dde-shell/org.deepin.ds.notification*
@@ -18,6 +19,7 @@ usr/share/dde-shell/*/translations
1819
usr/share/dde-shell/org.deepin.ds.dde-am*/
1920
usr/share/dde-shell/org.deepin.ds.dde-appearance*/
2021
usr/share/dde-shell/org.deepin.ds.dde-apps*/
22+
usr/share/dde-shell/org.deepin.ds.dde-key-notify*/
2123
usr/share/dde-shell/org.deepin.ds.dde-shutdown*/
2224
usr/share/dde-shell/org.deepin.ds.dock*/
2325
usr/share/dde-shell/org.deepin.ds.notification*/

0 commit comments

Comments
 (0)