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;
98 QEventPoint::State state = QEventPoint::State::Pressed;
100 QHash<
int, Contact> m_contacts;
101 QHash<
int, Contact> m_lastContacts;
102 Contact m_currentData;
106 double m_lastTimeStamp;
108 int findClosestContact(
const QHash<
int, Contact> &contacts,
int x,
int y,
int *dist);
109 void addTouchPoint(
const Contact &contact, QEventPoint::States *combinedStates);
111 void loadMultiScreenMappings();
113 QRect screenGeometry()
const;
123 bool m_forceToActiveWindow;
127 QString m_screenName;
128 mutable QPointer<QScreen> m_screen;
143QEvdevTouchScreenData::QEvdevTouchScreenData(QEvdevTouchScreenHandler *q_ptr,
const QStringList &args)
147 m_timeStamp(0), m_lastTimeStamp(0),
148 hw_range_x_min(0), hw_range_x_max(0),
149 hw_range_y_min(0), hw_range_y_max(0),
150 hw_pressure_min(0), hw_pressure_max(0),
151 m_forceToActiveWindow(
false), m_typeB(
false), m_singleTouch(
false),
152 m_filtered(
false), m_prediction(0)
154 for (
const QString &arg : args) {
155 if (arg == u"force_window")
156 m_forceToActiveWindow =
true;
157 else if (arg == u"filtered")
159 else if (
const QStringView prefix = u"prediction="; arg.startsWith(prefix))
160 m_prediction = QStringView(arg).mid(prefix.size()).toInt();
164#define LONG_BITS (sizeof(long) << 3
)
168static inline bool testBit(
long bit,
const long *array)
170 return (array[bit / LONG_BITS] >> bit % LONG_BITS) & 1;
175 : QObject(parent), m_notify(
nullptr), m_fd(-1), d(
nullptr), m_device(
nullptr)
180 setObjectName(
"Evdev Touch Handler"_L1);
182 const QStringList args = spec.split(u':');
183 int rotationAngle = 0;
184 bool invertx =
false;
185 bool inverty =
false;
186 for (
int i = 0; i < args.size(); ++i) {
187 if (args.at(i).startsWith(
"rotate"_L1)) {
188 QString rotateArg = args.at(i).section(u'=', 1, 1);
190 uint argValue = rotateArg.toUInt(&ok);
196 rotationAngle = argValue;
202 }
else if (args.at(i) ==
"invertx"_L1) {
204 }
else if (args.at(i) ==
"inverty"_L1) {
209 qCDebug(qLcEvdevTouch,
"evdevtouch: Using device %ls", qUtf16Printable(device));
211 m_fd = QT_OPEN(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);
214 m_notify =
new QSocketNotifier(m_fd, QSocketNotifier::Read,
this);
215 connect(m_notify, &QSocketNotifier::activated,
this, &QEvdevTouchScreenHandler::readData);
217 qErrnoWarning(
"evdevtouch: Cannot open input device %ls", qUtf16Printable(device));
222 m_mtdev =
static_cast<mtdev *>(calloc(1,
sizeof(mtdev)));
223 int mtdeverr = mtdev_open(m_mtdev, m_fd);
225 qWarning(
"evdevtouch: mtdev_open failed: %d", mtdeverr);
232 d =
new QEvdevTouchScreenData(
this, args);
235 const char *mtdevStr =
"(mtdev)";
238 const char *mtdevStr =
"";
240 if (ioctl(m_fd, EVIOCGBIT(
EV_ABS,
sizeof(absbits)), absbits) >= 0) {
246 d->deviceNode = device;
247 qCDebug(qLcEvdevTouch,
248 "evdevtouch: %ls: Protocol type %c %s (%s), filtered=%s",
249 qUtf16Printable(d->deviceNode),
250 d->m_typeB ?
'B' :
'A', mtdevStr,
251 d->m_singleTouch ?
"single" :
"multi",
252 d->m_filtered ?
"yes" :
"no");
254 qCDebug(qLcEvdevTouch,
" - prediction=%d", d->m_prediction);
256 input_absinfo absInfo;
257 memset(&absInfo, 0,
sizeof(input_absinfo));
258 bool has_x_range =
false, has_y_range =
false;
261 qCDebug(qLcEvdevTouch,
"evdevtouch: %ls: min X: %d max X: %d", qUtf16Printable(device),
262 absInfo.minimum, absInfo.maximum);
263 d->hw_range_x_min = absInfo.minimum;
264 d->hw_range_x_max = absInfo.maximum;
269 qCDebug(qLcEvdevTouch,
"evdevtouch: %ls: min Y: %d max Y: %d", qUtf16Printable(device),
270 absInfo.minimum, absInfo.maximum);
271 d->hw_range_y_min = absInfo.minimum;
272 d->hw_range_y_max = absInfo.maximum;
276 if (!has_x_range || !has_y_range)
277 qWarning(
"evdevtouch: %ls: Invalid ABS limits, behavior unspecified", qUtf16Printable(device));
279 if (ioctl(m_fd, EVIOCGABS(ABS_PRESSURE), &absInfo) >= 0) {
280 qCDebug(qLcEvdevTouch,
"evdevtouch: %ls: min pressure: %d max pressure: %d", qUtf16Printable(device),
281 absInfo.minimum, absInfo.maximum);
282 if (absInfo.maximum > absInfo.minimum) {
283 d->hw_pressure_min = absInfo.minimum;
284 d->hw_pressure_max = absInfo.maximum;
289 if (ioctl(m_fd, EVIOCGNAME(
sizeof(name) - 1), name) >= 0) {
290 d->hw_name = QString::fromLocal8Bit(name);
291 qCDebug(qLcEvdevTouch,
"evdevtouch: %ls: device name: %s", qUtf16Printable(device), name);
295 if (d->hw_name ==
"ti-tsc"_L1) {
296 if (d->hw_range_x_min == 0 && d->hw_range_x_max == 4095) {
297 d->hw_range_x_min = 165;
298 d->hw_range_x_max = 4016;
300 if (d->hw_range_y_min == 0 && d->hw_range_y_max == 4095) {
301 d->hw_range_y_min = 220;
302 d->hw_range_y_max = 3907;
304 qCDebug(qLcEvdevTouch,
"evdevtouch: found ti-tsc, overriding: min X: %d max X: %d min Y: %d max Y: %d",
305 d->hw_range_x_min, d->hw_range_x_max, d->hw_range_y_min, d->hw_range_y_max);
308 bool grabSuccess = !ioctl(m_fd, EVIOCGRAB, (
void *) 1);
310 ioctl(m_fd, EVIOCGRAB, (
void *) 0);
312 qWarning(
"evdevtouch: The device is grabbed by another process. No events will be read.");
315 d->m_rotate = QTransform::fromTranslate(0.5, 0.5).rotate(rotationAngle).translate(-0.5, -0.5);
318 d->m_rotate *= QTransform::fromTranslate(0.5, 0.5).scale(-1.0, 1.0).translate(-0.5, -0.5);
321 d->m_rotate *= QTransform::fromTranslate(0.5, 0.5).scale(1.0, -1.0).translate(-0.5, -0.5);
325 d->m_screenName = mapping->screenNameForDeviceNode(d->deviceNode);
326 if (!d->m_screenName.isEmpty())
327 qCDebug(qLcEvdevTouch,
"evdevtouch: Mapping device %ls to screen %ls",
328 qUtf16Printable(d->deviceNode), qUtf16Printable(d->m_screenName));
331 registerPointingDevice();
338 mtdev_close(m_mtdev);
348 unregisterPointingDevice();
353 return d && d->m_filtered;
363 ::input_event buffer[32];
369 events = mtdev_get(m_mtdev, m_fd, buffer,
sizeof(buffer) /
sizeof(::input_event));
373 }
while (events == -1 && errno == EINTR);
380 for (
int i = 0; i < events; ++i)
381 d->processInputEvent(&buffer[i]);
388 events = QT_READ(m_fd,
reinterpret_cast<
char*>(buffer) + n,
sizeof(buffer) - n);
392 if (n %
sizeof(::input_event) == 0)
396 n /=
sizeof(::input_event);
398 for (
int i = 0; i < n; ++i)
399 d->processInputEvent(&buffer[i]);
405 qWarning(
"evdevtouch: Got EOF from input device");
407 }
else if (events < 0) {
408 if (errno != EINTR && errno != EAGAIN) {
409 qErrnoWarning(
"evdevtouch: Could not read from input device");
410 if (errno == ENODEV) {
417 unregisterPointingDevice();
430 QPointingDevice::Capabilities caps = QPointingDevice::Capability::Position | QPointingDevice::Capability::Area;
431 if (d->hw_pressure_max > d->hw_pressure_min)
432 caps.setFlag(QPointingDevice::Capability::Pressure);
435 m_device =
new QPointingDevice(d->hw_name, id++,
436 QInputDevice::DeviceType::TouchScreen, QPointingDevice::PointerType::Finger,
439 auto geom = d->screenGeometry();
441 QPointingDevicePrivate::get(m_device)->setAvailableVirtualGeometry(geom);
443 QWindowSystemInterface::registerInputDevice(m_device);
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
480 if (QGuiApplication::instance()) {
481 m_device->moveToThread(QGuiApplication::instance()->thread());
482 m_device->deleteLater();
489void QEvdevTouchScreenData::addTouchPoint(
const Contact &contact, QEventPoint::States *combinedStates)
491 QWindowSystemInterface::TouchPoint tp;
492 tp.id = contact.trackingId;
493 tp.state = contact.state;
494 *combinedStates |= tp.state;
497 tp.area = QRectF(0, 0, contact.maj, contact.maj);
498 tp.area.moveCenter(QPoint(contact.x, contact.y));
499 tp.pressure = contact.pressure;
502 tp.normalPosition = QPointF((contact.x - hw_range_x_min) / qreal(hw_range_x_max - hw_range_x_min),
503 (contact.y - hw_range_y_min) / qreal(hw_range_y_max - hw_range_y_min));
505 if (!m_rotate.isIdentity())
506 tp.normalPosition = m_rotate.map(tp.normalPosition);
508 tp.rawPositions.append(QPointF(contact.x, contact.y));
510 m_touchPoints.append(tp);
513void QEvdevTouchScreenData::processInputEvent(input_event *data)
515 if (data->type ==
EV_ABS) {
518 m_currentData.x = qBound(hw_range_x_min, data->value, hw_range_x_max);
520 m_contacts[m_currentSlot].x = m_currentData.x;
522 m_contacts[m_currentSlot].x = m_currentData.x;
523 if (m_contacts[m_currentSlot].state == QEventPoint::State::Stationary)
524 m_contacts[m_currentSlot].state = QEventPoint::State::Updated;
527 m_currentData.y = qBound(hw_range_y_min, data->value, hw_range_y_max);
529 m_contacts[m_currentSlot].y = m_currentData.y;
531 m_contacts[m_currentSlot].y = m_currentData.y;
532 if (m_contacts[m_currentSlot].state == QEventPoint::State::Stationary)
533 m_contacts[m_currentSlot].state = QEventPoint::State::Updated;
536 m_currentData.trackingId = data->value;
538 if (m_currentData.trackingId == -1) {
539 m_contacts[m_currentSlot].state = QEventPoint::State::Released;
541 m_contacts[m_currentSlot].state = QEventPoint::State::Pressed;
542 m_contacts[m_currentSlot].trackingId = m_currentData.trackingId;
546 m_currentData.maj = data->value;
547 if (data->value == 0)
548 m_currentData.state = QEventPoint::State::Released;
550 m_contacts[m_currentSlot].maj = m_currentData.maj;
551 }
else if (data->code == ABS_PRESSURE || data->code ==
ABS_MT_PRESSURE) {
552 if (Q_UNLIKELY(qLcEvents().isDebugEnabled()))
553 qCDebug(qLcEvents,
"EV_ABS code 0x%x: pressure %d; bounding to [%d,%d]",
554 data->code, data->value, hw_pressure_min, hw_pressure_max);
555 m_currentData.pressure = qBound(hw_pressure_min, data->value, hw_pressure_max);
556 if (m_typeB || m_singleTouch)
557 m_contacts[m_currentSlot].pressure = m_currentData.pressure;
559 m_currentSlot = data->value;
562 }
else if (data->type ==
EV_KEY && !m_typeB) {
563 if (data->code ==
BTN_TOUCH && data->value == 0)
564 m_contacts[m_currentSlot].state = QEventPoint::State::Released;
569 int key = m_currentData.trackingId;
571 key = m_contacts.size();
573 m_contacts.insert(key, m_currentData);
574 m_currentData = Contact();
579 if (!m_contacts.isEmpty() && m_contacts.constBegin().value().trackingId == -1)
582 std::unique_lock<QMutex> locker;
584 locker = std::unique_lock<QMutex>{m_mutex};
587 m_lastTimeStamp = m_timeStamp;
590 m_lastTouchPoints = m_touchPoints;
591 m_touchPoints.clear();
592 QEventPoint::States combinedStates;
593 bool hasPressure =
false;
595 for (
auto it = m_contacts.begin(), end = m_contacts.end(); it != end; ) {
596 Contact &contact(it.value());
598 if (!contact.state) {
603 int key = m_typeB ? it.key() : contact.trackingId;
604 if (!m_typeB && m_lastContacts.contains(key)) {
605 const Contact &prev(m_lastContacts.value(key));
606 if (contact.state == QEventPoint::State::Released) {
610 contact.maj = prev.maj;
612 contact.state = (prev.x == contact.x && prev.y == contact.y)
613 ? QEventPoint::State::Stationary : QEventPoint::State::Updated;
618 if (!m_typeB && contact.state == QEventPoint::State::Released
619 && !m_lastContacts.contains(key)) {
620 it = m_contacts.erase(it);
624 if (contact.pressure)
627 addTouchPoint(contact, &combinedStates);
632 for (
auto it = m_lastContacts.begin(), end = m_lastContacts.end(); it != end; ++it) {
633 Contact &contact(it.value());
634 int key = m_typeB ? it.key() : contact.trackingId;
636 if (contact.trackingId != m_contacts[key].trackingId && contact.state) {
637 contact.state = QEventPoint::State::Released;
638 addTouchPoint(contact, &combinedStates);
641 if (!m_contacts.contains(key)) {
642 contact.state = QEventPoint::State::Released;
643 addTouchPoint(contact, &combinedStates);
649 for (
auto it = m_contacts.begin(), end = m_contacts.end(); it != end; ) {
650 Contact &contact(it.value());
652 if (!contact.state) {
657 if (contact.state == QEventPoint::State::Released) {
659 contact.state = QEventPoint::State::Unknown;
661 it = m_contacts.erase(it);
665 contact.state = QEventPoint::State::Stationary;
670 m_lastContacts = m_contacts;
671 if (!m_typeB && !m_singleTouch)
675 if (!m_touchPoints.isEmpty() && (hasPressure || combinedStates != QEventPoint::State::Stationary))
679 m_lastEventType = data->type;
682int QEvdevTouchScreenData::findClosestContact(
const QHash<
int, Contact> &contacts,
int x,
int y,
int *dist)
684 int minDist = -1, id = -1;
685 for (QHash<
int, Contact>::const_iterator it = contacts.constBegin(), ite = contacts.constEnd();
687 const Contact &contact(it.value());
688 int dx = x - contact.x;
689 int dy = y - contact.y;
690 int dist = dx * dx + dy * dy;
691 if (minDist == -1 || dist < minDist) {
693 id = contact.trackingId;
701void QEvdevTouchScreenData::assignIds()
703 QHash<
int, Contact> candidates = m_lastContacts, pending = m_contacts, newContacts;
705 QHash<
int, Contact>::iterator it, ite, bestMatch;
706 while (!pending.isEmpty() && !candidates.isEmpty()) {
707 int bestDist = -1, bestId = 0;
708 for (it = pending.begin(), ite = pending.end(); it != ite; ++it) {
710 int id = findClosestContact(candidates, it->x, it->y, &dist);
711 if (id >= 0 && (bestDist == -1 || dist < bestDist)) {
718 bestMatch->trackingId = bestId;
719 newContacts.insert(bestId, *bestMatch);
720 candidates.remove(bestId);
721 pending.erase(bestMatch);
726 if (candidates.isEmpty()) {
727 for (it = pending.begin(), ite = pending.end(); it != ite; ++it) {
728 it->trackingId = ++maxId;
729 newContacts.insert(it->trackingId, *it);
732 m_contacts = newContacts;
735QRect QEvdevTouchScreenData::screenGeometry()
const
737 if (m_forceToActiveWindow) {
738 QWindow *win = QGuiApplication::focusWindow();
739 return win ? QHighDpi::toNativeWindowGeometry(win->geometry(), win) : QRect();
752 QScreen *screen = QGuiApplication::primaryScreen();
753 if (!m_screenName.isEmpty()) {
755 const QList<QScreen *> screens = QGuiApplication::screens();
756 for (QScreen *s : screens) {
757 if (s->name() == m_screenName) {
766 return screen ? QHighDpi::toNativePixels(screen->geometry(), screen) : QRect();
769void QEvdevTouchScreenData::reportPoints()
771 QRect winRect = screenGeometry();
772 if (winRect.isNull())
775 const int hw_w = hw_range_x_max - hw_range_x_min;
776 const int hw_h = hw_range_y_max - hw_range_y_min;
780 const int pointCount = m_touchPoints.size();
781 for (
int i = 0; i < pointCount; ++i) {
782 QWindowSystemInterface::TouchPoint &tp(m_touchPoints[i]);
787 const qreal wx = winRect.left() + tp.normalPosition.x() * (winRect.width() - 1);
788 const qreal wy = winRect.top() + tp.normalPosition.y() * (winRect.height() - 1);
789 const qreal sizeRatio = (winRect.width() + winRect.height()) / qreal(hw_w + hw_h);
790 if (tp.area.width() == -1)
791 tp.area = QRectF(0, 0, 8, 8);
793 tp.area = QRectF(0, 0, tp.area.width() * sizeRatio, tp.area.height() * sizeRatio);
794 tp.area.moveCenter(QPointF(wx, wy));
797 if (!hw_pressure_min && !hw_pressure_max)
798 tp.pressure = tp.state == QEventPoint::State::Released ? 0 : 1;
800 tp.pressure = (tp.pressure - hw_pressure_min) / qreal(hw_pressure_max - hw_pressure_min);
802 if (Q_UNLIKELY(qLcEvents().isDebugEnabled()))
803 qCDebug(qLcEvents) <<
"reporting" << tp;
808 emit q->touchPointsUpdated();
810 QWindowSystemInterface::handleTouchEvent(
nullptr, q->touchDevice(), m_touchPoints);
814 : QDaemonThread(parent), m_device(device), m_spec(spec), m_handler(
nullptr), m_touchDeviceRegistered(
false)
815 , m_touchUpdatePending(
false)
816 , m_filterWindow(
nullptr)
830 m_handler =
new QEvdevTouchScreenHandler(m_device, m_spec);
832 if (m_handler->isFiltered())
833 connect(m_handler, &QEvdevTouchScreenHandler::touchPointsUpdated,
this, &QEvdevTouchScreenHandlerThread::scheduleTouchPointUpdate);
836 QMetaObject::invokeMethod(
this,
"notifyTouchDeviceRegistered", Qt::QueuedConnection);
846 return m_touchDeviceRegistered;
851 m_touchDeviceRegistered =
true;
852 emit touchDeviceRegistered();
857 QWindow *window = QGuiApplication::focusWindow();
858 if (window != m_filterWindow) {
860 m_filterWindow->removeEventFilter(
this);
861 m_filterWindow = window;
863 m_filterWindow->installEventFilter(
this);
865 if (m_filterWindow) {
866 m_touchUpdatePending =
true;
867 m_filterWindow->requestUpdate();
873 if (m_touchUpdatePending && object == m_filterWindow && event->type() == QEvent::UpdateRequest) {
874 m_touchUpdatePending =
false;
875 filterAndSendTouchPoints();
882 QRect winRect = m_handler->d->screenGeometry();
883 if (winRect.isNull())
886 float vsyncDelta = 1.0f / QGuiApplication::primaryScreen()->refreshRate();
888 QHash<
int, FilteredTouchPoint> filteredPoints;
890 m_handler->d->m_mutex.lock();
892 double time = m_handler->d->m_timeStamp;
893 double lastTime = m_handler->d->m_lastTimeStamp;
894 double touchDelta = time - lastTime;
895 if (m_touchRate < 0 || touchDelta > vsyncDelta) {
903 m_touchRate = (1.0 / QGuiApplication::primaryScreen()->refreshRate()) / 2.0;
909 const double ratio = 0.9;
910 m_touchRate = sqrt(m_touchRate * m_touchRate * ratio + touchDelta * touchDelta * (1.0 - ratio));
913 QList<QWindowSystemInterface::TouchPoint> points = m_handler->d->m_touchPoints;
914 QList<QWindowSystemInterface::TouchPoint> lastPoints = m_handler->d->m_lastTouchPoints;
916 m_handler->d->m_mutex.unlock();
918 for (
int i=0; i<points.size(); ++i) {
919 QWindowSystemInterface::TouchPoint &tp = points[i];
920 QPointF pos = tp.normalPosition;
921 FilteredTouchPoint f;
923 QWindowSystemInterface::TouchPoint ltp;
925 for (
int j=0; j<lastPoints.size(); ++j) {
926 if (lastPoints.at(j).id == tp.id) {
927 ltp = lastPoints.at(j);
933 if (lastTime != 0 && ltp.id >= 0)
934 velocity = (pos - ltp.normalPosition) / m_touchRate;
935 if (m_filteredPoints.contains(tp.id)) {
936 f = m_filteredPoints.take(tp.id);
937 f.x.update(pos.x(), velocity.x(), vsyncDelta);
938 f.y.update(pos.y(), velocity.y(), vsyncDelta);
939 pos = QPointF(f.x.position(), f.y.position());
941 f.x.initialize(pos.x(), velocity.x());
942 f.y.initialize(pos.y(), velocity.y());
945 if (tp.state != QEventPoint::State::Pressed)
946 tp.state = QEventPoint::State::Pressed;
949 tp.velocity = QVector2D(f.x.velocity() * winRect.width(), f.y.velocity() * winRect.height());
951 qreal filteredNormalizedX = f.x.position() + f.x.velocity() * m_handler->d->m_prediction / 1000.0;
952 qreal filteredNormalizedY = f.y.position() + f.y.velocity() * m_handler->d->m_prediction / 1000.0;
955 tp.normalPosition = QPointF(qBound<qreal>(0, filteredNormalizedX, 1),
956 qBound<qreal>(0, filteredNormalizedY, 1));
958 qreal x = winRect.x() + (tp.normalPosition.x() * (winRect.width() - 1));
959 qreal y = winRect.y() + (tp.normalPosition.y() * (winRect.height() - 1));
961 tp.area.moveCenter(QPointF(x, y));
968 if (tp.state != QEventPoint::State::Released)
969 filteredPoints[tp.id] = f;
972 for (QHash<
int, FilteredTouchPoint>::const_iterator it = m_filteredPoints.constBegin(), end = m_filteredPoints.constEnd(); it != end; ++it) {
973 const FilteredTouchPoint &f = it.value();
974 QWindowSystemInterface::TouchPoint tp = f.touchPoint;
975 tp.state = QEventPoint::State::Released;
976 tp.velocity = QVector2D();
980 m_filteredPoints = filteredPoints;
982 QWindowSystemInterface::handleTouchEvent(
nullptr,
983 m_handler->touchDevice(),
990#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")