| 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 "qinputdevice.h" |
| 5 | #include "qinputdevice_p.h" |
| 6 | #include "qpointingdevice.h" |
| 7 | #include "qwindowsysteminterface_p.h" |
| 8 | #include <QCoreApplication> |
| 9 | #include <QDebug> |
| 10 | #include <QMutex> |
| 11 | #include <QScreen> |
| 12 | |
| 13 | QT_BEGIN_NAMESPACE |
| 14 | |
| 15 | using namespace Qt::StringLiterals; |
| 16 | |
| 17 | /*! |
| 18 | \class QInputDevice |
| 19 | \brief The QInputDevice class describes a device from which a QInputEvent originates. |
| 20 | \since 6.0 |
| 21 | \inmodule QtGui |
| 22 | |
| 23 | Each QInputEvent contains a QInputDevice pointer to allow accessing |
| 24 | device-specific properties like type, capabilities and seat. It is the |
| 25 | responsibility of the platform or generic plug-ins to discover, create and |
| 26 | register an instance of this class corresponding to each available input |
| 27 | device, via QWindowSystemInterface::registerInputDevice(), before |
| 28 | generating any input event referring to that device. |
| 29 | |
| 30 | Applications do not need to instantiate this class, but can read the |
| 31 | instances pointed to by QInputEvent::device() and QInputDevice::devices(). |
| 32 | */ |
| 33 | |
| 34 | /*! |
| 35 | \enum QInputDevice::Capability |
| 36 | |
| 37 | Indicates what kind of information the input device or its driver can |
| 38 | provide. |
| 39 | |
| 40 | \value None |
| 41 | No information about input device capabilities available. |
| 42 | |
| 43 | \value Position |
| 44 | Indicates that position information is available, meaning that the |
| 45 | position() family of functions in the touch points return valid points. |
| 46 | |
| 47 | \value Area |
| 48 | Indicates that touch area information is available, meaning that |
| 49 | QEventPoint::ellipseDiameters() in the touch points return valid |
| 50 | values. |
| 51 | |
| 52 | \value Pressure |
| 53 | Indicates that pressure information is available, meaning that |
| 54 | QEventPoint::pressure() returns a valid value. |
| 55 | |
| 56 | \value Velocity |
| 57 | Indicates that velocity information is available, meaning that |
| 58 | QEventPoint::velocity() returns a valid vector. |
| 59 | |
| 60 | \value NormalizedPosition |
| 61 | Indicates that the normalized position is available, meaning that |
| 62 | QEventPoint::globalPosition() returns a valid value. |
| 63 | |
| 64 | \value MouseEmulation |
| 65 | Indicates that the device synthesizes mouse events. |
| 66 | |
| 67 | \value Scroll |
| 68 | Indicates that the device has a scroll capability. |
| 69 | |
| 70 | \value [since 6.2] PixelScroll |
| 71 | Indicates that the device (usually a |
| 72 | \l {QInputDevice::DeviceType::TouchPad}{touchpad}) |
| 73 | scrolls with \l {QWheelEvent::pixelDelta()}{pixel precision}. |
| 74 | |
| 75 | \value Hover |
| 76 | Indicates that the device has a hover capability. |
| 77 | |
| 78 | \value Rotation |
| 79 | Indicates that \l {QEventPoint::}{rotation} information is available. |
| 80 | |
| 81 | \value XTilt |
| 82 | Indicates that \l {QTabletEvent::xTilt()}{tilt} information is |
| 83 | available for the X-axis. |
| 84 | |
| 85 | \value YTilt |
| 86 | Indicates that \l {QTabletEvent::yTilt()}{tilt} information is |
| 87 | available for the Y-axis. |
| 88 | |
| 89 | \value TangentialPressure |
| 90 | Indicates that \l {QTabletEvent::tangentialPressure()} |
| 91 | {tangential pressure} information is available. |
| 92 | |
| 93 | \value ZPosition |
| 94 | Indicates that position information for the \l {QTabletEvent::z()} |
| 95 | {Z-axis} is available. |
| 96 | |
| 97 | \value All |
| 98 | */ |
| 99 | |
| 100 | /*! |
| 101 | Creates a new invalid input device instance as a child of \a parent. |
| 102 | */ |
| 103 | QInputDevice::QInputDevice(QObject *parent) |
| 104 | : QObject(*(new QInputDevicePrivate(QString(), -1, QInputDevice::DeviceType::Unknown)), parent) |
| 105 | { |
| 106 | } |
| 107 | |
| 108 | QInputDevice::~QInputDevice() |
| 109 | { |
| 110 | QInputDevicePrivate::unregisterDevice(dev: this); |
| 111 | } |
| 112 | |
| 113 | /*! |
| 114 | Creates a new input device instance. The given \a name is normally a |
| 115 | manufacturer-assigned model name if available, or something else |
| 116 | identifiable; \a id is a platform-specific number that will be unique per |
| 117 | device (for example the xinput ID on X11); \a type identifies what kind of |
| 118 | device. On window systems that are capable of handling input from multiple |
| 119 | users or sets of input devices at the same time (such as Wayland or X11), |
| 120 | \a seatName identifies the name of the set of devices that will be used |
| 121 | together. If the device is a child or slave device (for example one of |
| 122 | several mice that can take turns moving the "core pointer"), the master |
| 123 | device should be given as the \a parent. |
| 124 | |
| 125 | The platform plugin creates, registers and continues to own each device |
| 126 | instance; usually \a parent should be given for memory management purposes |
| 127 | even if there is no master for a particular device. |
| 128 | |
| 129 | By default, capabilities() are \c None. |
| 130 | */ |
| 131 | QInputDevice::QInputDevice(const QString &name, qint64 id, QInputDevice::DeviceType type, |
| 132 | const QString &seatName, QObject *parent) |
| 133 | : QObject(*new QInputDevicePrivate(name, id, type, QInputDevice::Capability::None, seatName), parent) |
| 134 | { |
| 135 | } |
| 136 | |
| 137 | /*! |
| 138 | \internal |
| 139 | */ |
| 140 | QInputDevice::QInputDevice(QInputDevicePrivate &d, QObject *parent) |
| 141 | : QObject(d, parent) |
| 142 | { |
| 143 | } |
| 144 | |
| 145 | /*! |
| 146 | Returns the region within the \l{QScreen::availableVirtualGeometry}{virtual desktop} |
| 147 | that this device can access. |
| 148 | |
| 149 | For example a \l {QInputDevice::DeviceType}{TouchScreen} input |
| 150 | device is fixed in place upon a single physical screen, and usually |
| 151 | calibrated so that this area is the same as QScreen::geometry(); whereas a |
| 152 | \l {QInputDevice::DeviceType}{Mouse} can probably access all screens |
| 153 | on the virtual desktop. A Wacom graphics tablet may be configured in a way |
| 154 | that it's mapped to all screens, or only to the screen where the user |
| 155 | prefers to create drawings, or to the window in which drawing occurs. |
| 156 | A \l {QInputDevice::DeviceType}{Stylus} device that is integrated |
| 157 | with a touchscreen may be physically limited to that screen. |
| 158 | |
| 159 | If the returned rectangle is \l {QRect::isNull()}{null}, it means this device |
| 160 | can access the entire virtual desktop. |
| 161 | */ |
| 162 | QRect QInputDevice::availableVirtualGeometry() const |
| 163 | { |
| 164 | Q_D(const QInputDevice); |
| 165 | return d->availableVirtualGeometry; |
| 166 | } |
| 167 | |
| 168 | /*! |
| 169 | Returns the device name. |
| 170 | |
| 171 | This string may be empty. It is however useful on systems that have |
| 172 | multiple input devices: it can be used to differentiate from which device a |
| 173 | QPointerEvent originates. |
| 174 | */ |
| 175 | QString QInputDevice::name() const |
| 176 | { |
| 177 | Q_D(const QInputDevice); |
| 178 | return d->name; |
| 179 | } |
| 180 | |
| 181 | /*! |
| 182 | Returns the device type. |
| 183 | */ |
| 184 | QInputDevice::DeviceType QInputDevice::type() const |
| 185 | { |
| 186 | Q_D(const QInputDevice); |
| 187 | return d->deviceType; |
| 188 | } |
| 189 | |
| 190 | /*! |
| 191 | Returns the device capabilities. |
| 192 | */ |
| 193 | QInputDevice::Capabilities QInputDevice::capabilities() const |
| 194 | { |
| 195 | Q_D(const QInputDevice); |
| 196 | return QInputDevice::Capabilities(d->capabilities); |
| 197 | } |
| 198 | |
| 199 | /*! |
| 200 | Returns whether the device capabilities include the given \a capability. |
| 201 | */ |
| 202 | bool QInputDevice::hasCapability(QInputDevice::Capability capability) const |
| 203 | { |
| 204 | return capabilities().testFlag(flag: capability); |
| 205 | } |
| 206 | |
| 207 | /*! |
| 208 | Returns the platform specific system ID (for example xinput ID on the X11 platform). |
| 209 | |
| 210 | All platforms are expected to provide a unique system ID for each device. |
| 211 | */ |
| 212 | qint64 QInputDevice::systemId() const |
| 213 | { |
| 214 | Q_D(const QInputDevice); |
| 215 | return d->systemId; |
| 216 | } |
| 217 | |
| 218 | /*! |
| 219 | Returns the seat with which the device is associated, if known; otherwise empty. |
| 220 | |
| 221 | Devices that are intended to be used together by one user may be configured |
| 222 | to have the same seat name. That is only possible on Wayland and X11 |
| 223 | platforms so far. |
| 224 | */ |
| 225 | QString QInputDevice::seatName() const |
| 226 | { |
| 227 | Q_D(const QInputDevice); |
| 228 | return d->seatName; |
| 229 | } |
| 230 | |
| 231 | using InputDevicesList = QList<const QInputDevice *>; |
| 232 | Q_GLOBAL_STATIC(InputDevicesList, deviceList) |
| 233 | Q_CONSTINIT static QBasicMutex devicesMutex; |
| 234 | |
| 235 | /*! |
| 236 | Returns a list of all registered input devices (keyboards and pointing devices). |
| 237 | |
| 238 | \note The list of devices is not always complete on all platforms. So far, |
| 239 | the most-complete information is available on the \l {Qt for Linux/X11}{X11} |
| 240 | platform, at startup and in response to hot-plugging. Most other platforms |
| 241 | are only able to provide generic devices of various types, only after receiving |
| 242 | events from them; and most platforms do not tell Qt when a device is plugged in, |
| 243 | or when it is unplugged at runtime. |
| 244 | |
| 245 | \note The returned list cannot be used to add new devices. To add a simulated |
| 246 | touch screen for an autotest, QTest::createTouchDevice() can be used. |
| 247 | Platform plugins should call QWindowSystemInterface::registerInputDevice() |
| 248 | to add devices as they are discovered. |
| 249 | */ |
| 250 | QList<const QInputDevice *> QInputDevice::devices() |
| 251 | { |
| 252 | QMutexLocker lock(&devicesMutex); |
| 253 | return *deviceList(); |
| 254 | } |
| 255 | |
| 256 | /*! |
| 257 | \since 6.3 |
| 258 | |
| 259 | Returns a list of seat names for all registered input devices (keyboards and pointing devices). |
| 260 | */ |
| 261 | QStringList QInputDevice::seatNames() |
| 262 | { |
| 263 | QMutexLocker locker(&devicesMutex); |
| 264 | const InputDevicesList devices = *deviceList(); |
| 265 | locker.unlock(); |
| 266 | QStringList result; |
| 267 | for (const QInputDevice *d : devices) { |
| 268 | if (!result.contains(str: d->seatName())) |
| 269 | result.append(t: d->seatName()); |
| 270 | } |
| 271 | |
| 272 | return result; |
| 273 | } |
| 274 | |
| 275 | /*! |
| 276 | Returns the core or master keyboard on the given seat \a seatName. |
| 277 | */ |
| 278 | const QInputDevice *QInputDevice::primaryKeyboard(const QString& seatName) |
| 279 | { |
| 280 | QMutexLocker locker(&devicesMutex); |
| 281 | const InputDevicesList devices = *deviceList(); |
| 282 | locker.unlock(); |
| 283 | const QInputDevice *ret = nullptr; |
| 284 | for (const QInputDevice *d : devices) { |
| 285 | if (d->type() != DeviceType::Keyboard) |
| 286 | continue; |
| 287 | if (seatName.isNull() || d->seatName() == seatName) { |
| 288 | // the master keyboard's parent is not another input device |
| 289 | if (!d->parent() || !qobject_cast<const QInputDevice *>(object: d->parent())) |
| 290 | return d; |
| 291 | if (!ret) |
| 292 | ret = d; |
| 293 | } |
| 294 | } |
| 295 | if (!ret) { |
| 296 | qCDebug(lcQpaInputDevices) << "no keyboards registered for seat" << seatName |
| 297 | << "The platform plugin should have provided one via " |
| 298 | "QWindowSystemInterface::registerInputDevice(). Creating a default one for now." ; |
| 299 | ret = new QInputDevice("core keyboard"_L1 , 0, DeviceType::Keyboard, seatName, QCoreApplication::instance()); |
| 300 | QInputDevicePrivate::registerDevice(dev: ret); |
| 301 | return ret; |
| 302 | } |
| 303 | qWarning() << "core keyboard ambiguous for seat" << seatName; |
| 304 | return ret; |
| 305 | } |
| 306 | |
| 307 | QInputDevicePrivate::~QInputDevicePrivate() |
| 308 | = default; |
| 309 | |
| 310 | /*! |
| 311 | \internal |
| 312 | Checks whether a matching device is already registered |
| 313 | (via operator==, not pointer equality). |
| 314 | */ |
| 315 | bool QInputDevicePrivate::isRegistered(const QInputDevice *dev) |
| 316 | { |
| 317 | if (!dev) |
| 318 | return false; |
| 319 | QMutexLocker locker(&devicesMutex); |
| 320 | InputDevicesList v = *deviceList(); |
| 321 | for (const QInputDevice *d : v) |
| 322 | if (d && *d == *dev) |
| 323 | return true; |
| 324 | return false; |
| 325 | } |
| 326 | |
| 327 | /*! |
| 328 | \internal |
| 329 | Find the device with the given \a systemId (for example the xinput |
| 330 | device ID on X11), which is expected to be unique if nonzero. |
| 331 | |
| 332 | If the \a systemId is not unique, this function returns the first one found. |
| 333 | |
| 334 | \note Use QInputDevicePrivate::queryTabletDevice() if the device is a |
| 335 | tablet or a tablet stylus; in that case, \a id is not unique. |
| 336 | */ |
| 337 | const QInputDevice *QInputDevicePrivate::fromId(qint64 systemId) |
| 338 | { |
| 339 | QMutexLocker locker(&devicesMutex); |
| 340 | for (const QInputDevice *dev : *deviceList()) { |
| 341 | if (dev->systemId() == systemId) |
| 342 | return dev; |
| 343 | } |
| 344 | return nullptr; |
| 345 | } |
| 346 | |
| 347 | void QInputDevicePrivate::registerDevice(const QInputDevice *dev) |
| 348 | { |
| 349 | QMutexLocker lock(&devicesMutex); |
| 350 | deviceList()->append(t: dev); |
| 351 | qCInfo(lcQpaInputDevices) << "Registered" << dev; |
| 352 | } |
| 353 | |
| 354 | /*! |
| 355 | \internal |
| 356 | */ |
| 357 | void QInputDevicePrivate::unregisterDevice(const QInputDevice *dev) |
| 358 | { |
| 359 | if (deviceList.isDestroyed()) |
| 360 | return; // nothing to remove! |
| 361 | |
| 362 | QMutexLocker lock(&devicesMutex); |
| 363 | deviceList()->removeOne(t: dev); |
| 364 | qCInfo(lcQpaInputDevices) << "Unregistered" << dev; |
| 365 | } |
| 366 | |
| 367 | bool QInputDevice::operator==(const QInputDevice &other) const |
| 368 | { |
| 369 | return systemId() == other.systemId(); |
| 370 | } |
| 371 | |
| 372 | #ifndef QT_NO_DEBUG_STREAM |
| 373 | QDebug operator<<(QDebug debug, const QInputDevice *device) |
| 374 | { |
| 375 | QDebugStateSaver saver(debug); |
| 376 | debug.nospace(); |
| 377 | debug.noquote(); |
| 378 | |
| 379 | debug << "QInputDevice(" ; |
| 380 | if (!device) { |
| 381 | debug << "0)" ; |
| 382 | return debug; |
| 383 | } |
| 384 | |
| 385 | const QInputDevicePrivate *d = QInputDevicePrivate::get(q: device); |
| 386 | |
| 387 | if (d->pointingDeviceType) |
| 388 | return operator<<(debug, static_cast<const QPointingDevice *>(device)); |
| 389 | |
| 390 | debug << "QInputDevice(" ; |
| 391 | debug << '"' << device->name() << "\", type=" << device->type() |
| 392 | << ", ID=" << device->systemId() << ", seat='" << device->seatName() << "'" ; |
| 393 | debug << ')'; |
| 394 | return debug; |
| 395 | } |
| 396 | #endif // !QT_NO_DEBUG_STREAM |
| 397 | |
| 398 | QT_END_NAMESPACE |
| 399 | |
| 400 | #include "moc_qinputdevice.cpp" |
| 401 | |