1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 The Qt Company Ltd.
4 ** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the plugins module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #include "qevdevtouchhandler_p.h"
42 #include "qtouchoutputmapping_p.h"
43 #include <QStringList>
44 #include <QHash>
45 #include <QSocketNotifier>
46 #include <QGuiApplication>
47 #include <QTouchDevice>
48 #include <QLoggingCategory>
49 #include <QtCore/private/qcore_unix_p.h>
50 #include <QtGui/private/qhighdpiscaling_p.h>
51 #include <QtGui/private/qguiapplication_p.h>
52 
53 #include <mutex>
54 
55 #include <linux/input.h>
56 
57 #ifndef input_event_sec
58 #define input_event_sec time.tv_sec
59 #endif
60 
61 #ifndef input_event_usec
62 #define input_event_usec time.tv_usec
63 #endif
64 
65 #include <math.h>
66 
67 #if QT_CONFIG(mtdev)
68 extern "C" {
69 #include <mtdev.h>
70 }
71 #endif
72 
73 QT_BEGIN_NAMESPACE
74 
75 Q_LOGGING_CATEGORY(qLcEvdevTouch, "qt.qpa.input")
76 Q_LOGGING_CATEGORY(qLcEvents, "qt.qpa.input.events")
77 
78 /* android (and perhaps some other linux-derived stuff) don't define everything
79  * in linux/input.h, so we'll need to do that ourselves.
80  */
81 #ifndef ABS_MT_TOUCH_MAJOR
82 #define ABS_MT_TOUCH_MAJOR      0x30    /* Major axis of touching ellipse */
83 #endif
84 #ifndef ABS_MT_POSITION_X
85 #define ABS_MT_POSITION_X       0x35    /* Center X ellipse position */
86 #endif
87 #ifndef ABS_MT_POSITION_Y
88 #define ABS_MT_POSITION_Y       0x36    /* Center Y ellipse position */
89 #endif
90 #ifndef ABS_MT_SLOT
91 #define ABS_MT_SLOT 0x2f
92 #endif
93 #ifndef ABS_CNT
94 #define ABS_CNT                 (ABS_MAX+1)
95 #endif
96 #ifndef ABS_MT_TRACKING_ID
97 #define ABS_MT_TRACKING_ID      0x39    /* Unique ID of initiated contact */
98 #endif
99 #ifndef ABS_MT_PRESSURE
100 #define ABS_MT_PRESSURE         0x3a
101 #endif
102 #ifndef SYN_MT_REPORT
103 #define SYN_MT_REPORT           2
104 #endif
105 
106 class QEvdevTouchScreenData
107 {
108 public:
109     QEvdevTouchScreenData(QEvdevTouchScreenHandler *q_ptr, const QStringList &args);
110 
111     void processInputEvent(input_event *data);
112     void assignIds();
113 
114     QEvdevTouchScreenHandler *q;
115     int m_lastEventType;
116     QList<QWindowSystemInterface::TouchPoint> m_touchPoints;
117     QList<QWindowSystemInterface::TouchPoint> m_lastTouchPoints;
118 
119     struct Contact {
120         int trackingId = -1;
121         int x = 0;
122         int y = 0;
123         int maj = -1;
124         int pressure = 0;
125         Qt::TouchPointState state = Qt::TouchPointPressed;
126         QTouchEvent::TouchPoint::InfoFlags flags;
127     };
128     QHash<int, Contact> m_contacts; // The key is a tracking id for type A, slot number for type B.
129     QHash<int, Contact> m_lastContacts;
130     Contact m_currentData;
131     int m_currentSlot;
132 
133     double m_timeStamp;
134     double m_lastTimeStamp;
135 
136     int findClosestContact(const QHash<int, Contact> &contacts, int x, int y, int *dist);
137     void addTouchPoint(const Contact &contact, Qt::TouchPointStates *combinedStates);
138     void reportPoints();
139     void loadMultiScreenMappings();
140 
141     QRect screenGeometry() const;
142 
143     int hw_range_x_min;
144     int hw_range_x_max;
145     int hw_range_y_min;
146     int hw_range_y_max;
147     int hw_pressure_min;
148     int hw_pressure_max;
149     QString hw_name;
150     QString deviceNode;
151     bool m_forceToActiveWindow;
152     bool m_typeB;
153     QTransform m_rotate;
154     bool m_singleTouch;
155     QString m_screenName;
156     mutable QPointer<QScreen> m_screen;
157 
158     // Touch filtering and prediction are part of the same thing. The default
159     // prediction is 0ms, but sensible results can be achieved by setting it
160     // to, for instance, 16ms.
161     // For filtering to work well, the QPA plugin should provide a dead-steady
162     // implementation of QPlatformWindow::requestUpdate().
163     bool m_filtered;
164     int m_prediction;
165 
166     // When filtering is enabled, protect the access to current and last
167     // timeStamp and touchPoints, as these are being read on the gui thread.
168     QMutex m_mutex;
169 };
170 
QEvdevTouchScreenData(QEvdevTouchScreenHandler * q_ptr,const QStringList & args)171 QEvdevTouchScreenData::QEvdevTouchScreenData(QEvdevTouchScreenHandler *q_ptr, const QStringList &args)
172     : q(q_ptr),
173       m_lastEventType(-1),
174       m_currentSlot(0),
175       m_timeStamp(0), m_lastTimeStamp(0),
176       hw_range_x_min(0), hw_range_x_max(0),
177       hw_range_y_min(0), hw_range_y_max(0),
178       hw_pressure_min(0), hw_pressure_max(0),
179       m_forceToActiveWindow(false), m_typeB(false), m_singleTouch(false),
180       m_filtered(false), m_prediction(0)
181 {
182     for (const QString &arg : args) {
183         if (arg == QStringLiteral("force_window"))
184             m_forceToActiveWindow = true;
185         else if (arg == QStringLiteral("filtered"))
186             m_filtered = true;
187         else if (arg.startsWith(QStringLiteral("prediction=")))
188             m_prediction = arg.mid(11).toInt();
189     }
190 }
191 
192 #define LONG_BITS (sizeof(long) << 3)
193 #define NUM_LONGS(bits) (((bits) + LONG_BITS - 1) / LONG_BITS)
194 
195 #if !QT_CONFIG(mtdev)
testBit(long bit,const long * array)196 static inline bool testBit(long bit, const long *array)
197 {
198     return (array[bit / LONG_BITS] >> bit % LONG_BITS) & 1;
199 }
200 #endif
201 
QEvdevTouchScreenHandler(const QString & device,const QString & spec,QObject * parent)202 QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const QString &spec, QObject *parent)
203     : QObject(parent), m_notify(nullptr), m_fd(-1), d(nullptr), m_device(nullptr)
204 #if QT_CONFIG(mtdev)
205       , m_mtdev(nullptr)
206 #endif
207 {
208     setObjectName(QLatin1String("Evdev Touch Handler"));
209 
210     const QStringList args = spec.split(QLatin1Char(':'));
211     int rotationAngle = 0;
212     bool invertx = false;
213     bool inverty = false;
214     for (int i = 0; i < args.count(); ++i) {
215         if (args.at(i).startsWith(QLatin1String("rotate"))) {
216             QString rotateArg = args.at(i).section(QLatin1Char('='), 1, 1);
217             bool ok;
218             uint argValue = rotateArg.toUInt(&ok);
219             if (ok) {
220                 switch (argValue) {
221                 case 90:
222                 case 180:
223                 case 270:
224                     rotationAngle = argValue;
225                 default:
226                     break;
227                 }
228             }
229         } else if (args.at(i) == QLatin1String("invertx")) {
230             invertx = true;
231         } else if (args.at(i) == QLatin1String("inverty")) {
232             inverty = true;
233         }
234     }
235 
236     qCDebug(qLcEvdevTouch, "evdevtouch: Using device %ls", qUtf16Printable(device));
237 
238     m_fd = QT_OPEN(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);
239 
240     if (m_fd >= 0) {
241         m_notify = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
242         connect(m_notify, &QSocketNotifier::activated, this, &QEvdevTouchScreenHandler::readData);
243     } else {
244         qErrnoWarning("evdevtouch: Cannot open input device %ls", qUtf16Printable(device));
245         return;
246     }
247 
248 #if QT_CONFIG(mtdev)
249     m_mtdev = static_cast<mtdev *>(calloc(1, sizeof(mtdev)));
250     int mtdeverr = mtdev_open(m_mtdev, m_fd);
251     if (mtdeverr) {
252         qWarning("evdevtouch: mtdev_open failed: %d", mtdeverr);
253         QT_CLOSE(m_fd);
254         free(m_mtdev);
255         return;
256     }
257 #endif
258 
259     d = new QEvdevTouchScreenData(this, args);
260 
261 #if QT_CONFIG(mtdev)
262     const char *mtdevStr = "(mtdev)";
263     d->m_typeB = true;
264 #else
265     const char *mtdevStr = "";
266     long absbits[NUM_LONGS(ABS_CNT)];
267     if (ioctl(m_fd, EVIOCGBIT(EV_ABS, sizeof(absbits)), absbits) >= 0) {
268         d->m_typeB = testBit(ABS_MT_SLOT, absbits);
269         d->m_singleTouch = !testBit(ABS_MT_POSITION_X, absbits);
270     }
271 #endif
272 
273     d->deviceNode = device;
274     qCDebug(qLcEvdevTouch,
275             "evdevtouch: %ls: Protocol type %c %s (%s), filtered=%s",
276             qUtf16Printable(d->deviceNode),
277             d->m_typeB ? 'B' : 'A', mtdevStr,
278             d->m_singleTouch ? "single" : "multi",
279             d->m_filtered ? "yes" : "no");
280     if (d->m_filtered)
281         qCDebug(qLcEvdevTouch, " - prediction=%d", d->m_prediction);
282 
283     input_absinfo absInfo;
284     memset(&absInfo, 0, sizeof(input_absinfo));
285     bool has_x_range = false, has_y_range = false;
286 
287     if (ioctl(m_fd, EVIOCGABS((d->m_singleTouch ? ABS_X : ABS_MT_POSITION_X)), &absInfo) >= 0) {
288         qCDebug(qLcEvdevTouch, "evdevtouch: %ls: min X: %d max X: %d", qUtf16Printable(device),
289                 absInfo.minimum, absInfo.maximum);
290         d->hw_range_x_min = absInfo.minimum;
291         d->hw_range_x_max = absInfo.maximum;
292         has_x_range = true;
293     }
294 
295     if (ioctl(m_fd, EVIOCGABS((d->m_singleTouch ? ABS_Y : ABS_MT_POSITION_Y)), &absInfo) >= 0) {
296         qCDebug(qLcEvdevTouch, "evdevtouch: %ls: min Y: %d max Y: %d", qUtf16Printable(device),
297                 absInfo.minimum, absInfo.maximum);
298         d->hw_range_y_min = absInfo.minimum;
299         d->hw_range_y_max = absInfo.maximum;
300         has_y_range = true;
301     }
302 
303     if (!has_x_range || !has_y_range)
304         qWarning("evdevtouch: %ls: Invalid ABS limits, behavior unspecified", qUtf16Printable(device));
305 
306     if (ioctl(m_fd, EVIOCGABS(ABS_PRESSURE), &absInfo) >= 0) {
307         qCDebug(qLcEvdevTouch, "evdevtouch: %ls: min pressure: %d max pressure: %d", qUtf16Printable(device),
308                 absInfo.minimum, absInfo.maximum);
309         if (absInfo.maximum > absInfo.minimum) {
310             d->hw_pressure_min = absInfo.minimum;
311             d->hw_pressure_max = absInfo.maximum;
312         }
313     }
314 
315     char name[1024];
316     if (ioctl(m_fd, EVIOCGNAME(sizeof(name) - 1), name) >= 0) {
317         d->hw_name = QString::fromLocal8Bit(name);
318         qCDebug(qLcEvdevTouch, "evdevtouch: %ls: device name: %s", qUtf16Printable(device), name);
319     }
320 
321     // Fix up the coordinate ranges for am335x in case the kernel driver does not have them fixed.
322     if (d->hw_name == QLatin1String("ti-tsc")) {
323         if (d->hw_range_x_min == 0 && d->hw_range_x_max == 4095) {
324             d->hw_range_x_min = 165;
325             d->hw_range_x_max = 4016;
326         }
327         if (d->hw_range_y_min == 0 && d->hw_range_y_max == 4095) {
328             d->hw_range_y_min = 220;
329             d->hw_range_y_max = 3907;
330         }
331         qCDebug(qLcEvdevTouch, "evdevtouch: found ti-tsc, overriding: min X: %d max X: %d min Y: %d max Y: %d",
332                 d->hw_range_x_min, d->hw_range_x_max, d->hw_range_y_min, d->hw_range_y_max);
333     }
334 
335     bool grabSuccess = !ioctl(m_fd, EVIOCGRAB, (void *) 1);
336     if (grabSuccess)
337         ioctl(m_fd, EVIOCGRAB, (void *) 0);
338     else
339         qWarning("evdevtouch: The device is grabbed by another process. No events will be read.");
340 
341     if (rotationAngle)
342         d->m_rotate = QTransform::fromTranslate(0.5, 0.5).rotate(rotationAngle).translate(-0.5, -0.5);
343 
344     if (invertx)
345         d->m_rotate *= QTransform::fromTranslate(0.5, 0.5).scale(-1.0, 1.0).translate(-0.5, -0.5);
346 
347     if (inverty)
348         d->m_rotate *= QTransform::fromTranslate(0.5, 0.5).scale(1.0, -1.0).translate(-0.5, -0.5);
349 
350     QTouchOutputMapping mapping;
351     if (mapping.load()) {
352         d->m_screenName = mapping.screenNameForDeviceNode(d->deviceNode);
353         if (!d->m_screenName.isEmpty())
354             qCDebug(qLcEvdevTouch, "evdevtouch: Mapping device %ls to screen %ls",
355                     qUtf16Printable(d->deviceNode), qUtf16Printable(d->m_screenName));
356     }
357 
358     registerTouchDevice();
359 }
360 
~QEvdevTouchScreenHandler()361 QEvdevTouchScreenHandler::~QEvdevTouchScreenHandler()
362 {
363 #if QT_CONFIG(mtdev)
364     if (m_mtdev) {
365         mtdev_close(m_mtdev);
366         free(m_mtdev);
367     }
368 #endif
369 
370     if (m_fd >= 0)
371         QT_CLOSE(m_fd);
372 
373     delete d;
374 
375     unregisterTouchDevice();
376 }
377 
isFiltered() const378 bool QEvdevTouchScreenHandler::isFiltered() const
379 {
380     return d && d->m_filtered;
381 }
382 
touchDevice() const383 QTouchDevice *QEvdevTouchScreenHandler::touchDevice() const
384 {
385     return m_device;
386 }
387 
readData()388 void QEvdevTouchScreenHandler::readData()
389 {
390     ::input_event buffer[32];
391     int events = 0;
392 
393 #if QT_CONFIG(mtdev)
394     forever {
395         do {
396             events = mtdev_get(m_mtdev, m_fd, buffer, sizeof(buffer) / sizeof(::input_event));
397             // keep trying mtdev_get if we get interrupted. note that we do not
398             // (and should not) handle EAGAIN; EAGAIN means that reading would
399             // block and we'll get back here later to try again anyway.
400         } while (events == -1 && errno == EINTR);
401 
402         // 0 events is EOF, -1 means error, handle both in the same place
403         if (events <= 0)
404             goto err;
405 
406         // process our shiny new events
407         for (int i = 0; i < events; ++i)
408             d->processInputEvent(&buffer[i]);
409 
410         // and try to get more
411     }
412 #else
413     int n = 0;
414     for (; ;) {
415         events = QT_READ(m_fd, reinterpret_cast<char*>(buffer) + n, sizeof(buffer) - n);
416         if (events <= 0)
417             goto err;
418         n += events;
419         if (n % sizeof(::input_event) == 0)
420             break;
421     }
422 
423     n /= sizeof(::input_event);
424 
425     for (int i = 0; i < n; ++i)
426         d->processInputEvent(&buffer[i]);
427 #endif
428     return;
429 
430 err:
431     if (!events) {
432         qWarning("evdevtouch: Got EOF from input device");
433         return;
434     } else if (events < 0) {
435         if (errno != EINTR && errno != EAGAIN) {
436             qErrnoWarning("evdevtouch: Could not read from input device");
437             if (errno == ENODEV) { // device got disconnected -> stop reading
438                 delete m_notify;
439                 m_notify = nullptr;
440 
441                 QT_CLOSE(m_fd);
442                 m_fd = -1;
443 
444                 unregisterTouchDevice();
445             }
446             return;
447         }
448     }
449 }
450 
registerTouchDevice()451 void QEvdevTouchScreenHandler::registerTouchDevice()
452 {
453     if (m_device)
454         return;
455 
456     m_device = new QTouchDevice;
457     m_device->setName(d->hw_name);
458     m_device->setType(QTouchDevice::TouchScreen);
459     m_device->setCapabilities(QTouchDevice::Position | QTouchDevice::Area);
460     if (d->hw_pressure_max > d->hw_pressure_min)
461         m_device->setCapabilities(m_device->capabilities() | QTouchDevice::Pressure);
462 
463     QWindowSystemInterface::registerTouchDevice(m_device);
464 }
465 
unregisterTouchDevice()466 void QEvdevTouchScreenHandler::unregisterTouchDevice()
467 {
468     if (!m_device)
469         return;
470 
471     // At app exit the cleanup may have already been done, avoid
472     // double delete by checking the list first.
473     if (QWindowSystemInterface::isTouchDeviceRegistered(m_device)) {
474         QWindowSystemInterface::unregisterTouchDevice(m_device);
475         delete m_device;
476     }
477 
478     m_device = nullptr;
479 }
480 
addTouchPoint(const Contact & contact,Qt::TouchPointStates * combinedStates)481 void QEvdevTouchScreenData::addTouchPoint(const Contact &contact, Qt::TouchPointStates *combinedStates)
482 {
483     QWindowSystemInterface::TouchPoint tp;
484     tp.id = contact.trackingId;
485     tp.flags = contact.flags;
486     tp.state = contact.state;
487     *combinedStates |= tp.state;
488 
489     // Store the HW coordinates for now, will be updated later.
490     tp.area = QRectF(0, 0, contact.maj, contact.maj);
491     tp.area.moveCenter(QPoint(contact.x, contact.y));
492     tp.pressure = contact.pressure;
493 
494     // Get a normalized position in range 0..1.
495     tp.normalPosition = QPointF((contact.x - hw_range_x_min) / qreal(hw_range_x_max - hw_range_x_min),
496                                 (contact.y - hw_range_y_min) / qreal(hw_range_y_max - hw_range_y_min));
497 
498     if (!m_rotate.isIdentity())
499         tp.normalPosition = m_rotate.map(tp.normalPosition);
500 
501     tp.rawPositions.append(QPointF(contact.x, contact.y));
502 
503     m_touchPoints.append(tp);
504 }
505 
processInputEvent(input_event * data)506 void QEvdevTouchScreenData::processInputEvent(input_event *data)
507 {
508     if (data->type == EV_ABS) {
509 
510         if (data->code == ABS_MT_POSITION_X || (m_singleTouch && data->code == ABS_X)) {
511             m_currentData.x = qBound(hw_range_x_min, data->value, hw_range_x_max);
512             if (m_singleTouch)
513                 m_contacts[m_currentSlot].x = m_currentData.x;
514             if (m_typeB) {
515                 m_contacts[m_currentSlot].x = m_currentData.x;
516                 if (m_contacts[m_currentSlot].state == Qt::TouchPointStationary)
517                     m_contacts[m_currentSlot].state = Qt::TouchPointMoved;
518             }
519         } else if (data->code == ABS_MT_POSITION_Y || (m_singleTouch && data->code == ABS_Y)) {
520             m_currentData.y = qBound(hw_range_y_min, data->value, hw_range_y_max);
521             if (m_singleTouch)
522                 m_contacts[m_currentSlot].y = m_currentData.y;
523             if (m_typeB) {
524                 m_contacts[m_currentSlot].y = m_currentData.y;
525                 if (m_contacts[m_currentSlot].state == Qt::TouchPointStationary)
526                     m_contacts[m_currentSlot].state = Qt::TouchPointMoved;
527             }
528         } else if (data->code == ABS_MT_TRACKING_ID) {
529             m_currentData.trackingId = data->value;
530             if (m_typeB) {
531                 if (m_currentData.trackingId == -1) {
532                     m_contacts[m_currentSlot].state = Qt::TouchPointReleased;
533                 } else {
534                     m_contacts[m_currentSlot].state = Qt::TouchPointPressed;
535                     m_contacts[m_currentSlot].trackingId = m_currentData.trackingId;
536                 }
537             }
538         } else if (data->code == ABS_MT_TOUCH_MAJOR) {
539             m_currentData.maj = data->value;
540             if (data->value == 0)
541                 m_currentData.state = Qt::TouchPointReleased;
542             if (m_typeB)
543                 m_contacts[m_currentSlot].maj = m_currentData.maj;
544         } else if (data->code == ABS_PRESSURE || data->code == ABS_MT_PRESSURE) {
545             if (Q_UNLIKELY(qLcEvents().isDebugEnabled()))
546                 qCDebug(qLcEvents, "EV_ABS code 0x%x: pressure %d; bounding to [%d,%d]",
547                         data->code, data->value, hw_pressure_min, hw_pressure_max);
548             m_currentData.pressure = qBound(hw_pressure_min, data->value, hw_pressure_max);
549             if (m_typeB || m_singleTouch)
550                 m_contacts[m_currentSlot].pressure = m_currentData.pressure;
551         } else if (data->code == ABS_MT_SLOT) {
552             m_currentSlot = data->value;
553         }
554 
555     } else if (data->type == EV_KEY && !m_typeB) {
556         if (data->code == BTN_TOUCH && data->value == 0)
557             m_contacts[m_currentSlot].state = Qt::TouchPointReleased;
558     } else if (data->type == EV_SYN && data->code == SYN_MT_REPORT && m_lastEventType != EV_SYN) {
559 
560         // If there is no tracking id, one will be generated later.
561         // Until that use a temporary key.
562         int key = m_currentData.trackingId;
563         if (key == -1)
564             key = m_contacts.count();
565 
566         m_contacts.insert(key, m_currentData);
567         m_currentData = Contact();
568 
569     } else if (data->type == EV_SYN && data->code == SYN_REPORT) {
570 
571         // Ensure valid IDs even when the driver does not report ABS_MT_TRACKING_ID.
572         if (!m_contacts.isEmpty() && m_contacts.constBegin().value().trackingId == -1)
573             assignIds();
574 
575         std::unique_lock<QMutex> locker;
576         if (m_filtered)
577             locker = std::unique_lock<QMutex>{m_mutex};
578 
579         // update timestamps
580         m_lastTimeStamp = m_timeStamp;
581         m_timeStamp = data->input_event_sec + data->input_event_usec / 1000000.0;
582 
583         m_lastTouchPoints = m_touchPoints;
584         m_touchPoints.clear();
585         Qt::TouchPointStates combinedStates;
586         bool hasPressure = false;
587 
588         for (auto i = m_contacts.begin(), end = m_contacts.end(); i != end; /*erasing*/) {
589             auto it = i++;
590 
591             Contact &contact(it.value());
592 
593             if (!contact.state)
594                 continue;
595 
596             int key = m_typeB ? it.key() : contact.trackingId;
597             if (!m_typeB && m_lastContacts.contains(key)) {
598                 const Contact &prev(m_lastContacts.value(key));
599                 if (contact.state == Qt::TouchPointReleased) {
600                     // Copy over the previous values for released points, just in case.
601                     contact.x = prev.x;
602                     contact.y = prev.y;
603                     contact.maj = prev.maj;
604                 } else {
605                     contact.state = (prev.x == contact.x && prev.y == contact.y)
606                             ? Qt::TouchPointStationary : Qt::TouchPointMoved;
607                 }
608             }
609 
610             // Avoid reporting a contact in released state more than once.
611             if (!m_typeB && contact.state == Qt::TouchPointReleased
612                     && !m_lastContacts.contains(key)) {
613                 m_contacts.erase(it);
614                 continue;
615             }
616 
617             if (contact.pressure)
618                 hasPressure = true;
619 
620             addTouchPoint(contact, &combinedStates);
621         }
622 
623         // Now look for contacts that have disappeared since the last sync.
624         for (auto it = m_lastContacts.begin(), end = m_lastContacts.end(); it != end; ++it) {
625             Contact &contact(it.value());
626             int key = m_typeB ? it.key() : contact.trackingId;
627             if (m_typeB) {
628                 if (contact.trackingId != m_contacts[key].trackingId && contact.state) {
629                     contact.state = Qt::TouchPointReleased;
630                     addTouchPoint(contact, &combinedStates);
631                 }
632             } else {
633                 if (!m_contacts.contains(key)) {
634                     contact.state = Qt::TouchPointReleased;
635                     addTouchPoint(contact, &combinedStates);
636                 }
637             }
638         }
639 
640         // Remove contacts that have just been reported as released.
641         for (auto i = m_contacts.begin(), end = m_contacts.end(); i != end; /*erasing*/) {
642             auto it = i++;
643 
644             Contact &contact(it.value());
645 
646             if (!contact.state)
647                 continue;
648 
649             if (contact.state == Qt::TouchPointReleased) {
650                 if (m_typeB)
651                     contact.state = static_cast<Qt::TouchPointState>(0);
652                 else
653                     m_contacts.erase(it);
654             } else {
655                 contact.state = Qt::TouchPointStationary;
656             }
657         }
658 
659         m_lastContacts = m_contacts;
660         if (!m_typeB && !m_singleTouch)
661             m_contacts.clear();
662 
663 
664         if (!m_touchPoints.isEmpty() && (hasPressure || combinedStates != Qt::TouchPointStationary))
665             reportPoints();
666     }
667 
668     m_lastEventType = data->type;
669 }
670 
findClosestContact(const QHash<int,Contact> & contacts,int x,int y,int * dist)671 int QEvdevTouchScreenData::findClosestContact(const QHash<int, Contact> &contacts, int x, int y, int *dist)
672 {
673     int minDist = -1, id = -1;
674     for (QHash<int, Contact>::const_iterator it = contacts.constBegin(), ite = contacts.constEnd();
675          it != ite; ++it) {
676         const Contact &contact(it.value());
677         int dx = x - contact.x;
678         int dy = y - contact.y;
679         int dist = dx * dx + dy * dy;
680         if (minDist == -1 || dist < minDist) {
681             minDist = dist;
682             id = contact.trackingId;
683         }
684     }
685     if (dist)
686         *dist = minDist;
687     return id;
688 }
689 
assignIds()690 void QEvdevTouchScreenData::assignIds()
691 {
692     QHash<int, Contact> candidates = m_lastContacts, pending = m_contacts, newContacts;
693     int maxId = -1;
694     QHash<int, Contact>::iterator it, ite, bestMatch;
695     while (!pending.isEmpty() && !candidates.isEmpty()) {
696         int bestDist = -1, bestId = 0;
697         for (it = pending.begin(), ite = pending.end(); it != ite; ++it) {
698             int dist;
699             int id = findClosestContact(candidates, it->x, it->y, &dist);
700             if (id >= 0 && (bestDist == -1 || dist < bestDist)) {
701                 bestDist = dist;
702                 bestId = id;
703                 bestMatch = it;
704             }
705         }
706         if (bestDist >= 0) {
707             bestMatch->trackingId = bestId;
708             newContacts.insert(bestId, *bestMatch);
709             candidates.remove(bestId);
710             pending.erase(bestMatch);
711             if (bestId > maxId)
712                 maxId = bestId;
713         }
714     }
715     if (candidates.isEmpty()) {
716         for (it = pending.begin(), ite = pending.end(); it != ite; ++it) {
717             it->trackingId = ++maxId;
718             newContacts.insert(it->trackingId, *it);
719         }
720     }
721     m_contacts = newContacts;
722 }
723 
screenGeometry() const724 QRect QEvdevTouchScreenData::screenGeometry() const
725 {
726     if (m_forceToActiveWindow) {
727         QWindow *win = QGuiApplication::focusWindow();
728         return win ? QHighDpi::toNativePixels(win->geometry(), win) : QRect();
729     }
730 
731     // Now it becomes tricky. Traditionally we picked the primaryScreen()
732     // and were done with it. But then, enter multiple screens, and
733     // suddenly it was all broken.
734     //
735     // For now we only support the display configuration of the KMS/DRM
736     // backends of eglfs. See QTouchOutputMapping.
737     //
738     // The good news it that once winRect refers to the correct screen
739     // geometry in the full virtual desktop space, there is nothing else
740     // left to do since qguiapp will handle the rest.
741     QScreen *screen = QGuiApplication::primaryScreen();
742     if (!m_screenName.isEmpty()) {
743         if (!m_screen) {
744             const QList<QScreen *> screens = QGuiApplication::screens();
745             for (QScreen *s : screens) {
746                 if (s->name() == m_screenName) {
747                     m_screen = s;
748                     break;
749                 }
750             }
751         }
752         if (m_screen)
753             screen = m_screen;
754     }
755     return QHighDpi::toNativePixels(screen->geometry(), screen);
756 }
757 
reportPoints()758 void QEvdevTouchScreenData::reportPoints()
759 {
760     QRect winRect = screenGeometry();
761     if (winRect.isNull())
762         return;
763 
764     const int hw_w = hw_range_x_max - hw_range_x_min;
765     const int hw_h = hw_range_y_max - hw_range_y_min;
766 
767     // Map the coordinates based on the normalized position. QPA expects 'area'
768     // to be in screen coordinates.
769     const int pointCount = m_touchPoints.count();
770     for (int i = 0; i < pointCount; ++i) {
771         QWindowSystemInterface::TouchPoint &tp(m_touchPoints[i]);
772 
773         // Generate a screen position that is always inside the active window
774         // or the primary screen.  Even though we report this as a QRectF, internally
775         // Qt uses QRect/QPoint so we need to bound the size to winRect.size() - QSize(1, 1)
776         const qreal wx = winRect.left() + tp.normalPosition.x() * (winRect.width() - 1);
777         const qreal wy = winRect.top() + tp.normalPosition.y() * (winRect.height() - 1);
778         const qreal sizeRatio = (winRect.width() + winRect.height()) / qreal(hw_w + hw_h);
779         if (tp.area.width() == -1) // touch major was not provided
780             tp.area = QRectF(0, 0, 8, 8);
781         else
782             tp.area = QRectF(0, 0, tp.area.width() * sizeRatio, tp.area.height() * sizeRatio);
783         tp.area.moveCenter(QPointF(wx, wy));
784 
785         // Calculate normalized pressure.
786         if (!hw_pressure_min && !hw_pressure_max)
787             tp.pressure = tp.state == Qt::TouchPointReleased ? 0 : 1;
788         else
789             tp.pressure = (tp.pressure - hw_pressure_min) / qreal(hw_pressure_max - hw_pressure_min);
790 
791         if (Q_UNLIKELY(qLcEvents().isDebugEnabled()))
792             qCDebug(qLcEvents) << "reporting" << tp;
793     }
794 
795     // Let qguiapp pick the target window.
796     if (m_filtered)
797         emit q->touchPointsUpdated();
798     else
799         QWindowSystemInterface::handleTouchEvent(nullptr, q->touchDevice(), m_touchPoints);
800 }
801 
QEvdevTouchScreenHandlerThread(const QString & device,const QString & spec,QObject * parent)802 QEvdevTouchScreenHandlerThread::QEvdevTouchScreenHandlerThread(const QString &device, const QString &spec, QObject *parent)
803     : QDaemonThread(parent), m_device(device), m_spec(spec), m_handler(nullptr), m_touchDeviceRegistered(false)
804     , m_touchUpdatePending(false)
805     , m_filterWindow(nullptr)
806     , m_touchRate(-1)
807 {
808     start();
809 }
810 
~QEvdevTouchScreenHandlerThread()811 QEvdevTouchScreenHandlerThread::~QEvdevTouchScreenHandlerThread()
812 {
813     quit();
814     wait();
815 }
816 
run()817 void QEvdevTouchScreenHandlerThread::run()
818 {
819     m_handler = new QEvdevTouchScreenHandler(m_device, m_spec);
820 
821     if (m_handler->isFiltered())
822         connect(m_handler, &QEvdevTouchScreenHandler::touchPointsUpdated, this, &QEvdevTouchScreenHandlerThread::scheduleTouchPointUpdate);
823 
824     // Report the registration to the parent thread by invoking the method asynchronously
825     QMetaObject::invokeMethod(this, "notifyTouchDeviceRegistered", Qt::QueuedConnection);
826 
827     exec();
828 
829     delete m_handler;
830     m_handler = nullptr;
831 }
832 
isTouchDeviceRegistered() const833 bool QEvdevTouchScreenHandlerThread::isTouchDeviceRegistered() const
834 {
835     return m_touchDeviceRegistered;
836 }
837 
notifyTouchDeviceRegistered()838 void QEvdevTouchScreenHandlerThread::notifyTouchDeviceRegistered()
839 {
840     m_touchDeviceRegistered = true;
841     emit touchDeviceRegistered();
842 }
843 
scheduleTouchPointUpdate()844 void QEvdevTouchScreenHandlerThread::scheduleTouchPointUpdate()
845 {
846     QWindow *window = QGuiApplication::focusWindow();
847     if (window != m_filterWindow) {
848         if (m_filterWindow)
849             m_filterWindow->removeEventFilter(this);
850         m_filterWindow = window;
851         if (m_filterWindow)
852             m_filterWindow->installEventFilter(this);
853     }
854     if (m_filterWindow) {
855         m_touchUpdatePending = true;
856         m_filterWindow->requestUpdate();
857     }
858 }
859 
eventFilter(QObject * object,QEvent * event)860 bool QEvdevTouchScreenHandlerThread::eventFilter(QObject *object, QEvent *event)
861 {
862     if (m_touchUpdatePending && object == m_filterWindow && event->type() == QEvent::UpdateRequest) {
863         m_touchUpdatePending = false;
864         filterAndSendTouchPoints();
865     }
866     return false;
867 }
868 
filterAndSendTouchPoints()869 void QEvdevTouchScreenHandlerThread::filterAndSendTouchPoints()
870 {
871     QRect winRect = m_handler->d->screenGeometry();
872     if (winRect.isNull())
873         return;
874 
875     float vsyncDelta = 1.0f / QGuiApplication::primaryScreen()->refreshRate();
876 
877     QHash<int, FilteredTouchPoint> filteredPoints;
878 
879     m_handler->d->m_mutex.lock();
880 
881     double time = m_handler->d->m_timeStamp;
882     double lastTime = m_handler->d->m_lastTimeStamp;
883     double touchDelta = time - lastTime;
884     if (m_touchRate < 0 || touchDelta > vsyncDelta) {
885         // We're at the very start, with nothing to go on, so make a guess
886         // that the touch rate will be somewhere in the range of half a vsync.
887         // This doesn't have to be accurate as we will calibrate it over time,
888         // but it gives us a better starting point so calibration will be
889         // slightly quicker. If, on the other hand, we already have an
890         // estimate, we'll leave it as is and keep it.
891         if (m_touchRate < 0)
892             m_touchRate = (1.0 / QGuiApplication::primaryScreen()->refreshRate()) / 2.0;
893 
894     } else {
895         // Update our estimate for the touch rate. We're making the assumption
896         // that this value will be mostly accurate with the occational bump,
897         // so we're weighting the existing value high compared to the update.
898         const double ratio = 0.9;
899         m_touchRate = sqrt(m_touchRate * m_touchRate * ratio + touchDelta * touchDelta * (1.0 - ratio));
900     }
901 
902     QList<QWindowSystemInterface::TouchPoint> points = m_handler->d->m_touchPoints;
903     QList<QWindowSystemInterface::TouchPoint> lastPoints = m_handler->d->m_lastTouchPoints;
904 
905     m_handler->d->m_mutex.unlock();
906 
907     for (int i=0; i<points.size(); ++i) {
908         QWindowSystemInterface::TouchPoint &tp = points[i];
909         QPointF pos = tp.normalPosition;
910         FilteredTouchPoint f;
911 
912         QWindowSystemInterface::TouchPoint ltp;
913         ltp.id = -1;
914         for (int j=0; j<lastPoints.size(); ++j) {
915             if (lastPoints.at(j).id == tp.id) {
916                 ltp = lastPoints.at(j);
917                 break;
918             }
919         }
920 
921         QPointF velocity;
922         if (lastTime != 0 && ltp.id >= 0)
923             velocity = (pos - ltp.normalPosition) / m_touchRate;
924         if (m_filteredPoints.contains(tp.id)) {
925             f = m_filteredPoints.take(tp.id);
926             f.x.update(pos.x(), velocity.x(), vsyncDelta);
927             f.y.update(pos.y(), velocity.y(), vsyncDelta);
928             pos = QPointF(f.x.position(), f.y.position());
929         } else {
930             f.x.initialize(pos.x(), velocity.x());
931             f.y.initialize(pos.y(), velocity.y());
932             // Make sure the first instance of a touch point we send has the
933             // 'pressed' state.
934             if (tp.state != Qt::TouchPointPressed)
935                 tp.state = Qt::TouchPointPressed;
936         }
937 
938         tp.velocity = QVector2D(f.x.velocity() * winRect.width(), f.y.velocity() * winRect.height());
939 
940         qreal filteredNormalizedX = f.x.position() + f.x.velocity() * m_handler->d->m_prediction / 1000.0;
941         qreal filteredNormalizedY = f.y.position() + f.y.velocity() * m_handler->d->m_prediction / 1000.0;
942 
943         // Clamp to the screen
944         tp.normalPosition = QPointF(qBound<qreal>(0, filteredNormalizedX, 1),
945                                     qBound<qreal>(0, filteredNormalizedY, 1));
946 
947         qreal x = winRect.x() + (tp.normalPosition.x() * (winRect.width() - 1));
948         qreal y = winRect.y() + (tp.normalPosition.y() * (winRect.height() - 1));
949 
950         tp.area.moveCenter(QPointF(x, y));
951 
952         // Store the touch point for later so we can release it if we've
953         // missed the actual release between our last update and this.
954         f.touchPoint = tp;
955 
956         // Don't store the point for future reference if it is a release.
957         if (tp.state != Qt::TouchPointReleased)
958             filteredPoints[tp.id] = f;
959     }
960 
961     for (QHash<int, FilteredTouchPoint>::const_iterator it = m_filteredPoints.constBegin(), end = m_filteredPoints.constEnd(); it != end; ++it) {
962         const FilteredTouchPoint &f = it.value();
963         QWindowSystemInterface::TouchPoint tp = f.touchPoint;
964         tp.state = Qt::TouchPointReleased;
965         tp.velocity = QVector2D();
966         points.append(tp);
967     }
968 
969     m_filteredPoints = filteredPoints;
970 
971     QWindowSystemInterface::handleTouchEvent(nullptr,
972                                              m_handler->touchDevice(),
973                                              points);
974 }
975 
976 
977 QT_END_NAMESPACE
978