Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qbsdkeyboard.cpp
Go to the documentation of this file.
1// Copyright (C) 2015-2016 Oleksandr Tymoshenko <gonzo@bluezbox.com>
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 "qbsdkeyboard.h"
5
6#include <QByteArray>
7#include <QFile>
8#include <QGuiApplication>
9#include <QPoint>
10#include <QSocketNotifier>
11#include <QString>
12#include <QStringList>
13
14#include <QtCore/qglobal.h>
15#include <qpa/qwindowsysteminterface.h>
16#include <private/qcore_unix_p.h>
17#include <private/qguiapplication_p.h>
18#include <private/qinputdevicemanager_p_p.h>
19
20#include <qdebug.h>
21#include <cstdio>
22
23#include <cerrno>
24#include <fcntl.h>
25#include <unistd.h>
26
27#include <termios.h>
28#include <sys/kbio.h>
29
30// #define QT_BSD_KEYBOARD_DEBUG
31
32#ifdef QT_BSD_KEYBOARD_DEBUG
33#include <qdebug.h>
34#endif
35
37
38enum {
41};
42
44
46{
48
49 setObjectName(QLatin1String("BSD Keyboard Handler"));
50
52 if (specification.startsWith("/dev/"))
53 device = QFile::encodeName(specification);
54
55 if (device.isEmpty()) {
56 device = QByteArrayLiteral("STDIN");
57 m_fd = fileno(stdin);
58 }
59 else {
60 m_fd = QT_OPEN(device.constData(), O_RDONLY);
61 if (!m_fd) {
62 qErrnoWarning(errno, "open(%s) failed", device.constData());
63 return;
64 }
65 m_shouldClose = true;
66 }
67
68 if (ioctl(m_fd, KDGKBMODE, &m_origKbdMode)) {
69 qErrnoWarning(errno, "ioctl(%s, KDGKBMODE) failed", device.constData());
71 return;
72 }
73
74 if (ioctl(m_fd, KDSKBMODE, K_CODE) < 0) {
75 qErrnoWarning(errno, "ioctl(%s, KDSKBMODE) failed", device.constData());
77 return;
78 }
79
80 termios kbdtty;
81 if (tcgetattr(m_fd, &kbdtty) == 0) {
82
83 m_kbdOrigTty.reset(new termios);
84 *m_kbdOrigTty = kbdtty;
85
86 kbdtty.c_iflag = IGNPAR | IGNBRK;
87 kbdtty.c_oflag = 0;
88 kbdtty.c_cflag = CREAD | CS8;
89 kbdtty.c_lflag = 0;
90 kbdtty.c_cc[VTIME] = 0;
91 kbdtty.c_cc[VMIN] = 0;
92 cfsetispeed(&kbdtty, 9600);
93 cfsetospeed(&kbdtty, 9600);
94 if (tcsetattr(m_fd, TCSANOW, &kbdtty) < 0) {
95 qErrnoWarning(errno, "tcsetattr(%s) failed", device.constData());
96
97 // TTY is still at old settings so we can
98 // dispose of original termios data
99 m_kbdOrigTty.reset();
100
102 return;
103 }
104 } else {
105 qErrnoWarning(errno, "tcgetattr(%s) failed", device.constData());
107 return;
108 }
109
110 if (fcntl(m_fd, F_SETFL, O_NONBLOCK)) {
111 qErrnoWarning(errno, "fcntl(%s, F_SETFL, O_NONBLOCK) failed", device.constData());
113 return;
114 }
115
116 resetKeymap();
117
118 m_notifier.reset(new QSocketNotifier(m_fd, QSocketNotifier::Read, this));
122}
123
128
130{
131 if (m_fd >= 0) {
132 if (m_kbdOrigTty) {
133 tcsetattr(m_fd, TCSANOW, m_kbdOrigTty.data());
134 m_kbdOrigTty.reset();
135 }
136
137 if (m_origKbdMode != Bsd_NoKeyMode) {
138 ioctl(m_fd, KDSKBMODE, m_origKbdMode);
139 m_origKbdMode = Bsd_NoKeyMode;
140 }
141
142 if (m_shouldClose)
143 close(m_fd);
144 m_fd = -1;
145 }
146}
147
149{
150
151 for (;;) {
152 uint8_t buffer[32];
153 int bytesRead = qt_safe_read(m_fd, buffer, sizeof(buffer));
154
155 if (!bytesRead) {
156 qWarning("Got EOF from the input device.");
157 return;
158 } else if (bytesRead < 0) {
159 if (errno != EINTR && errno != EAGAIN)
160 qWarning("Could not read from input device: %s", strerror(errno));
161 return;
162 }
163
164 for (int i = 0; i < bytesRead; ++i) {
165 const quint16 code = buffer[i] & Bsd_KeyCodeMask;
166 const bool pressed = (buffer[i] & Bsd_KeyPressedMask) ? false : true;
167
168 processKeycode(code, pressed, false);
169 }
170 }
171}
172
173void QBsdKeyboardHandler::processKeyEvent(int nativecode, int unicode, int qtcode,
174 Qt::KeyboardModifiers modifiers, bool isPress,
175 bool autoRepeat)
176{
177 const QString text = (unicode != 0xffff ) ? QString(unicode) : QString();
178 const QEvent::Type eventType = isPress ? QEvent::KeyPress : QEvent::KeyRelease;
179
180 QWindowSystemInterface::handleExtendedKeyEvent(0, eventType, qtcode, modifiers, nativecode, 0,
181 int(modifiers), text, autoRepeat);
182}
183
184void QBsdKeyboardHandler::processKeycode(quint16 keycode, bool pressed, bool autorepeat)
185{
186 const bool first_press = pressed && !autorepeat;
187
188 const QBsdKeyboardMap::Mapping *map_plain = nullptr;
189 const QBsdKeyboardMap::Mapping *map_withmod = nullptr;
190
191 quint8 modifiers = m_modifiers;
192
193 // get a specific and plain mapping for the keycode and the current modifiers
194 for (const QBsdKeyboardMap::Mapping &m : m_keymap) {
195 if (m.keycode == keycode) {
196 if (m.modifiers == 0)
197 map_plain = &m;
198
199 quint8 testmods = m_modifiers;
200 if (m_capsLock && (m.flags & QBsdKeyboardMap::IsLetter))
201 testmods ^= QBsdKeyboardMap::ModShift;
202 if (m.modifiers == testmods)
203 map_withmod = &m;
204 }
205 }
206
207 if (m_capsLock && map_withmod && (map_withmod->flags & QBsdKeyboardMap::IsLetter))
209
210#ifdef QT_BSD_KEYBOARD_DEBUG
211 qWarning("Processing key event: keycode=%3d, modifiers=%02x pressed=%d, autorepeat=%d", \
212 keycode, modifiers, pressed ? 1 : 0, autorepeat ? 1 : 0);
213#endif
214
215 const QBsdKeyboardMap::Mapping *it = map_withmod ? map_withmod : map_plain;
216
217 if (!it) {
218#ifdef QT_BSD_KEYBOARD_DEBUG
219 // we couldn't even find a plain mapping
220 qWarning("Could not find a suitable mapping for keycode: %3d, modifiers: %02x", keycode, modifiers);
221#endif
222 return;
223 }
224
225 bool skip = false;
226 quint16 unicode = it->unicode;
227 quint32 qtcode = it->qtcode;
228
229 if ((it->flags & QBsdKeyboardMap::IsModifier) && it->special) {
230 // this is a modifier, i.e. Shift, Alt, ...
231 if (pressed)
232 m_modifiers |= quint8(it->special);
233 else
234 m_modifiers &= ~quint8(it->special);
235 } else if (qtcode >= Qt::Key_CapsLock && qtcode <= Qt::Key_ScrollLock) {
236 // (Caps|Num|Scroll)Lock
237 if (first_press) {
238 switch (qtcode) {
239 case Qt::Key_CapsLock:
240 m_capsLock = !m_capsLock;
241 switchLed(LED_CAP, m_capsLock);
242 break;
243 case Qt::Key_NumLock:
244 m_numLock = !m_numLock;
245 switchLed(LED_NUM, m_numLock);
246 break;
248 m_scrollLock = !m_scrollLock;
249 switchLed(LED_SCR, m_scrollLock);
250 break;
251 default:
252 break;
253 }
254 }
255 }
256
257 if (!skip) {
258 // a normal key was pressed
261
262 // we couldn't find a specific mapping for the current modifiers,
263 // or that mapping didn't have special modifiers:
264 // so just report the plain mapping with additional modifiers.
265 if ((it == map_plain && it != map_withmod) ||
266 (map_withmod && !(map_withmod->qtcode & modmask))) {
268 }
269
270#ifdef QT_BSD_KEYBOARD_DEBUG
271 qWarning("Processing: uni=%04x, qt=%08x, qtmod=%08x", unicode, qtcode & ~modmask, (qtcode & modmask));
272#endif
273 //If NumLockOff and keypad key pressed remap event sent
274 if (!m_numLock &&
275 (qtcode & Qt::KeypadModifier)) {
276 unicode = 0xffff;
277 const int oldMask = (qtcode & modmask);
278 switch (qtcode & ~modmask) {
279 case Qt::Key_7: //7 --> Home
280 qtcode = Qt::Key_Home;
281 break;
282 case Qt::Key_8: //8 --> Up
283 qtcode = Qt::Key_Up;
284 break;
285 case Qt::Key_9: //9 --> PgUp
286 qtcode = Qt::Key_PageUp;
287 break;
288 case Qt::Key_4: //4 --> Left
289 qtcode = Qt::Key_Left;
290 break;
291 case Qt::Key_5: //5 --> Clear
292 qtcode = Qt::Key_Clear;
293 break;
294 case Qt::Key_6: //6 --> right
295 qtcode = Qt::Key_Right;
296 break;
297 case Qt::Key_1: //1 --> End
298 qtcode = Qt::Key_End;
299 break;
300 case Qt::Key_2: //2 --> Down
301 qtcode = Qt::Key_Down;
302 break;
303 case Qt::Key_3: //3 --> PgDn
304 qtcode = Qt::Key_PageDown;
305 break;
306 case Qt::Key_0: //0 --> Ins
307 qtcode = Qt::Key_Insert;
308 break;
309 case Qt::Key_Period: //. --> Del
310 qtcode = Qt::Key_Delete;
311 break;
312 }
313 qtcode |= oldMask;
314 }
315
316 // send the result to the server
317 processKeyEvent(keycode, unicode, qtcode & ~modmask,
318 Qt::KeyboardModifiers(qtcode & modmask), pressed, autorepeat);
319 }
320}
321
323{
324#ifdef QT_BSD_KEYBOARD_DEBUG
325 qWarning() << "switchLed" << led << state;
326#endif
327 int leds = 0;
328 if (ioctl(m_fd, KDGETLED, &leds) < 0) {
329 qWarning("switchLed: Failed to query led states.");
330 return;
331 }
332
333 if (state)
334 leds |= led;
335 else
336 leds &= ~led;
337
338 if (ioctl(m_fd, KDSETLED, leds) < 0)
339 qWarning("switchLed: Failed to set led states.");
340}
341
343{
344#ifdef QT_BSD_KEYBOARD_DEBUG
345 qWarning() << "Unload current keymap and restore built-in";
346#endif
347
348 m_keymap.clear();
349
350 const size_t mappingSize = sizeof(keymapDefault) / sizeof(keymapDefault[0]);
351 m_keymap.resize(mappingSize);
352 std::copy_n( &keymapDefault[0], mappingSize, m_keymap.begin() );
353
354 // reset state, so we could switch keymaps at runtime
355 m_modifiers = 0;
356 m_capsLock = false;
357 m_numLock = false;
358 m_scrollLock = false;
359
360 //Set locks according to keyboard leds
361 int leds = 0;
362 if (ioctl(m_fd, KDGETLED, &leds) < 0) {
363 qWarning("Failed to query led states. Settings numlock & capslock off");
364 switchLed(LED_NUM, false);
365 switchLed(LED_CAP, false);
366 switchLed(LED_SCR, false);
367 } else {
368 if ((leds & LED_CAP) > 0)
369 m_capsLock = true;
370 if ((leds & LED_NUM) > 0)
371 m_numLock = true;
372 if ((leds & LED_SCR) > 0)
373 m_scrollLock = true;
374#ifdef QT_BSD_KEYBOARD_DEBUG
375 qWarning("numlock=%d , capslock=%d, scrolllock=%d",m_numLock, m_capsLock, m_scrollLock);
376#endif
377 }
378}
IOBluetoothDevice * device
void processKeycode(quint16 keycode, bool pressed, bool autorepeat)
static Qt::KeyboardModifiers toQtModifiers(quint8 mod)
void switchLed(int led, bool state)
QBsdKeyboardHandler(const QString &key, const QString &specification)
void processKeyEvent(int nativecode, int unicode, int qtcode, Qt::KeyboardModifiers modifiers, bool isPress, bool autoRepeat)
~QBsdKeyboardHandler() override
\inmodule QtCore
Definition qbytearray.h:57
Type
This enum type defines the valid event types in Qt.
Definition qcoreevent.h:51
@ KeyRelease
Definition qcoreevent.h:65
@ KeyPress
Definition qcoreevent.h:64
static QByteArray encodeName(const QString &fileName)
Converts fileName to an 8-bit encoding that you can use in native APIs.
Definition qfile.h:158
static QInputDeviceManager * inputDeviceManager()
static QInputDeviceManagerPrivate * get(QInputDeviceManager *mgr)
iterator begin()
Definition qlist.h:625
void resize(qsizetype size)
Definition qlist.h:403
void clear()
Definition qlist.h:434
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
Q_WEAK_OVERLOAD void setObjectName(const QString &name)
Sets the object's name to name.
Definition qobject.h:127
T * data() const noexcept
Returns the value of the pointer referenced by this object.
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
\inmodule QtCore
void activated(QSocketDescriptor socket, QSocketNotifier::Type activationEvent, QPrivateSignal)
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static bool handleExtendedKeyEvent(QWindow *window, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers, const QString &text=QString(), bool autorep=false, ushort count=1)
EGLImageKHR int int EGLuint64KHR * modifiers
QString text
QSet< QString >::iterator it
else opt state
[0]
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
@ Key_9
Definition qnamespace.h:539
@ Key_Right
Definition qnamespace.h:679
@ Key_PageUp
Definition qnamespace.h:681
@ Key_4
Definition qnamespace.h:534
@ Key_2
Definition qnamespace.h:532
@ Key_7
Definition qnamespace.h:537
@ Key_8
Definition qnamespace.h:538
@ Key_Insert
Definition qnamespace.h:669
@ Key_Left
Definition qnamespace.h:677
@ Key_0
Definition qnamespace.h:530
@ Key_1
Definition qnamespace.h:531
@ Key_Up
Definition qnamespace.h:678
@ Key_Down
Definition qnamespace.h:680
@ Key_6
Definition qnamespace.h:536
@ Key_Delete
Definition qnamespace.h:670
@ Key_NumLock
Definition qnamespace.h:688
@ Key_ScrollLock
Definition qnamespace.h:689
@ Key_3
Definition qnamespace.h:533
@ Key_Period
Definition qnamespace.h:528
@ Key_PageDown
Definition qnamespace.h:682
@ Key_Home
Definition qnamespace.h:675
@ Key_Clear
Definition qnamespace.h:674
@ Key_CapsLock
Definition qnamespace.h:687
@ Key_5
Definition qnamespace.h:535
@ Key_End
Definition qnamespace.h:676
@ ShiftModifier
@ ControlModifier
@ MetaModifier
@ KeypadModifier
@ AltModifier
@ Bsd_KeyCodeMask
@ Bsd_KeyPressedMask
@ Bsd_NoKeyMode
const QBsdKeyboardMap::Mapping keymapDefault[]
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
static qint64 qt_safe_read(int fd, void *data, qint64 maxlen)
#define QT_OPEN
#define qWarning
Definition qlogging.h:166
const GLfloat * m
GLuint64 key
GLenum GLuint buffer
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:50
unsigned short quint16
Definition qtypes.h:48
unsigned char quint8
Definition qtypes.h:46
int keycode
Definition qvnc.cpp:140