Revert "Move QWindowSystemInterface out of qpa."
[profile/ivi/qtbase.git] / src / platformsupport / input / evdevtablet / qevdevtablet.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 plugins 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 "qevdevtablet_p.h"
43 #include <qpa/qwindowsysteminterface.h>
44 #include <QStringList>
45 #include <QSocketNotifier>
46 #include <QGuiApplication>
47 #include <QDebug>
48 #include <QtCore/private/qcore_unix_p.h>
49 #include <QtPlatformSupport/private/qdevicediscovery_p.h>
50 #include <linux/input.h>
51
52 QT_BEGIN_NAMESPACE
53
54 class QEvdevTabletData
55 {
56 public:
57     QEvdevTabletData(QEvdevTabletHandler *q_ptr);
58     bool queryLimits();
59     void testGrab();
60     void processInputEvent(input_event *ev);
61     void reportProximityEnter();
62     void reportProximityLeave();
63     void report();
64
65     QEvdevTabletHandler *q;
66     QSocketNotifier *notifier;
67     int fd;
68     int lastEventType;
69     QString devName;
70     struct {
71         int x, y, p, d;
72     } minValues, maxValues;
73     struct {
74         int x, y, p, d;
75         bool down, lastReportDown;
76         int tool, lastReportTool;
77         QPointF lastReportPos;
78     } state;
79 };
80
81 QEvdevTabletData::QEvdevTabletData(QEvdevTabletHandler *q_ptr)
82     : q(q_ptr), notifier(0), fd(-1), lastEventType(0)
83 {
84     memset(&minValues, 0, sizeof(minValues));
85     memset(&maxValues, 0, sizeof(maxValues));
86     memset(&state, 0, sizeof(state));
87 }
88
89 bool QEvdevTabletData::queryLimits()
90 {
91     bool ok = true;
92     input_absinfo absInfo;
93     memset(&absInfo, 0, sizeof(input_absinfo));
94     ok &= ioctl(fd, EVIOCGABS(ABS_X), &absInfo) >= 0;
95     if (ok) {
96         minValues.x = absInfo.minimum;
97         maxValues.x = absInfo.maximum;
98         qDebug("evdevtablet: min X: %d max X: %d", minValues.x, maxValues.x);
99     }
100     ok &= ioctl(fd, EVIOCGABS(ABS_Y), &absInfo) >= 0;
101     if (ok) {
102         minValues.y = absInfo.minimum;
103         maxValues.y = absInfo.maximum;
104         qDebug("evdevtablet: min Y: %d max Y: %d", minValues.y, maxValues.y);
105     }
106     if (ioctl(fd, EVIOCGABS(ABS_PRESSURE), &absInfo) >= 0) {
107         minValues.p = absInfo.minimum;
108         maxValues.p = absInfo.maximum;
109         qDebug("evdevtablet: min pressure: %d max pressure: %d", minValues.p, maxValues.p);
110     }
111     if (ioctl(fd, EVIOCGABS(ABS_DISTANCE), &absInfo) >= 0) {
112         minValues.d = absInfo.minimum;
113         maxValues.d = absInfo.maximum;
114         qDebug("evdevtablet: min distance: %d max distance: %d", minValues.d, maxValues.d);
115     }
116     char name[128];
117     if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), name) >= 0) {
118         devName = QString::fromLocal8Bit(name);
119         qDebug("evdevtablet: device name: %s", name);
120     }
121     return ok;
122 }
123
124 void QEvdevTabletData::testGrab()
125 {
126     bool grabSuccess = !ioctl(fd, EVIOCGRAB, (void *) 1);
127     if (grabSuccess)
128         ioctl(fd, EVIOCGRAB, (void *) 0);
129     else
130         qWarning("evdevtablet: ERROR: The device is grabbed by another process. No events will be read.");
131 }
132
133 void QEvdevTabletData::processInputEvent(input_event *ev)
134 {
135     if (ev->type == EV_ABS) {
136         switch (ev->code) {
137         case ABS_X:
138             state.x = ev->value;
139             break;
140         case ABS_Y:
141             state.y = ev->value;
142             break;
143         case ABS_PRESSURE:
144             state.p = ev->value;
145             break;
146         case ABS_DISTANCE:
147             state.d = ev->value;
148             break;
149         default:
150             break;
151         }
152     } else if (ev->type == EV_KEY) {
153         // code BTN_TOOL_* value 1 -> proximity enter
154         // code BTN_TOOL_* value 0 -> proximity leave
155         // code BTN_TOUCH value 1 -> contact with screen
156         // code BTN_TOUCH value 0 -> no contact
157         switch (ev->code) {
158         case BTN_TOUCH:
159             state.down = ev->value != 0;
160             break;
161         case BTN_TOOL_PEN:
162             state.tool = ev->value ? QTabletEvent::Pen : 0;
163             break;
164         case BTN_TOOL_RUBBER:
165             state.tool = ev->value ? QTabletEvent::Eraser : 0;
166             break;
167         default:
168             break;
169         }
170     } else if (ev->type == EV_SYN && ev->code == SYN_REPORT && lastEventType != ev->type) {
171         report();
172     }
173     lastEventType = ev->type;
174 }
175
176 void QEvdevTabletData::reportProximityEnter()
177 {
178     QWindowSystemInterface::handleTabletEnterProximityEvent(QTabletEvent::Stylus, state.tool, 1);
179 }
180
181 void QEvdevTabletData::reportProximityLeave()
182 {
183     QWindowSystemInterface::handleTabletLeaveProximityEvent(QTabletEvent::Stylus, state.tool, 1);
184 }
185
186 void QEvdevTabletData::report()
187 {
188     if (!state.lastReportTool && state.tool)
189         reportProximityEnter();
190
191     qreal nx = (state.x - minValues.x) / qreal(maxValues.x - minValues.x);
192     qreal ny = (state.y - minValues.y) / qreal(maxValues.y - minValues.y);
193
194     QRect winRect = QGuiApplication::primaryScreen()->geometry();
195     QPointF globalPos(nx * winRect.width(), ny * winRect.height());
196     int pointer = state.tool;
197     // Prevent sending confusing values of 0 when moving the pen outside the active area.
198     if (!state.down && state.lastReportDown) {
199         globalPos = state.lastReportPos;
200         pointer = state.lastReportTool;
201     }
202
203     qreal pressure = (state.p - minValues.p) / qreal(maxValues.p - minValues.p);
204
205     if (state.down || state.lastReportDown) {
206         QWindowSystemInterface::handleTabletEvent(0, state.down, QPointF(), globalPos,
207                                                   QTabletEvent::Stylus, pointer,
208                                                   pressure, 0, 0, 0, 0, 0, 1, qGuiApp->keyboardModifiers());
209     }
210
211     if (state.lastReportTool && !state.tool)
212         reportProximityLeave();
213
214     state.lastReportDown = state.down;
215     state.lastReportTool = state.tool;
216     state.lastReportPos = globalPos;
217 }
218
219
220 QEvdevTabletHandler::QEvdevTabletHandler(const QString &spec, QObject *parent)
221     : QObject(parent), d(0)
222 {
223     setObjectName(QLatin1String("Evdev Tablet Handler"));
224     d = new QEvdevTabletData(this);
225     QString dev;
226     QStringList args = spec.split(QLatin1Char(':'));
227     for (int i = 0; i < args.count(); ++i) {
228         if (args.at(i).startsWith(QLatin1String("/dev/"))) {
229             dev = args.at(i);
230             break;
231         }
232     }
233     if (dev.isEmpty()) {
234         QScopedPointer<QDeviceDiscovery> deviceDiscovery(
235                     QDeviceDiscovery::create(QDeviceDiscovery::Device_Tablet, this));
236         if (deviceDiscovery) {
237             QStringList devices = deviceDiscovery->scanConnectedDevices();
238             if (!devices.isEmpty())
239                 dev = devices.at(0);
240         }
241     }
242     if (!dev.isEmpty()) {
243         qDebug("evdevtablet: using %s", qPrintable(dev));
244         d->fd = QT_OPEN(dev.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);
245         if (d->fd >= 0) {
246             d->testGrab();
247             if (d->queryLimits()) {
248                 d->notifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, this);
249                 connect(d->notifier, SIGNAL(activated(int)), this, SLOT(readData()));
250             }
251         } else {
252             qErrnoWarning(errno, "evdevtablet: Cannot open input device");
253         }
254     }
255 }
256
257 QEvdevTabletHandler::~QEvdevTabletHandler()
258 {
259     delete d->notifier;
260     if (d->fd >= 0)
261         QT_CLOSE(d->fd);
262
263     delete d;
264 }
265
266 void QEvdevTabletHandler::readData()
267 {
268     static input_event buffer[32];
269     int n = 0;
270     for (; ;) {
271         int result = QT_READ(d->fd, reinterpret_cast<char*>(buffer) + n, sizeof(buffer) - n);
272         if (!result) {
273             qWarning("evdevtablet: Got EOF from input device");
274             return;
275         } else if (result < 0) {
276             if (errno != EINTR && errno != EAGAIN) {
277                 qErrnoWarning(errno, "evdevtablet: Could not read from input device");
278                 if (errno == ENODEV) { // device got disconnected -> stop reading
279                     delete d->notifier;
280                     d->notifier = 0;
281                     QT_CLOSE(d->fd);
282                     d->fd = -1;
283                 }
284                 return;
285             }
286         } else {
287             n += result;
288             if (n % sizeof(input_event) == 0)
289                 break;
290         }
291     }
292
293     n /= sizeof(input_event);
294
295     for (int i = 0; i < n; ++i)
296         d->processInputEvent(&buffer[i]);
297 }
298
299
300 QEvdevTabletHandlerThread::QEvdevTabletHandlerThread(const QString &spec, QObject *parent)
301     : QThread(parent), m_spec(spec), m_handler(0)
302 {
303     start();
304 }
305
306 QEvdevTabletHandlerThread::~QEvdevTabletHandlerThread()
307 {
308     quit();
309     wait();
310 }
311
312 void QEvdevTabletHandlerThread::run()
313 {
314     m_handler = new QEvdevTabletHandler(m_spec);
315     exec();
316     delete m_handler;
317     m_handler = 0;
318 }
319
320
321 QT_END_NAMESPACE