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