8#include <QSocketNotifier>
9#include <QGuiApplication>
10#include <QLoggingCategory>
11#include <QtCore/private/qcore_unix_p.h>
12#include <QtGui/qpointingdevice.h>
13#include <QtGui/private/qhighdpiscaling_p.h>
14#include <QtGui/private/qguiapplication_p.h>
15#include <QtGui/private/qpointingdevice_p.h>
17#include <QtCore/qpointer.h>
21#include <qpa/qplatformscreen.h>
24#define EV_SYN EV_DEV_SYN
25#define EV_KEY EV_DEV_KEY
26#define EV_ABS EV_DEV_ABS
27#define ABS_X EV_DEV_PTR_ABS_X
28#define ABS_Y EV_DEV_PTR_ABS_Y
29#define BTN_TOUCH EV_DEV_PTR_BTN_TOUCH
31#define ABS_MT_SLOT EV_DEV_PTR_ABS_MT_SLOT
32#define ABS_MT_POSITION_X EV_DEV_PTR_ABS_MT_POSITION_X
33#define ABS_MT_POSITION_Y EV_DEV_PTR_ABS_MT_POSITION_Y
34#define ABS_MT_TRACKING_ID EV_DEV_PTR_ABS_MT_TRACKING_ID
37#ifndef input_event_sec
38#define input_event_sec time.tv_sec
41#ifndef input_event_usec
42#define input_event_usec time.tv_usec
49using namespace Qt::StringLiterals;
52Q_STATIC_LOGGING_CATEGORY(qLcVxEvents,
"qt.qpa.input.events")
55
56
57#ifndef ABS_MT_TOUCH_MAJOR
58#define ABS_MT_TOUCH_MAJOR 0x30
61#define ABS_MT_POSITION_X 0x35
64#define ABS_MT_POSITION_Y 0x36
67#define ABS_MT_SLOT 0x2f
73#define ABS_MT_TRACKING_ID 0x39
75#ifndef ABS_MT_PRESSURE
76#define ABS_MT_PRESSURE 0x3a
79#define SYN_MT_REPORT 2
82class QVxTouchScreenData
85 QVxTouchScreenData(QVxTouchScreenHandler *q_ptr,
const QStringList &args);
87 void processInputEvent(input_event *data);
90 QVxTouchScreenHandler *q;
92 QList<QWindowSystemInterface::TouchPoint> m_touchPoints;
93 QList<QWindowSystemInterface::TouchPoint> m_lastTouchPoints;
101 QEventPoint::State state = QEventPoint::State::Pressed;
103 QHash<
int, Contact> m_contacts;
104 QHash<
int, Contact> m_lastContacts;
105 Contact m_currentData;
109 double m_lastTimeStamp;
111 int findClosestContact(
const QHash<
int, Contact> &contacts,
int x,
int y,
int *dist);
112 void addTouchPoint(
const Contact &contact, QEventPoint::States *combinedStates);
114 void loadMultiScreenMappings();
116 QRect screenGeometry()
const;
126 bool m_forceToActiveWindow;
130 QString m_screenName;
131 mutable QPointer<QScreen> m_screen;
146QVxTouchScreenData::QVxTouchScreenData(QVxTouchScreenHandler *q_ptr,
const QStringList &args)
150 m_timeStamp(0), m_lastTimeStamp(0),
151 hw_range_x_min(0), hw_range_x_max(0),
152 hw_range_y_min(0), hw_range_y_max(0),
153 hw_pressure_min(0), hw_pressure_max(0),
154 m_forceToActiveWindow(
false), m_typeB(
false), m_singleTouch(
false),
155 m_filtered(
false), m_prediction(0)
157 for (
const QString &arg : args) {
158 if (arg == u"force_window")
159 m_forceToActiveWindow =
true;
160 else if (arg == u"filtered")
162 else if (
const QStringView prefix = u"prediction="; arg.startsWith(prefix))
163 m_prediction = QStringView(arg).mid(prefix.size()).toInt();
167#define LONG_BITS (sizeof(long) << 3
)
171 : QObject(parent), m_notify(
nullptr), m_fd(-1), d(
nullptr), m_device(
nullptr)
173 setObjectName(
"Vx Touch Handler"_L1);
178 auto updateRange = [](
const QString& argString,
int& rangeMin,
int& rangeMax) {
179 QString rangeDefinition = argString.section(u'=', 1, 1);
180 auto rangeMinMax = rangeDefinition.split(u',');
182 if (rangeMinMax.size() != 2)
187 int min = rangeMinMax[0].toUInt(&minOk);
188 int max = rangeMinMax[1].toUInt(&maxOk);
189 if (!minOk || !maxOk)
197 const QStringList args = spec.split(u':');
199 d =
new QVxTouchScreenData(
this, args);
201 int rotationAngle = 0;
202 bool invertx =
false;
203 bool inverty =
false;
204 bool rangeXOverride =
false;
205 bool rangeYOverride =
false;
206 for (
int i = 0; i < args.size(); ++i) {
207 if (args.at(i).startsWith(
"rotate"_L1)) {
208 QString rotateArg = args.at(i).section(u'=', 1, 1);
210 uint argValue = rotateArg.toUInt(&ok);
216 rotationAngle = argValue;
222 }
else if (args.at(i) ==
"invertx"_L1) {
224 }
else if (args.at(i) ==
"inverty"_L1) {
226 }
else if (args.at(i).startsWith(
"rangex"_L1)) {
227 rangeXOverride = updateRange(args.at(i), d->hw_range_x_min, d->hw_range_x_max);
228 }
else if (args.at(i).startsWith(
"rangey"_L1)) {
229 rangeYOverride = updateRange(args.at(i), d->hw_range_y_min, d->hw_range_y_max);
233 qCDebug(qLcVxTouch,
"vxtouch: Using device %ls", qUtf16Printable(device));
235 m_fd = QT_OPEN(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);
238 m_notify =
new QSocketNotifier(m_fd, QSocketNotifier::Read,
this);
239 connect(m_notify, &QSocketNotifier::activated,
this, &QVxTouchScreenHandler::readData);
241 qErrnoWarning(
"vxtouch: Cannot open input device %ls", qUtf16Printable(device));
247 if (ioctl(m_fd, EV_DEV_IO_GET_CAP, (
char *)&devCap) != ERROR) {
248 if (devCap & EV_DEV_ABS_MT)
253 d->m_singleTouch =
true;
255 d->deviceNode = device;
257 "vxtouch: %ls: Protocol type %c (%s), filtered=%s",
258 qUtf16Printable(d->deviceNode),
259 d->m_typeB ?
'B' :
'A',
260 d->m_singleTouch ?
"single" :
"multi",
261 d->m_filtered ?
"yes" :
"no");
263 qCDebug(qLcVxTouch,
" - prediction=%d", d->m_prediction);
265 bool has_x_range =
false, has_y_range =
false;
267 EV_DEV_DEVICE_AXIS_VAL axisVal[2];
268 axisVal[0].axisIndex = 0;
269 axisVal[1].axisIndex = 1;
271 if (!rangeXOverride && ioctl(m_fd, EV_DEV_IO_GET_AXIS_VAL, (
char *)&axisVal[0]) != ERROR) {
272 qCDebug(qLcVxTouch,
"vxtouch: %s: min X: %d max X: %d", qPrintable(device),
273 axisVal[0].minVal, axisVal[0].maxVal);
274 d->hw_range_x_min = axisVal[0].minVal;
275 d->hw_range_x_max = axisVal[0].maxVal;
279 if (!rangeYOverride && ioctl(m_fd, EV_DEV_IO_GET_AXIS_VAL, (
char *)&axisVal[1]) != ERROR) {
280 qCDebug(qLcVxTouch,
"vxtouch: %s: min Y: %d max Y: %d", qPrintable(device),
281 axisVal[1].minVal, axisVal[1].maxVal);
282 d->hw_range_y_min = axisVal[1].minVal;
283 d->hw_range_y_max = axisVal[1].maxVal;
287 if (!has_x_range || !has_y_range)
288 qWarning(
"vxtouch: %ls: Invalid ABS limits, behavior unspecified", qUtf16Printable(device));
291 if (d->hw_name ==
"ti-tsc"_L1) {
292 if (!rangeXOverride && d->hw_range_x_min == 0 && d->hw_range_x_max == 4095) {
293 d->hw_range_x_min = 165;
294 d->hw_range_x_max = 4016;
296 if (!rangeYOverride && d->hw_range_y_min == 0 && d->hw_range_y_max == 4095) {
297 d->hw_range_y_min = 220;
298 d->hw_range_y_max = 3907;
300 qCDebug(qLcVxTouch,
"vxtouch: found ti-tsc, overriding: min X: %d max X: %d min Y: %d max Y: %d",
301 d->hw_range_x_min, d->hw_range_x_max, d->hw_range_y_min, d->hw_range_y_max);
305 d->m_rotate = QTransform::fromTranslate(0.5, 0.5).rotate(rotationAngle).translate(-0.5, -0.5);
308 d->m_rotate *= QTransform::fromTranslate(0.5, 0.5).scale(-1.0, 1.0).translate(-0.5, -0.5);
311 d->m_rotate *= QTransform::fromTranslate(0.5, 0.5).scale(1.0, -1.0).translate(-0.5, -0.5);
315 d->m_screenName = mapping->screenNameForDeviceNode(d->deviceNode);
316 if (!d->m_screenName.isEmpty())
317 qCDebug(qLcVxTouch,
"vxtouch: Mapping device %ls to screen %ls",
318 qUtf16Printable(d->deviceNode), qUtf16Printable(d->m_screenName));
321 registerPointingDevice();
331 unregisterPointingDevice();
336 return d && d->m_filtered;
348 size_t n = qt_safe_read(m_fd, (
char *)(&ev),
sizeof(EV_DEV_EVENT));
349 if (n <
sizeof(EV_DEV_EVENT)) {
353 d->processInputEvent(&ev);
358 qWarning(
"vxtouch: Got EOF from input device");
360 }
else if (events < 0) {
361 if (errno != EINTR && errno != EAGAIN) {
362 qErrnoWarning(
"vxtouch: Could not read from input device");
363 if (errno == ENODEV) {
370 unregisterPointingDevice();
383 QPointingDevice::Capabilities caps = QPointingDevice::Capability::Position | QPointingDevice::Capability::Area;
384 if (d->hw_pressure_max > d->hw_pressure_min)
385 caps.setFlag(QPointingDevice::Capability::Pressure);
388 m_device =
new QPointingDevice(d->hw_name, id++,
389 QInputDevice::DeviceType::TouchScreen, QPointingDevice::PointerType::Finger,
392 auto geom = d->screenGeometry();
394 QPointingDevicePrivate::get(m_device)->setAvailableVirtualGeometry(geom);
396 QWindowSystemInterface::registerInputDevice(m_device);
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
433 if (QGuiApplication::instance()) {
434 m_device->moveToThread(QGuiApplication::instance()->thread());
435 m_device->deleteLater();
442void QVxTouchScreenData::addTouchPoint(
const Contact &contact, QEventPoint::States *combinedStates)
444 QWindowSystemInterface::TouchPoint tp;
445 tp.id = contact.trackingId;
446 tp.state = contact.state;
447 *combinedStates |= tp.state;
450 tp.area = QRectF(0, 0, contact.maj, contact.maj);
451 tp.area.moveCenter(QPoint(contact.x, contact.y));
452 tp.pressure = contact.pressure;
455 tp.normalPosition = QPointF((contact.x - hw_range_x_min) / qreal(hw_range_x_max - hw_range_x_min),
456 (contact.y - hw_range_y_min) / qreal(hw_range_y_max - hw_range_y_min));
458 if (!m_rotate.isIdentity())
459 tp.normalPosition = m_rotate.map(tp.normalPosition);
461 tp.rawPositions.append(QPointF(contact.x, contact.y));
463 m_touchPoints.append(tp);
466void QVxTouchScreenData::processInputEvent(input_event *data)
468 if (data->type ==
EV_ABS) {
470 m_currentData.x = qBound(hw_range_x_min, data->value, hw_range_x_max);
472 m_contacts[m_currentSlot].x = m_currentData.x;
474 m_contacts[m_currentSlot].x = m_currentData.x;
475 if (m_contacts[m_currentSlot].state == QEventPoint::State::Stationary)
476 m_contacts[m_currentSlot].state = QEventPoint::State::Updated;
479 m_currentData.y = qBound(hw_range_y_min, data->value, hw_range_y_max);
481 m_contacts[m_currentSlot].y = m_currentData.y;
483 m_contacts[m_currentSlot].y = m_currentData.y;
484 if (m_contacts[m_currentSlot].state == QEventPoint::State::Stationary)
485 m_contacts[m_currentSlot].state = QEventPoint::State::Updated;
488 m_currentData.trackingId = data->value;
490 if (m_currentData.trackingId == -1) {
491 m_contacts[m_currentSlot].state = QEventPoint::State::Released;
493 if (m_contacts.contains(m_currentData.trackingId)) {
494 m_contacts[m_currentSlot].state = QEventPoint::State::Updated;
496 m_contacts[m_currentSlot].state = QEventPoint::State::Pressed;
497 m_contacts[m_currentSlot].trackingId = m_currentData.trackingId;
502 m_currentData.maj = data->value;
503 if (data->value == 0)
504 m_currentData.state = QEventPoint::State::Released;
506 m_contacts[m_currentSlot].maj = m_currentData.maj;
508 m_currentSlot = data->value;
511 }
else if (data->type ==
EV_KEY && !m_typeB) {
512 if (data->code ==
BTN_TOUCH && data->value == 0)
513 m_contacts[m_currentSlot].state = QEventPoint::State::Released;
518 int key = m_currentData.trackingId;
520 key = m_contacts.size();
522 m_contacts.insert(key, m_currentData);
523 m_currentData = Contact();
528 if (!m_contacts.isEmpty() && m_contacts.constBegin().value().trackingId == -1)
531 std::unique_lock<QMutex> locker;
533 locker = std::unique_lock<QMutex>{m_mutex};
536 m_lastTimeStamp = m_timeStamp;
539 m_lastTouchPoints = m_touchPoints;
540 m_touchPoints.clear();
541 QEventPoint::States combinedStates;
542 bool hasPressure =
false;
544 for (
auto it = m_contacts.begin(), end = m_contacts.end(); it != end; ) {
545 Contact &contact(it.value());
547 if (!contact.state) {
552 int key = m_typeB ? it.key() : contact.trackingId;
553 if (!m_typeB && m_lastContacts.contains(key)) {
554 const Contact &prev(m_lastContacts.value(key));
555 if (contact.state == QEventPoint::State::Released) {
559 contact.maj = prev.maj;
561 contact.state = (prev.x == contact.x && prev.y == contact.y)
562 ? QEventPoint::State::Stationary : QEventPoint::State::Updated;
567 if (!m_typeB && contact.state == QEventPoint::State::Released
568 && !m_lastContacts.contains(key)) {
569 it = m_contacts.erase(it);
573 if (contact.pressure)
576 addTouchPoint(contact, &combinedStates);
581 for (
auto it = m_lastContacts.begin(), end = m_lastContacts.end(); it != end; ++it) {
582 Contact &contact(it.value());
583 int key = m_typeB ? it.key() : contact.trackingId;
585 if (contact.trackingId != m_contacts[key].trackingId && contact.state) {
586 contact.state = QEventPoint::State::Released;
587 addTouchPoint(contact, &combinedStates);
590 if (!m_contacts.contains(key)) {
591 contact.state = QEventPoint::State::Released;
592 addTouchPoint(contact, &combinedStates);
598 for (
auto it = m_contacts.begin(), end = m_contacts.end(); it != end; ) {
599 Contact &contact(it.value());
601 if (!contact.state) {
606 if (contact.state == QEventPoint::State::Released) {
607 it = m_contacts.erase(it);
610 contact.state = QEventPoint::State::Stationary;
615 m_lastContacts = m_contacts;
616 if (!m_typeB && !m_singleTouch)
620 if (!m_touchPoints.isEmpty() && (hasPressure || combinedStates != QEventPoint::State::Stationary))
624 m_lastEventType = data->type;
627int QVxTouchScreenData::findClosestContact(
const QHash<
int, Contact> &contacts,
int x,
int y,
int *dist)
629 int minDist = -1, id = -1;
630 for (QHash<
int, Contact>::const_iterator it = contacts.constBegin(), ite = contacts.constEnd();
632 const Contact &contact(it.value());
633 int dx = x - contact.x;
634 int dy = y - contact.y;
635 int dist = dx * dx + dy * dy;
636 if (minDist == -1 || dist < minDist) {
638 id = contact.trackingId;
646void QVxTouchScreenData::assignIds()
648 QHash<
int, Contact> candidates = m_lastContacts, pending = m_contacts, newContacts;
650 QHash<
int, Contact>::iterator it, ite, bestMatch;
651 while (!pending.isEmpty() && !candidates.isEmpty()) {
652 int bestDist = -1, bestId = 0;
653 for (it = pending.begin(), ite = pending.end(); it != ite; ++it) {
655 int id = findClosestContact(candidates, it->x, it->y, &dist);
656 if (id >= 0 && (bestDist == -1 || dist < bestDist)) {
663 bestMatch->trackingId = bestId;
664 newContacts.insert(bestId, *bestMatch);
665 candidates.remove(bestId);
666 pending.erase(bestMatch);
671 if (candidates.isEmpty()) {
672 for (it = pending.begin(), ite = pending.end(); it != ite; ++it) {
673 it->trackingId = ++maxId;
674 newContacts.insert(it->trackingId, *it);
677 m_contacts = newContacts;
680QRect QVxTouchScreenData::screenGeometry()
const
682 if (m_forceToActiveWindow) {
683 QWindow *win = QGuiApplication::focusWindow();
684 return win ? QHighDpi::toNativeWindowGeometry(win->geometry(), win) : QRect();
697 QScreen *screen = QGuiApplication::primaryScreen();
698 if (!m_screenName.isEmpty()) {
700 const QList<QScreen *> screens = QGuiApplication::screens();
701 for (QScreen *s : screens) {
702 if (s->name() == m_screenName) {
711 return screen ? QHighDpi::toNativePixels(screen->geometry(), screen) : QRect();
714void QVxTouchScreenData::reportPoints()
716 QRect winRect = screenGeometry();
717 if (winRect.isNull())
720 const int hw_w = hw_range_x_max - hw_range_x_min;
721 const int hw_h = hw_range_y_max - hw_range_y_min;
725 const int pointCount = m_touchPoints.size();
726 for (
int i = 0; i < pointCount; ++i) {
727 QWindowSystemInterface::TouchPoint &tp(m_touchPoints[i]);
732 const qreal wx = winRect.left() + tp.normalPosition.x() * (winRect.width() - 1);
733 const qreal wy = winRect.top() + tp.normalPosition.y() * (winRect.height() - 1);
734 const qreal sizeRatio = (winRect.width() + winRect.height()) / qreal(hw_w + hw_h);
735 if (tp.area.width() == -1)
736 tp.area = QRectF(0, 0, 8, 8);
738 tp.area = QRectF(0, 0, tp.area.width() * sizeRatio, tp.area.height() * sizeRatio);
739 tp.area.moveCenter(QPointF(wx, wy));
742 if (!hw_pressure_min && !hw_pressure_max)
743 tp.pressure = tp.state == QEventPoint::State::Released ? 0 : 1;
745 tp.pressure = (tp.pressure - hw_pressure_min) / qreal(hw_pressure_max - hw_pressure_min);
747 if (Q_UNLIKELY(qLcVxEvents().isDebugEnabled()))
748 qCDebug(qLcVxEvents) <<
"reporting" << tp;
753 emit q->touchPointsUpdated();
755 QWindowSystemInterface::handleTouchEvent(
nullptr, q->touchDevice(), m_touchPoints);
759 : QDaemonThread(parent), m_device(device), m_spec(spec), m_handler(
nullptr), m_touchDeviceRegistered(
false)
760 , m_touchUpdatePending(
false)
761 , m_filterWindow(
nullptr)
775 m_handler =
new QVxTouchScreenHandler(m_device, m_spec);
777 if (m_handler->isFiltered())
778 connect(m_handler, &QVxTouchScreenHandler::touchPointsUpdated,
this, &QVxTouchScreenHandlerThread::scheduleTouchPointUpdate);
781 QMetaObject::invokeMethod(
this,
"notifyTouchDeviceRegistered", Qt::QueuedConnection);
791 return m_touchDeviceRegistered;
796 m_touchDeviceRegistered =
true;
797 emit touchDeviceRegistered();
802 QWindow *window = QGuiApplication::focusWindow();
803 if (window != m_filterWindow) {
805 m_filterWindow->removeEventFilter(
this);
806 m_filterWindow = window;
808 m_filterWindow->installEventFilter(
this);
810 if (m_filterWindow) {
811 m_touchUpdatePending =
true;
812 m_filterWindow->requestUpdate();
818 if (m_touchUpdatePending && object == m_filterWindow && event->type() == QEvent::UpdateRequest) {
819 m_touchUpdatePending =
false;
820 filterAndSendTouchPoints();
827 QRect winRect = m_handler->d->screenGeometry();
828 if (winRect.isNull())
831 float vsyncDelta = 1.0f / QGuiApplication::primaryScreen()->refreshRate();
833 QHash<
int, FilteredTouchPoint> filteredPoints;
835 m_handler->d->m_mutex.lock();
837 double time = m_handler->d->m_timeStamp;
838 double lastTime = m_handler->d->m_lastTimeStamp;
839 double touchDelta = time - lastTime;
840 if (m_touchRate < 0 || touchDelta > vsyncDelta) {
848 m_touchRate = (1.0 / QGuiApplication::primaryScreen()->refreshRate()) / 2.0;
854 const double ratio = 0.9;
855 m_touchRate = sqrt(m_touchRate * m_touchRate * ratio + touchDelta * touchDelta * (1.0 - ratio));
858 QList<QWindowSystemInterface::TouchPoint> points = m_handler->d->m_touchPoints;
859 QList<QWindowSystemInterface::TouchPoint> lastPoints = m_handler->d->m_lastTouchPoints;
861 m_handler->d->m_mutex.unlock();
863 for (
int i=0; i<points.size(); ++i) {
864 QWindowSystemInterface::TouchPoint &tp = points[i];
865 QPointF pos = tp.normalPosition;
866 FilteredTouchPoint f;
868 QWindowSystemInterface::TouchPoint ltp;
870 for (
int j=0; j<lastPoints.size(); ++j) {
871 if (lastPoints.at(j).id == tp.id) {
872 ltp = lastPoints.at(j);
878 if (lastTime != 0 && ltp.id >= 0)
879 velocity = (pos - ltp.normalPosition) / m_touchRate;
880 if (m_filteredPoints.contains(tp.id)) {
881 f = m_filteredPoints.take(tp.id);
882 f.x.update(pos.x(), velocity.x(), vsyncDelta);
883 f.y.update(pos.y(), velocity.y(), vsyncDelta);
884 pos = QPointF(f.x.position(), f.y.position());
886 f.x.initialize(pos.x(), velocity.x());
887 f.y.initialize(pos.y(), velocity.y());
890 if (tp.state != QEventPoint::State::Pressed)
891 tp.state = QEventPoint::State::Pressed;
894 tp.velocity = QVector2D(f.x.velocity() * winRect.width(), f.y.velocity() * winRect.height());
896 qreal filteredNormalizedX = f.x.position() + f.x.velocity() * m_handler->d->m_prediction / 1000.0;
897 qreal filteredNormalizedY = f.y.position() + f.y.velocity() * m_handler->d->m_prediction / 1000.0;
900 tp.normalPosition = QPointF(qBound<qreal>(0, filteredNormalizedX, 1),
901 qBound<qreal>(0, filteredNormalizedY, 1));
903 qreal x = winRect.x() + (tp.normalPosition.x() * (winRect.width() - 1));
904 qreal y = winRect.y() + (tp.normalPosition.y() * (winRect.height() - 1));
906 tp.area.moveCenter(QPointF(x, y));
913 if (tp.state != QEventPoint::State::Released)
914 filteredPoints[tp.id] = f;
917 for (QHash<
int, FilteredTouchPoint>::const_iterator it = m_filteredPoints.constBegin(), end = m_filteredPoints.constEnd(); it != end; ++it) {
918 const FilteredTouchPoint &f = it.value();
919 QWindowSystemInterface::TouchPoint tp = f.touchPoint;
920 tp.state = QEventPoint::State::Released;
921 tp.velocity = QVector2D();
925 m_filteredPoints = filteredPoints;
927 QWindowSystemInterface::handleTouchEvent(
nullptr,
928 m_handler->touchDevice(),
935#include "moc_qvxtouchhandler_p.cpp"
static QOutputMapping * get()
bool eventFilter(QObject *object, QEvent *event) override
~QVxTouchScreenHandlerThread()
bool isPointingDeviceRegistered() const
void scheduleTouchPointUpdate()
QPointingDevice * touchDevice() const
#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")