Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qpointingdevice.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qpointingdevice.h"
5#include "qpointingdevice_p.h"
7#include "qeventpoint_p.h"
8
9#include <QList>
10#include <QLoggingCategory>
11#include <QMutex>
12#include <QCoreApplication>
13
14#include <private/qdebug_p.h>
15
17
18using namespace Qt::StringLiterals;
19
20Q_LOGGING_CATEGORY(lcPointerGrab, "qt.pointer.grab");
21
160 : QInputDevice(*(new QPointingDevicePrivate("unknown"_L1, -1,
162 Capability::None, 0, 0)), parent)
163{
164}
165
169
176 QPointingDevice::PointerType pointerType, Capabilities capabilities, int maxPoints, int buttonCount,
177 const QString &seatName, QPointingDeviceUniqueId uniqueId, QObject *parent)
178 : QInputDevice(*(new QPointingDevicePrivate(name, id, deviceType, pointerType, capabilities, maxPoints, buttonCount, seatName, uniqueId)), parent)
179{
180}
181
189
190#if QT_DEPRECATED_SINCE(6, 0)
197void QPointingDevice::setType(DeviceType devType)
198{
199 Q_D(QPointingDevice);
200 d->deviceType = devType;
201 if (d->pointerType == PointerType::Unknown)
202 switch (devType) {
204 d->pointerType = PointerType::Generic;
205 break;
208 d->pointerType = PointerType::Finger;
209 break;
210 case DeviceType::Puck:
211 d->pointerType = PointerType::Cursor;
212 break;
215 d->pointerType = PointerType::Pen;
216 break;
217 default:
218 break;
219 }
220}
221
226void QPointingDevice::setCapabilities(QInputDevice::Capabilities caps)
227{
228 Q_D(QPointingDevice);
229 d->capabilities = caps;
230}
231
236void QPointingDevice::setMaximumTouchPoints(int c)
237{
238 Q_D(QPointingDevice);
239 d->maximumTouchPoints = c;
240}
241#endif // QT_DEPRECATED_SINCE(6, 0)
242
247{
248 Q_D(const QPointingDevice);
249 return d->pointerType;
250}
251
257{
258 Q_D(const QPointingDevice);
259 return d->maximumTouchPoints;
260}
261
266{
267 Q_D(const QPointingDevice);
268 return d->buttonCount;
269}
270
277{
278 Q_D(const QPointingDevice);
279 return d->uniqueId;
280}
281
294{
295 const auto v = devices();
296 const QPointingDevice *mouse = nullptr;
297 const QPointingDevice *touchpad = nullptr;
298 for (const QInputDevice *dev : v) {
299 if (!seatName.isNull() && dev->seatName() != seatName)
300 continue;
301 if (dev->type() == QInputDevice::DeviceType::Mouse) {
302 if (!mouse)
303 mouse = static_cast<const QPointingDevice *>(dev);
304 // the core pointer is likely a mouse, and its parent is not another input device
305 if (!mouse->parent() || !qobject_cast<const QInputDevice *>(mouse->parent()))
306 return mouse;
307 } else if (dev->type() == QInputDevice::DeviceType::TouchPad) {
308 if (!touchpad || !dev->parent() || dev->parent()->metaObject() != dev->metaObject())
309 touchpad = static_cast<const QPointingDevice *>(dev);
310 }
311 }
312 if (!mouse && !touchpad) {
313 qCDebug(lcQpaInputDevices) << "no mouse-like devices registered for seat" << seatName
314 << "The platform plugin should have provided one via "
315 "QWindowSystemInterface::registerInputDevice(). Creating a default mouse for now.";
316 mouse = new QPointingDevice("core pointer"_L1, 1, DeviceType::Mouse,
320 return mouse;
321 }
322 if (v.size() > 1)
323 qCDebug(lcQpaInputDevices) << "core pointer ambiguous for seat" << seatName;
324 if (mouse)
325 return mouse;
326 return touchpad;
327}
328
330 = default;
331
351 QPointingDevice::Capabilities capabilities,
352 qint64 systemId)
353{
354 const auto &devices = QInputDevice::devices();
355 for (const QInputDevice *dev : devices) {
357 continue;
358 const QPointingDevice *pdev = static_cast<const QPointingDevice *>(dev);
359 const auto devPriv = QPointingDevicePrivate::get(pdev);
360 bool uniqueIdDiscovered = (devPriv->uniqueId.numericId() == 0 && uniqueId.numericId() != 0);
361 if (devPriv->deviceType == deviceType && devPriv->pointerType == pointerType &&
362 (!systemId || devPriv->systemId == systemId) &&
363 (devPriv->uniqueId == uniqueId || uniqueIdDiscovered)) {
364 if (uniqueIdDiscovered) {
365 const_cast<QPointingDevicePrivate *>(devPriv)->uniqueId = uniqueId;
366 if (capabilities)
367 const_cast<QPointingDevicePrivate *>(devPriv)->capabilities = capabilities;
368 qCDebug(lcQpaInputDevices) << "discovered unique ID and capabilities of tablet tool" << pdev;
369 }
370 return pdev;
371 }
372 }
373 return nullptr;
374}
375
382{
383 const auto &devices = QInputDevice::devices();
384 for (const QInputDevice *dev : devices) {
385 if (dev->type() >= QPointingDevice::DeviceType::Keyboard)
386 continue;
387 const QPointingDevice *pdev = static_cast<const QPointingDevice *>(dev);
388 const auto devPriv = QPointingDevicePrivate::get(pdev);
389 if (devPriv->systemId == systemId)
390 return pdev;
391 }
392 return nullptr;
393}
394
402{
403 // An incoming TouchCancel event will typically not contain any points, but
404 // QQuickPointerHandler::onGrabChanged needs to be called for each point
405 // that has an exclusive grabber. Adding those points to the event makes it
406 // an easy iteration there.
407 if (cancelEvent->points().isEmpty()) {
408 for (auto &epd : activePoints.values()) {
409 if (epd.exclusiveGrabber)
410 QMutableTouchEvent::from(cancelEvent)->addPoint(epd.eventPoint);
411 }
412 }
413 for (auto &epd : activePoints.values()) {
414 if (epd.exclusiveGrabber)
415 QCoreApplication::sendEvent(epd.exclusiveGrabber, cancelEvent);
416 // The next touch event can only be a TouchBegin, so clean up.
417 cancelEvent->setExclusiveGrabber(epd.eventPoint, nullptr);
418 cancelEvent->clearPassiveGrabbers(epd.eventPoint);
419 }
420}
421
427{
428 auto it = activePoints.find(id);
429 if (it == activePoints.end())
430 return nullptr;
431 return &it.value();
432}
433
439{
440 const auto [it, inserted] = activePoints.try_emplace(id);
441 if (inserted) {
442 Q_Q(const QPointingDevice);
443 auto &epd = it.value();
444 QMutableEventPoint::setId(epd.eventPoint, id);
445 QMutableEventPoint::setDevice(epd.eventPoint, q);
446 }
447 return &it.value();
448}
449
454{
455 activePoints.remove(id);
456}
457
466{
467 for (auto &pt : activePoints.values()) {
468 if (auto target = QMutableEventPoint::target(pt.eventPoint))
469 return target;
470 }
471 return nullptr;
472}
473
481{
482 for (auto &pt : activePoints.values()) {
483 if (auto window = QMutableEventPoint::window(pt.eventPoint))
484 return window;
485 }
486 return nullptr;
487}
488
496{
497 if (activePoints.isEmpty())
498 return nullptr;
499 return activePoints.values().first().exclusiveGrabber;
500}
501
503{
504 Q_Q(QPointingDevice);
505 auto persistentPoint = queryPointById(point.id());
506 if (!persistentPoint) {
507 qWarning() << "point is not in activePoints" << point;
508 return;
509 }
510 Q_ASSERT(persistentPoint->eventPoint.id() == point.id());
511 if (persistentPoint->exclusiveGrabber == exclusiveGrabber)
512 return;
513 auto oldGrabber = persistentPoint->exclusiveGrabber;
514 persistentPoint->exclusiveGrabber = exclusiveGrabber;
515 if (oldGrabber)
516 emit q->grabChanged(oldGrabber, exclusiveGrabber ? QPointingDevice::CancelGrabExclusive : QPointingDevice::UngrabExclusive,
517 event, persistentPoint->eventPoint);
518 if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
519 qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
520 << "@" << point.scenePosition()
521 << ": grab" << oldGrabber << "->" << exclusiveGrabber;
522 }
523 QMutableEventPoint::setGlobalGrabPosition(persistentPoint->eventPoint, point.globalPosition());
524 if (exclusiveGrabber)
525 emit q->grabChanged(exclusiveGrabber, QPointingDevice::GrabExclusive, event, point);
526 else
527 persistentPoint->exclusiveGrabberContext.clear();
528}
529
535{
536 bool ret = false;
537 for (auto &pt : activePoints.values()) {
538 if (pt.exclusiveGrabber == grabber) {
539 setExclusiveGrabber(event, pt.eventPoint, nullptr);
540 ret = true;
541 }
542 }
543 return ret;
544}
545
547{
548 Q_Q(QPointingDevice);
549 auto persistentPoint = queryPointById(point.id());
550 if (!persistentPoint) {
551 qWarning() << "point is not in activePoints" << point;
552 return false;
553 }
554 if (persistentPoint->passiveGrabbers.contains(grabber))
555 return false;
556 if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
557 qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
558 << ": grab (passive)" << grabber;
559 }
560 persistentPoint->passiveGrabbers << grabber;
561 emit q->grabChanged(grabber, QPointingDevice::GrabPassive, event, point);
562 return true;
563}
564
566{
567 qsizetype i = epd->passiveGrabbers.indexOf(grabber);
568 if (i < 0)
569 return false;
570 if (epd->passiveGrabbersContext.size() <= i)
571 epd->passiveGrabbersContext.resize(i + 1);
572 epd->passiveGrabbersContext[i] = context;
573 return true;
574}
575
577{
578 Q_Q(QPointingDevice);
579 auto persistentPoint = queryPointById(point.id());
580 if (!persistentPoint) {
581 qWarning() << "point is not in activePoints" << point;
582 return false;
583 }
584 qsizetype i = persistentPoint->passiveGrabbers.indexOf(grabber);
585 if (i >= 0) {
586 if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
587 qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
588 << ": removing passive grabber" << grabber;
589 }
590 emit q->grabChanged(grabber, QPointingDevice::UngrabPassive, event, point);
591 persistentPoint->passiveGrabbers.removeAt(i);
592 if (persistentPoint->passiveGrabbersContext.size()) {
593 Q_ASSERT(persistentPoint->passiveGrabbersContext.size() > i);
594 persistentPoint->passiveGrabbersContext.removeAt(i);
595 }
596 return true;
597 }
598 return false;
599}
600
602{
603 Q_Q(QPointingDevice);
604 auto persistentPoint = queryPointById(point.id());
605 if (!persistentPoint) {
606 qWarning() << "point is not in activePoints" << point;
607 return;
608 }
609 if (persistentPoint->passiveGrabbers.isEmpty())
610 return;
611 if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
612 qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
613 << ": clearing" << persistentPoint->passiveGrabbers;
614 }
615 for (auto g : persistentPoint->passiveGrabbers)
616 emit q->grabChanged(g, QPointingDevice::UngrabPassive, event, point);
617 persistentPoint->passiveGrabbers.clear();
618 persistentPoint->passiveGrabbersContext.clear();
619}
620
633{
634 Q_Q(QPointingDevice);
635 for (auto ap : activePoints) {
636 auto &epd = ap.second;
637 if (epd.exclusiveGrabber.data() == grabber) {
638 qCDebug(lcPointerGrab) << name << "point" << epd.eventPoint.id() << epd.eventPoint.state()
639 << "@" << epd.eventPoint.scenePosition()
640 << ": grab" << grabber << "-> nullptr";
641 epd.exclusiveGrabber.clear();
642 epd.exclusiveGrabberContext.clear();
643 emit q->grabChanged(grabber,
645 nullptr, epd.eventPoint);
646 }
647 qsizetype pi = epd.passiveGrabbers.indexOf(grabber);
648 if (pi >= 0) {
649 qCDebug(lcPointerGrab) << name << "point" << epd.eventPoint.id() << epd.eventPoint.state()
650 << ": removing passive grabber" << grabber;
651 epd.passiveGrabbers.removeAt(pi);
652 if (epd.passiveGrabbersContext.size()) {
653 Q_ASSERT(epd.passiveGrabbersContext.size() > pi);
654 epd.passiveGrabbersContext.removeAt(pi);
655 }
656 emit q->grabChanged(grabber,
658 nullptr, epd.eventPoint);
659 }
660 }
661}
662
676{
678 if (!dev) {
679 qCDebug(lcQpaInputDevices) << "failed to find registered tablet device"
681 << "The platform plugin should have provided one via "
682 "QWindowSystemInterface::registerInputDevice(). Creating a default one for now.";
683 dev = new QPointingDevice("fake tablet"_L1, 2, deviceType, pointerType,
687 }
688 return dev;
689}
690
692{
693 // Wacom tablets generate separate instances for each end of each stylus;
694 // QInputDevice::operator==() says they are all the same, but we use
695 // the stylus unique serial number and pointerType to distinguish them
697 pointerType() == other.pointerType() &&
698 uniqueId() == other.uniqueId();
699}
700
701#ifndef QT_NO_DEBUG_STREAM
703{
704 QDebugStateSaver saver(debug);
705 debug.nospace();
706 debug.noquote();
707 debug << "QPointingDevice(";
708 if (device) {
709 debug << '"' << device->name() << "\" ";
711 debug << " id=" << device->systemId();
712 if (!device->seatName().isEmpty())
713 debug << " seat=" << device->seatName();
714 if (device->pointerType() != QPointingDevice::PointerType::Generic) {
715 debug << " ptrType=";
716 QtDebugUtils::formatQEnum(debug, device->pointerType());
717 }
718 if (int(device->capabilities()) != int(QInputDevice::Capability::Position)) {
719 debug << " caps=";
720 QtDebugUtils::formatQFlags(debug, device->capabilities());
721 }
722 if (device->maximumPoints() > 1)
723 debug << " maxPts=" << device->maximumPoints();
724 if (device->uniqueId().isValid())
725 debug << " uniqueId=" << Qt::hex << device->uniqueId().numericId() << Qt::dec;
726 } else {
727 debug << '0';
728 }
729 debug << ')';
730 return debug;
731}
732#endif // !QT_NO_DEBUG_STREAM
733
768
790{
791 return m_numericId;
792}
793
816size_t qHash(QPointingDeviceUniqueId key, size_t seed) noexcept
817{
818 return qHash(key.numericId(), seed);
819}
820
822
823#include "moc_qpointingdevice.cpp"
IOBluetoothDevice * device
static bool sendEvent(QObject *receiver, QEvent *event)
Sends event event directly to receiver receiver, using the notify() function.
static QCoreApplication * instance() noexcept
Returns a pointer to the application's QCoreApplication (or QGuiApplication/QApplication) instance.
\inmodule QtCore
\inmodule QtCore
The QEventPoint class provides information about a point in a QPointerEvent.
Definition qeventpoint.h:20
QPointF globalPosition
the global position of this point.
Definition qeventpoint.h:43
int id
the ID number of this event point.
Definition qeventpoint.h:24
QPointF scenePosition
the scene position of this point.
Definition qeventpoint.h:39
State state
the current state of the event point.
Definition qeventpoint.h:26
QInputDevice::Capabilities capabilities
QInputDevice::DeviceType deviceType
static void registerDevice(const QInputDevice *dev)
The QInputDevice class describes a device from which a QInputEvent originates.
DeviceType
This enum represents the type of device that generated a QPointerEvent.
Capability
Indicates what kind of information the input device or its driver can provide.
bool operator==(const QInputDevice &other) const
QString seatName
static QList< const QInputDevice * > devices()
Returns a list of all registered input devices (keyboards and pointing devices).
static QObject * target(const QEventPoint &p)
static QWindow * window(const QEventPoint &p)
static QMutableTouchEvent * from(QTouchEvent *e)
Definition qevent_p.h:37
\inmodule QtCore
Definition qobject.h:103
A base class for pointer events.
Definition qevent.h:73
QPointingDevice::PointerType pointerType
QObject * firstActiveTarget() const
void removeGrabber(QObject *grabber, bool cancel=false)
static bool setPassiveGrabberContext(EventPointData *epd, QObject *grabber, QObject *context)
~QPointingDevicePrivate() override
static const QPointingDevice * tabletDevice(QInputDevice::DeviceType deviceType, QPointingDevice::PointerType pointerType, QPointingDeviceUniqueId uniqueId)
bool removePassiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *grabber)
static const QPointingDevice * queryTabletDevice(QInputDevice::DeviceType deviceType, QPointingDevice::PointerType pointerType, QPointingDeviceUniqueId uniqueId, QInputDevice::Capabilities capabilities=QInputDevice::Capability::None, qint64 systemId=0)
QWindow * firstActiveWindow() const
void clearPassiveGrabbers(const QPointerEvent *event, const QEventPoint &point)
void sendTouchCancelEvent(QTouchEvent *cancelEvent)
bool removeExclusiveGrabber(const QPointerEvent *event, const QObject *grabber)
bool addPassiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *grabber)
QPointingDeviceUniqueId uniqueId
EventPointData * queryPointById(int id) const
static const QPointingDevice * pointingDeviceById(qint64 systemId)
QObject * firstPointExclusiveGrabber() const
EventPointData * pointById(int id) const
static QPointingDevicePrivate * get(QPointingDevice *q)
void setExclusiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *exclusiveGrabber)
QPointingDeviceUniqueId identifies a unique object, such as a tagged token or stylus,...
qint64 numericId
the numeric unique ID of the token represented by a touchpoint
size_t qHash(QPointingDeviceUniqueId key, size_t seed) noexcept
static QPointingDeviceUniqueId fromNumericId(qint64 id)
Constructs a unique pointer ID from numeric ID id.
The QPointingDevice class describes a device from which mouse, touch or tablet events originate.
PointerType pointerType
static const QPointingDevice * primaryPointingDevice(const QString &seatName=QString())
Returns the primary pointing device (the core pointer, traditionally assumed to be a mouse) on the gi...
bool operator==(const QPointingDevice &other) const
PointerType
This enum represents what is interacting with the pointing device.
QPointingDeviceUniqueId uniqueId
QPointingDevice(QObject *parent=nullptr)
Creates a new invalid pointing device instance as a child of parent.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool isNull() const
Returns true if this string is null; otherwise returns false.
Definition qstring.h:994
The QTouchEvent class contains parameters that describe a touch event.
Definition qevent.h:917
\inmodule QtGui
Definition qwindow.h:63
QSet< QString >::iterator it
Combined button and popup list for selecting options.
static void formatQEnum(QDebug &debug, QEnum value)
Definition qdebug_p.h:59
static void formatQFlags(QDebug &debug, const QFlags< Enum > &value)
Definition qdebug_p.h:79
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
QTextStream & dec(QTextStream &stream)
Calls QTextStream::setIntegerBase(10) on stream and returns stream.
static void * context
#define Q_UNLIKELY(x)
EGLDeviceEXT * devices
size_t qHash(const QFileSystemWatcherPathKey &key, size_t seed=0)
@ None
Definition qhash.cpp:531
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
return ret
GLsizei const GLfloat * v
[13]
GLuint64 key
GLenum GLuint id
[7]
GLenum target
GLboolean GLboolean g
GLuint name
struct _cl_event * event
const GLubyte * c
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
QDebug operator<<(QDebug debug, const QPointingDevice *device)
static Q_CONSTINIT QBasicAtomicInteger< unsigned > seed
Definition qrandom.cpp:196
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
ptrdiff_t qsizetype
Definition qtypes.h:165
long long qint64
Definition qtypes.h:60
static QPointingDevice::PointerType pointerType(unsigned currentCursor)
static QInputDevice::DeviceType deviceType(const UINT cursorType)
future cancel()
QSharedPointer< T > other(t)
[5]
aWidget window() -> setWindowTitle("New Window Title")
[2]