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 "qkbdlinuxinput_qws.h"
43
44 #ifndef QT_NO_QWS_KEYBOARD
45
46 #include <QSocketNotifier>
47 #include <QStringList>
48
49 #include <qplatformdefs.h>
50 #include <private/qcore_unix_p.h> // overrides QT_OPEN
51
52 #include <errno.h>
53 #include <termios.h>
54
55 #include <linux/kd.h>
56 #include <linux/input.h>
57
58 QT_BEGIN_NAMESPACE
59
60
61 class QWSLinuxInputKbPrivate : public QObject
62 {
63 Q_OBJECT
64 public:
65 QWSLinuxInputKbPrivate(QWSLinuxInputKeyboardHandler *, const QString &);
66 ~QWSLinuxInputKbPrivate();
67
68 private:
69 void switchLed(int, bool);
70
71 private Q_SLOTS:
72 void readKeycode();
73
74 private:
75 QWSLinuxInputKeyboardHandler *m_handler;
76 int m_fd;
77 int m_tty_fd;
78 struct termios m_tty_attr;
79 int m_orig_kbmode;
80 };
81
QWSLinuxInputKeyboardHandler(const QString & device)82 QWSLinuxInputKeyboardHandler::QWSLinuxInputKeyboardHandler(const QString &device)
83 : QWSKeyboardHandler(device)
84 {
85 d = new QWSLinuxInputKbPrivate(this, device);
86 }
87
~QWSLinuxInputKeyboardHandler()88 QWSLinuxInputKeyboardHandler::~QWSLinuxInputKeyboardHandler()
89 {
90 delete d;
91 }
92
filterInputEvent(quint16 &,qint32 &)93 bool QWSLinuxInputKeyboardHandler::filterInputEvent(quint16 &, qint32 &)
94 {
95 return false;
96 }
97
QWSLinuxInputKbPrivate(QWSLinuxInputKeyboardHandler * h,const QString & device)98 QWSLinuxInputKbPrivate::QWSLinuxInputKbPrivate(QWSLinuxInputKeyboardHandler *h, const QString &device)
99 : m_handler(h), m_fd(-1), m_tty_fd(-1), m_orig_kbmode(K_XLATE)
100 {
101 setObjectName(QLatin1String("LinuxInputSubsystem Keyboard Handler"));
102
103 QString dev = QLatin1String("/dev/input/event1");
104 int repeat_delay = -1;
105 int repeat_rate = -1;
106 int grab = 0;
107
108 QStringList args = device.split(QLatin1Char(':'));
109 foreach (const QString &arg, args) {
110 if (arg.startsWith(QLatin1String("repeat-delay=")))
111 repeat_delay = arg.mid(13).toInt();
112 else if (arg.startsWith(QLatin1String("repeat-rate=")))
113 repeat_rate = arg.mid(12).toInt();
114 else if (arg.startsWith(QLatin1String("grab=")))
115 grab = arg.mid(5).toInt();
116 else if (arg.startsWith(QLatin1String("/dev/")))
117 dev = arg;
118 }
119
120 m_fd = QT_OPEN(dev.toLocal8Bit().constData(), O_RDWR, 0);
121 if (m_fd >= 0) {
122 ::ioctl(m_fd, EVIOCGRAB, grab);
123 if (repeat_delay > 0 && repeat_rate > 0) {
124 int kbdrep[2] = { repeat_delay, repeat_rate };
125 ::ioctl(m_fd, EVIOCSREP, kbdrep);
126 }
127
128 QSocketNotifier *notifier;
129 notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
130 connect(notifier, SIGNAL(activated(int)), this, SLOT(readKeycode()));
131
132 // play nice in case we are started from a shell (e.g. for debugging)
133 m_tty_fd = isatty(0) ? 0 : -1;
134
135 if (m_tty_fd >= 0) {
136 // save tty config for restore.
137 tcgetattr(m_tty_fd, &m_tty_attr);
138
139 struct ::termios termdata;
140 tcgetattr(m_tty_fd, &termdata);
141
142 // record the original mode so we can restore it again in the destructor.
143 ::ioctl(m_tty_fd, KDGKBMODE, &m_orig_kbmode);
144
145 // setting this translation mode is even needed in INPUT mode to prevent
146 // the shell from also interpreting codes, if the process has a tty
147 // attached: e.g. Ctrl+C wouldn't copy, but kill the application.
148 ::ioctl(m_tty_fd, KDSKBMODE, K_MEDIUMRAW);
149
150 // set the tty layer to pass-through
151 termdata.c_iflag = (IGNPAR | IGNBRK) & (~PARMRK) & (~ISTRIP);
152 termdata.c_oflag = 0;
153 termdata.c_cflag = CREAD | CS8;
154 termdata.c_lflag = 0;
155 termdata.c_cc[VTIME]=0;
156 termdata.c_cc[VMIN]=1;
157 cfsetispeed(&termdata, 9600);
158 cfsetospeed(&termdata, 9600);
159 tcsetattr(m_tty_fd, TCSANOW, &termdata);
160 }
161 } else {
162 qWarning("Cannot open keyboard input device '%s': %s", qPrintable(dev), strerror(errno));
163 return;
164 }
165 }
166
~QWSLinuxInputKbPrivate()167 QWSLinuxInputKbPrivate::~QWSLinuxInputKbPrivate()
168 {
169 if (m_tty_fd >= 0) {
170 ::ioctl(m_tty_fd, KDSKBMODE, m_orig_kbmode);
171 tcsetattr(m_tty_fd, TCSANOW, &m_tty_attr);
172 }
173 if (m_fd >= 0)
174 QT_CLOSE(m_fd);
175 }
176
switchLed(int led,bool state)177 void QWSLinuxInputKbPrivate::switchLed(int led, bool state)
178 {
179 struct ::input_event led_ie;
180 ::gettimeofday(&led_ie.time, 0);
181 led_ie.type = EV_LED;
182 led_ie.code = led;
183 led_ie.value = state;
184
185 QT_WRITE(m_fd, &led_ie, sizeof(led_ie));
186 }
187
readKeycode()188 void QWSLinuxInputKbPrivate::readKeycode()
189 {
190 struct ::input_event buffer[32];
191 int n = 0;
192
193 forever {
194 n = QT_READ(m_fd, reinterpret_cast<char *>(buffer) + n, sizeof(buffer) - n);
195
196 if (n == 0) {
197 qWarning("Got EOF from the input device.");
198 return;
199 } else if (n < 0 && (errno != EINTR && errno != EAGAIN)) {
200 qWarning("Could not read from input device: %s", strerror(errno));
201 return;
202 } else if (n % sizeof(buffer[0]) == 0) {
203 break;
204 }
205 }
206
207 n /= sizeof(buffer[0]);
208
209 for (int i = 0; i < n; ++i) {
210 if (buffer[i].type != EV_KEY)
211 continue;
212
213 quint16 code = buffer[i].code;
214 qint32 value = buffer[i].value;
215
216 if (m_handler->filterInputEvent(code, value))
217 continue;
218
219 QWSKeyboardHandler::KeycodeAction ka;
220 ka = m_handler->processKeycode(code, value != 0, value == 2);
221
222 switch (ka) {
223 case QWSKeyboardHandler::CapsLockOn:
224 case QWSKeyboardHandler::CapsLockOff:
225 switchLed(LED_CAPSL, ka == QWSKeyboardHandler::CapsLockOn);
226 break;
227
228 case QWSKeyboardHandler::NumLockOn:
229 case QWSKeyboardHandler::NumLockOff:
230 switchLed(LED_NUML, ka == QWSKeyboardHandler::NumLockOn);
231 break;
232
233 case QWSKeyboardHandler::ScrollLockOn:
234 case QWSKeyboardHandler::ScrollLockOff:
235 switchLed(LED_SCROLLL, ka == QWSKeyboardHandler::ScrollLockOn);
236 break;
237
238 default:
239 // ignore console switching and reboot
240 break;
241 }
242 }
243 }
244
245 QT_END_NAMESPACE
246
247 #include "qkbdlinuxinput_qws.moc"
248
249 #endif // QT_NO_QWS_KEYBOARD
250