summaryrefslogtreecommitdiffstats
path: root/src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp
diff options
context:
space:
mode:
authorRomain Pokrzywka <romain.pokrzywka@bluescape.com>2015-09-02 15:15:58 -0700
committerRomain Pokrzywka <romain.pokrzywka@gmail.com>2015-09-03 17:01:39 +0000
commit352c357e6f0785c0775a85151d6716b47aea1006 (patch)
tree7832108242289458c809521720d9d258699e5197 /src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp
parent916909f8a9683b2d6006602e419231d5f3b66b88 (diff)
Improve evdevtablet plugin with support for multiple tablet devices
Migrated the evdevtablet code to use the same Manager/Handler model used by the other evdev plugins. The input event parsing code remains the same, it has just been merged into the new handler class, and hardcoded device ids have been replaced by proper per-device ids to support multiple tablet devices as detected by udev. Also added tablet count reporting to QInputDeviceManager. Change-Id: Ibd72db568646da4f32e7680dd51698a0a86dca99 Reviewed-by: Laszlo Agocs <laszlo.agocs@theqtcompany.com>
Diffstat (limited to 'src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp')
-rw-r--r--src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp292
1 files changed, 292 insertions, 0 deletions
diff --git a/src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp b/src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp
new file mode 100644
index 0000000000..aa87340112
--- /dev/null
+++ b/src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp
@@ -0,0 +1,292 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the plugins module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qevdevtablethandler_p.h"
+
+#include <QStringList>
+#include <QSocketNotifier>
+#include <QGuiApplication>
+#include <QLoggingCategory>
+#include <QtCore/private/qcore_unix_p.h>
+#include <qpa/qwindowsysteminterface.h>
+#include <linux/input.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qLcEvdevTablet, "qt.qpa.input")
+
+class QEvdevTabletData
+{
+public:
+ QEvdevTabletData(QEvdevTabletHandler *q_ptr);
+
+ void processInputEvent(input_event *ev);
+ void report();
+
+ QEvdevTabletHandler *q;
+ int lastEventType;
+ QString devName;
+ struct {
+ int x, y, p, d;
+ } minValues, maxValues;
+ struct {
+ int x, y, p, d;
+ bool down, lastReportDown;
+ int tool, lastReportTool;
+ QPointF lastReportPos;
+ } state;
+};
+
+QEvdevTabletData::QEvdevTabletData(QEvdevTabletHandler *q_ptr)
+ : q(q_ptr), lastEventType(0)
+{
+ memset(&minValues, 0, sizeof(minValues));
+ memset(&maxValues, 0, sizeof(maxValues));
+ memset(&state, 0, sizeof(state));
+}
+
+void QEvdevTabletData::processInputEvent(input_event *ev)
+{
+ if (ev->type == EV_ABS) {
+ switch (ev->code) {
+ case ABS_X:
+ state.x = ev->value;
+ break;
+ case ABS_Y:
+ state.y = ev->value;
+ break;
+ case ABS_PRESSURE:
+ state.p = ev->value;
+ break;
+ case ABS_DISTANCE:
+ state.d = ev->value;
+ break;
+ default:
+ break;
+ }
+ } else if (ev->type == EV_KEY) {
+ // code BTN_TOOL_* value 1 -> proximity enter
+ // code BTN_TOOL_* value 0 -> proximity leave
+ // code BTN_TOUCH value 1 -> contact with screen
+ // code BTN_TOUCH value 0 -> no contact
+ switch (ev->code) {
+ case BTN_TOUCH:
+ state.down = ev->value != 0;
+ break;
+ case BTN_TOOL_PEN:
+ state.tool = ev->value ? QTabletEvent::Pen : 0;
+ break;
+ case BTN_TOOL_RUBBER:
+ state.tool = ev->value ? QTabletEvent::Eraser : 0;
+ break;
+ default:
+ break;
+ }
+ } else if (ev->type == EV_SYN && ev->code == SYN_REPORT && lastEventType != ev->type) {
+ report();
+ }
+ lastEventType = ev->type;
+}
+
+void QEvdevTabletData::report()
+{
+ if (!state.lastReportTool && state.tool)
+ QWindowSystemInterface::handleTabletEnterProximityEvent(QTabletEvent::Stylus, state.tool, q->deviceId());
+
+ qreal nx = (state.x - minValues.x) / qreal(maxValues.x - minValues.x);
+ qreal ny = (state.y - minValues.y) / qreal(maxValues.y - minValues.y);
+
+ QRect winRect = QGuiApplication::primaryScreen()->geometry();
+ QPointF globalPos(nx * winRect.width(), ny * winRect.height());
+ int pointer = state.tool;
+ // Prevent sending confusing values of 0 when moving the pen outside the active area.
+ if (!state.down && state.lastReportDown) {
+ globalPos = state.lastReportPos;
+ pointer = state.lastReportTool;
+ }
+
+ int pressureRange = maxValues.p - minValues.p;
+ qreal pressure = pressureRange ? (state.p - minValues.p) / qreal(pressureRange) : qreal(1);
+
+ if (state.down || state.lastReportDown) {
+ QWindowSystemInterface::handleTabletEvent(0, state.down, QPointF(), globalPos,
+ QTabletEvent::Stylus, pointer,
+ pressure, 0, 0, 0, 0, 0, q->deviceId(), qGuiApp->keyboardModifiers());
+ }
+
+ if (state.lastReportTool && !state.tool)
+ QWindowSystemInterface::handleTabletLeaveProximityEvent(QTabletEvent::Stylus, state.tool, q->deviceId());
+
+ state.lastReportDown = state.down;
+ state.lastReportTool = state.tool;
+ state.lastReportPos = globalPos;
+}
+
+
+QEvdevTabletHandler::QEvdevTabletHandler(const QString &device, const QString &spec, QObject *parent)
+ : QObject(parent), m_fd(-1), m_device(device), m_notifier(0), d(0)
+{
+ Q_UNUSED(spec)
+
+ setObjectName(QLatin1String("Evdev Tablet Handler"));
+
+ qCDebug(qLcEvdevTablet, "evdevtablet: using %s", qPrintable(device));
+
+ m_fd = QT_OPEN(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);
+ if (m_fd < 0) {
+ qErrnoWarning(errno, "evdevtablet: Cannot open input device %s", qPrintable(device));
+ return;
+ }
+
+ bool grabSuccess = !ioctl(m_fd, EVIOCGRAB, (void *) 1);
+ if (grabSuccess)
+ ioctl(m_fd, EVIOCGRAB, (void *) 0);
+ else
+ qWarning("evdevtablet: %s: The device is grabbed by another process. No events will be read.", qPrintable(device));
+
+ d = new QEvdevTabletData(this);
+ if (!queryLimits())
+ qWarning("evdevtablet: %s: Unset or invalid ABS limits. Behavior will be unspecified.", qPrintable(device));
+
+ m_notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
+ connect(m_notifier, &QSocketNotifier::activated, this, &QEvdevTabletHandler::readData);
+}
+
+QEvdevTabletHandler::~QEvdevTabletHandler()
+{
+ if (m_fd >= 0)
+ QT_CLOSE(m_fd);
+
+ delete d;
+}
+
+qint64 QEvdevTabletHandler::deviceId() const
+{
+ return m_fd;
+}
+
+bool QEvdevTabletHandler::queryLimits()
+{
+ bool ok = true;
+ input_absinfo absInfo;
+ memset(&absInfo, 0, sizeof(input_absinfo));
+ ok &= ioctl(m_fd, EVIOCGABS(ABS_X), &absInfo) >= 0;
+ if (ok) {
+ d->minValues.x = absInfo.minimum;
+ d->maxValues.x = absInfo.maximum;
+ qCDebug(qLcEvdevTablet, "evdevtablet: %s: min X: %d max X: %d", qPrintable(m_device),
+ d->minValues.x, d->maxValues.x);
+ }
+ ok &= ioctl(m_fd, EVIOCGABS(ABS_Y), &absInfo) >= 0;
+ if (ok) {
+ d->minValues.y = absInfo.minimum;
+ d->maxValues.y = absInfo.maximum;
+ qCDebug(qLcEvdevTablet, "evdevtablet: %s: min Y: %d max Y: %d", qPrintable(m_device),
+ d->minValues.y, d->maxValues.y);
+ }
+ if (ioctl(m_fd, EVIOCGABS(ABS_PRESSURE), &absInfo) >= 0) {
+ d->minValues.p = absInfo.minimum;
+ d->maxValues.p = absInfo.maximum;
+ qCDebug(qLcEvdevTablet, "evdevtablet: %s: min pressure: %d max pressure: %d", qPrintable(m_device),
+ d->minValues.p, d->maxValues.p);
+ }
+ if (ioctl(m_fd, EVIOCGABS(ABS_DISTANCE), &absInfo) >= 0) {
+ d->minValues.d = absInfo.minimum;
+ d->maxValues.d = absInfo.maximum;
+ qCDebug(qLcEvdevTablet, "evdevtablet: %s: min distance: %d max distance: %d", qPrintable(m_device),
+ d->minValues.d, d->maxValues.d);
+ }
+ char name[128];
+ if (ioctl(m_fd, EVIOCGNAME(sizeof(name) - 1), name) >= 0) {
+ d->devName = QString::fromLocal8Bit(name);
+ qCDebug(qLcEvdevTablet, "evdevtablet: %s: device name: %s", qPrintable(m_device), name);
+ }
+ return ok;
+}
+
+void QEvdevTabletHandler::readData()
+{
+ static input_event buffer[32];
+ int n = 0;
+ for (; ;) {
+ int result = QT_READ(m_fd, reinterpret_cast<char*>(buffer) + n, sizeof(buffer) - n);
+ if (!result) {
+ qWarning("evdevtablet: %s: Got EOF from input device", qPrintable(m_device));
+ return;
+ } else if (result < 0) {
+ if (errno != EINTR && errno != EAGAIN) {
+ qErrnoWarning(errno, "evdevtablet: %s: Could not read from input device", qPrintable(m_device));
+ if (errno == ENODEV) { // device got disconnected -> stop reading
+ delete m_notifier;
+ m_notifier = 0;
+ QT_CLOSE(m_fd);
+ m_fd = -1;
+ }
+ return;
+ }
+ } else {
+ n += result;
+ if (n % sizeof(input_event) == 0)
+ break;
+ }
+ }
+
+ n /= sizeof(input_event);
+
+ for (int i = 0; i < n; ++i)
+ d->processInputEvent(&buffer[i]);
+}
+
+
+QEvdevTabletHandlerThread::QEvdevTabletHandlerThread(const QString &device, const QString &spec, QObject *parent)
+ : QDaemonThread(parent), m_device(device), m_spec(spec), m_handler(0)
+{
+ start();
+}
+
+QEvdevTabletHandlerThread::~QEvdevTabletHandlerThread()
+{
+ quit();
+ wait();
+}
+
+void QEvdevTabletHandlerThread::run()
+{
+ m_handler = new QEvdevTabletHandler(m_device, m_spec);
+ exec();
+ delete m_handler;
+ m_handler = 0;
+}
+
+
+QT_END_NAMESPACE