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>
28#ifndef input_event_sec
29#define input_event_sec time.tv_sec
32#ifndef input_event_usec
33#define input_event_usec time.tv_usec
54#ifndef ABS_MT_TOUCH_MAJOR
55#define ABS_MT_TOUCH_MAJOR 0x30
57#ifndef ABS_MT_POSITION_X
58#define ABS_MT_POSITION_X 0x35
60#ifndef ABS_MT_POSITION_Y
61#define ABS_MT_POSITION_Y 0x36
64#define ABS_MT_SLOT 0x2f
67#define ABS_CNT (ABS_MAX+1)
69#ifndef ABS_MT_TRACKING_ID
70#define ABS_MT_TRACKING_ID 0x39
72#ifndef ABS_MT_PRESSURE
73#define ABS_MT_PRESSURE 0x3a
76#define SYN_MT_REPORT 2
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),
152 m_filtered(
false), m_prediction(0)
155 if (
arg == u
"force_window")
157 else if (
arg == u
"filtered")
159 else if (
const QStringView prefix = u
"prediction=";
arg.startsWith(prefix))
164#define LONG_BITS (sizeof(long) << 3)
165#define NUM_LONGS(bits) (((bits) + LONG_BITS - 1) / LONG_BITS)
183 int rotationAngle = 0;
184 bool invertx =
false;
185 bool inverty =
false;
187 if (
args.
at(
i).startsWith(
"rotate"_L1)) {
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) {
211 m_fd =
QT_OPEN(
device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);
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);
235 const char *mtdevStr =
"(mtdev)";
238 const char *mtdevStr =
"";
240 if (ioctl(m_fd, EVIOCGBIT(EV_ABS,
sizeof(absbits)), absbits) >= 0) {
248 "evdevtouch: %ls: Protocol type %c %s (%s), filtered=%s",
250 d->
m_typeB ?
'B' :
'A', mtdevStr,
256 input_absinfo absInfo;
257 memset(&absInfo, 0,
sizeof(input_absinfo));
258 bool has_x_range =
false, has_y_range =
false;
262 absInfo.minimum, absInfo.maximum);
270 absInfo.minimum, absInfo.maximum);
276 if (!has_x_range || !has_y_range)
279 if (ioctl(m_fd, EVIOCGABS(ABS_PRESSURE), &absInfo) >= 0) {
281 absInfo.minimum, absInfo.maximum);
282 if (absInfo.maximum > absInfo.minimum) {
289 if (ioctl(m_fd, EVIOCGNAME(
sizeof(
name) - 1),
name) >= 0) {
295 if (d->
hw_name ==
"ti-tsc"_L1) {
304 qCDebug(qLcEvdevTouch,
"evdevtouch: found ti-tsc, overriding: min X: %d max X: %d min Y: %d max Y: %d",
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.");
327 qCDebug(qLcEvdevTouch,
"evdevtouch: Mapping device %ls to screen %ls",
331 registerPointingDevice();
338 mtdev_close(m_mtdev);
348 unregisterPointingDevice();
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)
392 if (
n %
sizeof(::input_event) == 0)
396 n /=
sizeof(::input_event);
398 for (
int i = 0;
i <
n; ++
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();
424void QEvdevTouchScreenHandler::registerPointingDevice()
475void QEvdevTouchScreenHandler::unregisterPointingDevice()
492 tp.
id = contact.trackingId;
493 tp.
state = contact.state;
494 *combinedStates |= tp.
state;
497 tp.
area =
QRectF(0, 0, contact.maj, contact.maj);
515 if (
data->type == EV_ABS) {
547 if (
data->value == 0)
553 qCDebug(qLcEvents,
"EV_ABS code 0x%x: pressure %d; bounding to [%d,%d]",
563 if (
data->code == BTN_TOUCH &&
data->value == 0)
576 }
else if (
data->type == EV_SYN &&
data->code == SYN_REPORT) {
582 std::unique_lock<QMutex> locker;
584 locker = std::unique_lock<QMutex>{
m_mutex};
592 QEventPoint::States combinedStates;
593 bool hasPressure =
false;
598 if (!contact.state) {
610 contact.maj = prev.
maj;
612 contact.state = (prev.
x == contact.x && prev.
y == contact.y)
624 if (contact.pressure)
636 if (contact.trackingId !=
m_contacts[
key].trackingId && contact.state) {
652 if (!contact.state) {
684 int minDist = -1,
id = -1;
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;
706 while (!
pending.isEmpty() && !candidates.isEmpty()) {
707 int bestDist = -1, bestId = 0;
711 if (
id >= 0 && (bestDist == -1 ||
dist < bestDist)) {
718 bestMatch->trackingId = bestId;
719 newContacts.insert(bestId, *bestMatch);
720 candidates.remove(bestId);
726 if (candidates.isEmpty()) {
728 it->trackingId = ++maxId;
772 if (winRect.isNull())
781 for (
int i = 0;
i < pointCount; ++
i) {
789 const qreal sizeRatio = (winRect.width() + winRect.height()) /
qreal(hw_w + hw_h);
803 qCDebug(qLcEvents) <<
"reporting" << tp;
808 emit q->touchPointsUpdated();
814 : QDaemonThread(parent), m_device(
device), m_spec(spec), m_handler(
nullptr), m_touchDeviceRegistered(
false)
815 , m_touchUpdatePending(
false)
846 return m_touchDeviceRegistered;
849void QEvdevTouchScreenHandlerThread::notifyTouchDeviceRegistered()
851 m_touchDeviceRegistered =
true;
858 if (
window != m_filterWindow) {
865 if (m_filterWindow) {
866 m_touchUpdatePending =
true;
867 m_filterWindow->requestUpdate();
874 m_touchUpdatePending =
false;
875 filterAndSendTouchPoints();
880void QEvdevTouchScreenHandlerThread::filterAndSendTouchPoints()
883 if (winRect.isNull())
888 QHash<int, FilteredTouchPoint> filteredPoints;
894 double touchDelta =
time - lastTime;
895 if (m_touchRate < 0 || touchDelta > vsyncDelta) {
909 const double ratio = 0.9;
910 m_touchRate = sqrt(m_touchRate * m_touchRate * ratio + touchDelta * touchDelta * (1.0 - ratio));
914 QList<QWindowSystemInterface::TouchPoint> lastPoints = m_handler->d->
m_lastTouchPoints;
921 FilteredTouchPoint
f;
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;
936 f = m_filteredPoints.
take(tp.
id);
938 f.y.update(
pos.y(), velocity.
y(), vsyncDelta);
941 f.x.initialize(
pos.x(), velocity.
x());
942 f.y.initialize(
pos.y(), velocity.
y());
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;
956 qBound<qreal>(0, filteredNormalizedY, 1));
969 filteredPoints[tp.
id] =
f;
973 const FilteredTouchPoint &
f =
it.value();
980 m_filteredPoints = filteredPoints;
990#include "moc_qevdevtouchhandler_p.cpp"
IOBluetoothDevice * device
static QCoreApplication * instance() noexcept
Returns a pointer to the application's QCoreApplication (or QGuiApplication/QApplication) instance.
QEvdevTouchScreenHandler * q
int findClosestContact(const QHash< int, Contact > &contacts, int x, int y, int *dist)
QHash< int, Contact > m_lastContacts
QEvdevTouchScreenData(QEvdevTouchScreenHandler *q_ptr, const QStringList &args)
QList< QWindowSystemInterface::TouchPoint > m_lastTouchPoints
bool m_forceToActiveWindow
QHash< int, Contact > m_contacts
void loadMultiScreenMappings()
QRect screenGeometry() const
void addTouchPoint(const Contact &contact, QEventPoint::States *combinedStates)
QPointer< QScreen > m_screen
QList< QWindowSystemInterface::TouchPoint > m_touchPoints
void processInputEvent(input_event *data)
bool eventFilter(QObject *object, QEvent *event) override
QEvdevTouchScreenHandlerThread(const QString &device, const QString &spec, QObject *parent=nullptr)
void scheduleTouchPointUpdate()
bool isPointingDeviceRegistered() const
~QEvdevTouchScreenHandlerThread()
void touchDeviceRegistered()
QPointingDevice * touchDevice() const
void touchPointsUpdated()
~QEvdevTouchScreenHandler()
friend class QEvdevTouchScreenData
QEvdevTouchScreenHandler(const QString &device, const QString &spec=QString(), QObject *parent=nullptr)
State
Specifies the state of this event point.
QScreen * primaryScreen
the primary (or default) screen of the application.
static QWindow * focusWindow()
Returns the QWindow that receives events tied to focus, such as key events.
static QList< QScreen * > screens()
Returns a list of all the screens associated with the windowing system the application is connected t...
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
const_iterator constBegin() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
T take(const Key &key)
Removes the item with the key from the hash and returns the value associated with it.
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
qsizetype size() const noexcept
bool isEmpty() const noexcept
const_reference at(qsizetype i) const noexcept
void append(parameter_type t)
void unlock() noexcept
Unlocks the mutex.
void lock() noexcept
Locks the mutex.
void installEventFilter(QObject *filterObj)
Installs an event filter filterObj on this object.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
void removeEventFilter(QObject *obj)
Removes an event filter object obj from this object.
QThread * thread() const
Returns the thread in which the object lives.
bool moveToThread(QThread *thread QT6_DECL_NEW_OVERLOAD_TAIL)
Changes the thread affinity for this object and its children and returns true on success.
Q_WEAK_OVERLOAD void setObjectName(const QString &name)
Sets the object's name to name.
void deleteLater()
\threadsafe
static QOutputMapping * get()
\inmodule QtCore\reentrant
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
\inmodule QtCore\reentrant
static QPointingDevicePrivate * get(QPointingDevice *q)
The QPointingDevice class describes a device from which mouse, touch or tablet events originate.
\inmodule QtCore\reentrant
constexpr void moveCenter(const QPointF &p) noexcept
Moves the rectangle, leaving the center point at the given position.
constexpr qreal height() const noexcept
Returns the height of the rectangle.
constexpr qreal width() const noexcept
Returns the width of the rectangle.
\inmodule QtCore\reentrant
The QScreen class is used to query screen properties. \inmodule QtGui.
QRect geometry
the screen's geometry in pixels
qreal refreshRate
the approximate vertical refresh rate of the screen in Hz
iterator insert(const T &value)
void activated(QSocketDescriptor socket, QSocketNotifier::Type activationEvent, QPrivateSignal)
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
The QVector2D class represents a vector or vertex in 2D space.
static bool handleTouchEvent(QWindow *window, const QPointingDevice *device, const QList< struct TouchPoint > &points, Qt::KeyboardModifiers mods=Qt::NoModifier)
static void registerInputDevice(const QInputDevice *device)
QSet< QString >::iterator it
void qErrnoWarning(const char *msg,...)
T toNativePixels(const T &value, const C *context)
T toNativeWindowGeometry(const T &value, const C *context)
Combined button and popup list for selecting options.
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage return DBusPendingCall * pending
#define ABS_MT_POSITION_X
static bool testBit(long bit, const long *field)
#define ABS_MT_POSITION_Y
#define ABS_MT_POSITION_X
static bool testBit(long bit, const long *array)
#define ABS_MT_TRACKING_ID
#define ABS_MT_TOUCH_MAJOR
#define ABS_MT_POSITION_Y
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
constexpr const T & qBound(const T &min, const T &val, const T &max)
GLint GLint GLint GLint GLint x
[0]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfixed GLfixed GLint GLint GLfixed points
GLdouble GLdouble GLdouble GLdouble q
GLenum GLenum GLenum GLenum mapping
GLenum GLenum GLenum input
#define qUtf16Printable(string)
#define QT_CONFIG(feature)
static int toInt(const QChar &qc, int R)
if(qFloatDistance(a, b)<(1<< 7))
[0]
std::uniform_real_distribution dist(1, 2.5)
[2]
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
void update(float pos, float velocity, float timeDelta)
QList< QPointF > rawPositions