9#include <QSocketNotifier>
10#include <QGuiApplication>
11#include <QLoggingCategory>
12#include <QtCore/private/qcore_unix_p.h>
13#include <QtGui/qpointingdevice.h>
14#include <QtGui/private/qhighdpiscaling_p.h>
15#include <QtGui/private/qguiapplication_p.h>
16#include <QtGui/private/qpointingdevice_p.h>
18#include <QtCore/qpointer.h>
23#include <dev/evdev/input.h>
25#include <linux/input.h>
29#define input_event_sec time.tv_sec
33#define input_event_usec time.tv_usec
46using namespace Qt::StringLiterals;
49Q_STATIC_LOGGING_CATEGORY(qLcEvents,
"qt.qpa.input.events")
52
53
55#define ABS_MT_TOUCH_MAJOR 0x30
58#define ABS_MT_POSITION_X 0x35
61#define ABS_MT_POSITION_Y 0x36
64#define ABS_MT_SLOT 0x2f
67#define ABS_CNT (ABS_MAX+1
)
70#define ABS_MT_TRACKING_ID 0x39
73#define ABS_MT_PRESSURE 0x3a
76#define SYN_MT_REPORT 2
79class QEvdevTouchScreenData
82 QEvdevTouchScreenData(QEvdevTouchScreenHandler *q_ptr,
const QStringList &args);
84 void processInputEvent(input_event *data);
87 QEvdevTouchScreenHandler *q;
89 QList<QWindowSystemInterface::TouchPoint> m_touchPoints;
90 QList<QWindowSystemInterface::TouchPoint> m_lastTouchPoints;
91 QVarLengthFlatMap<
int, QList<QWindowSystemInterface::TouchPoint>, 3> m_pointsByToolType;
99 int toolType = MT_TOOL_FINGER;
100 QEventPoint::State state = QEventPoint::State::Pressed;
102 QHash<
int, Contact> m_contacts;
103 QHash<
int, Contact> m_lastContacts;
104 Contact m_currentData;
108 double m_lastTimeStamp;
110 int findClosestContact(
const QHash<
int, Contact> &contacts,
int x,
int y,
int *dist);
111 void addTouchPoint(
const Contact &contact, QEventPoint::States *combinedStates);
113 void loadMultiScreenMappings();
115 QPointingDevice *getDeviceForToolType(
int toolType);
116 QPointingDevice::PointerType mapToolTypeToPointerType(
int toolType);
118 QRect screenGeometry()
const;
128 bool m_forceToActiveWindow;
132 QString m_screenName;
133 mutable QPointer<QScreen> m_screen;
136 QVarLengthFlatMap<
int, QPointer<QPointingDevice>, 3> m_devices;
151QEvdevTouchScreenData::QEvdevTouchScreenData(QEvdevTouchScreenHandler *q_ptr,
const QStringList &args)
155 m_timeStamp(0), m_lastTimeStamp(0),
156 hw_range_x_min(0), hw_range_x_max(0),
157 hw_range_y_min(0), hw_range_y_max(0),
158 hw_pressure_min(0), hw_pressure_max(0),
159 m_forceToActiveWindow(
false), m_typeB(
false), m_singleTouch(
false),
160 m_filtered(
false), m_prediction(0)
162 for (
const QString &arg : args) {
163 if (arg == u"force_window")
164 m_forceToActiveWindow =
true;
165 else if (arg == u"filtered")
167 else if (
const QStringView prefix = u"prediction="; arg.startsWith(prefix))
168 m_prediction = QStringView(arg).mid(prefix.size()).toInt();
172#define LONG_BITS (sizeof(long) << 3
)
176static inline bool testBit(
long bit,
const long *array)
178 return (array[bit / LONG_BITS] >> bit % LONG_BITS) & 1;
183 : QObject(parent), m_notify(
nullptr), m_fd(-1), d(
nullptr), m_device(
nullptr)
188 setObjectName(
"Evdev Touch Handler"_L1);
190 const QStringList args = spec.split(u':');
191 int rotationAngle = 0;
192 bool invertx =
false;
193 bool inverty =
false;
194 for (
int i = 0; i < args.size(); ++i) {
195 if (args.at(i).startsWith(
"rotate"_L1)) {
196 QString rotateArg = args.at(i).section(u'=', 1, 1);
198 uint argValue = rotateArg.toUInt(&ok);
204 rotationAngle = argValue;
210 }
else if (args.at(i) ==
"invertx"_L1) {
212 }
else if (args.at(i) ==
"inverty"_L1) {
217 qCDebug(qLcEvdevTouch,
"evdevtouch: Using device %ls", qUtf16Printable(device));
219 m_fd = QT_OPEN(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);
222 m_notify =
new QSocketNotifier(m_fd, QSocketNotifier::Read,
this);
225 qErrnoWarning(
"evdevtouch: Cannot open input device %ls", qUtf16Printable(device));
230 m_mtdev =
static_cast<mtdev *>(calloc(1,
sizeof(mtdev)));
231 int mtdeverr = mtdev_open(m_mtdev, m_fd);
233 qWarning(
"evdevtouch: mtdev_open failed: %d", mtdeverr);
240 d =
new QEvdevTouchScreenData(
this, args);
243 const char *mtdevStr =
"(mtdev)";
246 const char *mtdevStr =
"";
248 if (ioctl(m_fd, EVIOCGBIT(
EV_ABS,
sizeof(absbits)), absbits) >= 0) {
254 d->deviceNode = device;
255 qCDebug(qLcEvdevTouch,
256 "evdevtouch: %ls: Protocol type %c %s (%s), filtered=%s",
257 qUtf16Printable(d->deviceNode),
258 d->m_typeB ?
'B' :
'A', mtdevStr,
259 d->m_singleTouch ?
"single" :
"multi",
260 d->m_filtered ?
"yes" :
"no");
262 qCDebug(qLcEvdevTouch,
" - prediction=%d", d->m_prediction);
264 input_absinfo absInfo;
265 memset(&absInfo, 0,
sizeof(input_absinfo));
266 bool has_x_range =
false, has_y_range =
false;
269 qCDebug(qLcEvdevTouch,
"evdevtouch: %ls: min X: %d max X: %d", qUtf16Printable(device),
270 absInfo.minimum, absInfo.maximum);
271 d->hw_range_x_min = absInfo.minimum;
272 d->hw_range_x_max = absInfo.maximum;
277 qCDebug(qLcEvdevTouch,
"evdevtouch: %ls: min Y: %d max Y: %d", qUtf16Printable(device),
278 absInfo.minimum, absInfo.maximum);
279 d->hw_range_y_min = absInfo.minimum;
280 d->hw_range_y_max = absInfo.maximum;
284 if (!has_x_range || !has_y_range)
285 qWarning(
"evdevtouch: %ls: Invalid ABS limits, behavior unspecified", qUtf16Printable(device));
287 if (ioctl(m_fd, EVIOCGABS(ABS_PRESSURE), &absInfo) >= 0) {
288 qCDebug(qLcEvdevTouch,
"evdevtouch: %ls: min pressure: %d max pressure: %d", qUtf16Printable(device),
289 absInfo.minimum, absInfo.maximum);
290 if (absInfo.maximum > absInfo.minimum) {
291 d->hw_pressure_min = absInfo.minimum;
292 d->hw_pressure_max = absInfo.maximum;
297 if (ioctl(m_fd, EVIOCGNAME(
sizeof(name) - 1), name) >= 0) {
298 d->hw_name = QString::fromLocal8Bit(name);
299 qCDebug(qLcEvdevTouch,
"evdevtouch: %ls: device name: %s", qUtf16Printable(device), name);
303 if (d->hw_name ==
"ti-tsc"_L1) {
304 if (d->hw_range_x_min == 0 && d->hw_range_x_max == 4095) {
305 d->hw_range_x_min = 165;
306 d->hw_range_x_max = 4016;
308 if (d->hw_range_y_min == 0 && d->hw_range_y_max == 4095) {
309 d->hw_range_y_min = 220;
310 d->hw_range_y_max = 3907;
312 qCDebug(qLcEvdevTouch,
"evdevtouch: found ti-tsc, overriding: min X: %d max X: %d min Y: %d max Y: %d",
313 d->hw_range_x_min, d->hw_range_x_max, d->hw_range_y_min, d->hw_range_y_max);
316 bool grabSuccess = !ioctl(m_fd, EVIOCGRAB, (
void *) 1);
318 ioctl(m_fd, EVIOCGRAB, (
void *) 0);
320 qWarning(
"evdevtouch: The device is grabbed by another process. No events will be read.");
323 d->m_rotate = QTransform::fromTranslate(0.5, 0.5).rotate(rotationAngle).translate(-0.5, -0.5);
326 d->m_rotate *= QTransform::fromTranslate(0.5, 0.5).scale(-1.0, 1.0).translate(-0.5, -0.5);
329 d->m_rotate *= QTransform::fromTranslate(0.5, 0.5).scale(1.0, -1.0).translate(-0.5, -0.5);
333 d->m_screenName = mapping->screenNameForDeviceNode(d->deviceNode);
334 if (!d->m_screenName.isEmpty())
335 qCDebug(qLcEvdevTouch,
"evdevtouch: Mapping device %ls to screen %ls",
336 qUtf16Printable(d->deviceNode), qUtf16Printable(d->m_screenName));
339 registerPointingDevice();
346 mtdev_close(m_mtdev);
356 unregisterPointingDevice();
361 return d && d->m_filtered;
371 ::input_event buffer[32];
377 events = mtdev_get(m_mtdev, m_fd, buffer,
sizeof(buffer) /
sizeof(::input_event));
381 }
while (events == -1 && errno == EINTR);
388 for (
int i = 0; i < events; ++i)
389 d->processInputEvent(&buffer[i]);
396 events = QT_READ(m_fd,
reinterpret_cast<
char*>(buffer) + n,
sizeof(buffer) - n);
400 if (n %
sizeof(::input_event) == 0)
404 n /=
sizeof(::input_event);
406 for (
int i = 0; i < n; ++i)
407 d->processInputEvent(&buffer[i]);
413 qWarning(
"evdevtouch: Got EOF from input device");
415 }
else if (events < 0) {
416 if (errno != EINTR && errno != EAGAIN) {
417 qErrnoWarning(
"evdevtouch: Could not read from input device");
418 if (errno == ENODEV) {
425 unregisterPointingDevice();
435 qCDebug(qLcEvdevTouch,
"evdevtouch: Device already registered, skipping");
439 qCDebug(qLcEvdevTouch,
"evdevtouch: Registering pointing device for %ls", qUtf16Printable(d->deviceNode));
442 QPointingDevice::Capabilities caps = QPointingDevice::Capability::Position | QPointingDevice::Capability::Area;
443 if (d->hw_pressure_max > d->hw_pressure_min)
444 caps.setFlag(QPointingDevice::Capability::Pressure);
449 qCDebug(qLcEvdevTouch,
"evdevtouch: Registering type B device with multiple tool types");
451 for (
int toolType : { MT_TOOL_FINGER, MT_TOOL_PEN, MT_TOOL_PALM }) {
452 auto pointerType = d->mapToolTypeToPointerType(toolType);
453 QString deviceName = d->hw_name;
458 deviceName += QStringLiteral(
" (Pen)");
461 deviceName += QStringLiteral(
" (Palm)");
465 deviceName += QStringLiteral(
" (Finger)");
470 auto device =
new QPointingDevice(deviceName, id++,
471 QInputDevice::DeviceType::TouchScreen, pointerType,
474 auto geom = d->screenGeometry();
476 QPointingDevicePrivate::get(device)->setAvailableVirtualGeometry(geom);
478 d->m_devices[toolType] = device;
479 QWindowSystemInterface::registerInputDevice(device);
480 qCDebug(qLcEvdevTouch,
"evdevtouch: Registered device %ls (toolType: %d, pointerType: %d)",
481 qUtf16Printable(deviceName), toolType,
static_cast<
int>(pointerType));
485 m_device = d->getDeviceForToolType(MT_TOOL_FINGER);
487 qCDebug(qLcEvdevTouch,
"evdevtouch: Registering type A device (single device)");
490 m_device =
new QPointingDevice(d->hw_name, id++,
491 QInputDevice::DeviceType::TouchScreen, QPointingDevice::PointerType::Finger,
494 auto geom = d->screenGeometry();
496 QPointingDevicePrivate::get(m_device)->setAvailableVirtualGeometry(geom);
498 QWindowSystemInterface::registerInputDevice(m_device);
499 qCDebug(qLcEvdevTouch,
"evdevtouch: Registered single device %ls",
500 qUtf16Printable(d->hw_name));
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
535 if (!m_device && d->m_devices.isEmpty())
538 if (QGuiApplication::instance()) {
540 for (
auto device : d->m_devices) {
542 device.second->moveToThread(QGuiApplication::instance()->thread());
543 device.second->deleteLater();
546 d->m_devices.clear();
550 m_device->moveToThread(QGuiApplication::instance()->thread());
551 m_device->deleteLater();
555 for (
auto device : d->m_devices) {
557 delete device.second;
559 d->m_devices.clear();
568void QEvdevTouchScreenData::addTouchPoint(
const Contact &contact, QEventPoint::States *combinedStates)
570 QWindowSystemInterface::TouchPoint tp;
571 tp.id = contact.trackingId;
572 tp.state = contact.state;
573 *combinedStates |= tp.state;
576 tp.area = QRectF(0, 0, contact.maj, contact.maj);
577 tp.area.moveCenter(QPoint(contact.x, contact.y));
578 tp.pressure = contact.pressure;
581 tp.normalPosition = QPointF((contact.x - hw_range_x_min) / qreal(hw_range_x_max - hw_range_x_min),
582 (contact.y - hw_range_y_min) / qreal(hw_range_y_max - hw_range_y_min));
584 if (!m_rotate.isIdentity())
585 tp.normalPosition = m_rotate.map(tp.normalPosition);
587 tp.rawPositions.append(QPointF(contact.x, contact.y));
589 m_touchPoints.append(tp);
592void QEvdevTouchScreenData::processInputEvent(input_event *data)
594 qCDebug(qLcEvdevTouch,
"evdevtouch: Processing input event. type: %d, code: %d, value: %d",
595 data->type, data->code, data->value);
597 if (data->type ==
EV_ABS) {
600 m_currentData.x = qBound(hw_range_x_min, data->value, hw_range_x_max);
602 m_contacts[m_currentSlot].x = m_currentData.x;
604 m_contacts[m_currentSlot].x = m_currentData.x;
605 if (m_contacts[m_currentSlot].state == QEventPoint::State::Stationary)
606 m_contacts[m_currentSlot].state = QEventPoint::State::Updated;
609 m_currentData.y = qBound(hw_range_y_min, data->value, hw_range_y_max);
611 m_contacts[m_currentSlot].y = m_currentData.y;
613 m_contacts[m_currentSlot].y = m_currentData.y;
614 if (m_contacts[m_currentSlot].state == QEventPoint::State::Stationary)
615 m_contacts[m_currentSlot].state = QEventPoint::State::Updated;
618 m_currentData.trackingId = data->value;
620 if (m_currentData.trackingId == -1) {
621 m_contacts[m_currentSlot].state = QEventPoint::State::Released;
623 m_contacts[m_currentSlot].state = QEventPoint::State::Pressed;
624 m_contacts[m_currentSlot].trackingId = m_currentData.trackingId;
628 m_currentData.maj = data->value;
629 if (data->value == 0)
630 m_currentData.state = QEventPoint::State::Released;
632 m_contacts[m_currentSlot].maj = m_currentData.maj;
633 }
else if (data->code == ABS_PRESSURE || data->code ==
ABS_MT_PRESSURE) {
634 if (Q_UNLIKELY(qLcEvents().isDebugEnabled()))
635 qCDebug(qLcEvents,
"EV_ABS code 0x%x: pressure %d; bounding to [%d,%d]",
636 data->code, data->value, hw_pressure_min, hw_pressure_max);
637 m_currentData.pressure = qBound(hw_pressure_min, data->value, hw_pressure_max);
638 if (m_typeB || m_singleTouch)
639 m_contacts[m_currentSlot].pressure = m_currentData.pressure;
641 m_currentSlot = data->value;
642 }
else if (data->code == ABS_MT_TOOL_TYPE) {
643 m_currentData.toolType = data->value;
645 switch (m_currentData.toolType) {
649 m_contacts[m_currentSlot].toolType = m_currentData.toolType;
652 qCWarning(qLcEvents,
"unhandled tool type 0x%x", m_currentData.toolType);
658 }
else if (data->type ==
EV_KEY && !m_typeB) {
659 if (data->code ==
BTN_TOUCH && data->value == 0)
660 m_contacts[m_currentSlot].state = QEventPoint::State::Released;
665 int key = m_currentData.trackingId;
667 key = m_contacts.size();
669 m_contacts.insert(key, m_currentData);
670 m_currentData = Contact();
675 if (!m_contacts.isEmpty() && m_contacts.constBegin().value().trackingId == -1)
678 std::unique_lock<QMutex> locker;
680 locker = std::unique_lock<QMutex>{m_mutex};
683 m_lastTimeStamp = m_timeStamp;
686 m_lastTouchPoints = m_touchPoints;
687 m_touchPoints.clear();
688 QEventPoint::States combinedStates;
689 bool hasPressure =
false;
691 for (
auto it = m_contacts.begin(), end = m_contacts.end(); it != end; ) {
692 Contact &contact(it.value());
694 if (!contact.state) {
699 int key = m_typeB ? it.key() : contact.trackingId;
700 if (!m_typeB && m_lastContacts.contains(key)) {
701 const Contact &prev(m_lastContacts.value(key));
702 if (contact.state == QEventPoint::State::Released) {
706 contact.maj = prev.maj;
707 contact.toolType = prev.toolType;
709 contact.state = (prev.x == contact.x && prev.y == contact.y)
710 ? QEventPoint::State::Stationary : QEventPoint::State::Updated;
715 if (!m_typeB && contact.state == QEventPoint::State::Released
716 && !m_lastContacts.contains(key)) {
717 it = m_contacts.erase(it);
721 if (contact.pressure)
724 addTouchPoint(contact, &combinedStates);
729 for (
auto it = m_lastContacts.begin(), end = m_lastContacts.end(); it != end; ++it) {
730 Contact &contact(it.value());
731 int key = m_typeB ? it.key() : contact.trackingId;
733 if (contact.trackingId != m_contacts[key].trackingId && contact.state) {
734 contact.state = QEventPoint::State::Released;
735 addTouchPoint(contact, &combinedStates);
738 if (!m_contacts.contains(key)) {
739 contact.state = QEventPoint::State::Released;
740 addTouchPoint(contact, &combinedStates);
746 for (
auto it = m_contacts.begin(), end = m_contacts.end(); it != end; ) {
747 Contact &contact(it.value());
749 if (!contact.state) {
754 if (contact.state == QEventPoint::State::Released) {
756 contact.state = QEventPoint::State::Unknown;
758 it = m_contacts.erase(it);
762 contact.state = QEventPoint::State::Stationary;
767 m_lastContacts = m_contacts;
768 if (!m_typeB && !m_singleTouch)
772 if (!m_touchPoints.isEmpty() && (hasPressure || combinedStates != QEventPoint::State::Stationary))
776 m_lastEventType = data->type;
779int QEvdevTouchScreenData::findClosestContact(
const QHash<
int, Contact> &contacts,
int x,
int y,
int *dist)
781 int minDist = -1, id = -1;
782 for (QHash<
int, Contact>::const_iterator it = contacts.constBegin(), ite = contacts.constEnd();
784 const Contact &contact(it.value());
785 int dx = x - contact.x;
786 int dy = y - contact.y;
787 int dist = dx * dx + dy * dy;
788 if (minDist == -1 || dist < minDist) {
790 id = contact.trackingId;
798void QEvdevTouchScreenData::assignIds()
800 QHash<
int, Contact> candidates = m_lastContacts, pending = m_contacts, newContacts;
802 QHash<
int, Contact>::iterator it, ite, bestMatch;
803 while (!pending.isEmpty() && !candidates.isEmpty()) {
804 int bestDist = -1, bestId = 0;
805 for (it = pending.begin(), ite = pending.end(); it != ite; ++it) {
807 int id = findClosestContact(candidates, it->x, it->y, &dist);
808 if (id >= 0 && (bestDist == -1 || dist < bestDist)) {
815 bestMatch->trackingId = bestId;
816 newContacts.insert(bestId, *bestMatch);
817 candidates.remove(bestId);
818 pending.erase(bestMatch);
823 if (candidates.isEmpty()) {
824 for (it = pending.begin(), ite = pending.end(); it != ite; ++it) {
825 it->trackingId = ++maxId;
826 newContacts.insert(it->trackingId, *it);
829 m_contacts = newContacts;
832QPointingDevice *QEvdevTouchScreenData::getDeviceForToolType(
int toolType)
838 return m_devices.value(toolType);
844QPointingDevice::PointerType QEvdevTouchScreenData::mapToolTypeToPointerType(
int toolType)
848 return QPointingDevice::PointerType::Pen;
850 return QPointingDevice::PointerType::Palm;
853 return QPointingDevice::PointerType::Finger;
857QRect QEvdevTouchScreenData::screenGeometry()
const
859 if (m_forceToActiveWindow) {
860 QWindow *win = QGuiApplication::focusWindow();
861 return win ? QHighDpi::toNativeWindowGeometry(win->geometry(), win) : QRect();
874 QScreen *screen = QGuiApplication::primaryScreen();
875 if (!m_screenName.isEmpty()) {
877 const QList<QScreen *> screens = QGuiApplication::screens();
878 for (QScreen *s : screens) {
879 if (s->name() == m_screenName) {
888 return screen ? QHighDpi::toNativePixels(screen->geometry(), screen) : QRect();
891void QEvdevTouchScreenData::reportPoints()
893 qCDebug(qLcEvdevTouch,
"evdevtouch: Reporting points");
895 QRect winRect = screenGeometry();
896 if (winRect.isNull())
899 const int hw_w = hw_range_x_max - hw_range_x_min;
900 const int hw_h = hw_range_y_max - hw_range_y_min;
904 const int pointCount = m_touchPoints.size();
905 qCDebug(qLcEvdevTouch,
"evdevtouch: pointCount %d", pointCount);
906 for (
int i = 0; i < pointCount; ++i) {
907 QWindowSystemInterface::TouchPoint &tp(m_touchPoints[i]);
912 const qreal wx = winRect.left() + tp.normalPosition.x() * (winRect.width() - 1);
913 const qreal wy = winRect.top() + tp.normalPosition.y() * (winRect.height() - 1);
914 const qreal sizeRatio = (winRect.width() + winRect.height()) / qreal(hw_w + hw_h);
915 if (tp.area.width() == -1)
916 tp.area = QRectF(0, 0, 8, 8);
918 tp.area = QRectF(0, 0, tp.area.width() * sizeRatio, tp.area.height() * sizeRatio);
919 tp.area.moveCenter(QPointF(wx, wy));
922 if (!hw_pressure_min && !hw_pressure_max)
923 tp.pressure = tp.state == QEventPoint::State::Released ? 0 : 1;
925 tp.pressure = (tp.pressure - hw_pressure_min) / qreal(hw_pressure_max - hw_pressure_min);
927 if (Q_UNLIKELY(qLcEvents().isDebugEnabled()))
928 qCDebug(qLcEvents) <<
"reporting" << tp;
931 qCDebug(qLcEvdevTouch,
"evdevtouch: m_typeB %d", m_typeB);
933 if (m_typeB && !m_devices.isEmpty()) {
934 m_pointsByToolType.clear();
937 for (
const auto &tp : m_touchPoints) {
939 int toolType = MT_TOOL_FINGER;
940 for (
auto it = m_contacts.begin(); it != m_contacts.end(); ++it) {
941 const Contact &contact = it.value();
942 if (contact.trackingId == tp.id) {
943 toolType = contact.toolType;
951 m_pointsByToolType[toolType].append(tp);
954 qCWarning(qLcEvents,
"unhandled tool type 0x%x", toolType);
960 for (
auto it = m_pointsByToolType.begin(); it != m_pointsByToolType.end(); ++it) {
961 int toolType = it.key();
962 const QList<QWindowSystemInterface::TouchPoint> &points = it.value();
964 QPointingDevice *device = getDeviceForToolType(toolType);
965 qCDebug(qLcEvdevTouch,
"evdevtouch: device %ls, pointerType %ls, points %lld", qUtf16Printable(device->name()), qUtf16Printable(QVariant::fromValue(device->pointerType()).toString()), qlonglong{points.size()});
966 if (device && !points.isEmpty()) {
968 emit q->touchPointsUpdated();
970 QWindowSystemInterface::handleTouchEvent(
nullptr, device, points);
976 emit q->touchPointsUpdated();
978 QWindowSystemInterface::handleTouchEvent(
nullptr, q->touchDevice(), m_touchPoints);
983 : QDaemonThread(parent), m_device(device), m_spec(spec), m_handler(
nullptr), m_touchDeviceRegistered(
false)
984 , m_touchUpdatePending(
false)
985 , m_filterWindow(
nullptr)
999 m_handler =
new QEvdevTouchScreenHandler(m_device, m_spec);
1005 QMetaObject::invokeMethod(
this,
"notifyTouchDeviceRegistered", Qt::QueuedConnection);
1010 m_handler =
nullptr;
1015 return m_touchDeviceRegistered;
1020 m_touchDeviceRegistered =
true;
1021 emit touchDeviceRegistered();
1026 QWindow *window = QGuiApplication::focusWindow();
1027 if (window != m_filterWindow) {
1029 m_filterWindow->removeEventFilter(
this);
1030 m_filterWindow = window;
1032 m_filterWindow->installEventFilter(
this);
1034 if (m_filterWindow) {
1035 m_touchUpdatePending =
true;
1036 m_filterWindow->requestUpdate();
1042 if (m_touchUpdatePending && object == m_filterWindow && event->type() == QEvent::UpdateRequest) {
1043 m_touchUpdatePending =
false;
1044 filterAndSendTouchPoints();
1051 QRect winRect = m_handler->d->screenGeometry();
1052 if (winRect.isNull())
1055 float vsyncDelta = 1.0f / QGuiApplication::primaryScreen()->refreshRate();
1057 QHash<
int, FilteredTouchPoint> filteredPoints;
1059 m_handler->d->m_mutex.lock();
1061 double time = m_handler->d->m_timeStamp;
1062 double lastTime = m_handler->d->m_lastTimeStamp;
1063 double touchDelta = time - lastTime;
1064 if (m_touchRate < 0 || touchDelta > vsyncDelta) {
1071 if (m_touchRate < 0)
1072 m_touchRate = (1.0 / QGuiApplication::primaryScreen()->refreshRate()) / 2.0;
1078 const double ratio = 0.9;
1079 m_touchRate = sqrt(m_touchRate * m_touchRate * ratio + touchDelta * touchDelta * (1.0 - ratio));
1082 QList<QWindowSystemInterface::TouchPoint> points = m_handler->d->m_touchPoints;
1083 QList<QWindowSystemInterface::TouchPoint> lastPoints = m_handler->d->m_lastTouchPoints;
1085 m_handler->d->m_mutex.unlock();
1087 for (
int i=0; i<points.size(); ++i) {
1088 QWindowSystemInterface::TouchPoint &tp = points[i];
1089 QPointF pos = tp.normalPosition;
1090 FilteredTouchPoint f;
1092 QWindowSystemInterface::TouchPoint ltp;
1094 for (
int j=0; j<lastPoints.size(); ++j) {
1095 if (lastPoints.at(j).id == tp.id) {
1096 ltp = lastPoints.at(j);
1102 if (lastTime != 0 && ltp.id >= 0)
1103 velocity = (pos - ltp.normalPosition) / m_touchRate;
1104 if (m_filteredPoints.contains(tp.id)) {
1105 f = m_filteredPoints.take(tp.id);
1106 f.x.update(pos.x(), velocity.x(), vsyncDelta);
1107 f.y.update(pos.y(), velocity.y(), vsyncDelta);
1108 pos = QPointF(f.x.position(), f.y.position());
1110 f.x.initialize(pos.x(), velocity.x());
1111 f.y.initialize(pos.y(), velocity.y());
1114 if (tp.state != QEventPoint::State::Pressed)
1115 tp.state = QEventPoint::State::Pressed;
1118 tp.velocity = QVector2D(f.x.velocity() * winRect.width(), f.y.velocity() * winRect.height());
1120 qreal filteredNormalizedX = f.x.position() + f.x.velocity() * m_handler->d->m_prediction / 1000.0;
1121 qreal filteredNormalizedY = f.y.position() + f.y.velocity() * m_handler->d->m_prediction / 1000.0;
1124 tp.normalPosition = QPointF(qBound<qreal>(0, filteredNormalizedX, 1),
1125 qBound<qreal>(0, filteredNormalizedY, 1));
1127 qreal x = winRect.x() + (tp.normalPosition.x() * (winRect.width() - 1));
1128 qreal y = winRect.y() + (tp.normalPosition.y() * (winRect.height() - 1));
1130 tp.area.moveCenter(QPointF(x, y));
1137 if (tp.state != QEventPoint::State::Released)
1138 filteredPoints[tp.id] = f;
1141 for (QHash<
int, FilteredTouchPoint>::const_iterator it = m_filteredPoints.constBegin(), end = m_filteredPoints.constEnd(); it != end; ++it) {
1142 const FilteredTouchPoint &f = it.value();
1143 QWindowSystemInterface::TouchPoint tp = f.touchPoint;
1144 tp.state = QEventPoint::State::Released;
1145 tp.velocity = QVector2D();
1149 m_filteredPoints = filteredPoints;
1151 QWindowSystemInterface::handleTouchEvent(
nullptr,
1152 m_handler->touchDevice(),
1159#include "moc_qevdevtouchhandler_p.cpp"
bool eventFilter(QObject *object, QEvent *event) override
void scheduleTouchPointUpdate()
bool isPointingDeviceRegistered() const
~QEvdevTouchScreenHandlerThread()
QPointingDevice * touchDevice() const
~QEvdevTouchScreenHandler()
static QOutputMapping * get()
#define ABS_MT_POSITION_X
#define ABS_MT_POSITION_Y
#define ABS_MT_TRACKING_ID
#define ABS_MT_TOUCH_MAJOR
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")