1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qevdevkeyboardhandler_p.h"
5#include "qoutputmapping_p.h"
6
7#include <qplatformdefs.h>
8
9#include <QFile>
10#include <QSocketNotifier>
11#include <QStringList>
12#include <QCoreApplication>
13#include <QLoggingCategory>
14#include <qpa/qwindowsysteminterface.h>
15#include <private/qcore_unix_p.h>
16
17#include <QtGui/private/qguiapplication_p.h>
18#include <QtGui/private/qinputdevicemanager_p.h>
19
20#ifdef Q_OS_FREEBSD
21#include <dev/evdev/input.h>
22#elif defined(Q_OS_VXWORKS)
23#include <evdevLib.h>
24#else
25#include <linux/input.h>
26#endif
27
28#ifndef input_event_sec
29#define input_event_sec time.tv_sec
30#endif
31
32#ifndef input_event_usec
33#define input_event_usec time.tv_usec
34#endif
35
36QT_BEGIN_NAMESPACE
37
38using namespace Qt::StringLiterals;
39
40Q_LOGGING_CATEGORY(qLcEvdevKey, "qt.qpa.input")
41Q_LOGGING_CATEGORY(qLcEvdevKeyMap, "qt.qpa.input.keymap")
42
43// simple builtin US keymap
44#include "qevdevkeyboard_defaultmap_p.h"
45
46void QFdContainer::reset() noexcept
47{
48 if (m_fd >= 0)
49 qt_safe_close(fd: m_fd);
50 m_fd = -1;
51}
52
53QEvdevKeyboardHandler::QEvdevKeyboardHandler(const QString &device, QFdContainer &fd, bool disableZap, bool enableCompose, const QString &keymapFile)
54 : m_device(device), m_fd(fd.release()), m_notify(nullptr),
55 m_modifiers(0), m_composing(0), m_dead_unicode(0xffff),
56 m_langLock(0), m_no_zap(disableZap), m_do_compose(enableCompose),
57 m_keymap(0), m_keymap_size(0), m_keycompose(0), m_keycompose_size(0)
58{
59 qCDebug(qLcEvdevKey) << "Create keyboard handler with for device" << device;
60
61 setObjectName("LinuxInput Keyboard Handler"_L1);
62
63 memset(s: m_locks, c: 0, n: sizeof(m_locks));
64
65 if (keymapFile.isEmpty() || !loadKeymap(file: keymapFile))
66 unloadKeymap();
67
68 // socket notifier for events on the keyboard device
69 m_notify = new QSocketNotifier(m_fd.get(), QSocketNotifier::Read, this);
70 connect(sender: m_notify, signal: &QSocketNotifier::activated, context: this, slot: &QEvdevKeyboardHandler::readKeycode);
71}
72
73QEvdevKeyboardHandler::~QEvdevKeyboardHandler()
74{
75 unloadKeymap();
76}
77
78std::unique_ptr<QEvdevKeyboardHandler> QEvdevKeyboardHandler::create(const QString &device,
79 const QString &specification,
80 const QString &defaultKeymapFile)
81{
82 qCDebug(qLcEvdevKey, "Try to create keyboard handler for \"%ls\" \"%ls\"",
83 qUtf16Printable(device), qUtf16Printable(specification));
84
85 QString keymapFile = defaultKeymapFile;
86 int repeatDelay = 400;
87 int repeatRate = 80;
88 bool disableZap = false;
89 bool enableCompose = false;
90 int grab = 0;
91
92 const auto args = QStringView{specification}.split(sep: u':');
93 for (const auto &arg : args) {
94 if (arg.startsWith(s: "keymap="_L1))
95 keymapFile = arg.mid(pos: 7).toString();
96 else if (arg == "disable-zap"_L1)
97 disableZap = true;
98 else if (arg == "enable-compose"_L1)
99 enableCompose = true;
100 else if (arg.startsWith(s: "repeat-delay="_L1))
101 repeatDelay = arg.mid(pos: 13).toInt();
102 else if (arg.startsWith(s: "repeat-rate="_L1))
103 repeatRate = arg.mid(pos: 12).toInt();
104 else if (arg.startsWith(s: "grab="_L1))
105 grab = arg.mid(pos: 5).toInt();
106 }
107
108 qCDebug(qLcEvdevKey, "Opening keyboard at %ls", qUtf16Printable(device));
109
110 QFdContainer fd(qt_safe_open(pathname: device.toLocal8Bit().constData(), O_RDWR | O_NDELAY, mode: 0));
111 if (fd.get() < 0) {
112 qCDebug(qLcEvdevKey, "Keyboard device could not be opened as read-write, trying read-only");
113 fd.reset(fd: qt_safe_open(pathname: device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, mode: 0));
114 }
115 if (fd.get() >= 0) {
116#if !defined(Q_OS_VXWORKS)
117 ::ioctl(fd: fd.get(), EVIOCGRAB, grab);
118 if (repeatDelay > 0 && repeatRate > 0) {
119 int kbdrep[2] = { repeatDelay, repeatRate };
120 ::ioctl(fd: fd.get(), EVIOCSREP, kbdrep);
121 }
122#else
123 Q_UNUSED(repeatDelay)
124 Q_UNUSED(repeatRate)
125 Q_UNUSED(grab)
126 UINT32 kbdMode = EV_DEV_KBD_KEYCODE_MODE;
127 if (ERROR == ioctl (fd.get(), EV_DEV_IO_SET_KBD_MODE, (char *)&kbdMode)) {
128 qWarning("Cannot set keyboard mapping mode to KEYCODE mode '%s': %s", qPrintable(device), strerror(errno));
129 return 0;
130 }
131#endif
132
133 return std::unique_ptr<QEvdevKeyboardHandler>(new QEvdevKeyboardHandler(device, fd, disableZap, enableCompose, keymapFile));
134 } else {
135 qErrnoWarning(msg: "Cannot open keyboard input device '%ls'", qUtf16Printable(device));
136 return nullptr;
137 }
138}
139
140#if !defined(Q_OS_VXWORKS)
141void QEvdevKeyboardHandler::switchLed(int led, bool state)
142{
143 qCDebug(qLcEvdevKey, "switchLed %d %d", led, int(state));
144
145 struct timeval tv;
146 ::gettimeofday(tv: &tv, tz: 0);
147 struct ::input_event led_ie;
148 led_ie.input_event_sec = tv.tv_sec;
149 led_ie.input_event_usec = tv.tv_usec;
150 led_ie.type = EV_LED;
151 led_ie.code = led;
152 led_ie.value = state;
153
154 qt_safe_write(fd: m_fd.get(), data: &led_ie, len: sizeof(led_ie));
155}
156#endif
157
158void QEvdevKeyboardHandler::readKeycode()
159{
160#if defined(Q_OS_VXWORKS)
161 EV_DEV_EVENT ev;
162 int n = ERROR;
163
164 while (n == ERROR) {
165 n = read(m_fd.get(), (char *)(&ev), sizeof(EV_DEV_EVENT));
166
167 if (n == 0) {
168 qWarning("evdevkeyboard: Got EOF from the input device");
169 return;
170 }
171
172 if (n == ERROR) {
173 if (errno == EINTR || errno == EAGAIN)
174 continue;
175
176 qErrnoWarning("evdevkeyboard: Could not read from input device");
177 if (errno == ENXIO) {
178 close(m_fd.get());
179 delete m_notify;
180 m_notify = nullptr;
181 m_fd.reset();
182 }
183 return;
184 }
185 }
186
187 if (n < sizeof(EV_DEV_EVENT)) return;
188 if (ev.type != EV_DEV_KEY) return;
189
190 quint16 code = ev.code;
191 qint32 value = ev.value;
192
193 processKeycode(code, value != 0, value == 2);
194#else
195 struct ::input_event buffer[32];
196 int n = 0;
197
198 forever {
199 int result = qt_safe_read(fd: m_fd.get(), data: reinterpret_cast<char *>(buffer) + n, maxlen: sizeof(buffer) - n);
200
201 if (result == 0) {
202 qWarning(msg: "evdevkeyboard: Got EOF from the input device");
203 return;
204 } else if (result < 0) {
205 if (errno != EINTR && errno != EAGAIN) {
206 qErrnoWarning(msg: "evdevkeyboard: Could not read from input device");
207 // If the device got disconnected, stop reading, otherwise we get flooded
208 // by the above error over and over again.
209 if (errno == ENODEV) {
210 delete m_notify;
211 m_notify = nullptr;
212 m_fd.reset();
213 }
214 return;
215 }
216 } else {
217 n += result;
218 if (n % sizeof(buffer[0]) == 0)
219 break;
220 }
221 }
222
223 n /= sizeof(buffer[0]);
224
225 for (int i = 0; i < n; ++i) {
226 if (buffer[i].type != EV_KEY)
227 continue;
228
229 quint16 code = buffer[i].code;
230 qint32 value = buffer[i].value;
231
232 QEvdevKeyboardHandler::KeycodeAction ka;
233 ka = processKeycode(keycode: code, pressed: value != 0, autorepeat: value == 2);
234
235 switch (ka) {
236 case QEvdevKeyboardHandler::CapsLockOn:
237 case QEvdevKeyboardHandler::CapsLockOff:
238 switchLed(LED_CAPSL, state: ka == QEvdevKeyboardHandler::CapsLockOn);
239 break;
240
241 case QEvdevKeyboardHandler::NumLockOn:
242 case QEvdevKeyboardHandler::NumLockOff:
243 switchLed(LED_NUML, state: ka == QEvdevKeyboardHandler::NumLockOn);
244 break;
245
246 case QEvdevKeyboardHandler::ScrollLockOn:
247 case QEvdevKeyboardHandler::ScrollLockOff:
248 switchLed(LED_SCROLLL, state: ka == QEvdevKeyboardHandler::ScrollLockOn);
249 break;
250
251 default:
252 // ignore console switching and reboot
253 break;
254 }
255 }
256#endif
257}
258
259void QEvdevKeyboardHandler::processKeyEvent(int nativecode, int unicode, int qtcode,
260 Qt::KeyboardModifiers modifiers, bool isPress, bool autoRepeat)
261{
262 if (!autoRepeat)
263 QGuiApplicationPrivate::inputDeviceManager()->setKeyboardModifiers(QEvdevKeyboardHandler::toQtModifiers(mod: m_modifiers));
264
265 QWindow *window = nullptr;
266#ifdef Q_OS_WEBOS
267 window = QOutputMapping::get()->windowForDeviceNode(m_device);
268#endif
269 QWindowSystemInterface::handleExtendedKeyEvent(window, type: (isPress ? QEvent::KeyPress : QEvent::KeyRelease),
270 key: qtcode, modifiers, nativeScanCode: nativecode + 8, nativeVirtualKey: 0, nativeModifiers: int(modifiers),
271 text: (unicode != 0xffff ) ? QString(QChar(unicode)) : QString(), autorep: autoRepeat);
272}
273
274QEvdevKeyboardHandler::KeycodeAction QEvdevKeyboardHandler::processKeycode(quint16 keycode, bool pressed, bool autorepeat)
275{
276 KeycodeAction result = None;
277 bool first_press = pressed && !autorepeat;
278
279 const QEvdevKeyboardMap::Mapping *map_plain = nullptr;
280 const QEvdevKeyboardMap::Mapping *map_withmod = nullptr;
281
282 quint8 modifiers = m_modifiers;
283
284 // get a specific and plain mapping for the keycode and the current modifiers
285 for (int i = 0; i < m_keymap_size && !(map_plain && map_withmod); ++i) {
286 const QEvdevKeyboardMap::Mapping *m = m_keymap + i;
287 if (m->keycode == keycode) {
288 if (m->modifiers == 0)
289 map_plain = m;
290
291 quint8 testmods = m_modifiers;
292 if (m_locks[0] /*CapsLock*/ && (m->flags & QEvdevKeyboardMap::IsLetter))
293 testmods ^= QEvdevKeyboardMap::ModShift;
294 if (m_langLock)
295 testmods ^= QEvdevKeyboardMap::ModAltGr;
296 if (m->modifiers == testmods)
297 map_withmod = m;
298 }
299 }
300
301 if (m_locks[0] /*CapsLock*/ && map_withmod && (map_withmod->flags & QEvdevKeyboardMap::IsLetter))
302 modifiers ^= QEvdevKeyboardMap::ModShift;
303
304 qCDebug(qLcEvdevKeyMap, "Processing key event: keycode=%3d, modifiers=%02x pressed=%d, autorepeat=%d | plain=%d, withmod=%d, size=%d",
305 keycode, modifiers, pressed ? 1 : 0, autorepeat ? 1 : 0,
306 int(map_plain ? map_plain - m_keymap : -1),
307 int(map_withmod ? map_withmod - m_keymap : -1),
308 m_keymap_size);
309
310 const QEvdevKeyboardMap::Mapping *it = map_withmod ? map_withmod : map_plain;
311
312 if (!it) {
313 // we couldn't even find a plain mapping
314 qCDebug(qLcEvdevKeyMap, "Could not find a suitable mapping for keycode: %3d, modifiers: %02x", keycode, modifiers);
315 return result;
316 }
317
318 bool skip = false;
319 quint16 unicode = it->unicode;
320 quint32 qtcode = it->qtcode;
321
322 if ((it->flags & QEvdevKeyboardMap::IsModifier) && it->special) {
323 // this is a modifier, i.e. Shift, Alt, ...
324 if (pressed)
325 m_modifiers |= quint8(it->special);
326 else
327 m_modifiers &= ~quint8(it->special);
328 } else if (qtcode >= Qt::Key_CapsLock && qtcode <= Qt::Key_ScrollLock) {
329 // (Caps|Num|Scroll)Lock
330 if (first_press) {
331 quint8 &lock = m_locks[qtcode - Qt::Key_CapsLock];
332 lock ^= 1;
333
334 switch (qtcode) {
335 case Qt::Key_CapsLock : result = lock ? CapsLockOn : CapsLockOff; break;
336 case Qt::Key_NumLock : result = lock ? NumLockOn : NumLockOff; break;
337 case Qt::Key_ScrollLock: result = lock ? ScrollLockOn : ScrollLockOff; break;
338 default : break;
339 }
340 }
341 } else if ((it->flags & QEvdevKeyboardMap::IsSystem) && it->special && first_press) {
342 switch (it->special) {
343 case QEvdevKeyboardMap::SystemReboot:
344 result = Reboot;
345 break;
346
347 case QEvdevKeyboardMap::SystemZap:
348 if (!m_no_zap)
349 qApp->quit();
350 break;
351
352 case QEvdevKeyboardMap::SystemConsolePrevious:
353 result = PreviousConsole;
354 break;
355
356 case QEvdevKeyboardMap::SystemConsoleNext:
357 result = NextConsole;
358 break;
359
360 default:
361 if (it->special >= QEvdevKeyboardMap::SystemConsoleFirst &&
362 it->special <= QEvdevKeyboardMap::SystemConsoleLast) {
363 result = KeycodeAction(SwitchConsoleFirst + ((it->special & QEvdevKeyboardMap::SystemConsoleMask) & SwitchConsoleMask));
364 }
365 break;
366 }
367
368 skip = true; // no need to tell Qt about it
369 } else if ((qtcode == Qt::Key_Multi_key) && m_do_compose) {
370 // the Compose key was pressed
371 if (first_press)
372 m_composing = 2;
373 skip = true;
374 } else if ((it->flags & QEvdevKeyboardMap::IsDead) && m_do_compose) {
375 // a Dead key was pressed
376 if (first_press && m_composing == 1 && m_dead_unicode == unicode) { // twice
377 m_composing = 0;
378 qtcode = Qt::Key_unknown; // otherwise it would be Qt::Key_Dead...
379 } else if (first_press && unicode != 0xffff) {
380 m_dead_unicode = unicode;
381 m_composing = 1;
382 skip = true;
383 } else {
384 skip = true;
385 }
386 }
387
388 if (!skip) {
389 // a normal key was pressed
390 const int modmask = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier | Qt::KeypadModifier;
391
392 // we couldn't find a specific mapping for the current modifiers,
393 // or that mapping didn't have special modifiers:
394 // so just report the plain mapping with additional modifiers.
395 if ((it == map_plain && it != map_withmod) ||
396 (map_withmod && !(map_withmod->qtcode & modmask))) {
397 qtcode |= QEvdevKeyboardHandler::toQtModifiers(mod: modifiers);
398 }
399
400 if (m_composing == 2 && first_press && !(it->flags & QEvdevKeyboardMap::IsModifier)) {
401 // the last key press was the Compose key
402 if (unicode != 0xffff) {
403 int idx = 0;
404 // check if this code is in the compose table at all
405 for ( ; idx < m_keycompose_size; ++idx) {
406 if (m_keycompose[idx].first == unicode)
407 break;
408 }
409 if (idx < m_keycompose_size) {
410 // found it -> simulate a Dead key press
411 m_dead_unicode = unicode;
412 unicode = 0xffff;
413 m_composing = 1;
414 skip = true;
415 } else {
416 m_composing = 0;
417 }
418 } else {
419 m_composing = 0;
420 }
421 } else if (m_composing == 1 && first_press && !(it->flags & QEvdevKeyboardMap::IsModifier)) {
422 // the last key press was a Dead key
423 bool valid = false;
424 if (unicode != 0xffff) {
425 int idx = 0;
426 // check if this code is in the compose table at all
427 for ( ; idx < m_keycompose_size; ++idx) {
428 if (m_keycompose[idx].first == m_dead_unicode && m_keycompose[idx].second == unicode)
429 break;
430 }
431 if (idx < m_keycompose_size) {
432 quint16 composed = m_keycompose[idx].result;
433 if (composed != 0xffff) {
434 unicode = composed;
435 qtcode = Qt::Key_unknown;
436 valid = true;
437 }
438 }
439 }
440 if (!valid) {
441 unicode = m_dead_unicode;
442 qtcode = Qt::Key_unknown;
443 }
444 m_composing = 0;
445 }
446
447 if (!skip) {
448 // Up until now qtcode contained both the key and modifiers. Split it.
449 Qt::KeyboardModifiers qtmods = Qt::KeyboardModifiers(qtcode & modmask);
450 qtcode &= ~modmask;
451
452 // qtmods here is the modifier state before the event, i.e. not
453 // including the current key in case it is a modifier.
454 qCDebug(qLcEvdevKeyMap, "Processing: uni=%04x, qt=%08x, qtmod=%08x", unicode, qtcode, int(qtmods));
455
456 // If NumLockOff and keypad key pressed remap event sent
457 if (!m_locks[1] && (qtmods & Qt::KeypadModifier) &&
458 keycode >= 71 &&
459 keycode <= 83 &&
460 keycode != 74 &&
461 keycode != 78) {
462
463 unicode = 0xffff;
464 switch (keycode) {
465 case 71: //7 --> Home
466 qtcode = Qt::Key_Home;
467 break;
468 case 72: //8 --> Up
469 qtcode = Qt::Key_Up;
470 break;
471 case 73: //9 --> PgUp
472 qtcode = Qt::Key_PageUp;
473 break;
474 case 75: //4 --> Left
475 qtcode = Qt::Key_Left;
476 break;
477 case 76: //5 --> Clear
478 qtcode = Qt::Key_Clear;
479 break;
480 case 77: //6 --> right
481 qtcode = Qt::Key_Right;
482 break;
483 case 79: //1 --> End
484 qtcode = Qt::Key_End;
485 break;
486 case 80: //2 --> Down
487 qtcode = Qt::Key_Down;
488 break;
489 case 81: //3 --> PgDn
490 qtcode = Qt::Key_PageDown;
491 break;
492 case 82: //0 --> Ins
493 qtcode = Qt::Key_Insert;
494 break;
495 case 83: //, --> Del
496 qtcode = Qt::Key_Delete;
497 break;
498 }
499 }
500
501 // Map SHIFT + Tab to SHIFT + Backtab, QShortcutMap knows about this translation
502 if (qtcode == Qt::Key_Tab && (qtmods & Qt::ShiftModifier) == Qt::ShiftModifier)
503 qtcode = Qt::Key_Backtab;
504
505 // Generate the QPA event.
506 processKeyEvent(nativecode: keycode, unicode, qtcode, modifiers: qtmods, isPress: pressed, autoRepeat: autorepeat);
507 }
508 }
509 return result;
510}
511
512void QEvdevKeyboardHandler::unloadKeymap()
513{
514 qCDebug(qLcEvdevKey, "Unload current keymap and restore built-in");
515
516 if (m_keymap && m_keymap != s_keymap_default)
517 delete [] m_keymap;
518 if (m_keycompose && m_keycompose != s_keycompose_default)
519 delete [] m_keycompose;
520
521 m_keymap = s_keymap_default;
522 m_keymap_size = sizeof(s_keymap_default) / sizeof(s_keymap_default[0]);
523 m_keycompose = s_keycompose_default;
524 m_keycompose_size = sizeof(s_keycompose_default) / sizeof(s_keycompose_default[0]);
525
526 // reset state, so we could switch keymaps at runtime
527 m_modifiers = 0;
528 memset(s: m_locks, c: 0, n: sizeof(m_locks));
529 m_composing = 0;
530 m_dead_unicode = 0xffff;
531
532#if !defined(Q_OS_VXWORKS)
533 //Set locks according to keyboard leds
534 quint16 ledbits[1];
535 memset(s: ledbits, c: 0, n: sizeof(ledbits));
536 if (::ioctl(fd: m_fd.get(), EVIOCGLED(sizeof(ledbits)), ledbits) < 0) {
537 qWarning(msg: "evdevkeyboard: Failed to query led states");
538 switchLed(LED_NUML,state: false);
539 switchLed(LED_CAPSL, state: false);
540 switchLed(LED_SCROLLL,state: false);
541 } else {
542 //Capslock
543 if ((ledbits[0]&0x02) > 0)
544 m_locks[0] = 1;
545 //Numlock
546 if ((ledbits[0]&0x01) > 0)
547 m_locks[1] = 1;
548 //Scrollock
549 if ((ledbits[0]&0x04) > 0)
550 m_locks[2] = 1;
551 qCDebug(qLcEvdevKey, "numlock=%d , capslock=%d, scrolllock=%d", m_locks[1], m_locks[0], m_locks[2]);
552 }
553
554 m_langLock = 0;
555#endif
556}
557
558bool QEvdevKeyboardHandler::loadKeymap(const QString &file)
559{
560 qCDebug(qLcEvdevKey, "Loading keymap %ls", qUtf16Printable(file));
561
562 QFile f(file);
563
564 if (!f.open(flags: QIODevice::ReadOnly)) {
565 qWarning(msg: "Could not open keymap file '%ls'", qUtf16Printable(file));
566 return false;
567 }
568
569 // .qmap files have a very simple structure:
570 // quint32 magic (QKeyboard::FileMagic)
571 // quint32 version (1)
572 // quint32 keymap_size (# of struct QKeyboard::Mappings)
573 // quint32 keycompose_size (# of struct QKeyboard::Composings)
574 // all QKeyboard::Mappings via QDataStream::operator(<<|>>)
575 // all QKeyboard::Composings via QDataStream::operator(<<|>>)
576
577 quint32 qmap_magic, qmap_version, qmap_keymap_size, qmap_keycompose_size;
578
579 QDataStream ds(&f);
580
581 ds >> qmap_magic >> qmap_version >> qmap_keymap_size >> qmap_keycompose_size;
582
583 if (ds.status() != QDataStream::Ok || qmap_magic != QEvdevKeyboardMap::FileMagic || qmap_version != 1 || qmap_keymap_size == 0) {
584 qWarning(msg: "'%ls' is not a valid .qmap keymap file", qUtf16Printable(file));
585 return false;
586 }
587
588 QEvdevKeyboardMap::Mapping *qmap_keymap = new QEvdevKeyboardMap::Mapping[qmap_keymap_size];
589 QEvdevKeyboardMap::Composing *qmap_keycompose = qmap_keycompose_size ? new QEvdevKeyboardMap::Composing[qmap_keycompose_size] : 0;
590
591 for (quint32 i = 0; i < qmap_keymap_size; ++i)
592 ds >> qmap_keymap[i];
593 for (quint32 i = 0; i < qmap_keycompose_size; ++i)
594 ds >> qmap_keycompose[i];
595
596 if (ds.status() != QDataStream::Ok) {
597 delete [] qmap_keymap;
598 delete [] qmap_keycompose;
599
600 qWarning(msg: "Keymap file '%ls' cannot be loaded.", qUtf16Printable(file));
601 return false;
602 }
603
604 // unload currently active and clear state
605 unloadKeymap();
606
607 m_keymap = qmap_keymap;
608 m_keymap_size = qmap_keymap_size;
609 m_keycompose = qmap_keycompose;
610 m_keycompose_size = qmap_keycompose_size;
611
612 m_do_compose = true;
613
614 return true;
615}
616
617void QEvdevKeyboardHandler::switchLang()
618{
619 m_langLock ^= 1;
620}
621
622QT_END_NAMESPACE
623

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtbase/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp