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