From 352c357e6f0785c0775a85151d6716b47aea1006 Mon Sep 17 00:00:00 2001 From: Romain Pokrzywka Date: Wed, 2 Sep 2015 15:15:58 -0700 Subject: 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 --- src/gui/kernel/qinputdevicemanager_p.h | 3 +- .../input/evdevtablet/evdevtablet.pri | 6 +- .../input/evdevtablet/qevdevtablet.cpp | 315 --------------------- .../input/evdevtablet/qevdevtablet_p.h | 87 ------ .../input/evdevtablet/qevdevtablethandler.cpp | 292 +++++++++++++++++++ .../input/evdevtablet/qevdevtablethandler_p.h | 96 +++++++ .../input/evdevtablet/qevdevtabletmanager.cpp | 122 ++++++++ .../input/evdevtablet/qevdevtabletmanager_p.h | 76 +++++ src/plugins/generic/evdevtablet/main.cpp | 12 +- 9 files changed, 595 insertions(+), 414 deletions(-) delete mode 100644 src/platformsupport/input/evdevtablet/qevdevtablet.cpp delete mode 100644 src/platformsupport/input/evdevtablet/qevdevtablet_p.h create mode 100644 src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp create mode 100644 src/platformsupport/input/evdevtablet/qevdevtablethandler_p.h create mode 100644 src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp create mode 100644 src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h diff --git a/src/gui/kernel/qinputdevicemanager_p.h b/src/gui/kernel/qinputdevicemanager_p.h index d64793c23c..4c24b1bc93 100644 --- a/src/gui/kernel/qinputdevicemanager_p.h +++ b/src/gui/kernel/qinputdevicemanager_p.h @@ -61,7 +61,8 @@ public: DeviceTypeUnknown, DeviceTypePointer, DeviceTypeKeyboard, - DeviceTypeTouch + DeviceTypeTouch, + DeviceTypeTablet }; QInputDeviceManager(QObject *parent = 0); diff --git a/src/platformsupport/input/evdevtablet/evdevtablet.pri b/src/platformsupport/input/evdevtablet/evdevtablet.pri index 5ace0df61d..fb9489353c 100644 --- a/src/platformsupport/input/evdevtablet/evdevtablet.pri +++ b/src/platformsupport/input/evdevtablet/evdevtablet.pri @@ -1,8 +1,10 @@ HEADERS += \ - $$PWD/qevdevtablet_p.h + $$PWD/qevdevtablethandler_p.h \ + $$PWD/qevdevtabletmanager_p.h SOURCES += \ - $$PWD/qevdevtablet.cpp + $$PWD/qevdevtablethandler.cpp \ + $$PWD/qevdevtabletmanager.cpp contains(QT_CONFIG, libudev) { LIBS_PRIVATE += $$QMAKE_LIBS_LIBUDEV diff --git a/src/platformsupport/input/evdevtablet/qevdevtablet.cpp b/src/platformsupport/input/evdevtablet/qevdevtablet.cpp deleted file mode 100644 index c6f952c64d..0000000000 --- a/src/platformsupport/input/evdevtablet/qevdevtablet.cpp +++ /dev/null @@ -1,315 +0,0 @@ -/**************************************************************************** -** -** 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 "qevdevtablet_p.h" -#include -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(qLcEvdevTablet, "qt.qpa.input") - -class QEvdevTabletData -{ -public: - QEvdevTabletData(QEvdevTabletHandler *q_ptr); - bool queryLimits(); - void testGrab(); - void processInputEvent(input_event *ev); - void reportProximityEnter(); - void reportProximityLeave(); - void report(); - - QEvdevTabletHandler *q; - QSocketNotifier *notifier; - int fd; - 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), notifier(0), fd(-1), lastEventType(0) -{ - memset(&minValues, 0, sizeof(minValues)); - memset(&maxValues, 0, sizeof(maxValues)); - memset(&state, 0, sizeof(state)); -} - -bool QEvdevTabletData::queryLimits() -{ - bool ok = true; - input_absinfo absInfo; - memset(&absInfo, 0, sizeof(input_absinfo)); - ok &= ioctl(fd, EVIOCGABS(ABS_X), &absInfo) >= 0; - if (ok) { - minValues.x = absInfo.minimum; - maxValues.x = absInfo.maximum; - qCDebug(qLcEvdevTablet, "evdevtablet: min X: %d max X: %d", minValues.x, maxValues.x); - } - ok &= ioctl(fd, EVIOCGABS(ABS_Y), &absInfo) >= 0; - if (ok) { - minValues.y = absInfo.minimum; - maxValues.y = absInfo.maximum; - qCDebug(qLcEvdevTablet, "evdevtablet: min Y: %d max Y: %d", minValues.y, maxValues.y); - } - if (ioctl(fd, EVIOCGABS(ABS_PRESSURE), &absInfo) >= 0) { - minValues.p = absInfo.minimum; - maxValues.p = absInfo.maximum; - qCDebug(qLcEvdevTablet, "evdevtablet: min pressure: %d max pressure: %d", minValues.p, maxValues.p); - } - if (ioctl(fd, EVIOCGABS(ABS_DISTANCE), &absInfo) >= 0) { - minValues.d = absInfo.minimum; - maxValues.d = absInfo.maximum; - qCDebug(qLcEvdevTablet, "evdevtablet: min distance: %d max distance: %d", minValues.d, maxValues.d); - } - char name[128]; - if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), name) >= 0) { - devName = QString::fromLocal8Bit(name); - qCDebug(qLcEvdevTablet, "evdevtablet: device name: %s", name); - } - return ok; -} - -void QEvdevTabletData::testGrab() -{ - bool grabSuccess = !ioctl(fd, EVIOCGRAB, (void *) 1); - if (grabSuccess) - ioctl(fd, EVIOCGRAB, (void *) 0); - else - qWarning("evdevtablet: ERROR: The device is grabbed by another process. No events will be read."); -} - -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::reportProximityEnter() -{ - QWindowSystemInterface::handleTabletEnterProximityEvent(QTabletEvent::Stylus, state.tool, 1); -} - -void QEvdevTabletData::reportProximityLeave() -{ - QWindowSystemInterface::handleTabletLeaveProximityEvent(QTabletEvent::Stylus, state.tool, 1); -} - -void QEvdevTabletData::report() -{ - if (!state.lastReportTool && state.tool) - reportProximityEnter(); - - 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; - } - - qreal pressure = (state.p - minValues.p) / qreal(maxValues.p - minValues.p); - - if (state.down || state.lastReportDown) { - QWindowSystemInterface::handleTabletEvent(0, state.down, QPointF(), globalPos, - QTabletEvent::Stylus, pointer, - pressure, 0, 0, 0, 0, 0, 1, qGuiApp->keyboardModifiers()); - } - - if (state.lastReportTool && !state.tool) - reportProximityLeave(); - - state.lastReportDown = state.down; - state.lastReportTool = state.tool; - state.lastReportPos = globalPos; -} - - -QEvdevTabletHandler::QEvdevTabletHandler(const QString &spec, QObject *parent) - : QObject(parent), d(0) -{ - setObjectName(QLatin1String("Evdev Tablet Handler")); - d = new QEvdevTabletData(this); - QString dev; - QStringList args = spec.split(QLatin1Char(':')); - for (int i = 0; i < args.count(); ++i) { - if (args.at(i).startsWith(QLatin1String("/dev/"))) { - dev = args.at(i); - break; - } - } - if (dev.isEmpty()) { - QScopedPointer deviceDiscovery( - QDeviceDiscovery::create(QDeviceDiscovery::Device_Tablet, this)); - if (deviceDiscovery) { - QStringList devices = deviceDiscovery->scanConnectedDevices(); - if (!devices.isEmpty()) - dev = devices.at(0); - } - } - if (!dev.isEmpty()) { - qCDebug(qLcEvdevTablet, "evdevtablet: using %s", qPrintable(dev)); - d->fd = QT_OPEN(dev.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0); - if (d->fd >= 0) { - d->testGrab(); - if (d->queryLimits()) { - d->notifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, this); - connect(d->notifier, SIGNAL(activated(int)), this, SLOT(readData())); - } - } else { - qErrnoWarning(errno, "evdevtablet: Cannot open input device"); - } - } -} - -QEvdevTabletHandler::~QEvdevTabletHandler() -{ - delete d->notifier; - if (d->fd >= 0) - QT_CLOSE(d->fd); - - delete d; -} - -void QEvdevTabletHandler::readData() -{ - static input_event buffer[32]; - int n = 0; - for (; ;) { - int result = QT_READ(d->fd, reinterpret_cast(buffer) + n, sizeof(buffer) - n); - if (!result) { - qWarning("evdevtablet: Got EOF from input device"); - return; - } else if (result < 0) { - if (errno != EINTR && errno != EAGAIN) { - qErrnoWarning(errno, "evdevtablet: Could not read from input device"); - if (errno == ENODEV) { // device got disconnected -> stop reading - delete d->notifier; - d->notifier = 0; - QT_CLOSE(d->fd); - d->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 &spec, QObject *parent) - : QDaemonThread(parent), m_spec(spec), m_handler(0) -{ - start(); -} - -QEvdevTabletHandlerThread::~QEvdevTabletHandlerThread() -{ - quit(); - wait(); -} - -void QEvdevTabletHandlerThread::run() -{ - m_handler = new QEvdevTabletHandler(m_spec); - exec(); - delete m_handler; - m_handler = 0; -} - - -QT_END_NAMESPACE diff --git a/src/platformsupport/input/evdevtablet/qevdevtablet_p.h b/src/platformsupport/input/evdevtablet/qevdevtablet_p.h deleted file mode 100644 index f546f9a88a..0000000000 --- a/src/platformsupport/input/evdevtablet/qevdevtablet_p.h +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QEVDEVTABLET_P_H -#define QEVDEVTABLET_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QEvdevTabletData; - -class QEvdevTabletHandler : public QObject -{ - Q_OBJECT - -public: - explicit QEvdevTabletHandler(const QString &spec = QString(), QObject *parent = 0); - ~QEvdevTabletHandler(); - -private slots: - void readData(); - -private: - QEvdevTabletData *d; -}; - -class QEvdevTabletHandlerThread : public QDaemonThread -{ -public: - explicit QEvdevTabletHandlerThread(const QString &spec, QObject *parent = 0); - ~QEvdevTabletHandlerThread(); - void run() Q_DECL_OVERRIDE; - QEvdevTabletHandler *handler() { return m_handler; } - -private: - QString m_spec; - QEvdevTabletHandler *m_handler; -}; - -QT_END_NAMESPACE - -#endif // QEVDEVTABLET_P_H 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 +#include +#include +#include +#include +#include +#include + +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(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 diff --git a/src/platformsupport/input/evdevtablet/qevdevtablethandler_p.h b/src/platformsupport/input/evdevtablet/qevdevtablethandler_p.h new file mode 100644 index 0000000000..4a9b2bab34 --- /dev/null +++ b/src/platformsupport/input/evdevtablet/qevdevtablethandler_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QEVDEVTABLETHANDLER_P_H +#define QEVDEVTABLETHANDLER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QSocketNotifier; +class QEvdevTabletData; + +class QEvdevTabletHandler : public QObject +{ + Q_OBJECT + +public: + explicit QEvdevTabletHandler(const QString &device, const QString &spec = QString(), QObject *parent = 0); + ~QEvdevTabletHandler(); + + qint64 deviceId() const; + +private slots: + void readData(); + +private: + bool queryLimits(); + + int m_fd; + QString m_device; + QSocketNotifier *m_notifier; + QEvdevTabletData *d; +}; + +class QEvdevTabletHandlerThread : public QDaemonThread +{ +public: + explicit QEvdevTabletHandlerThread(const QString &device, const QString &spec, QObject *parent = 0); + ~QEvdevTabletHandlerThread(); + void run() Q_DECL_OVERRIDE; + QEvdevTabletHandler *handler() { return m_handler; } + +private: + QString m_device; + QString m_spec; + QEvdevTabletHandler *m_handler; +}; + +QT_END_NAMESPACE + +#endif // QEVDEVTABLETHANDLER_P_H diff --git a/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp new file mode 100644 index 0000000000..05fd6e655c --- /dev/null +++ b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** 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 "qevdevtabletmanager_p.h" +#include "qevdevtablethandler_p.h" + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(qLcEvdevTablet) + +QEvdevTabletManager::QEvdevTabletManager(const QString &key, const QString &specification, QObject *parent) + : QObject(parent) +{ + Q_UNUSED(key); + + if (qEnvironmentVariableIsSet("QT_QPA_EVDEV_DEBUG")) + const_cast(qLcEvdevTablet()).setEnabled(QtDebugMsg, true); + + QString spec = QString::fromLocal8Bit(qgetenv("QT_QPA_EVDEV_TABLET_PARAMETERS")); + + if (spec.isEmpty()) + spec = specification; + + QStringList args = spec.split(QLatin1Char(':')); + QStringList devices; + + foreach (const QString &arg, args) { + if (arg.startsWith(QLatin1String("/dev/"))) { + devices.append(arg); + args.removeAll(arg); + } + } + + // build new specification without /dev/ elements + m_spec = args.join(QLatin1Char(':')); + + foreach (const QString &device, devices) + addDevice(device); + + // when no devices specified, use device discovery to scan and monitor + if (devices.isEmpty()) { + qCDebug(qLcEvdevTablet) << "evdevtablet: Using device discovery"; + m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Tablet, this); + if (m_deviceDiscovery) { + QStringList devices = m_deviceDiscovery->scanConnectedDevices(); + foreach (const QString &device, devices) + addDevice(device); + connect(m_deviceDiscovery, SIGNAL(deviceDetected(QString)), this, SLOT(addDevice(QString))); + connect(m_deviceDiscovery, SIGNAL(deviceRemoved(QString)), this, SLOT(removeDevice(QString))); + } + } +} + +QEvdevTabletManager::~QEvdevTabletManager() +{ + qDeleteAll(m_activeDevices); +} + +void QEvdevTabletManager::addDevice(const QString &deviceNode) +{ + qCDebug(qLcEvdevTablet) << "Adding device at" << deviceNode; + QEvdevTabletHandlerThread *handler; + handler = new QEvdevTabletHandlerThread(deviceNode, m_spec); + if (handler) { + m_activeDevices.insert(deviceNode, handler); + QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( + QInputDeviceManager::DeviceTypeTablet, m_activeDevices.count()); + } else { + qWarning("evdevtablet: Failed to open tablet device %s", qPrintable(deviceNode)); + } +} + +void QEvdevTabletManager::removeDevice(const QString &deviceNode) +{ + if (m_activeDevices.contains(deviceNode)) { + qCDebug(qLcEvdevTablet) << "Removing device at" << deviceNode; + QEvdevTabletHandlerThread *handler = m_activeDevices.value(deviceNode); + m_activeDevices.remove(deviceNode); + QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( + QInputDeviceManager::DeviceTypeTablet, m_activeDevices.count()); + delete handler; + } +} + +QT_END_NAMESPACE diff --git a/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h b/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h new file mode 100644 index 0000000000..893ff03fa7 --- /dev/null +++ b/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtGui 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$ +** +****************************************************************************/ + +#ifndef QEVDEVTABLETMANAGER_P_H +#define QEVDEVTABLETMANAGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeviceDiscovery; +class QEvdevTabletHandlerThread; + +class QEvdevTabletManager : public QObject +{ + Q_OBJECT +public: + QEvdevTabletManager(const QString &key, const QString &spec, QObject *parent = 0); + ~QEvdevTabletManager(); + +private slots: + void addDevice(const QString &deviceNode); + void removeDevice(const QString &deviceNode); + +private: + QString m_spec; + QDeviceDiscovery *m_deviceDiscovery; + QHash m_activeDevices; +}; + +QT_END_NAMESPACE + +#endif // QEVDEVTABLETMANAGER_P_H diff --git a/src/plugins/generic/evdevtablet/main.cpp b/src/plugins/generic/evdevtablet/main.cpp index 62524e8f33..3a9fd4f1dd 100644 --- a/src/plugins/generic/evdevtablet/main.cpp +++ b/src/plugins/generic/evdevtablet/main.cpp @@ -32,7 +32,7 @@ ****************************************************************************/ #include -#include +#include QT_BEGIN_NAMESPACE @@ -44,7 +44,6 @@ class QEvdevTabletPlugin : public QGenericPlugin public: QEvdevTabletPlugin(); - QStringList keys() const; QObject* create(const QString &key, const QString &specification) Q_DECL_OVERRIDE; }; @@ -52,16 +51,11 @@ QEvdevTabletPlugin::QEvdevTabletPlugin() { } -QStringList QEvdevTabletPlugin::keys() const -{ - return QStringList() << "EvdevTablet"; -} - -QObject *QEvdevTabletPlugin::create(const QString &key, +QObject* QEvdevTabletPlugin::create(const QString &key, const QString &spec) { if (!key.compare(QLatin1String("EvdevTablet"), Qt::CaseInsensitive)) - return new QEvdevTabletHandlerThread(spec); + return new QEvdevTabletManager(key, spec); return 0; } -- cgit v1.2.3