Changeset View
Changeset View
Standalone View
Standalone View
keyboard_input.cpp
- This file was copied to xkb.cpp.
Show All 29 Lines | |||||
30 | #include "wayland_server.h" | 30 | #include "wayland_server.h" | ||
31 | #include "workspace.h" | 31 | #include "workspace.h" | ||
32 | // KWayland | 32 | // KWayland | ||
33 | #include <KWayland/Server/datadevice_interface.h> | 33 | #include <KWayland/Server/datadevice_interface.h> | ||
34 | #include <KWayland/Server/seat_interface.h> | 34 | #include <KWayland/Server/seat_interface.h> | ||
35 | //screenlocker | 35 | //screenlocker | ||
36 | #include <KScreenLocker/KsldApp> | 36 | #include <KScreenLocker/KsldApp> | ||
37 | // Frameworks | 37 | // Frameworks | ||
38 | #include <KKeyServer> | | |||
39 | #include <KGlobalAccel> | 38 | #include <KGlobalAccel> | ||
40 | // Qt | 39 | // Qt | ||
41 | #include <QKeyEvent> | 40 | #include <QKeyEvent> | ||
42 | #include <QTemporaryFile> | | |||
43 | // xkbcommon | | |||
44 | #include <xkbcommon/xkbcommon.h> | | |||
45 | #include <xkbcommon/xkbcommon-compose.h> | | |||
46 | #include <xkbcommon/xkbcommon-keysyms.h> | | |||
47 | // system | | |||
48 | #include <sys/mman.h> | | |||
49 | #include <unistd.h> | | |||
50 | | ||||
51 | Q_LOGGING_CATEGORY(KWIN_XKB, "kwin_xkbcommon", QtCriticalMsg) | | |||
52 | 41 | | |||
53 | namespace KWin | 42 | namespace KWin | ||
54 | { | 43 | { | ||
55 | 44 | | |||
56 | static void xkbLogHandler(xkb_context *context, xkb_log_level priority, const char *format, va_list args) | | |||
57 | { | | |||
58 | Q_UNUSED(context) | | |||
59 | char buf[1024]; | | |||
60 | if (std::vsnprintf(buf, 1023, format, args) <= 0) { | | |||
61 | return; | | |||
62 | } | | |||
63 | switch (priority) { | | |||
64 | case XKB_LOG_LEVEL_DEBUG: | | |||
65 | qCDebug(KWIN_XKB) << "XKB:" << buf; | | |||
66 | break; | | |||
67 | case XKB_LOG_LEVEL_INFO: | | |||
68 | qCInfo(KWIN_XKB) << "XKB:" << buf; | | |||
69 | break; | | |||
70 | case XKB_LOG_LEVEL_WARNING: | | |||
71 | qCWarning(KWIN_XKB) << "XKB:" << buf; | | |||
72 | break; | | |||
73 | case XKB_LOG_LEVEL_ERROR: | | |||
74 | case XKB_LOG_LEVEL_CRITICAL: | | |||
75 | default: | | |||
76 | qCCritical(KWIN_XKB) << "XKB:" << buf; | | |||
77 | break; | | |||
78 | } | | |||
79 | } | | |||
80 | | ||||
81 | Xkb::Xkb(InputRedirection *input) | | |||
82 | : m_input(input) | | |||
83 | , m_context(xkb_context_new(static_cast<xkb_context_flags>(0))) | | |||
84 | , m_keymap(NULL) | | |||
85 | , m_state(NULL) | | |||
86 | , m_shiftModifier(0) | | |||
87 | , m_capsModifier(0) | | |||
88 | , m_controlModifier(0) | | |||
89 | , m_altModifier(0) | | |||
90 | , m_metaModifier(0) | | |||
91 | , m_numLock(0) | | |||
92 | , m_capsLock(0) | | |||
93 | , m_scrollLock(0) | | |||
94 | , m_modifiers(Qt::NoModifier) | | |||
95 | , m_consumedModifiers(Qt::NoModifier) | | |||
96 | , m_keysym(XKB_KEY_NoSymbol) | | |||
97 | , m_leds() | | |||
98 | { | | |||
99 | qRegisterMetaType<KWin::Xkb::LEDs>(); | | |||
100 | if (!m_context) { | | |||
101 | qCDebug(KWIN_XKB) << "Could not create xkb context"; | | |||
102 | } else { | | |||
103 | xkb_context_set_log_level(m_context, XKB_LOG_LEVEL_DEBUG); | | |||
104 | xkb_context_set_log_fn(m_context, &xkbLogHandler); | | |||
105 | | ||||
106 | // get locale as described in xkbcommon doc | | |||
107 | // cannot use QLocale as it drops the modifier part | | |||
108 | QByteArray locale = qgetenv("LC_ALL"); | | |||
109 | if (locale.isEmpty()) { | | |||
110 | locale = qgetenv("LC_CTYPE"); | | |||
111 | } | | |||
112 | if (locale.isEmpty()) { | | |||
113 | locale = qgetenv("LANG"); | | |||
114 | } | | |||
115 | if (locale.isEmpty()) { | | |||
116 | locale = QByteArrayLiteral("C"); | | |||
117 | } | | |||
118 | | ||||
119 | m_compose.table = xkb_compose_table_new_from_locale(m_context, locale.constData(), XKB_COMPOSE_COMPILE_NO_FLAGS); | | |||
120 | if (m_compose.table) { | | |||
121 | m_compose.state = xkb_compose_state_new(m_compose.table, XKB_COMPOSE_STATE_NO_FLAGS); | | |||
122 | } | | |||
123 | } | | |||
124 | } | | |||
125 | | ||||
126 | Xkb::~Xkb() | | |||
127 | { | | |||
128 | xkb_compose_state_unref(m_compose.state); | | |||
129 | xkb_compose_table_unref(m_compose.table); | | |||
130 | xkb_state_unref(m_state); | | |||
131 | xkb_keymap_unref(m_keymap); | | |||
132 | xkb_context_unref(m_context); | | |||
133 | } | | |||
134 | | ||||
135 | void Xkb::reconfigure() | | |||
136 | { | | |||
137 | if (!m_context) { | | |||
138 | return; | | |||
139 | } | | |||
140 | | ||||
141 | xkb_keymap *keymap = nullptr; | | |||
142 | if (!qEnvironmentVariableIsSet("KWIN_XKB_DEFAULT_KEYMAP")) { | | |||
143 | keymap = loadKeymapFromConfig(); | | |||
144 | } | | |||
145 | if (!keymap) { | | |||
146 | qCDebug(KWIN_XKB) << "Could not create xkb keymap from configuration"; | | |||
147 | keymap = loadDefaultKeymap(); | | |||
148 | } | | |||
149 | if (keymap) { | | |||
150 | updateKeymap(keymap); | | |||
151 | } else { | | |||
152 | qCDebug(KWIN_XKB) << "Could not create default xkb keymap"; | | |||
153 | } | | |||
154 | } | | |||
155 | | ||||
156 | xkb_keymap *Xkb::loadKeymapFromConfig() | | |||
157 | { | | |||
158 | // load config | | |||
159 | if (!m_config) { | | |||
160 | return nullptr; | | |||
161 | } | | |||
162 | const KConfigGroup config = m_config->group("Layout"); | | |||
163 | const QByteArray model = config.readEntry("Model", "pc104").toLocal8Bit(); | | |||
164 | const QByteArray layout = config.readEntry("LayoutList", "").toLocal8Bit(); | | |||
165 | const QByteArray options = config.readEntry("Options", "").toLocal8Bit(); | | |||
166 | | ||||
167 | xkb_rule_names ruleNames = { | | |||
168 | .rules = nullptr, | | |||
169 | .model = model.constData(), | | |||
170 | .layout = layout.constData(), | | |||
171 | .variant = nullptr, | | |||
172 | .options = options.constData() | | |||
173 | }; | | |||
174 | return xkb_keymap_new_from_names(m_context, &ruleNames, XKB_KEYMAP_COMPILE_NO_FLAGS); | | |||
175 | } | | |||
176 | | ||||
177 | xkb_keymap *Xkb::loadDefaultKeymap() | | |||
178 | { | | |||
179 | return xkb_keymap_new_from_names(m_context, nullptr, XKB_KEYMAP_COMPILE_NO_FLAGS); | | |||
180 | } | | |||
181 | | ||||
182 | void Xkb::installKeymap(int fd, uint32_t size) | | |||
183 | { | | |||
184 | if (!m_context) { | | |||
185 | return; | | |||
186 | } | | |||
187 | char *map = reinterpret_cast<char*>(mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0)); | | |||
188 | if (map == MAP_FAILED) { | | |||
189 | return; | | |||
190 | } | | |||
191 | xkb_keymap *keymap = xkb_keymap_new_from_string(m_context, map, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_MAP_COMPILE_PLACEHOLDER); | | |||
192 | munmap(map, size); | | |||
193 | if (!keymap) { | | |||
194 | qCDebug(KWIN_XKB) << "Could not map keymap from file"; | | |||
195 | return; | | |||
196 | } | | |||
197 | updateKeymap(keymap); | | |||
198 | } | | |||
199 | | ||||
200 | void Xkb::updateKeymap(xkb_keymap *keymap) | | |||
201 | { | | |||
202 | Q_ASSERT(keymap); | | |||
203 | xkb_state *state = xkb_state_new(keymap); | | |||
204 | if (!state) { | | |||
205 | qCDebug(KWIN_XKB) << "Could not create XKB state"; | | |||
206 | xkb_keymap_unref(keymap); | | |||
207 | return; | | |||
208 | } | | |||
209 | // now release the old ones | | |||
210 | xkb_state_unref(m_state); | | |||
211 | xkb_keymap_unref(m_keymap); | | |||
212 | | ||||
213 | m_keymap = keymap; | | |||
214 | m_state = state; | | |||
215 | | ||||
216 | m_shiftModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_SHIFT); | | |||
217 | m_capsModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_CAPS); | | |||
218 | m_controlModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_CTRL); | | |||
219 | m_altModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_ALT); | | |||
220 | m_metaModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_LOGO); | | |||
221 | | ||||
222 | m_numLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_NUM); | | |||
223 | m_capsLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_CAPS); | | |||
224 | m_scrollLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_SCROLL); | | |||
225 | | ||||
226 | m_currentLayout = xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE); | | |||
227 | | ||||
228 | createKeymapFile(); | | |||
229 | } | | |||
230 | | ||||
231 | void Xkb::createKeymapFile() | | |||
232 | { | | |||
233 | if (!waylandServer()) { | | |||
234 | return; | | |||
235 | } | | |||
236 | // TODO: uninstall keymap on server? | | |||
237 | if (!m_keymap) { | | |||
238 | return; | | |||
239 | } | | |||
240 | | ||||
241 | ScopedCPointer<char> keymapString(xkb_keymap_get_as_string(m_keymap, XKB_KEYMAP_FORMAT_TEXT_V1)); | | |||
242 | if (keymapString.isNull()) { | | |||
243 | return; | | |||
244 | } | | |||
245 | const uint size = qstrlen(keymapString.data()) + 1; | | |||
246 | | ||||
247 | QTemporaryFile *tmp = new QTemporaryFile(m_input); | | |||
248 | if (!tmp->open()) { | | |||
249 | delete tmp; | | |||
250 | return; | | |||
251 | } | | |||
252 | unlink(tmp->fileName().toUtf8().constData()); | | |||
253 | if (!tmp->resize(size)) { | | |||
254 | delete tmp; | | |||
255 | return; | | |||
256 | } | | |||
257 | uchar *address = tmp->map(0, size); | | |||
258 | if (!address) { | | |||
259 | return; | | |||
260 | } | | |||
261 | if (qstrncpy(reinterpret_cast<char*>(address), keymapString.data(), size) == nullptr) { | | |||
262 | delete tmp; | | |||
263 | return; | | |||
264 | } | | |||
265 | waylandServer()->seat()->setKeymap(tmp->handle(), size); | | |||
266 | } | | |||
267 | | ||||
268 | void Xkb::updateModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group) | | |||
269 | { | | |||
270 | if (!m_keymap || !m_state) { | | |||
271 | return; | | |||
272 | } | | |||
273 | xkb_state_update_mask(m_state, modsDepressed, modsLatched, modsLocked, 0, 0, group); | | |||
274 | updateModifiers(); | | |||
275 | } | | |||
276 | | ||||
277 | void Xkb::updateKey(uint32_t key, InputRedirection::KeyboardKeyState state) | | |||
278 | { | | |||
279 | if (!m_keymap || !m_state) { | | |||
280 | return; | | |||
281 | } | | |||
282 | xkb_state_update_key(m_state, key + 8, static_cast<xkb_key_direction>(state)); | | |||
283 | if (state == InputRedirection::KeyboardKeyPressed) { | | |||
284 | const auto sym = toKeysym(key); | | |||
285 | if (m_compose.state && xkb_compose_state_feed(m_compose.state, sym) == XKB_COMPOSE_FEED_ACCEPTED) { | | |||
286 | switch (xkb_compose_state_get_status(m_compose.state)) { | | |||
287 | case XKB_COMPOSE_NOTHING: | | |||
288 | m_keysym = sym; | | |||
289 | break; | | |||
290 | case XKB_COMPOSE_COMPOSED: | | |||
291 | m_keysym = xkb_compose_state_get_one_sym(m_compose.state); | | |||
292 | break; | | |||
293 | default: | | |||
294 | m_keysym = XKB_KEY_NoSymbol; | | |||
295 | break; | | |||
296 | } | | |||
297 | } else { | | |||
298 | m_keysym = sym; | | |||
299 | } | | |||
300 | } | | |||
301 | updateModifiers(); | | |||
302 | updateConsumedModifiers(key); | | |||
303 | } | | |||
304 | | ||||
305 | void Xkb::updateModifiers() | | |||
306 | { | | |||
307 | Qt::KeyboardModifiers mods = Qt::NoModifier; | | |||
308 | if (xkb_state_mod_index_is_active(m_state, m_shiftModifier, XKB_STATE_MODS_EFFECTIVE) == 1 || | | |||
309 | xkb_state_mod_index_is_active(m_state, m_capsModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { | | |||
310 | mods |= Qt::ShiftModifier; | | |||
311 | } | | |||
312 | if (xkb_state_mod_index_is_active(m_state, m_altModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { | | |||
313 | mods |= Qt::AltModifier; | | |||
314 | } | | |||
315 | if (xkb_state_mod_index_is_active(m_state, m_controlModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { | | |||
316 | mods |= Qt::ControlModifier; | | |||
317 | } | | |||
318 | if (xkb_state_mod_index_is_active(m_state, m_metaModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { | | |||
319 | mods |= Qt::MetaModifier; | | |||
320 | } | | |||
321 | m_modifiers = mods; | | |||
322 | | ||||
323 | // update LEDs | | |||
324 | LEDs leds; | | |||
325 | if (xkb_state_led_index_is_active(m_state, m_numLock) == 1) { | | |||
326 | leds = leds | LED::NumLock; | | |||
327 | } | | |||
328 | if (xkb_state_led_index_is_active(m_state, m_capsLock) == 1) { | | |||
329 | leds = leds | LED::CapsLock; | | |||
330 | } | | |||
331 | if (xkb_state_led_index_is_active(m_state, m_scrollLock) == 1) { | | |||
332 | leds = leds | LED::ScrollLock; | | |||
333 | } | | |||
334 | if (m_leds != leds) { | | |||
335 | m_leds = leds; | | |||
336 | emit m_input->keyboard()->ledsChanged(m_leds); | | |||
337 | } | | |||
338 | | ||||
339 | const xkb_layout_index_t layout = xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE); | | |||
340 | if (layout != m_currentLayout) { | | |||
341 | m_currentLayout = layout; | | |||
342 | } | | |||
343 | if (waylandServer()) { | | |||
344 | waylandServer()->seat()->updateKeyboardModifiers(xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_DEPRESSED)), | | |||
345 | xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LATCHED)), | | |||
346 | xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LOCKED)), | | |||
347 | layout); | | |||
348 | } | | |||
349 | } | | |||
350 | | ||||
351 | QString Xkb::layoutName() const | | |||
352 | { | | |||
353 | return layoutName(m_currentLayout); | | |||
354 | } | | |||
355 | | ||||
356 | QString Xkb::layoutName(xkb_layout_index_t layout) const | | |||
357 | { | | |||
358 | return QString::fromLocal8Bit(xkb_keymap_layout_get_name(m_keymap, layout)); | | |||
359 | } | | |||
360 | | ||||
361 | QMap<xkb_layout_index_t, QString> Xkb::layoutNames() const | | |||
362 | { | | |||
363 | QMap<xkb_layout_index_t, QString> layouts; | | |||
364 | const auto size = xkb_keymap_num_layouts(m_keymap); | | |||
365 | for (xkb_layout_index_t i = 0; i < size; i++) { | | |||
366 | layouts.insert(i, layoutName(i)); | | |||
367 | } | | |||
368 | return layouts; | | |||
369 | } | | |||
370 | | ||||
371 | void Xkb::updateConsumedModifiers(uint32_t key) | | |||
372 | { | | |||
373 | Qt::KeyboardModifiers mods = Qt::NoModifier; | | |||
374 | if (xkb_state_mod_index_is_consumed2(m_state, key + 8, m_shiftModifier, XKB_CONSUMED_MODE_GTK) == 1) { | | |||
375 | mods |= Qt::ShiftModifier; | | |||
376 | } | | |||
377 | if (xkb_state_mod_index_is_consumed2(m_state, key + 8, m_altModifier, XKB_CONSUMED_MODE_GTK) == 1) { | | |||
378 | mods |= Qt::AltModifier; | | |||
379 | } | | |||
380 | if (xkb_state_mod_index_is_consumed2(m_state, key + 8, m_controlModifier, XKB_CONSUMED_MODE_GTK) == 1) { | | |||
381 | mods |= Qt::ControlModifier; | | |||
382 | } | | |||
383 | if (xkb_state_mod_index_is_consumed2(m_state, key + 8, m_metaModifier, XKB_CONSUMED_MODE_GTK) == 1) { | | |||
384 | mods |= Qt::MetaModifier; | | |||
385 | } | | |||
386 | m_consumedModifiers = mods; | | |||
387 | } | | |||
388 | | ||||
389 | Qt::KeyboardModifiers Xkb::modifiersRelevantForGlobalShortcuts() const | | |||
390 | { | | |||
391 | Qt::KeyboardModifiers mods = Qt::NoModifier; | | |||
392 | if (xkb_state_mod_index_is_active(m_state, m_shiftModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { | | |||
393 | mods |= Qt::ShiftModifier; | | |||
394 | } | | |||
395 | if (xkb_state_mod_index_is_active(m_state, m_altModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { | | |||
396 | mods |= Qt::AltModifier; | | |||
397 | } | | |||
398 | if (xkb_state_mod_index_is_active(m_state, m_controlModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { | | |||
399 | mods |= Qt::ControlModifier; | | |||
400 | } | | |||
401 | if (xkb_state_mod_index_is_active(m_state, m_metaModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { | | |||
402 | mods |= Qt::MetaModifier; | | |||
403 | } | | |||
404 | | ||||
405 | Qt::KeyboardModifiers consumedMods = m_consumedModifiers; | | |||
406 | if ((mods & Qt::ShiftModifier) && (consumedMods == Qt::ShiftModifier)) { | | |||
407 | // test whether current keysym is a letter | | |||
408 | // in that case the shift should be removed from the consumed modifiers again | | |||
409 | // otherwise it would not be possible to trigger e.g. Shift+W as a shortcut | | |||
410 | // see BUG: 370341 | | |||
411 | if (QChar(toQtKey(m_keysym)).isLetter()) { | | |||
412 | consumedMods = Qt::KeyboardModifiers(); | | |||
413 | } | | |||
414 | } | | |||
415 | | ||||
416 | return mods & ~consumedMods; | | |||
417 | } | | |||
418 | | ||||
419 | xkb_keysym_t Xkb::toKeysym(uint32_t key) | | |||
420 | { | | |||
421 | if (!m_state) { | | |||
422 | return XKB_KEY_NoSymbol; | | |||
423 | } | | |||
424 | return xkb_state_key_get_one_sym(m_state, key + 8); | | |||
425 | } | | |||
426 | | ||||
427 | QString Xkb::toString(xkb_keysym_t keysym) | | |||
428 | { | | |||
429 | if (!m_state || keysym == XKB_KEY_NoSymbol) { | | |||
430 | return QString(); | | |||
431 | } | | |||
432 | QByteArray byteArray(7, 0); | | |||
433 | int ok = xkb_keysym_to_utf8(keysym, byteArray.data(), byteArray.size()); | | |||
434 | if (ok == -1 || ok == 0) { | | |||
435 | return QString(); | | |||
436 | } | | |||
437 | return QString::fromUtf8(byteArray.constData()); | | |||
438 | } | | |||
439 | | ||||
440 | Qt::Key Xkb::toQtKey(xkb_keysym_t keysym) const | | |||
441 | { | | |||
442 | int key = Qt::Key_unknown; | | |||
443 | KKeyServer::symXToKeyQt(keysym, &key); | | |||
444 | return static_cast<Qt::Key>(key); | | |||
445 | } | | |||
446 | | ||||
447 | bool Xkb::shouldKeyRepeat(quint32 key) const | | |||
448 | { | | |||
449 | if (!m_keymap) { | | |||
450 | return false; | | |||
451 | } | | |||
452 | return xkb_keymap_key_repeats(m_keymap, key + 8) != 0; | | |||
453 | } | | |||
454 | | ||||
455 | void Xkb::switchToNextLayout() | | |||
456 | { | | |||
457 | if (!m_keymap || !m_state) { | | |||
458 | return; | | |||
459 | } | | |||
460 | const xkb_layout_index_t numLayouts = xkb_keymap_num_layouts(m_keymap); | | |||
461 | const xkb_layout_index_t nextLayout = (xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE) + 1) % numLayouts; | | |||
462 | switchToLayout(nextLayout); | | |||
463 | } | | |||
464 | | ||||
465 | void Xkb::switchToPreviousLayout() | | |||
466 | { | | |||
467 | if (!m_keymap || !m_state) { | | |||
468 | return; | | |||
469 | } | | |||
470 | const xkb_layout_index_t previousLayout = m_currentLayout == 0 ? numberOfLayouts() - 1 : m_currentLayout -1; | | |||
471 | switchToLayout(previousLayout); | | |||
472 | } | | |||
473 | | ||||
474 | void Xkb::switchToLayout(xkb_layout_index_t layout) | | |||
475 | { | | |||
476 | if (!m_keymap || !m_state) { | | |||
477 | return; | | |||
478 | } | | |||
479 | if (layout >= numberOfLayouts()) { | | |||
480 | return; | | |||
481 | } | | |||
482 | const xkb_mod_mask_t depressed = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_DEPRESSED)); | | |||
483 | const xkb_mod_mask_t latched = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LATCHED)); | | |||
484 | const xkb_mod_mask_t locked = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LOCKED)); | | |||
485 | xkb_state_update_mask(m_state, depressed, latched, locked, 0, 0, layout); | | |||
486 | updateModifiers(); | | |||
487 | } | | |||
488 | | ||||
489 | quint32 Xkb::numberOfLayouts() const | | |||
490 | { | | |||
491 | if (!m_keymap) { | | |||
492 | return 0; | | |||
493 | } | | |||
494 | return xkb_keymap_num_layouts(m_keymap); | | |||
495 | } | | |||
496 | | ||||
497 | KeyboardInputRedirection::KeyboardInputRedirection(InputRedirection *parent) | 45 | KeyboardInputRedirection::KeyboardInputRedirection(InputRedirection *parent) | ||
498 | : QObject(parent) | 46 | : QObject(parent) | ||
499 | , m_input(parent) | 47 | , m_input(parent) | ||
500 | , m_xkb(new Xkb(parent)) | 48 | , m_xkb(new Xkb(parent)) | ||
501 | { | 49 | { | ||
502 | } | 50 | } | ||
503 | 51 | | |||
504 | KeyboardInputRedirection::~KeyboardInputRedirection() = default; | 52 | KeyboardInputRedirection::~KeyboardInputRedirection() = default; | ||
▲ Show 20 Lines • Show All 209 Lines • Show Last 20 Lines |