1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://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 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qkbdqnx_qws.h"
43 
44 #include "qplatformdefs.h"
45 #include "qsocketnotifier.h"
46 #include "private/qcore_unix_p.h"
47 #include "QtCore/qdebug.h"
48 
49 #include <sys/dcmd_input.h>
50 #include <sys/keycodes.h>
51 #include <errno.h>
52 
53 QT_BEGIN_NAMESPACE
54 
55 /*!
56     \class QWSQnxKeyboardHandler
57     \preliminary
58     \ingroup qws
59     \since 4.6
60     \internal
61 
62     \brief The QWSQnxKeyboardHandler class implements a keyboard driver
63     for the QNX \c{devi-hid} input manager.
64 
65     To be able to compile this mouse handler, \l{Qt for Embedded Linux}
66     must be configured with the \c -qt-kbd-qnx option, see the
67     \l{Qt for Embedded Linux Character Input} documentation for details.
68 
69     In order to use this keyboard handler, the \c{devi-hid} input manager
70     must be set up and run with the resource manager interface (option \c{-r}).
71     Also, Photon must not be running.
72 
73     Example invocation from command line: \c{/usr/photon/bin/devi-hid -Pr kbd mouse}
74     Note that after running \c{devi-hid}, you will not be able to use the local
75     shell anymore. It is suggested to run the command in a shell script, that launches
76     a Qt application after invocation of \c{devi-hid}.
77 
78     To make \l{Qt for Embedded Linux} explicitly choose the qnx keyboard
79     handler, set the QWS_KEYBOARD environment variable to \c{qnx}. By default,
80     the first keyboard device (\c{/dev/devi/keyboard0}) is used. To override, pass a device
81     name as the first and only parameter, for example
82     \c{QWS_KEYBOARD=qnx:/dev/devi/keyboard1; export QWS_KEYBOARD}.
83 
84     \sa {Qt for Embedded Linux Character Input}, {Qt for Embedded Linux}
85 */
86 
87 /*!
88     Constructs a keyboard handler for the specified \a device, defaulting to
89     \c{/dev/devi/keyboard0}.
90 
91     Note that you should never instanciate this class, instead let QKbdDriverFactory
92     handle the keyboard handlers.
93 
94     \sa QKbdDriverFactory
95  */
QWSQnxKeyboardHandler(const QString & device)96 QWSQnxKeyboardHandler::QWSQnxKeyboardHandler(const QString &device)
97 {
98     // open the keyboard device
99     keyboardFD = QT_OPEN(device.isEmpty() ? "/dev/devi/keyboard0" : device.toLatin1().constData(),
100                          QT_OPEN_RDONLY);
101     if (keyboardFD == -1) {
102         qErrnoWarning(errno, "QWSQnxKeyboardHandler: Unable to open device");
103     } else {
104         // create a socket notifier so we'll wake up whenever keyboard input is detected.
105         QSocketNotifier *notifier = new QSocketNotifier(keyboardFD, QSocketNotifier::Read, this);
106         connect(notifier, SIGNAL(activated(int)), SLOT(socketActivated()));
107 
108         qDebug("QWSQnxKeyboardHandler: connected.");
109     }
110 }
111 
112 /*!
113     Destroys this keyboard handler and closes the connection to the keyboard device.
114  */
~QWSQnxKeyboardHandler()115 QWSQnxKeyboardHandler::~QWSQnxKeyboardHandler()
116 {
117     if (keyboardFD != -1)
118         QT_CLOSE(keyboardFD);
119 }
120 
121 // similar to PhKeyToMb
key_sym_displayable(unsigned long sym)122 static inline bool key_sym_displayable(unsigned long sym)
123 {
124     if (sym >= 0xF000)
125         return sym >= 0xF100 && (sizeof(wchar_t) > 2 || sym < 0x10000);
126     return (sym & ~0x9F) != 0; // exclude 0...0x1F and 0x80...0x9F
127 }
128 
129 /*! \internal
130     Translates the QNX keyboard events to Qt keyboard events
131  */
socketActivated()132 void QWSQnxKeyboardHandler::socketActivated()
133 {
134     _keyboard_packet packet;
135 
136     // read one keyboard event
137     int bytesRead = QT_READ(keyboardFD, &packet, sizeof(_keyboard_packet));
138     if (bytesRead == -1) {
139         qErrnoWarning(errno, "QWSQnxKeyboardHandler::socketActivated(): Unable to read data.");
140         return;
141     }
142 
143     // the bytes read must be the size of a keyboard packet
144     Q_ASSERT(bytesRead == sizeof(_keyboard_packet));
145 
146     if (packet.data.flags & KEY_SYM_VALID_EX)
147         packet.data.flags |= KEY_SYM_VALID;
148     else if (!(packet.data.flags & (KEY_SYM_VALID | KEY_CAP_VALID)))
149         return;
150 
151 #if 0
152     qDebug() << "keyboard got scancode"
153              << hex << packet.data.modifiers
154              << packet.data.flags
155              << packet.data.key_cap
156              << packet.data.key_sym
157              << packet.data.key_scan;
158 #endif
159 
160     // QNX is nice enough to translate the raw keyboard data into generic format for us.
161     // Now we just have to translate it into a format Qt understands.
162 
163     // figure out the modifiers that are currently pressed
164     Qt::KeyboardModifiers modifiers = Qt::NoModifier;
165     if (packet.data.modifiers & KEYMOD_SHIFT)
166         modifiers |= Qt::ShiftModifier;
167     if (packet.data.modifiers & KEYMOD_CTRL)
168         modifiers |= Qt::ControlModifier;
169     if (packet.data.modifiers & KEYMOD_ALT)
170         modifiers |= Qt::AltModifier;
171     if (packet.data.modifiers & KEYMOD_NUM_LOCK)
172         modifiers |= Qt::KeypadModifier;
173 #if 0
174     // special case for AltGr
175     if (packet.data.modifiers & KEYMOD_ALTGR)
176         key = Qt::Key_AltGr;
177 #endif
178 
179     // figure out whether it's a press
180     bool isPress = packet.data.flags & KEY_DOWN;
181     // figure out whether the key is still pressed and the key event is repeated
182     bool isRepeat = packet.data.flags & KEY_REPEAT;
183 
184     int key = Qt::Key_unknown;
185     int unicode = 0;
186 
187     if (((packet.data.flags & KEY_SYM_VALID) && key_sym_displayable(unicode = packet.data.key_sym))
188         || ((packet.data.flags & KEY_CAP_VALID) && key_sym_displayable(unicode = packet.data.key_cap))) {
189         if (unicode <= 0x0ff) {
190             if (unicode >= 'a' && unicode <= 'z')
191                 key = Qt::Key_A + unicode - 'a';
192             else
193                 key = unicode;
194         }
195         // Ctrl<something> or Alt<something> is not a displayable character
196         if (modifiers & (Qt::ControlModifier | Qt::AltModifier))
197             unicode = 0;
198     } else {
199         unicode = 0;
200 
201         unsigned long sym = 0;
202         if (packet.data.flags & KEY_SYM_VALID)
203             sym = packet.data.key_sym;
204         else if (packet.data.flags & KEY_CAP_VALID)
205             sym = packet.data.key_cap;
206 
207         switch (sym) {
208         case KEYCODE_ESCAPE: key = Qt::Key_Escape; unicode = 27; break;
209         case KEYCODE_TAB: key = Qt::Key_Tab; unicode = 9; break;
210         case KEYCODE_BACK_TAB: key = Qt::Key_Backtab; break;
211         case KEYCODE_BACKSPACE: key = Qt::Key_Backspace; unicode = 127; break;
212         case KEYCODE_RETURN: key = Qt::Key_Return; break;
213         case KEYCODE_KP_ENTER: key = Qt::Key_Enter; break;
214         case KEYCODE_INSERT:
215         case KEYCODE_KP_INSERT:
216             key = Qt::Key_Insert; break;
217         case KEYCODE_KP_DELETE:
218             if (modifiers & Qt::KeypadModifier) {
219                 key = Qt::Key_Comma;
220                 break;
221             }
222         // fall through
223         case KEYCODE_DELETE:
224             key = Qt::Key_Delete; break;
225         case KEYCODE_PAUSE:
226         case KEYCODE_BREAK:
227             if (modifiers & (Qt::ControlModifier | Qt::AltModifier))
228                 return; // sometimes occurs at the middle of a key sequence
229             key = Qt::Key_Pause; break;
230         case KEYCODE_PRINT:
231             if (modifiers & (Qt::ControlModifier | Qt::AltModifier))
232                 return; // sometimes occurs at the middle of a key sequence
233             key = Qt::Key_Print; break;
234         case KEYCODE_SYSREQ:
235             key = Qt::Key_SysReq; break;
236         case KEYCODE_HOME:
237         case KEYCODE_KP_HOME:
238             key = Qt::Key_Home; break;
239         case KEYCODE_END:
240         case KEYCODE_KP_END:
241             key = Qt::Key_End; break;
242         case KEYCODE_LEFT:
243         case KEYCODE_KP_LEFT:
244             key = Qt::Key_Left; break;
245         case KEYCODE_UP:
246         case KEYCODE_KP_UP:
247             key = Qt::Key_Up; break;
248         case KEYCODE_RIGHT:
249         case KEYCODE_KP_RIGHT:
250             key = Qt::Key_Right; break;
251         case KEYCODE_DOWN:
252         case KEYCODE_KP_DOWN:
253             key = Qt::Key_Down; break;
254         case KEYCODE_PG_UP:
255         case KEYCODE_KP_PG_UP:
256             key = Qt::Key_PageUp; break;
257         case KEYCODE_PG_DOWN:
258         case KEYCODE_KP_PG_DOWN:
259             key = Qt::Key_PageDown; break;
260 
261         case KEYCODE_LEFT_SHIFT:
262         case KEYCODE_RIGHT_SHIFT:
263             key = Qt::Key_Shift; break;
264         case KEYCODE_LEFT_CTRL:
265         case KEYCODE_RIGHT_CTRL:
266             key = Qt::Key_Control; break;
267         case KEYCODE_LEFT_ALT:
268         case KEYCODE_RIGHT_ALT:
269             key = Qt::Key_Alt; break;
270         case KEYCODE_CAPS_LOCK:
271             key = Qt::Key_CapsLock; break;
272         case KEYCODE_NUM_LOCK:
273             key = Qt::Key_NumLock; break;
274         case KEYCODE_SCROLL_LOCK:
275             key = Qt::Key_ScrollLock; break;
276 
277         case KEYCODE_F1:
278         case KEYCODE_F2:
279         case KEYCODE_F3:
280         case KEYCODE_F4:
281         case KEYCODE_F5:
282         case KEYCODE_F6:
283         case KEYCODE_F7:
284         case KEYCODE_F8:
285         case KEYCODE_F9:
286         case KEYCODE_F10:
287         case KEYCODE_F11:
288         case KEYCODE_F12:
289             key = Qt::Key_F1 + sym - KEYCODE_F1; break;
290 
291         case KEYCODE_MENU: key = Qt::Key_Menu; break;
292         case KEYCODE_LEFT_HYPER: key = Qt::Key_Hyper_L; break;
293         case KEYCODE_RIGHT_HYPER: key = Qt::Key_Hyper_R; break;
294 
295         case KEYCODE_KP_PLUS: key = Qt::Key_Plus; break;
296         case KEYCODE_KP_MINUS: key = Qt::Key_Minus; break;
297         case KEYCODE_KP_MULTIPLY: key = Qt::Key_multiply; break;
298         case KEYCODE_KP_DIVIDE: key = Qt::Key_Slash; break;
299         case KEYCODE_KP_FIVE:
300             if (!(modifiers & Qt::KeypadModifier))
301                 key = Qt::Key_5;
302             break;
303 
304         default: // none of the above
305             break;
306         }
307     }
308 
309     if (key == Qt::Key_unknown && unicode == 0)
310         return;
311 
312     // call processKeyEvent. This is where all the magic happens to insert a
313     // key event into Qt's event loop.
314     // Note that for repeated key events, isPress must be true
315     // (on QNX, isPress is not set when the key event is repeated).
316     processKeyEvent(unicode, key, modifiers, isPress || isRepeat, isRepeat);
317 }
318 
319 QT_END_NAMESPACE
320