From d40f4105cde2c7ad3602dbfe80b8597bd1193349 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 21 Oct 2011 10:36:56 +0300 Subject: Remove mtdev dependency from the touchscreen QPA plugin. There is no reason to enforce the usage of the mtdev library. As long as ABS_MT_TRACKING_ID is provided protocol type A is perfectly enough. This makes the plugin more suitable for embedded systems. Change-Id: I73ce4a1056a6dc27daacb69dc4761bca393a7e43 Reviewed-by: Paul Olav Tvete --- .../generic/touchscreen/70-qtouchscreen.rules | 2 +- src/plugins/generic/touchscreen/README | 26 +-- .../generic/touchscreen/qtoucheventsenderqpa.cpp | 7 - src/plugins/generic/touchscreen/qtouchscreen.cpp | 244 +++++++++++---------- src/plugins/generic/touchscreen/qtouchscreen.h | 6 +- src/plugins/generic/touchscreen/touchscreen.pro | 2 +- 6 files changed, 145 insertions(+), 142 deletions(-) (limited to 'src/plugins') diff --git a/src/plugins/generic/touchscreen/70-qtouchscreen.rules b/src/plugins/generic/touchscreen/70-qtouchscreen.rules index 8119a14382..2afde8f8a3 100644 --- a/src/plugins/generic/touchscreen/70-qtouchscreen.rules +++ b/src/plugins/generic/touchscreen/70-qtouchscreen.rules @@ -1 +1 @@ -KERNEL=="event*", ENV{ID_INPUT_TOUCHPAD}=="1", ENV{QT_TOUCH}="1", MODE="0644" +KERNEL=="event*", ENV{ID_INPUT_TOUCHPAD}=="1", MODE="0644" diff --git a/src/plugins/generic/touchscreen/README b/src/plugins/generic/touchscreen/README index 25e0e1abe8..bed9016329 100644 --- a/src/plugins/generic/touchscreen/README +++ b/src/plugins/generic/touchscreen/README @@ -2,12 +2,12 @@ Generic plug-in for evdev touch events (a) Using as a QPA generic plug-in -1. set up the touch device -2. sudo apt-get install libmtdev-dev libudev-dev +1. set up and connect the touch device +2. install libudev-dev or similar 3. build this plug-in (qmake && make) 4. sudo cp 70-qtouchscreen.rules /etc/udev/rules.d 5. sudo udevadm trigger --subsystem-match=input -6. run apps like this: app -platform xcb -plugin LinuxTouchScreen +6. ./fingerpaint -plugin LinuxTouchScreen:force_window If automatic detection does not work, use -plugin LinuxTouchScreen:/dev/input/eventN to explicitly set the device file @@ -15,15 +15,14 @@ name. By default the surface of the touch device is mapped to the entire screen. If this is not desired, pass force_window in the plugin -specification. This will cause mapping the touch surface to the active -window instead. +specification as shown in the example above. This will cause mapping +the touch surface to the active window instead. -Only touch events are generated (via -QWindowSystemInterface::handleTouchEvent), mouse events are not. This -is because on desktop the touch device will usually act as a -single-touch mouse replacement anyway. For pointer-less systems the -code needs to be extended to generate also mouse events (by calling -handleMouseEvent too). +Only touch events are generated, mouse events are not. This is because +on desktop the touch device will usually act as a single-point mouse +replacement by default. For embedded systems the code could to be +extended to generate also mouse events (by calling handleMouseEvent +for the primary touch point for example). (b) Using in a compositor @@ -40,6 +39,5 @@ Known issues: The udev rule matches any touchpad device. If there are multiple ones, specify the device as described above. -On recent distributions (e.g. Ubuntu 11.04) you may need to remove -50-synaptics.conf from /usr/share/X11/xorg.conf.d (followed by -logout/login) otherwise no evdev events can be read. +If no evdev events are read, remove 50-synaptics.conf from +/usr/share/X11/xorg.conf.d and restart X. diff --git a/src/plugins/generic/touchscreen/qtoucheventsenderqpa.cpp b/src/plugins/generic/touchscreen/qtoucheventsenderqpa.cpp index 284330fd32..d9ec92cda7 100644 --- a/src/plugins/generic/touchscreen/qtoucheventsenderqpa.cpp +++ b/src/plugins/generic/touchscreen/qtoucheventsenderqpa.cpp @@ -86,15 +86,8 @@ void QTouchEventSenderQPA::touch_point(QEvent::Type state, for (int i = 0; i < touchPoints.size(); ++i) { QWindowSystemInterface::TouchPoint &tp(touchPoints[i]); - // Translate so that (0, 0) is the top-left corner. - const int hw_x = qBound(hw_range_x_min, int(tp.area.left()), hw_range_x_max) - hw_range_x_min; - const int hw_y = qBound(hw_range_y_min, int(tp.area.top()), hw_range_y_max) - hw_range_y_min; - - // Get a normalized position in range 0..1. const int hw_w = hw_range_x_max - hw_range_x_min; const int hw_h = hw_range_y_max - hw_range_y_min; - tp.normalPosition = QPointF(hw_x / qreal(hw_w), - hw_y / qreal(hw_h)); qreal nx = tp.normalPosition.x(); qreal ny = tp.normalPosition.y(); diff --git a/src/plugins/generic/touchscreen/qtouchscreen.cpp b/src/plugins/generic/touchscreen/qtouchscreen.cpp index 8ab320ae9f..fd2de62d6b 100644 --- a/src/plugins/generic/touchscreen/qtouchscreen.cpp +++ b/src/plugins/generic/touchscreen/qtouchscreen.cpp @@ -42,20 +42,15 @@ #include "qtouchscreen.h" #include #include -#include -#include #include - +#include +#include #include -extern "C" { -#include -} +QT_BEGIN_NAMESPACE //#define POINT_DEBUG -QT_BEGIN_NAMESPACE - class QTouchScreenData { public: @@ -63,25 +58,25 @@ public: void processInputEvent(input_event *data); + void dump(); + QTouchScreenHandler *q; QEvent::Type m_state; QEvent::Type m_prevState; + int m_lastEventType; QList m_touchPoints; - struct Slot { + struct Contact { int trackingId; int x; int y; int maj; Qt::TouchPointState state; bool primary; - Slot() : trackingId(0), x(0), y(0), maj(1), state(Qt::TouchPointPressed), primary(false) { } + Contact() : trackingId(0), x(0), y(0), maj(1), state(Qt::TouchPointPressed), primary(false) { } }; - QMap m_slots; - QMap m_lastReport; - int m_currentSlot; - QTimer m_clearTimer; - bool m_clearTimerEnabled; + QMap m_contacts, m_lastContacts; + Contact m_currentData; int hw_range_x_min; int hw_range_x_max; @@ -96,23 +91,15 @@ QTouchScreenData::QTouchScreenData(QTouchScreenHandler *q_ptr, const QStringList : q(q_ptr), m_state(QEvent::TouchBegin), m_prevState(m_state), - m_currentSlot(0), + m_lastEventType(-1), hw_range_x_min(0), hw_range_x_max(0), hw_range_y_min(0), hw_range_y_max(0) { - m_clearTimerEnabled = !args.contains(QLatin1String("no_timeout")); - if (m_clearTimerEnabled) { - QObject::connect(&m_clearTimer, SIGNAL(timeout()), q, SLOT(onTimeout())); - m_clearTimer.setSingleShot(true); - m_clearTimer.setInterval(2000); // default timeout is 2 seconds - for (int i = 0; i < args.count(); ++i) - if (args.at(i).startsWith(QLatin1String("timeout="))) - m_clearTimer.setInterval(args.at(i).mid(8).toInt()); - } + Q_UNUSED(args); } QTouchScreenHandler::QTouchScreenHandler(const QString &spec) - : m_notify(0), m_fd(-1), m_mtdev(0), d(0) + : m_notify(0), m_fd(-1), d(0) { setObjectName(QLatin1String("LinuxInputSubsystem Touch Handler")); @@ -135,14 +122,6 @@ QTouchScreenHandler::QTouchScreenHandler(const QString &spec) return; } - m_mtdev = (mtdev *) calloc(1, sizeof(mtdev)); - int mtdeverr = mtdev_open(m_mtdev, m_fd); - if (mtdeverr) { - qWarning("mtdev_open failed: %d", mtdeverr); - QT_CLOSE(m_fd); - return; - } - d = new QTouchScreenData(this, args); input_absinfo absInfo; @@ -159,8 +138,8 @@ QTouchScreenHandler::QTouchScreenHandler(const QString &spec) } char name[1024]; if (ioctl(m_fd, EVIOCGNAME(sizeof(name) - 1), name) >= 0) { - d->hw_name = QString::fromUtf8(name); - qDebug() << "device name" << d->hw_name; + d->hw_name = QString::fromLocal8Bit(name); + qDebug("device name: %s", name); } } @@ -169,11 +148,6 @@ QTouchScreenHandler::~QTouchScreenHandler() if (m_fd >= 0) QT_CLOSE(m_fd); - if (m_mtdev) { - mtdev_close(m_mtdev); - free(m_mtdev); - } - delete d; } @@ -191,7 +165,7 @@ void QTouchScreenHandler::try_udev(QString *path) udev *u = udev_new(); udev_enumerate *ue = udev_enumerate_new(u); udev_enumerate_add_match_subsystem(ue, "input"); - udev_enumerate_add_match_property(ue, "QT_TOUCH", "1"); + udev_enumerate_add_match_property(ue, "ID_INPUT_TOUCHPAD", "1"); udev_enumerate_scan_devices(ue); udev_list_entry *entry; udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(ue)) { @@ -207,88 +181,100 @@ void QTouchScreenHandler::try_udev(QString *path) void QTouchScreenHandler::readData() { - input_event buffer[32]; + ::input_event buffer[32]; int n = 0; - n = mtdev_get(m_mtdev, m_fd, buffer, sizeof(buffer) / sizeof(input_event)); - if (n < 0) { - if (errno != EINTR && errno != EAGAIN) + for (; ;) { + n = QT_READ(m_fd, reinterpret_cast(buffer) + n, sizeof(buffer) - n); + + if (!n) { + qWarning("Got EOF from input device"); + return; + } else if (n < 0 && (errno != EINTR && errno != EAGAIN)) { qWarning("Could not read from input device: %s", strerror(errno)); - } else if (n > 0) { - for (int i = 0; i < n; ++i) { - input_event *data = &buffer[i]; - d->processInputEvent(data); + if (errno == ENODEV) { // device got disconnected -> stop reading + delete m_notify; + m_notify = 0; + QT_CLOSE(m_fd); + m_fd = -1; + } + return; + } else if (n % sizeof(::input_event) == 0) { + break; } } -} -void QTouchScreenHandler::onTimeout() -{ -#ifdef POINT_DEBUG - qDebug("TIMEOUT (%d slots)", d->m_slots.count()); -#endif - d->m_slots.clear(); - if (d->m_state != QEvent::TouchEnd) - for (int i = 0; i < d->m_observers.count(); ++i) - d->m_observers.at(i)->touch_point(QEvent::TouchEnd, - QList()); - d->m_state = QEvent::TouchBegin; + n /= sizeof(::input_event); + + for (int i = 0; i < n; ++i) + d->processInputEvent(&buffer[i]); } void QTouchScreenData::processInputEvent(input_event *data) { if (data->type == EV_ABS) { + if (data->code == ABS_MT_POSITION_X) { - m_slots[m_currentSlot].x = data->value; + m_currentData.x = data->value; } else if (data->code == ABS_MT_POSITION_Y) { - m_slots[m_currentSlot].y = data->value; - } else if (data->code == ABS_MT_SLOT) { - m_currentSlot = data->value; + m_currentData.y = data->value; } else if (data->code == ABS_MT_TRACKING_ID) { - if (data->value == -1) { - bool wasPrimary = m_slots[m_currentSlot].primary; - m_lastReport.remove(m_slots[m_currentSlot].trackingId); - m_slots.remove(m_currentSlot); - if (wasPrimary && !m_slots.isEmpty()) - m_slots[m_slots.keys().at(0)].primary = true; - } else { - m_slots[m_currentSlot].trackingId = data->value; - m_slots[m_currentSlot].primary = m_slots.count() == 1; - } + m_currentData.trackingId = data->value; + m_currentData.primary = m_contacts.isEmpty(); } else if (data->code == ABS_MT_TOUCH_MAJOR) { - m_slots[m_currentSlot].maj = data->value; + m_currentData.maj = data->value; if (data->value == 0) - m_slots[m_currentSlot].state = Qt::TouchPointReleased; + m_currentData.state = Qt::TouchPointReleased; } + + } else if (data->type == EV_SYN && data->code == SYN_MT_REPORT && m_lastEventType != EV_SYN) { + + m_contacts.insert(m_currentData.trackingId, m_currentData); + m_currentData = Contact(); + } else if (data->type == EV_SYN && data->code == SYN_REPORT) { - if (m_clearTimerEnabled) - m_clearTimer.stop(); + m_touchPoints.clear(); - QList keys = m_slots.keys(); - int ignoredSlotCount = 0; - for (int i = 0; i < keys.count(); ++i) { - const Slot &slot(m_slots.value(keys.at(i))); - if (slot.trackingId == 0) { - ++ignoredSlotCount; - continue; - } + for (QMap::iterator it = m_contacts.begin(), ite = m_contacts.end(); + it != ite; ++it) { QWindowSystemInterface::TouchPoint tp; - tp.id = slot.trackingId; - tp.isPrimary = slot.primary; - tp.pressure = slot.state == Qt::TouchPointReleased ? 0 : 1; - tp.area = QRectF(slot.x, slot.y, slot.maj, slot.maj); - tp.state = slot.state; - if (slot.state == Qt::TouchPointMoved && m_lastReport.contains(slot.trackingId)) { - QPoint lastPos = m_lastReport.value(slot.trackingId); - if (lastPos.x() == slot.x && lastPos.y() == slot.y) - tp.state = Qt::TouchPointStationary; + tp.id = it->trackingId; + tp.isPrimary = it->primary; + tp.pressure = it->state == Qt::TouchPointReleased ? 0 : 1; + + if (m_lastContacts.contains(it->trackingId)) { + const Contact &prev(m_lastContacts.value(it->trackingId)); + if (it->state == Qt::TouchPointReleased) { + // Copy over the previous values for released points, just in case. + it->x = prev.x; + it->y = prev.y; + it->maj = prev.maj; + } else { + it->state = (prev.x == it->x && prev.y == it->y) ? Qt::TouchPointStationary : Qt::TouchPointMoved; + } } + + tp.state = it->state; + tp.area = QRectF(it->x, it->y, it->maj, it->maj); + + // Translate so that (0, 0) is the top-left corner. + const int hw_x = qBound(hw_range_x_min, int(tp.area.left()), hw_range_x_max) - hw_range_x_min; + const int hw_y = qBound(hw_range_y_min, int(tp.area.top()), hw_range_y_max) - hw_range_y_min; + // Get a normalized position in range 0..1. + const int hw_w = hw_range_x_max - hw_range_x_min; + const int hw_h = hw_range_y_max - hw_range_y_min; + tp.normalPosition = QPointF(hw_x / qreal(hw_w), + hw_y / qreal(hw_h)); + m_touchPoints.append(tp); - m_lastReport.insert(slot.trackingId, QPoint(slot.x, slot.y)); } - if (m_slots.count() - ignoredSlotCount == 0) + + if (m_contacts.isEmpty()) m_state = QEvent::TouchEnd; - // Skip if state is TouchUpdate and all points are Stationary. + m_lastContacts = m_contacts; + m_contacts.clear(); + + // No need to deliver if all points are stationary. bool skip = false; if (m_state == QEvent::TouchUpdate) { skip = true; @@ -300,32 +286,62 @@ void QTouchScreenData::processInputEvent(input_event *data) } #ifdef POINT_DEBUG - qDebug() << m_touchPoints.count() << "touchpoints, event type" << m_state; - for (int i = 0; i < m_touchPoints.count(); ++i) - qDebug() << " " << m_touchPoints[i].id << m_touchPoints[i].state << m_touchPoints[i].area; + dump(); #endif if (!skip && !(m_state == m_prevState && m_state == QEvent::TouchEnd)) for (int i = 0; i < m_observers.count(); ++i) m_observers.at(i)->touch_point(m_state, m_touchPoints); - for (int i = 0; i < keys.count(); ++i) { - Slot &slot(m_slots[keys.at(i)]); - if (slot.state == Qt::TouchPointPressed) - slot.state = Qt::TouchPointMoved; - } m_prevState = m_state; if (m_state == QEvent::TouchBegin) m_state = QEvent::TouchUpdate; else if (m_state == QEvent::TouchEnd) m_state = QEvent::TouchBegin; + } - // The user's finger may fall off the touchscreen which in some rare - // cases may mean there will be no released event ever received for that - // particular point. Use a timer to clear all points when no activity - // occurs for a certain period of time. - if (m_clearTimerEnabled && m_state != QEvent::TouchBegin) - m_clearTimer.start(); + m_lastEventType = data->type; +} + +void QTouchScreenData::dump() +{ + const char *eventType; + switch (m_state) { + case QEvent::TouchBegin: + eventType = "TouchBegin"; + break; + case QEvent::TouchUpdate: + eventType = "TouchUpdate"; + break; + case QEvent::TouchEnd: + eventType = "TouchEnd"; + break; + default: + eventType = "unknown"; + break; + } + qDebug() << "touch event" << eventType; + foreach (const QWindowSystemInterface::TouchPoint &tp, m_touchPoints) { + const char *pointState; + switch (tp.state & Qt::TouchPointStateMask) { + case Qt::TouchPointPressed: + pointState = "pressed"; + break; + case Qt::TouchPointMoved: + pointState = "moved"; + break; + case Qt::TouchPointStationary: + pointState = "stationary"; + break; + case Qt::TouchPointReleased: + pointState = "released"; + break; + default: + pointState = "unknown"; + break; + } + qDebug() << " " << tp.id << tp.area << pointState << tp.normalPosition + << tp.pressure << tp.isPrimary << tp.area.center(); } } diff --git a/src/plugins/generic/touchscreen/qtouchscreen.h b/src/plugins/generic/touchscreen/qtouchscreen.h index 80a6d3ac58..3c35b0012f 100644 --- a/src/plugins/generic/touchscreen/qtouchscreen.h +++ b/src/plugins/generic/touchscreen/qtouchscreen.h @@ -46,12 +46,10 @@ #include #include #include -#include +#include QT_BEGIN_HEADER -struct mtdev; - QT_BEGIN_NAMESPACE class QSocketNotifier; @@ -75,14 +73,12 @@ public: private slots: void readData(); - void onTimeout(); private: void try_udev(QString *path); QSocketNotifier *m_notify; int m_fd; - mtdev *m_mtdev; QTouchScreenData *d; }; diff --git a/src/plugins/generic/touchscreen/touchscreen.pro b/src/plugins/generic/touchscreen/touchscreen.pro index 26725190b0..60aa29c5ec 100644 --- a/src/plugins/generic/touchscreen/touchscreen.pro +++ b/src/plugins/generic/touchscreen/touchscreen.pro @@ -15,4 +15,4 @@ SOURCES = main.cpp \ QT += core-private gui-private -LIBS += -ludev -lmtdev +LIBS += -ludev -- cgit v1.2.3