summaryrefslogtreecommitdiffstats
path: root/src/gui/kernel/qpointingdevice.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/kernel/qpointingdevice.cpp')
-rw-r--r--src/gui/kernel/qpointingdevice.cpp613
1 files changed, 507 insertions, 106 deletions
diff --git a/src/gui/kernel/qpointingdevice.cpp b/src/gui/kernel/qpointingdevice.cpp
index 97bc8f5554..c4c1e5fd5c 100644
--- a/src/gui/kernel/qpointingdevice.cpp
+++ b/src/gui/kernel/qpointingdevice.cpp
@@ -1,44 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtGui module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qpointingdevice.h"
#include "qpointingdevice_p.h"
+#include "qwindowsysteminterface_p.h"
+#include "qeventpoint_p.h"
+
#include <QList>
#include <QLoggingCategory>
#include <QMutex>
@@ -48,7 +15,9 @@
QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(lcQpaInputDevices)
+using namespace Qt::StringLiterals;
+
+Q_LOGGING_CATEGORY(lcPointerGrab, "qt.pointer.grab");
/*!
\class QPointingDevice
@@ -79,7 +48,7 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaInputDevices)
In this type of device, the touch surface and display are integrated.
This means the surface and display typically have the same size, such
that there is a direct relationship between the touch points' physical
- positions and the coordinate reported by QTouchEvent::TouchPoint. As a
+ positions and the coordinate reported by QEventPoint. As a
result, Qt allows the user to interact directly with multiple QWidgets,
QGraphicsItems, or Qt Quick Items at the same time.
@@ -103,6 +72,9 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaInputDevices)
A device that is similar to a flat mouse with a transparent circle with
cross-hairs.
+ \value Keyboard
+ A keyboard.
+
\value AllDevices
Any of the above (used as a default filter value).
*/
@@ -133,53 +105,61 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaInputDevices)
\value Eraser
The other end of the stylus (if it has a virtual eraser on the other end).
\value Cursor
- A transparent circle with cross-hairs as found on a \l {DeviceType.Puck}{Puck} device.
+ A transparent circle with cross-hairs as found on a
+ \l {QInputDevice::DeviceType}{Puck} device.
\value AllPointerTypes
Any of the above (used as a default filter value).
*/
-/*! \enum QPointingDevice::Capability
+/*! \enum QPointingDevice::GrabTransition
- This enum is used with QPointingDevice::capabilities() to indicate what kind of information the
- touch device or its driver can provide.
+ This enum represents a transition of exclusive or passive grab
+ from one object (possibly \c nullptr) to another (possibly \c nullptr).
+ It is emitted as an argument of the QPointingDevice::grabChanged() signal.
- \value Position
- Indicates that position information is available, meaning that the
- pos() family of functions in the touch points return valid points.
-
- \value Area
- Indicates that touch area information is available, meaning that the
- rect() family of functions in the touch points return valid rectangles.
+ Valid values are:
- \value Pressure
- Indicates that pressure information is available, meaning that
- QPointerEvent::EventPoint::pressure() returns a valid value.
+ \value GrabExclusive
+ Emitted after QPointerEvent::setExclusiveGrabber().
+ \value UngrabExclusive
+ Emitted after QPointerEvent::setExclusiveGrabber() when the grabber is
+ set to \c nullptr, to notify that the grab has terminated normally.
+ \value CancelGrabExclusive
+ Emitted after QPointerEvent::setExclusiveGrabber() when the grabber is set
+ to a different object, to notify that the old grabber's grab is "stolen".
+ \value GrabPassive
+ Emitted after QPointerEvent::addPassiveGrabber().
+ \value UngrabPassive
+ Emitted when a passive grab is terminated normally,
+ for example after QPointerEvent::removePassiveGrabber().
+ \value CancelGrabPassive
+ Emitted when a passive grab is terminated abnormally (a gesture is canceled).
+ \value OverrideGrabPassive
+ This value is not currently used.
+*/
- \value Velocity
- Indicates that velocity information is available, meaning that
- QPointerEvent::EventPoint::velocity() returns a valid vector.
+/*! \fn void QPointingDevice::grabChanged(QObject *grabber, QPointingDevice::GrabTransition transition, const QPointerEvent *event, const QEventPoint &point) const
- \value RawPositions
- Indicates that the list returned by
- QPointerEvent::EventPoint::rawScreenPositions() may contain one or more
- positions for each touch point. This is relevant when the touch input
- gets filtered or corrected on the driver level.
+ This signal is emitted when the \a grabber object gains or loses an
+ exclusive or passive grab of \a point during delivery of \a event.
+ The \a transition tells what happened, from the perspective of the
+ \c grabber object.
- \value NormalizedPosition
- Indicates that the normalized position is available, meaning that
- QPointerEvent::EventPoint::normalizedPos() returns a valid value.
+ \note A grab transition from one object to another results in two signals,
+ to notify that one object has lost its grab, and to notify that there is
+ another grabber. In other cases, when transitioning to or from a non-grabbing
+ state, only one signal is emitted: the \a grabber argument is never \c nullptr.
- \value MouseEmulation
- Indicates that the device synthesizes mouse events.
+ \sa QPointerEvent::setExclusiveGrabber(), QPointerEvent::addPassiveGrabber(), QPointerEvent::removePassiveGrabber()
*/
/*!
- Creates a new invalid pointing device instance.
+ Creates a new invalid pointing device instance as a child of \a parent.
*/
-QPointingDevice::QPointingDevice()
- : QInputDevice(*(new QPointingDevicePrivate(QLatin1String("unknown"), -1,
+QPointingDevice::QPointingDevice(QObject *parent)
+ : QInputDevice(*(new QPointingDevicePrivate("unknown"_L1, -1,
DeviceType::Unknown, PointerType::Unknown,
- Capability::None, 0, 0)))
+ Capability::None, 0, 0)), parent)
{
}
@@ -189,8 +169,8 @@ QPointingDevice::~QPointingDevice()
/*!
Creates a new pointing device instance with the given
- \a deviceType, \a pointerType, \a capabilities, \a maxPoints,
- \a buttonCount, \a name, \a id and \a seatId.
+ \a name, \a deviceType, \a pointerType, \a capabilities, \a maxPoints,
+ \a buttonCount, \a seatName, \a uniqueId and \a parent.
*/
QPointingDevice::QPointingDevice(const QString &name, qint64 id, QInputDevice::DeviceType deviceType,
QPointingDevice::PointerType pointerType, Capabilities capabilities, int maxPoints, int buttonCount,
@@ -207,9 +187,10 @@ QPointingDevice::QPointingDevice(QPointingDevicePrivate &d, QObject *parent)
{
}
+#if QT_DEPRECATED_SINCE(6, 0)
/*!
\internal
- \deprecated Please use the constructor rather than setters.
+ \deprecated [6.0] Please use the constructor rather than setters.
Sets the device type \a devType and infers the pointer type.
*/
@@ -240,7 +221,7 @@ void QPointingDevice::setType(DeviceType devType)
/*!
\internal
- \deprecated Please use the constructor rather than setters.
+ \deprecated [6.0] Please use the constructor rather than setters.
*/
void QPointingDevice::setCapabilities(QInputDevice::Capabilities caps)
{
@@ -250,13 +231,14 @@ void QPointingDevice::setCapabilities(QInputDevice::Capabilities caps)
/*!
\internal
- \deprecated Please use the constructor rather than setters.
+ \deprecated [6.0] Please use the constructor rather than setters.
*/
void QPointingDevice::setMaximumTouchPoints(int c)
{
Q_D(QPointingDevice);
d->maximumTouchPoints = c;
}
+#endif // QT_DEPRECATED_SINCE(6, 0)
/*!
Returns the pointer type.
@@ -314,7 +296,7 @@ const QPointingDevice *QPointingDevice::primaryPointingDevice(const QString& sea
const QPointingDevice *mouse = nullptr;
const QPointingDevice *touchpad = nullptr;
for (const QInputDevice *dev : v) {
- if (dev->seatName() != seatName)
+ if (!seatName.isNull() && dev->seatName() != seatName)
continue;
if (dev->type() == QInputDevice::DeviceType::Mouse) {
if (!mouse)
@@ -331,55 +313,378 @@ const QPointingDevice *QPointingDevice::primaryPointingDevice(const QString& sea
qCDebug(lcQpaInputDevices) << "no mouse-like devices registered for seat" << seatName
<< "The platform plugin should have provided one via "
"QWindowSystemInterface::registerInputDevice(). Creating a default mouse for now.";
- mouse = new QPointingDevice(QLatin1String("core pointer"), 1, DeviceType::Mouse,
+ mouse = new QPointingDevice("core pointer"_L1, 1, DeviceType::Mouse,
PointerType::Generic, Capability::Position, 1, 3, seatName,
QPointingDeviceUniqueId(), QCoreApplication::instance());
QInputDevicePrivate::registerDevice(mouse);
return mouse;
}
- if (v.length() > 1)
+ if (v.size() > 1)
qCDebug(lcQpaInputDevices) << "core pointer ambiguous for seat" << seatName;
if (mouse)
return mouse;
return touchpad;
}
+QPointingDevicePrivate::~QPointingDevicePrivate()
+ = default;
+
/*!
+ \internal
Finds the device instance belonging to the drawing or eraser end of a particular stylus,
- identified by its \a deviceType, \a pointerType and \a uniqueId. The given \a busId
- may be used to update the stored USB ID, if it was not known before.
+ identified by its \a deviceType, \a pointerType, \a uniqueId and \a systemId.
+ Returns the device found, or \c nullptr if none was found.
+
+ If \a systemId is \c 0, it's not significant for the search.
+
+ If an instance matching the given \a deviceType and \a pointerType but with
+ only a default-constructed \c uniqueId is found, it will be assumed to be
+ the one we're looking for, its \c uniqueId will be updated to match the
+ given \a uniqueId, and its \c capabilities will be updated to match the
+ given \a capabilities. This is for the benefit of any platform plugin that can
+ discover the tablet itself at startup, along with the supported stylus types,
+ but then discovers specific styli later on as they come into proximity.
*/
-const QPointingDevice *QPointingDevice::tabletDevice(QInputDevice::DeviceType deviceType,
- QPointingDevice::PointerType pointerType,
- QPointingDeviceUniqueId uniqueId, quint32 busId)
+const QPointingDevice *QPointingDevicePrivate::queryTabletDevice(QInputDevice::DeviceType deviceType,
+ QPointingDevice::PointerType pointerType,
+ QPointingDeviceUniqueId uniqueId,
+ QPointingDevice::Capabilities capabilities,
+ qint64 systemId)
{
const auto &devices = QInputDevice::devices();
for (const QInputDevice *dev : devices) {
- if (dev->type() < DeviceType::Puck || dev->type() > DeviceType::Airbrush)
+ if (dev->type() < QPointingDevice::DeviceType::Puck || dev->type() > QPointingDevice::DeviceType::Airbrush)
continue;
const QPointingDevice *pdev = static_cast<const QPointingDevice *>(dev);
const auto devPriv = QPointingDevicePrivate::get(pdev);
bool uniqueIdDiscovered = (devPriv->uniqueId.numericId() == 0 && uniqueId.numericId() != 0);
if (devPriv->deviceType == deviceType && devPriv->pointerType == pointerType &&
+ (!systemId || devPriv->systemId == systemId) &&
(devPriv->uniqueId == uniqueId || uniqueIdDiscovered)) {
if (uniqueIdDiscovered) {
const_cast<QPointingDevicePrivate *>(devPriv)->uniqueId = uniqueId;
- qCDebug(lcQpaInputDevices) << "discovered unique ID of tablet tool" << pdev;
- }
- if (devPriv->busId.isEmpty() && busId) {
- const_cast<QPointingDevicePrivate *>(devPriv)->busId = QString::number(busId, 16);
- qCDebug(lcQpaInputDevices) << "discovered USB ID" << devPriv->busId << "of" << pdev;
+ if (capabilities)
+ const_cast<QPointingDevicePrivate *>(devPriv)->capabilities = capabilities;
+ qCDebug(lcQpaInputDevices) << "discovered unique ID and capabilities of tablet tool" << pdev;
}
return pdev;
}
}
- qCDebug(lcQpaInputDevices) << "failed to find registered tablet device" << deviceType << pointerType << Qt::hex << uniqueId.numericId()
- << "The platform plugin should have provided one via "
- "QWindowSystemInterface::registerInputDevice(). Creating a default one for now.";
- QPointingDevice *dev = new QPointingDevice(QLatin1String("fake tablet"), 2, deviceType, pointerType,
- QInputDevice::Capability::Position | QInputDevice::Capability::Pressure,
- 1, 1, QString(), uniqueId);
- QInputDevicePrivate::registerDevice(dev);
+ return nullptr;
+}
+
+/*!
+ \internal
+ Finds the device instance identified by its \a systemId.
+ Returns the device found, or \c nullptr if none was found.
+*/
+const QPointingDevice *QPointingDevicePrivate::pointingDeviceById(qint64 systemId)
+{
+ const auto &devices = QInputDevice::devices();
+ for (const QInputDevice *dev : devices) {
+ if (dev->type() >= QPointingDevice::DeviceType::Keyboard)
+ continue;
+ const QPointingDevice *pdev = static_cast<const QPointingDevice *>(dev);
+ const auto devPriv = QPointingDevicePrivate::get(pdev);
+ if (devPriv->systemId == systemId)
+ return pdev;
+ }
+ return nullptr;
+}
+
+/*!
+ \internal
+ First, ensure that the \a cancelEvent's QTouchEvent::points() list contains
+ all points that have exclusive grabs. Then send the event to each object
+ that has an exclusive grab of any of the points.
+*/
+void QPointingDevicePrivate::sendTouchCancelEvent(QTouchEvent *cancelEvent)
+{
+ // An incoming TouchCancel event will typically not contain any points, but
+ // QQuickPointerHandler::onGrabChanged needs to be called for each point
+ // that has an exclusive grabber. Adding those points to the event makes it
+ // an easy iteration there.
+ if (cancelEvent->points().isEmpty()) {
+ for (auto &epd : activePoints.values()) {
+ if (epd.exclusiveGrabber)
+ QMutableTouchEvent::from(cancelEvent)->addPoint(epd.eventPoint);
+ }
+ }
+ for (auto &epd : activePoints.values()) {
+ if (epd.exclusiveGrabber)
+ QCoreApplication::sendEvent(epd.exclusiveGrabber, cancelEvent);
+ // The next touch event can only be a TouchBegin, so clean up.
+ cancelEvent->setExclusiveGrabber(epd.eventPoint, nullptr);
+ cancelEvent->clearPassiveGrabbers(epd.eventPoint);
+ }
+}
+
+/*! \internal
+ Returns the active EventPointData instance with the given \a id, if available,
+ or \c nullptr if not.
+*/
+QPointingDevicePrivate::EventPointData *QPointingDevicePrivate::queryPointById(int id) const
+{
+ auto it = activePoints.find(id);
+ if (it == activePoints.end())
+ return nullptr;
+ return &it.value();
+}
+
+/*! \internal
+ Returns the active EventPointData instance with the given \a id, if available;
+ if not, appends a new instance and returns it.
+*/
+QPointingDevicePrivate::EventPointData *QPointingDevicePrivate::pointById(int id) const
+{
+ const auto [it, inserted] = activePoints.try_emplace(id);
+ if (inserted) {
+ Q_Q(const QPointingDevice);
+ auto &epd = it.value();
+ QMutableEventPoint::setId(epd.eventPoint, id);
+ QMutableEventPoint::setDevice(epd.eventPoint, q);
+ }
+ return &it.value();
+}
+
+/*! \internal
+ Remove the active EventPointData instance with the given \a id.
+*/
+void QPointingDevicePrivate::removePointById(int id)
+{
+ activePoints.remove(id);
+}
+
+/*!
+ \internal
+ Find the first non-null target (widget) via QMutableEventPoint::target()
+ in the active points. This is the widget that will receive any event that
+ comes from a touchpad, even if some of the touchpoints fall spatially on
+ other windows.
+*/
+QObject *QPointingDevicePrivate::firstActiveTarget() const
+{
+ for (auto &pt : activePoints.values()) {
+ if (auto target = QMutableEventPoint::target(pt.eventPoint))
+ return target;
+ }
+ return nullptr;
+}
+
+/*! \internal
+ Find the first non-null QWindow instance via QMutableEventPoint::window()
+ in the active points. This is the window that will receive any event that
+ comes from a touchpad, even if some of the touchpoints fall spatially on
+ other windows.
+*/
+QWindow *QPointingDevicePrivate::firstActiveWindow() const
+{
+ for (auto &pt : activePoints.values()) {
+ if (auto window = QMutableEventPoint::window(pt.eventPoint))
+ return window;
+ }
+ return nullptr;
+}
+
+/*! \internal
+ Return the exclusive grabber of the first point in activePoints.
+ This is mainly for autotests that try to verify the "current" grabber
+ outside the context of event delivery, which is something that the rest
+ of the codebase should not be doing.
+*/
+QObject *QPointingDevicePrivate::firstPointExclusiveGrabber() const
+{
+ if (activePoints.isEmpty())
+ return nullptr;
+ return activePoints.values().first().exclusiveGrabber;
+}
+
+void QPointingDevicePrivate::setExclusiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *exclusiveGrabber)
+{
+ Q_Q(QPointingDevice);
+ auto persistentPoint = queryPointById(point.id());
+ if (!persistentPoint) {
+ qWarning() << "point is not in activePoints" << point;
+ return;
+ }
+ Q_ASSERT(persistentPoint->eventPoint.id() == point.id());
+ if (persistentPoint->exclusiveGrabber == exclusiveGrabber)
+ return;
+ auto oldGrabber = persistentPoint->exclusiveGrabber;
+ persistentPoint->exclusiveGrabber = exclusiveGrabber;
+ if (oldGrabber)
+ emit q->grabChanged(oldGrabber, exclusiveGrabber ? QPointingDevice::CancelGrabExclusive : QPointingDevice::UngrabExclusive,
+ event, persistentPoint->eventPoint);
+ if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
+ qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
+ << "@" << point.scenePosition()
+ << ": grab" << oldGrabber << "->" << exclusiveGrabber;
+ }
+ QMutableEventPoint::setGlobalGrabPosition(persistentPoint->eventPoint, point.globalPosition());
+ if (exclusiveGrabber)
+ emit q->grabChanged(exclusiveGrabber, QPointingDevice::GrabExclusive, event, point);
+ else
+ persistentPoint->exclusiveGrabberContext.clear();
+}
+
+/*!
+ \internal
+ Call QEventPoint::setExclusiveGrabber(nullptr) on each active point that has a grabber.
+*/
+bool QPointingDevicePrivate::removeExclusiveGrabber(const QPointerEvent *event, const QObject *grabber)
+{
+ bool ret = false;
+ for (auto &pt : activePoints.values()) {
+ if (pt.exclusiveGrabber == grabber) {
+ setExclusiveGrabber(event, pt.eventPoint, nullptr);
+ ret = true;
+ }
+ }
+ return ret;
+}
+
+bool QPointingDevicePrivate::addPassiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *grabber)
+{
+ Q_Q(QPointingDevice);
+ auto persistentPoint = queryPointById(point.id());
+ if (!persistentPoint) {
+ qWarning() << "point is not in activePoints" << point;
+ return false;
+ }
+ if (persistentPoint->passiveGrabbers.contains(grabber))
+ return false;
+ if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
+ qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
+ << ": grab (passive)" << grabber;
+ }
+ persistentPoint->passiveGrabbers << grabber;
+ emit q->grabChanged(grabber, QPointingDevice::GrabPassive, event, point);
+ return true;
+}
+
+bool QPointingDevicePrivate::setPassiveGrabberContext(QPointingDevicePrivate::EventPointData *epd, QObject *grabber, QObject *context)
+{
+ qsizetype i = epd->passiveGrabbers.indexOf(grabber);
+ if (i < 0)
+ return false;
+ if (epd->passiveGrabbersContext.size() <= i)
+ epd->passiveGrabbersContext.resize(i + 1);
+ epd->passiveGrabbersContext[i] = context;
+ return true;
+}
+
+bool QPointingDevicePrivate::removePassiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *grabber)
+{
+ Q_Q(QPointingDevice);
+ auto persistentPoint = queryPointById(point.id());
+ if (!persistentPoint) {
+ qWarning() << "point is not in activePoints" << point;
+ return false;
+ }
+ qsizetype i = persistentPoint->passiveGrabbers.indexOf(grabber);
+ if (i >= 0) {
+ if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
+ qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
+ << ": removing passive grabber" << grabber;
+ }
+ emit q->grabChanged(grabber, QPointingDevice::UngrabPassive, event, point);
+ persistentPoint->passiveGrabbers.removeAt(i);
+ if (persistentPoint->passiveGrabbersContext.size()) {
+ Q_ASSERT(persistentPoint->passiveGrabbersContext.size() > i);
+ persistentPoint->passiveGrabbersContext.removeAt(i);
+ }
+ return true;
+ }
+ return false;
+}
+
+void QPointingDevicePrivate::clearPassiveGrabbers(const QPointerEvent *event, const QEventPoint &point)
+{
+ Q_Q(QPointingDevice);
+ auto persistentPoint = queryPointById(point.id());
+ if (!persistentPoint) {
+ qWarning() << "point is not in activePoints" << point;
+ return;
+ }
+ if (persistentPoint->passiveGrabbers.isEmpty())
+ return;
+ if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
+ qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
+ << ": clearing" << persistentPoint->passiveGrabbers;
+ }
+ for (auto g : persistentPoint->passiveGrabbers)
+ emit q->grabChanged(g, QPointingDevice::UngrabPassive, event, point);
+ persistentPoint->passiveGrabbers.clear();
+ persistentPoint->passiveGrabbersContext.clear();
+}
+
+/*!
+ \internal
+ Removes the given \a grabber as both passive and exclusive grabber from all
+ points in activePoints where it's currently found. If \a cancel is \c true,
+ the transition emitted from the grabChanged() signal will be
+ \c CancelGrabExclusive or \c CancelGrabPassive. Otherwise it will be
+ \c UngrabExclusive or \c UngrabPassive.
+
+ \note This function provides a way to work around the limitation that we
+ normally change grabbers only during event delivery; but it's also more expensive.
+*/
+void QPointingDevicePrivate::removeGrabber(QObject *grabber, bool cancel)
+{
+ Q_Q(QPointingDevice);
+ for (auto ap : activePoints) {
+ auto &epd = ap.second;
+ if (epd.exclusiveGrabber.data() == grabber) {
+ qCDebug(lcPointerGrab) << name << "point" << epd.eventPoint.id() << epd.eventPoint.state()
+ << "@" << epd.eventPoint.scenePosition()
+ << ": grab" << grabber << "-> nullptr";
+ epd.exclusiveGrabber.clear();
+ epd.exclusiveGrabberContext.clear();
+ emit q->grabChanged(grabber,
+ cancel ? QPointingDevice::CancelGrabExclusive : QPointingDevice::UngrabExclusive,
+ nullptr, epd.eventPoint);
+ }
+ qsizetype pi = epd.passiveGrabbers.indexOf(grabber);
+ if (pi >= 0) {
+ qCDebug(lcPointerGrab) << name << "point" << epd.eventPoint.id() << epd.eventPoint.state()
+ << ": removing passive grabber" << grabber;
+ epd.passiveGrabbers.removeAt(pi);
+ if (epd.passiveGrabbersContext.size()) {
+ Q_ASSERT(epd.passiveGrabbersContext.size() > pi);
+ epd.passiveGrabbersContext.removeAt(pi);
+ }
+ emit q->grabChanged(grabber,
+ cancel ? QPointingDevice::CancelGrabPassive : QPointingDevice::UngrabPassive,
+ nullptr, epd.eventPoint);
+ }
+ }
+}
+
+/*!
+ \internal
+ Finds the device instance belonging to the drawing or eraser end of a particular stylus,
+ identified by its \a deviceType, \a pointerType and \a uniqueId. If an existing device
+ is not found, a new one is created and registered, with a warning.
+
+ This function is called from QWindowSystemInterface. Platform plugins should use
+ \l queryTabletDeviceInstance() to check whether a tablet stylus coming into proximity
+ is previously known; if not known, the plugin should create and register the stylus.
+*/
+const QPointingDevice *QPointingDevicePrivate::tabletDevice(QInputDevice::DeviceType deviceType,
+ QPointingDevice::PointerType pointerType,
+ QPointingDeviceUniqueId uniqueId)
+{
+ const QPointingDevice *dev = queryTabletDevice(deviceType, pointerType, uniqueId);
+ if (!dev) {
+ qCDebug(lcQpaInputDevices) << "failed to find registered tablet device"
+ << deviceType << pointerType << Qt::hex << uniqueId.numericId()
+ << "The platform plugin should have provided one via "
+ "QWindowSystemInterface::registerInputDevice(). Creating a default one for now.";
+ dev = new QPointingDevice("fake tablet"_L1, 2, deviceType, pointerType,
+ QInputDevice::Capability::Position | QInputDevice::Capability::Pressure,
+ 1, 1, QString(), uniqueId, QCoreApplication::instance());
+ QInputDevicePrivate::registerDevice(dev);
+ }
return dev;
}
@@ -401,16 +706,23 @@ QDebug operator<<(QDebug debug, const QPointingDevice *device)
debug.noquote();
debug << "QPointingDevice(";
if (device) {
- debug << '"' << device->name() << "\", type=";
+ debug << '"' << device->name() << "\" ";
QtDebugUtils::formatQEnum(debug, device->type());
- debug << ", id=" << Qt::hex << device->id() << Qt::dec << ", seat=" << device->seatName();
- debug << ", pointerType=";
- QtDebugUtils::formatQEnum(debug, device->pointerType());
- debug << ", capabilities=";
- QtDebugUtils::formatQFlags(debug, device->capabilities());
- debug << ", maximumTouchPoints=" << device->maximumPoints();
- if (device->uniqueId().numericId())
- debug << ", uniqueId=" << Qt::hex << device->uniqueId().numericId() << Qt::dec;
+ debug << " id=" << device->systemId();
+ if (!device->seatName().isEmpty())
+ debug << " seat=" << device->seatName();
+ if (device->pointerType() != QPointingDevice::PointerType::Generic) {
+ debug << " ptrType=";
+ QtDebugUtils::formatQEnum(debug, device->pointerType());
+ }
+ if (int(device->capabilities()) != int(QInputDevice::Capability::Position)) {
+ debug << " caps=";
+ QtDebugUtils::formatQFlags(debug, device->capabilities());
+ }
+ if (device->maximumPoints() > 1)
+ debug << " maxPts=" << device->maximumPoints();
+ if (device->uniqueId().isValid())
+ debug << " uniqueId=" << Qt::hex << device->uniqueId().numericId() << Qt::dec;
} else {
debug << '0';
}
@@ -419,4 +731,93 @@ QDebug operator<<(QDebug debug, const QPointingDevice *device)
}
#endif // !QT_NO_DEBUG_STREAM
+/*!
+ \class QPointingDeviceUniqueId
+ \since 5.8
+ \ingroup events
+ \inmodule QtGui
+
+ \brief QPointingDeviceUniqueId identifies a unique object, such as a tagged token
+ or stylus, which is used with a pointing device.
+
+ QPointingDeviceUniqueIds can be compared for equality, and can be used as keys in a QHash.
+ You get access to the numerical ID via numericId(), if the device supports such IDs.
+ For future extensions, though, you should not use that function, but compare objects
+ of this type using the equality operator.
+
+ This class is a thin wrapper around an integer ID. You pass it into and out of
+ functions by value.
+
+ \sa QEventPoint
+*/
+
+/*!
+ \fn QPointingDeviceUniqueId::QPointingDeviceUniqueId()
+ Constructs an invalid unique pointer ID.
+*/
+
+/*!
+ Constructs a unique pointer ID from numeric ID \a id.
+*/
+QPointingDeviceUniqueId QPointingDeviceUniqueId::fromNumericId(qint64 id)
+{
+ QPointingDeviceUniqueId result;
+ result.m_numericId = id;
+ return result;
+}
+
+/*!
+ \fn bool QPointingDeviceUniqueId::isValid() const
+
+ Returns whether this unique pointer ID is valid, that is, it represents an actual
+ pointer.
+*/
+
+/*!
+ \property QPointingDeviceUniqueId::numericId
+ \brief the numeric unique ID of the token represented by a touchpoint
+
+ If the device provides a numeric ID, isValid() returns true, and this
+ property provides the numeric ID;
+ otherwise it is -1.
+
+ You should not use the value of this property in portable code, but
+ instead rely on equality to identify pointers.
+
+ \sa isValid()
+*/
+qint64 QPointingDeviceUniqueId::numericId() const noexcept
+{
+ return m_numericId;
+}
+
+/*!
+ \fn bool QPointingDeviceUniqueId::operator==(QPointingDeviceUniqueId lhs, QPointingDeviceUniqueId rhs)
+ \since 5.8
+
+ Returns whether the two unique pointer IDs \a lhs and \a rhs identify the same pointer
+ (\c true) or not (\c false).
+*/
+
+/*!
+ \fn bool QPointingDeviceUniqueId::operator!=(QPointingDeviceUniqueId lhs, QPointingDeviceUniqueId rhs)
+ \since 5.8
+
+ Returns whether the two unique pointer IDs \a lhs and \a rhs identify different pointers
+ (\c true) or not (\c false).
+*/
+
+/*!
+ \relates QPointingDeviceUniqueId
+ \since 5.8
+
+ Returns the hash value for \a key, using \a seed to seed the calculation.
+*/
+size_t qHash(QPointingDeviceUniqueId key, size_t seed) noexcept
+{
+ return qHash(key.numericId(), seed);
+}
+
QT_END_NAMESPACE
+
+#include "moc_qpointingdevice.cpp"