1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the plugins module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qevdevtablet_p.h"
43 #include <qpa/qwindowsysteminterface.h>
44 #include <QStringList>
45 #include <QSocketNotifier>
46 #include <QGuiApplication>
48 #include <QtCore/private/qcore_unix_p.h>
49 #include <QtPlatformSupport/private/qdevicediscovery_p.h>
50 #include <linux/input.h>
54 class QEvdevTabletData
57 QEvdevTabletData(QEvdevTabletHandler *q_ptr);
60 void processInputEvent(input_event *ev);
61 void reportProximityEnter();
62 void reportProximityLeave();
65 QEvdevTabletHandler *q;
66 QSocketNotifier *notifier;
72 } minValues, maxValues;
75 bool down, lastReportDown;
76 int tool, lastReportTool;
77 QPointF lastReportPos;
81 QEvdevTabletData::QEvdevTabletData(QEvdevTabletHandler *q_ptr)
82 : q(q_ptr), notifier(0), fd(-1), lastEventType(0)
84 memset(&minValues, 0, sizeof(minValues));
85 memset(&maxValues, 0, sizeof(maxValues));
86 memset(&state, 0, sizeof(state));
89 bool QEvdevTabletData::queryLimits()
92 input_absinfo absInfo;
93 memset(&absInfo, 0, sizeof(input_absinfo));
94 ok &= ioctl(fd, EVIOCGABS(ABS_X), &absInfo) >= 0;
96 minValues.x = absInfo.minimum;
97 maxValues.x = absInfo.maximum;
98 qDebug("evdevtablet: min X: %d max X: %d", minValues.x, maxValues.x);
100 ok &= ioctl(fd, EVIOCGABS(ABS_Y), &absInfo) >= 0;
102 minValues.y = absInfo.minimum;
103 maxValues.y = absInfo.maximum;
104 qDebug("evdevtablet: min Y: %d max Y: %d", minValues.y, maxValues.y);
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);
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);
117 if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), name) >= 0) {
118 devName = QString::fromLocal8Bit(name);
119 qDebug("evdevtablet: device name: %s", name);
124 void QEvdevTabletData::testGrab()
126 bool grabSuccess = !ioctl(fd, EVIOCGRAB, (void *) 1);
128 ioctl(fd, EVIOCGRAB, (void *) 0);
130 qWarning("evdevtablet: ERROR: The device is grabbed by another process. No events will be read.");
133 void QEvdevTabletData::processInputEvent(input_event *ev)
135 if (ev->type == EV_ABS) {
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
159 state.down = ev->value != 0;
162 state.tool = ev->value ? QTabletEvent::Pen : 0;
164 case BTN_TOOL_RUBBER:
165 state.tool = ev->value ? QTabletEvent::Eraser : 0;
170 } else if (ev->type == EV_SYN && ev->code == SYN_REPORT && lastEventType != ev->type) {
173 lastEventType = ev->type;
176 void QEvdevTabletData::reportProximityEnter()
178 QWindowSystemInterface::handleTabletEnterProximityEvent(QTabletEvent::Stylus, state.tool, 1);
181 void QEvdevTabletData::reportProximityLeave()
183 QWindowSystemInterface::handleTabletLeaveProximityEvent(QTabletEvent::Stylus, state.tool, 1);
186 void QEvdevTabletData::report()
188 if (!state.lastReportTool && state.tool)
189 reportProximityEnter();
191 qreal nx = (state.x - minValues.x) / qreal(maxValues.x - minValues.x);
192 qreal ny = (state.y - minValues.y) / qreal(maxValues.y - minValues.y);
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;
203 qreal pressure = (state.p - minValues.p) / qreal(maxValues.p - minValues.p);
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());
211 if (state.lastReportTool && !state.tool)
212 reportProximityLeave();
214 state.lastReportDown = state.down;
215 state.lastReportTool = state.tool;
216 state.lastReportPos = globalPos;
220 QEvdevTabletHandler::QEvdevTabletHandler(const QString &spec, QObject *parent)
221 : QObject(parent), d(0)
223 setObjectName(QLatin1String("Evdev Tablet Handler"));
224 d = new QEvdevTabletData(this);
226 QStringList args = spec.split(QLatin1Char(':'));
227 for (int i = 0; i < args.count(); ++i) {
228 if (args.at(i).startsWith(QLatin1String("/dev/"))) {
234 QScopedPointer<QDeviceDiscovery> deviceDiscovery(
235 QDeviceDiscovery::create(QDeviceDiscovery::Device_Tablet, this));
236 if (deviceDiscovery) {
237 QStringList devices = deviceDiscovery->scanConnectedDevices();
238 if (!devices.isEmpty())
242 if (!dev.isEmpty()) {
243 qDebug("evdevtablet: using %s", qPrintable(dev));
244 d->fd = QT_OPEN(dev.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);
247 if (d->queryLimits()) {
248 d->notifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, this);
249 connect(d->notifier, SIGNAL(activated(int)), this, SLOT(readData()));
252 qErrnoWarning(errno, "evdevtablet: Cannot open input device");
257 QEvdevTabletHandler::~QEvdevTabletHandler()
266 void QEvdevTabletHandler::readData()
268 static input_event buffer[32];
271 int result = QT_READ(d->fd, reinterpret_cast<char*>(buffer) + n, sizeof(buffer) - n);
273 qWarning("evdevtablet: Got EOF from input device");
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
288 if (n % sizeof(input_event) == 0)
293 n /= sizeof(input_event);
295 for (int i = 0; i < n; ++i)
296 d->processInputEvent(&buffer[i]);
300 QEvdevTabletHandlerThread::QEvdevTabletHandlerThread(const QString &spec, QObject *parent)
301 : QThread(parent), m_spec(spec), m_handler(0)
306 QEvdevTabletHandlerThread::~QEvdevTabletHandlerThread()
312 void QEvdevTabletHandlerThread::run()
314 m_handler = new QEvdevTabletHandler(m_spec);