54a4f0c1b6a06d01c5b904546536c5a35bc22c28
[profile/ivi/qtbase.git] / src / platformsupport / input / evdevkeyboard / qevdevkeyboardhandler.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qevdevkeyboardhandler_p.h"
43
44 #include <qplatformdefs.h>
45
46 #include <QSocketNotifier>
47 #include <QStringList>
48 #include <QtGui/qwindowsysteminterface.h>
49 #include <QCoreApplication>
50 #include <private/qcore_unix_p.h>
51
52 #include <linux/input.h>
53
54 //#define QT_QPA_KEYMAP_DEBUG
55
56 #ifdef QT_QPA_KEYMAP_DEBUG
57 #include <qdebug.h>
58 #endif
59
60 QT_BEGIN_NAMESPACE
61
62 // simple builtin US keymap
63 #include "qevdevkeyboard_defaultmap_p.h"
64
65 QEvdevKeyboardHandler::QEvdevKeyboardHandler(const QString &device, int fd, bool disableZap, bool enableCompose, const QString &keymapFile)
66     : m_device(device), m_fd(fd),
67       m_modifiers(0), m_composing(0), m_dead_unicode(0xffff),
68       m_no_zap(disableZap), m_do_compose(enableCompose),
69       m_keymap(0), m_keymap_size(0), m_keycompose(0), m_keycompose_size(0)
70 {
71 #ifdef QT_QPA_KEYMAP_DEBUG
72     qWarning() << "Create keyboard handler with for device" << device;
73 #endif
74
75     setObjectName(QLatin1String("LinuxInput Keyboard Handler"));
76
77     memset(m_locks, 0, sizeof(m_locks));
78
79     if (keymapFile.isEmpty() || !loadKeymap(keymapFile))
80         unloadKeymap();
81
82     // socket notifier for events on the keyboard device
83     QSocketNotifier *notifier;
84     notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
85     connect(notifier, SIGNAL(activated(int)), this, SLOT(readKeycode()));
86 }
87
88 QEvdevKeyboardHandler::~QEvdevKeyboardHandler()
89 {
90     unloadKeymap();
91
92     if (m_fd >= 0)
93         qt_safe_close(m_fd);
94 }
95
96 QEvdevKeyboardHandler *QEvdevKeyboardHandler::create(const QString &device, const QString &specification)
97 {
98 #ifdef QT_QPA_KEYMAP_DEBUG
99     qWarning() << "Try to create keyboard handler for" << device << specification;
100 #endif
101
102     QString keymapFile;
103     int repeatDelay = 400;
104     int repeatRate = 80;
105     bool disableZap = false;
106     bool enableCompose = false;
107
108     QStringList args = specification.split(QLatin1Char(':'));
109     foreach (const QString &arg, args) {
110         if (arg.startsWith(QLatin1String("keymap=")))
111             keymapFile = arg.mid(7);
112         else if (arg == QLatin1String("disable-zap"))
113             disableZap = true;
114         else if (arg == QLatin1String("enable-compose"))
115             enableCompose = true;
116         else if (arg.startsWith(QLatin1String("repeat-delay=")))
117             repeatDelay = arg.mid(13).toInt();
118         else if (arg.startsWith(QLatin1String("repeat-rate=")))
119             repeatRate = arg.mid(12).toInt();
120     }
121
122 #ifdef QT_QPA_KEYMAP_DEBUG
123     qWarning() << "Opening keyboard at" << device;
124 #endif
125
126     int fd;
127     fd = qt_safe_open(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);
128     if (fd >= 0) {
129         if (repeatDelay > 0 && repeatRate > 0) {
130             int kbdrep[2] = { repeatDelay, repeatRate };
131             ::ioctl(fd, EVIOCSREP, kbdrep);
132         }
133
134         return new QEvdevKeyboardHandler(device, fd, disableZap, enableCompose, keymapFile);
135     } else {
136         qWarning("Cannot open keyboard input device '%s': %s", qPrintable(device), strerror(errno));
137         return 0;
138     }
139 }
140
141 void QEvdevKeyboardHandler::switchLed(int led, bool state)
142 {
143 #ifdef QT_QPA_KEYMAP_DEBUG
144     qWarning() << "switchLed" << led << state;
145 #endif
146
147     struct ::input_event led_ie;
148     ::gettimeofday(&led_ie.time, 0);
149     led_ie.type = EV_LED;
150     led_ie.code = led;
151     led_ie.value = state;
152
153     qt_safe_write(m_fd, &led_ie, sizeof(led_ie));
154 }
155
156 void QEvdevKeyboardHandler::readKeycode()
157 {
158 #ifdef QT_QPA_KEYMAP_DEBUG
159     qWarning() << "Read new keycode on" << m_device;
160 #endif
161
162     struct ::input_event buffer[32];
163     int n = 0;
164
165     forever {
166         int result = qt_safe_read(m_fd, reinterpret_cast<char *>(buffer) + n, sizeof(buffer) - n);
167
168         if (result == 0) {
169             qWarning("Got EOF from the input device.");
170             return;
171         } else if (result < 0) {
172             if (errno != EINTR && errno != EAGAIN) {
173                 qWarning("Could not read from input device: %s", strerror(errno));
174                 return;
175             }
176         } else {
177             n += result;
178             if (n % sizeof(buffer[0]) == 0)
179                 break;
180         }
181     }
182
183     n /= sizeof(buffer[0]);
184
185     for (int i = 0; i < n; ++i) {
186         if (buffer[i].type != EV_KEY)
187             continue;
188
189         quint16 code = buffer[i].code;
190         qint32 value = buffer[i].value;
191
192         QEvdevKeyboardHandler::KeycodeAction ka;
193         ka = processKeycode(code, value != 0, value == 2);
194
195         switch (ka) {
196         case QEvdevKeyboardHandler::CapsLockOn:
197         case QEvdevKeyboardHandler::CapsLockOff:
198             switchLed(LED_CAPSL, ka == QEvdevKeyboardHandler::CapsLockOn);
199             break;
200
201         case QEvdevKeyboardHandler::NumLockOn:
202         case QEvdevKeyboardHandler::NumLockOff:
203             switchLed(LED_NUML, ka == QEvdevKeyboardHandler::NumLockOn);
204             break;
205
206         case QEvdevKeyboardHandler::ScrollLockOn:
207         case QEvdevKeyboardHandler::ScrollLockOff:
208             switchLed(LED_SCROLLL, ka == QEvdevKeyboardHandler::ScrollLockOn);
209             break;
210
211         default:
212             // ignore console switching and reboot
213             break;
214         }
215     }
216 }
217
218 void QEvdevKeyboardHandler::processKeyEvent(int nativecode, int unicode, int qtcode,
219                                             Qt::KeyboardModifiers modifiers, bool isPress, bool autoRepeat)
220 {
221     QWindowSystemInterface::handleExtendedKeyEvent(0, (isPress ? QEvent::KeyPress : QEvent::KeyRelease),
222                                                    qtcode, modifiers, nativecode + 8, 0, int(modifiers),
223                                                    QString(unicode), autoRepeat);
224 }
225
226 QEvdevKeyboardHandler::KeycodeAction QEvdevKeyboardHandler::processKeycode(quint16 keycode, bool pressed, bool autorepeat)
227 {
228     KeycodeAction result = None;
229     bool first_press = pressed && !autorepeat;
230
231     const QEvdevKeyboardMap::Mapping *map_plain = 0;
232     const QEvdevKeyboardMap::Mapping *map_withmod = 0;
233
234     // get a specific and plain mapping for the keycode and the current modifiers
235     for (int i = 0; i < m_keymap_size && !(map_plain && map_withmod); ++i) {
236         const QEvdevKeyboardMap::Mapping *m = m_keymap + i;
237         if (m->keycode == keycode) {
238             if (m->modifiers == 0)
239                 map_plain = m;
240
241             quint8 testmods = m_modifiers;
242             if (m_locks[0] /*CapsLock*/ && (m->flags & QEvdevKeyboardMap::IsLetter))
243                 testmods ^= QEvdevKeyboardMap::ModShift;
244             if (m->modifiers == testmods)
245                 map_withmod = m;
246         }
247     }
248
249 #ifdef QT_QPA_KEYMAP_DEBUG
250     qWarning("Processing key event: keycode=%3d, modifiers=%02x pressed=%d, autorepeat=%d  |  plain=%d, withmod=%d, size=%d", \
251              keycode, m_modifiers, pressed ? 1 : 0, autorepeat ? 1 : 0, \
252              map_plain ? map_plain - m_keymap : -1, \
253              map_withmod ? map_withmod - m_keymap : -1, \
254              m_keymap_size);
255 #endif
256
257     const QEvdevKeyboardMap::Mapping *it = map_withmod ? map_withmod : map_plain;
258
259     if (!it) {
260 #ifdef QT_QPA_KEYMAP_DEBUG
261         // we couldn't even find a plain mapping
262         qWarning("Could not find a suitable mapping for keycode: %3d, modifiers: %02x", keycode, m_modifiers);
263 #endif
264         return result;
265     }
266
267     bool skip = false;
268     quint16 unicode = it->unicode;
269     quint32 qtcode = it->qtcode;
270
271     if ((it->flags & QEvdevKeyboardMap::IsModifier) && it->special) {
272         // this is a modifier, i.e. Shift, Alt, ...
273         if (pressed)
274             m_modifiers |= quint8(it->special);
275         else
276             m_modifiers &= ~quint8(it->special);
277     } else if (qtcode >= Qt::Key_CapsLock && qtcode <= Qt::Key_ScrollLock) {
278         // (Caps|Num|Scroll)Lock
279         if (first_press) {
280             quint8 &lock = m_locks[qtcode - Qt::Key_CapsLock];
281             lock ^= 1;
282
283             switch (qtcode) {
284             case Qt::Key_CapsLock  : result = lock ? CapsLockOn : CapsLockOff; m_modifiers ^= QEvdevKeyboardMap::ModShift; break;
285             case Qt::Key_NumLock   : result = lock ? NumLockOn : NumLockOff; break;
286             case Qt::Key_ScrollLock: result = lock ? ScrollLockOn : ScrollLockOff; break;
287             default                : break;
288             }
289         }
290     } else if ((it->flags & QEvdevKeyboardMap::IsSystem) && it->special && first_press) {
291         switch (it->special) {
292         case QEvdevKeyboardMap::SystemReboot:
293             result = Reboot;
294             break;
295
296         case QEvdevKeyboardMap::SystemZap:
297             if (!m_no_zap)
298                 qApp->quit();
299             break;
300
301         case QEvdevKeyboardMap::SystemConsolePrevious:
302             result = PreviousConsole;
303             break;
304
305         case QEvdevKeyboardMap::SystemConsoleNext:
306             result = NextConsole;
307             break;
308
309         default:
310             if (it->special >= QEvdevKeyboardMap::SystemConsoleFirst &&
311                 it->special <= QEvdevKeyboardMap::SystemConsoleLast) {
312                 result = KeycodeAction(SwitchConsoleFirst + ((it->special & QEvdevKeyboardMap::SystemConsoleMask) & SwitchConsoleMask));
313             }
314             break;
315         }
316
317         skip = true; // no need to tell Qt about it
318     } else if ((qtcode == Qt::Key_Multi_key) && m_do_compose) {
319         // the Compose key was pressed
320         if (first_press)
321             m_composing = 2;
322         skip = true;
323     } else if ((it->flags & QEvdevKeyboardMap::IsDead) && m_do_compose) {
324         // a Dead key was pressed
325         if (first_press && m_composing == 1 && m_dead_unicode == unicode) { // twice
326             m_composing = 0;
327             qtcode = Qt::Key_unknown; // otherwise it would be Qt::Key_Dead...
328         } else if (first_press && unicode != 0xffff) {
329             m_dead_unicode = unicode;
330             m_composing = 1;
331             skip = true;
332         } else {
333             skip = true;
334         }
335     }
336
337     if (!skip) {
338         // a normal key was pressed
339         const int modmask = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier | Qt::KeypadModifier;
340
341         // we couldn't find a specific mapping for the current modifiers,
342         // or that mapping didn't have special modifiers:
343         // so just report the plain mapping with additional modifiers.
344         if ((it == map_plain && it != map_withmod) ||
345             (map_withmod && !(map_withmod->qtcode & modmask))) {
346             qtcode |= QEvdevKeyboardHandler::toQtModifiers(m_modifiers);
347         }
348
349         if (m_composing == 2 && first_press && !(it->flags & QEvdevKeyboardMap::IsModifier)) {
350             // the last key press was the Compose key
351             if (unicode != 0xffff) {
352                 int idx = 0;
353                 // check if this code is in the compose table at all
354                 for ( ; idx < m_keycompose_size; ++idx) {
355                     if (m_keycompose[idx].first == unicode)
356                         break;
357                 }
358                 if (idx < m_keycompose_size) {
359                     // found it -> simulate a Dead key press
360                     m_dead_unicode = unicode;
361                     unicode = 0xffff;
362                     m_composing = 1;
363                     skip = true;
364                 } else {
365                     m_composing = 0;
366                 }
367             } else {
368                 m_composing = 0;
369             }
370         } else if (m_composing == 1 && first_press && !(it->flags & QEvdevKeyboardMap::IsModifier)) {
371             // the last key press was a Dead key
372             bool valid = false;
373             if (unicode != 0xffff) {
374                 int idx = 0;
375                 // check if this code is in the compose table at all
376                 for ( ; idx < m_keycompose_size; ++idx) {
377                     if (m_keycompose[idx].first == m_dead_unicode && m_keycompose[idx].second == unicode)
378                         break;
379                 }
380                 if (idx < m_keycompose_size) {
381                     quint16 composed = m_keycompose[idx].result;
382                     if (composed != 0xffff) {
383                         unicode = composed;
384                         qtcode = Qt::Key_unknown;
385                         valid = true;
386                     }
387                 }
388             }
389             if (!valid) {
390                 unicode = m_dead_unicode;
391                 qtcode = Qt::Key_unknown;
392             }
393             m_composing = 0;
394         }
395
396         if (!skip) {
397 #ifdef QT_QPA_KEYMAP_DEBUG
398             qWarning("Processing: uni=%04x, qt=%08x, qtmod=%08x", unicode, qtcode & ~modmask, (qtcode & modmask));
399 #endif
400
401             // send the result to the server
402             processKeyEvent(keycode, unicode, qtcode & ~modmask, Qt::KeyboardModifiers(qtcode & modmask), pressed, autorepeat);
403         }
404     }
405     return result;
406 }
407
408 void QEvdevKeyboardHandler::unloadKeymap()
409 {
410 #ifdef QT_QPA_KEYMAP_DEBUG
411     qWarning() << "Unload current keymap and restore built-in";
412 #endif
413
414     if (m_keymap && m_keymap != s_keymap_default)
415         delete [] m_keymap;
416     if (m_keycompose && m_keycompose != s_keycompose_default)
417         delete [] m_keycompose;
418
419     m_keymap = s_keymap_default;
420     m_keymap_size = sizeof(s_keymap_default) / sizeof(s_keymap_default[0]);
421     m_keycompose = s_keycompose_default;
422     m_keycompose_size = sizeof(s_keycompose_default) / sizeof(s_keycompose_default[0]);
423
424     // reset state, so we could switch keymaps at runtime
425     m_modifiers = 0;
426     memset(m_locks, 0, sizeof(m_locks));
427     m_composing = 0;
428     m_dead_unicode = 0xffff;
429 }
430
431 bool QEvdevKeyboardHandler::loadKeymap(const QString &file)
432 {
433 #ifdef QT_QPA_KEYMAP_DEBUG
434     qWarning() << "Load keymap" << file;
435 #endif
436
437     QFile f(file);
438
439     if (!f.open(QIODevice::ReadOnly)) {
440         qWarning("Could not open keymap file '%s'", qPrintable(file));
441         return false;
442     }
443
444     // .qmap files have a very simple structure:
445     // quint32 magic           (QKeyboard::FileMagic)
446     // quint32 version         (1)
447     // quint32 keymap_size     (# of struct QKeyboard::Mappings)
448     // quint32 keycompose_size (# of struct QKeyboard::Composings)
449     // all QKeyboard::Mappings via QDataStream::operator(<<|>>)
450     // all QKeyboard::Composings via QDataStream::operator(<<|>>)
451
452     quint32 qmap_magic, qmap_version, qmap_keymap_size, qmap_keycompose_size;
453
454     QDataStream ds(&f);
455
456     ds >> qmap_magic >> qmap_version >> qmap_keymap_size >> qmap_keycompose_size;
457
458     if (ds.status() != QDataStream::Ok || qmap_magic != QEvdevKeyboardMap::FileMagic || qmap_version != 1 || qmap_keymap_size == 0) {
459         qWarning("'%s' is ot a valid.qmap keymap file.", qPrintable(file));
460         return false;
461     }
462
463     QEvdevKeyboardMap::Mapping *qmap_keymap = new QEvdevKeyboardMap::Mapping[qmap_keymap_size];
464     QEvdevKeyboardMap::Composing *qmap_keycompose = qmap_keycompose_size ? new QEvdevKeyboardMap::Composing[qmap_keycompose_size] : 0;
465
466     for (quint32 i = 0; i < qmap_keymap_size; ++i)
467         ds >> qmap_keymap[i];
468     for (quint32 i = 0; i < qmap_keycompose_size; ++i)
469         ds >> qmap_keycompose[i];
470
471     if (ds.status() != QDataStream::Ok) {
472         delete [] qmap_keymap;
473         delete [] qmap_keycompose;
474
475         qWarning("Keymap file '%s' can not be loaded.", qPrintable(file));
476         return false;
477     }
478
479     // unload currently active and clear state
480     unloadKeymap();
481
482     m_keymap = qmap_keymap;
483     m_keymap_size = qmap_keymap_size;
484     m_keycompose = qmap_keycompose;
485     m_keycompose_size = qmap_keycompose_size;
486
487     m_do_compose = true;
488
489     return true;
490 }
491
492 QT_END_NAMESPACE