KWin
Loading...
Searching...
No Matches
x11_standalone_keyboard.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2022 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
8
9#include "main.h"
10
11#include <QDebug>
12
13#define explicit dont_use_cxx_explicit
14#include <xcb/xkb.h>
15#undef explicit
16#include <xkbcommon/xkbcommon-x11.h>
17
18namespace KWin
19{
20
22{
23public:
24 X11KeyboardFilter(X11Keyboard *kbd, int eventType)
25 : X11EventFilter(eventType)
26 , m_kbd(kbd)
27 {
28 }
29
30 bool event(xcb_generic_event_t *event) override
31 {
32 return m_kbd->event(event);
33 }
34
35private:
36 X11Keyboard *m_kbd;
37};
38
40 : m_xkbContext(xkb_context_new(XKB_CONTEXT_NO_FLAGS))
41{
42 const xcb_query_extension_reply_t *reply = xcb_get_extension_data(kwinApp()->x11Connection(), &xcb_xkb_id);
43 if (!reply || !reply->present) {
44 qWarning() << "XKeyboard extension is unavailable";
45 return;
46 }
47
48 m_deviceId = xkb_x11_get_core_keyboard_device_id(kwinApp()->x11Connection());
49 if (m_deviceId == -1) {
50 qWarning() << "xkb_x11_get_core_keyboard_device_id() failed";
51 return;
52 }
53
54 enum {
55 requiredEvents =
56 (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY
57 | XCB_XKB_EVENT_TYPE_MAP_NOTIFY
58 | XCB_XKB_EVENT_TYPE_STATE_NOTIFY),
59
60 requiredNknDetails =
61 (XCB_XKB_NKN_DETAIL_KEYCODES),
62
63 requiredMapParts =
64 (XCB_XKB_MAP_PART_KEY_TYPES
65 | XCB_XKB_MAP_PART_KEY_SYMS
66 | XCB_XKB_MAP_PART_MODIFIER_MAP
67 | XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS
68 | XCB_XKB_MAP_PART_KEY_ACTIONS
69 | XCB_XKB_MAP_PART_VIRTUAL_MODS
70 | XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP),
71
72 requiredStateDetails =
73 (XCB_XKB_STATE_PART_MODIFIER_BASE
74 | XCB_XKB_STATE_PART_MODIFIER_LATCH
75 | XCB_XKB_STATE_PART_MODIFIER_LOCK
76 | XCB_XKB_STATE_PART_GROUP_BASE
77 | XCB_XKB_STATE_PART_GROUP_LATCH
78 | XCB_XKB_STATE_PART_GROUP_LOCK),
79 };
80
81 static const xcb_xkb_select_events_details_t details = {
82 .affectNewKeyboard = requiredNknDetails,
83 .newKeyboardDetails = requiredNknDetails,
84 .affectState = requiredStateDetails,
85 .stateDetails = requiredStateDetails,
86 };
87
88 xcb_void_cookie_t cookie =
89 xcb_xkb_select_events_aux_checked(kwinApp()->x11Connection(),
90 m_deviceId,
91 requiredEvents,
92 0,
93 0,
94 requiredMapParts,
95 requiredMapParts,
96 &details);
97
98 xcb_generic_error_t *error = xcb_request_check(kwinApp()->x11Connection(), cookie);
99 if (error) {
100 free(error);
101 return;
102 }
103
104 updateKeymap();
105
106 m_filter = std::make_unique<X11KeyboardFilter>(this, reply->first_event);
107}
108
110{
111 if (m_xkbState) {
112 xkb_state_unref(m_xkbState);
113 m_xkbState = nullptr;
114 }
115 if (m_xkbKeymap) {
116 xkb_keymap_unref(m_xkbKeymap);
117 m_xkbKeymap = nullptr;
118 }
119 if (m_xkbContext) {
120 xkb_context_unref(m_xkbContext);
121 m_xkbContext = nullptr;
122 }
123}
124
125bool X11Keyboard::event(xcb_generic_event_t *gevent)
126{
127 union xkb_event {
128 struct
129 {
130 uint8_t response_type;
131 uint8_t xkbType;
132 uint16_t sequence;
133 xcb_timestamp_t time;
134 uint8_t deviceID;
135 } any;
136 xcb_xkb_new_keyboard_notify_event_t new_keyboard_notify;
137 xcb_xkb_map_notify_event_t map_notify;
138 xcb_xkb_state_notify_event_t state_notify;
139 } *event = reinterpret_cast<union xkb_event *>(gevent);
140
141 if (event->any.deviceID == m_deviceId) {
142 switch (event->any.xkbType) {
143 case XCB_XKB_NEW_KEYBOARD_NOTIFY:
144 if (event->new_keyboard_notify.changed & XCB_XKB_NKN_DETAIL_KEYCODES) {
145 updateKeymap();
146 }
147 break;
148
149 case XCB_XKB_MAP_NOTIFY:
150 updateKeymap();
151 break;
152
153 case XCB_XKB_STATE_NOTIFY:
154 xkb_state_update_mask(m_xkbState,
155 event->state_notify.baseMods,
156 event->state_notify.latchedMods,
157 event->state_notify.lockedMods,
158 event->state_notify.baseGroup,
159 event->state_notify.latchedGroup,
160 event->state_notify.lockedGroup);
161 break;
162 }
163 }
164
165 return false;
166}
167
168void X11Keyboard::updateKeymap()
169{
170 xkb_keymap *keymap = xkb_x11_keymap_new_from_device(m_xkbContext, kwinApp()->x11Connection(), m_deviceId, XKB_KEYMAP_COMPILE_NO_FLAGS);
171 if (!keymap) {
172 qWarning() << "xkb_x11_keymap_new_from_device() failed";
173 return;
174 }
175
176 xkb_state *state = xkb_x11_state_new_from_device(keymap, kwinApp()->x11Connection(), m_deviceId);
177 if (!state) {
178 xkb_keymap_unref(keymap);
179 qWarning() << "xkb_x11_state_new_from_device() failed";
180 return;
181 }
182
183 if (m_xkbState) {
184 xkb_state_unref(m_xkbState);
185 }
186 if (m_xkbKeymap) {
187 xkb_keymap_unref(m_xkbKeymap);
188 }
189
190 m_xkbState = state;
191 m_xkbKeymap = keymap;
192}
193
194xkb_keymap *X11Keyboard::xkbKeymap() const
195{
196 return m_xkbKeymap;
197}
198
199xkb_state *X11Keyboard::xkbState() const
200{
201 return m_xkbState;
202}
203
204Qt::KeyboardModifiers X11Keyboard::modifiers() const
205{
206 Qt::KeyboardModifiers mods;
207
208 if (xkb_state_mod_name_is_active(m_xkbState, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) == 1 || xkb_state_mod_name_is_active(m_xkbState, XKB_MOD_NAME_CAPS, XKB_STATE_MODS_EFFECTIVE) == 1) {
209 mods |= Qt::ShiftModifier;
210 }
211 if (xkb_state_mod_name_is_active(m_xkbState, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) == 1) {
212 mods |= Qt::AltModifier;
213 }
214 if (xkb_state_mod_name_is_active(m_xkbState, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) == 1) {
215 mods |= Qt::ControlModifier;
216 }
217 if (xkb_state_mod_name_is_active(m_xkbState, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE) == 1) {
218 mods |= Qt::MetaModifier;
219 }
220
221 return mods;
222}
223
224} // namespace KWin
bool event(xcb_generic_event_t *event) override
X11KeyboardFilter(X11Keyboard *kbd, int eventType)
xkb_state * xkbState() const
xkb_keymap * xkbKeymap() const
Qt::KeyboardModifiers modifiers() const
bool event(xcb_generic_event_t *event)