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