Client: Add basic support for tablet-unstable-v2
[ChangeLog][QPA plugin] Added support for drawing tablets through the tablet-unstable-v2 protocol. Adds support for the basic one tablet, one tool, no pads scenario. Besides the auto tests, I tested with a Huion Kamvas Pro 16 with a simple pressure sensitive pen with two buttons. As far as I can tell, it works the same way as on xcb on Sway and Gnome Shell. Also tested on a Wacom PTZ-630 with a mouse, art pen, airbrush and a stylus. Mapped the distance event to QTabletEvent::Z and slider to QTabletEvent::tangentialPressure. For now we send QTabletEvents even when there's a mouse or finger on the tablet. Those should ideally be sent as QMouseEvents/QTouchEvents, but that's out of scope for this patch, but at least we will send synthesized mouse events if the tablet events are not accepted. Change-Id: I93291ffa5f00fa2bb8533eddd8d873b84a3386b8 Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
This commit is contained in:
parent
8abd3e5f55
commit
4d844d3640
@ -89,6 +89,24 @@ Copyright (c) 2013 BMW Car IT GmbH"
|
||||
"Copyright": "Copyright © 2013-2014 Collabora, Ltd."
|
||||
},
|
||||
|
||||
{
|
||||
"Id": "wayland-tablet-protocol",
|
||||
"Name": "Wayland Tablet Protocol",
|
||||
"QDocModule": "qtwaylandcompositor",
|
||||
"QtUsage": "Used in the Qt Wayland platform plugin",
|
||||
"Files": "tablet-unstable-v2.xml",
|
||||
|
||||
"Description": "",
|
||||
"Homepage": "https://wayland.freedesktop.org",
|
||||
"Version": "unstable v2, version 1",
|
||||
"DownloadLocation": "https://cgit.freedesktop.org/wayland/wayland-protocols/plain/unstable/tablet/tablet-unstable-v2.xml",
|
||||
"LicenseId": "MIT",
|
||||
"License": "MIT License",
|
||||
"LicenseFile": "MIT_LICENSE.txt",
|
||||
"Copyright": "Copyright 2014 © Stephen "Lyude" Chandler Paul
|
||||
Copyright 2015-2016 © Red Hat, Inc."
|
||||
},
|
||||
|
||||
{
|
||||
"Id": "wayland-viewporter-protocol",
|
||||
"Name": "Wayland Viewporter Protocol",
|
||||
|
1178
src/3rdparty/wayland/protocols/tablet-unstable-v2.xml
vendored
Normal file
1178
src/3rdparty/wayland/protocols/tablet-unstable-v2.xml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -36,6 +36,7 @@ WAYLANDCLIENTSOURCES += \
|
||||
../extensions/qt-key-unstable-v1.xml \
|
||||
../extensions/qt-windowmanager.xml \
|
||||
../3rdparty/protocol/wp-primary-selection-unstable-v1.xml \
|
||||
../3rdparty/protocol/tablet-unstable-v2.xml \
|
||||
../3rdparty/protocol/text-input-unstable-v2.xml \
|
||||
../3rdparty/protocol/xdg-output-unstable-v1.xml \
|
||||
../3rdparty/protocol/wayland.xml
|
||||
@ -52,6 +53,7 @@ SOURCES += qwaylandintegration.cpp \
|
||||
qwaylandextendedsurface.cpp \
|
||||
qwaylandsubsurface.cpp \
|
||||
qwaylandsurface.cpp \
|
||||
qwaylandtabletv2.cpp \
|
||||
qwaylandtouch.cpp \
|
||||
qwaylandqtkey.cpp \
|
||||
../shared/qwaylandmimehelper.cpp \
|
||||
@ -77,6 +79,7 @@ HEADERS += qwaylandintegration_p.h \
|
||||
qwaylandextendedsurface_p.h \
|
||||
qwaylandsubsurface_p.h \
|
||||
qwaylandsurface_p.h \
|
||||
qwaylandtabletv2_p.h \
|
||||
qwaylandtouch_p.h \
|
||||
qwaylandqtkey_p.h \
|
||||
qwaylandabstractdecoration_p.h \
|
||||
|
@ -69,6 +69,7 @@
|
||||
#include "qwaylandextendedsurface_p.h"
|
||||
#include "qwaylandsubsurface_p.h"
|
||||
#include "qwaylandtouch_p.h"
|
||||
#include "qwaylandtabletv2_p.h"
|
||||
#include "qwaylandqtkey_p.h"
|
||||
|
||||
#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
|
||||
@ -330,6 +331,8 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
|
||||
mTouchExtension.reset(new QWaylandTouchExtension(this, id));
|
||||
} else if (interface == QStringLiteral("zqt_key_v1")) {
|
||||
mQtKeyExtension.reset(new QWaylandQtKeyExtension(this, id));
|
||||
} else if (interface == QStringLiteral("zwp_tablet_manager_v2")) {
|
||||
mTabletManager.reset(new QWaylandTabletManagerV2(this, id, qMin(1, int(version))));
|
||||
#if QT_CONFIG(wayland_client_primary_selection)
|
||||
} else if (interface == QStringLiteral("zwp_primary_selection_device_manager_v1")) {
|
||||
mPrimarySelectionManager.reset(new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1));
|
||||
|
@ -97,6 +97,7 @@ class QWaylandDataDeviceManager;
|
||||
#if QT_CONFIG(wayland_client_primary_selection)
|
||||
class QWaylandPrimarySelectionDeviceManagerV1;
|
||||
#endif
|
||||
class QWaylandTabletManagerV2;
|
||||
class QWaylandTouchExtension;
|
||||
class QWaylandQtKeyExtension;
|
||||
class QWaylandWindow;
|
||||
@ -160,6 +161,7 @@ public:
|
||||
QWaylandPrimarySelectionDeviceManagerV1 *primarySelectionManager() const { return mPrimarySelectionManager.data(); }
|
||||
#endif
|
||||
QtWayland::qt_surface_extension *windowExtension() const { return mWindowExtension.data(); }
|
||||
QWaylandTabletManagerV2 *tabletManager() const { return mTabletManager.data(); }
|
||||
QWaylandTouchExtension *touchExtension() const { return mTouchExtension.data(); }
|
||||
QtWayland::zwp_text_input_manager_v2 *textInputManager() const { return mTextInputManager.data(); }
|
||||
QWaylandHardwareIntegration *hardwareIntegration() const { return mHardwareIntegration.data(); }
|
||||
@ -247,6 +249,7 @@ private:
|
||||
QScopedPointer<QWaylandTouchExtension> mTouchExtension;
|
||||
QScopedPointer<QWaylandQtKeyExtension> mQtKeyExtension;
|
||||
QScopedPointer<QWaylandWindowManagerIntegration> mWindowManagerIntegration;
|
||||
QScopedPointer<QWaylandTabletManagerV2> mTabletManager;
|
||||
#if QT_CONFIG(wayland_client_primary_selection)
|
||||
QScopedPointer<QWaylandPrimarySelectionDeviceManagerV1> mPrimarySelectionManager;
|
||||
#endif
|
||||
|
@ -50,6 +50,7 @@
|
||||
#if QT_CONFIG(wayland_client_primary_selection)
|
||||
#include "qwaylandprimaryselectionv1_p.h"
|
||||
#endif
|
||||
#include "qwaylandtabletv2_p.h"
|
||||
#include "qwaylandtouch_p.h"
|
||||
#include "qwaylandscreen_p.h"
|
||||
#include "qwaylandcursor_p.h"
|
||||
@ -418,6 +419,8 @@ QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version,
|
||||
if (mQDisplay->textInputManager())
|
||||
mTextInput.reset(new QWaylandTextInput(mQDisplay, mQDisplay->textInputManager()->get_text_input(wl_seat())));
|
||||
|
||||
if (auto *tm = mQDisplay->tabletManager())
|
||||
mTabletSeat.reset(new QWaylandTabletSeatV2(tm, this));
|
||||
}
|
||||
|
||||
QWaylandInputDevice::~QWaylandInputDevice()
|
||||
|
@ -89,6 +89,7 @@ class QWaylandDisplay;
|
||||
#if QT_CONFIG(wayland_client_primary_selection)
|
||||
class QWaylandPrimarySelectionDeviceV1;
|
||||
#endif
|
||||
class QWaylandTabletSeatV2;
|
||||
class QWaylandTextInput;
|
||||
#if QT_CONFIG(cursor)
|
||||
class QWaylandCursorTheme;
|
||||
@ -127,6 +128,9 @@ public:
|
||||
QWaylandPrimarySelectionDeviceV1 *primarySelectionDevice() const;
|
||||
#endif
|
||||
|
||||
void setTabletSeat(QWaylandTabletSeatV2 *tabletSeat);
|
||||
QWaylandTabletSeatV2* tabletSeat() const;
|
||||
|
||||
void setTextInput(QWaylandTextInput *textInput);
|
||||
QWaylandTextInput *textInput() const;
|
||||
|
||||
@ -183,6 +187,7 @@ private:
|
||||
Touch *mTouch = nullptr;
|
||||
|
||||
QScopedPointer<QWaylandTextInput> mTextInput;
|
||||
QScopedPointer<QWaylandTabletSeatV2> mTabletSeat;
|
||||
|
||||
uint32_t mTime = 0;
|
||||
uint32_t mSerial = 0;
|
||||
|
@ -425,6 +425,8 @@ void QWaylandIntegration::initializeShellIntegration()
|
||||
qCWarning(lcQpaWayland) << "Loading shell integration failed.";
|
||||
qCWarning(lcQpaWayland) << "Attempted to load the following shells" << preferredShells;
|
||||
}
|
||||
|
||||
QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
|
||||
}
|
||||
|
||||
QWaylandInputDevice *QWaylandIntegration::createInputDevice(QWaylandDisplay *display, int version, uint32_t id)
|
||||
|
332
src/plugins/platforms/wayland/qwaylandtabletv2.cpp
Normal file
332
src/plugins/platforms/wayland/qwaylandtabletv2.cpp
Normal file
@ -0,0 +1,332 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the plugins 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qwaylandtabletv2_p.h"
|
||||
#include "qwaylandinputdevice_p.h"
|
||||
#include "qwaylanddisplay_p.h"
|
||||
#include "qwaylandsurface_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QtWaylandClient {
|
||||
|
||||
QWaylandTabletManagerV2::QWaylandTabletManagerV2(QWaylandDisplay *display, uint id, uint version)
|
||||
: zwp_tablet_manager_v2(display->wl_registry(), id, qMin(version, uint(1)))
|
||||
{
|
||||
// Create tabletSeats for all seats.
|
||||
// This only works if we get the manager after all seats
|
||||
const auto seats = display->inputDevices();
|
||||
for (auto *seat : seats)
|
||||
createTabletSeat(seat);
|
||||
}
|
||||
|
||||
QWaylandTabletSeatV2 *QWaylandTabletManagerV2::createTabletSeat(QWaylandInputDevice *seat)
|
||||
{
|
||||
return new QWaylandTabletSeatV2(this, seat);
|
||||
}
|
||||
|
||||
QWaylandTabletSeatV2::QWaylandTabletSeatV2(QWaylandTabletManagerV2 *manager, QWaylandInputDevice *seat)
|
||||
: QtWayland::zwp_tablet_seat_v2(manager->get_tablet_seat(seat->wl_seat()))
|
||||
{
|
||||
}
|
||||
|
||||
QWaylandTabletSeatV2::~QWaylandTabletSeatV2()
|
||||
{
|
||||
for (auto *tablet : m_tablets)
|
||||
tablet->destroy();
|
||||
for (auto *tool : m_tools)
|
||||
tool->destroy();
|
||||
for (auto *pad : m_pads)
|
||||
pad->destroy();
|
||||
destroy();
|
||||
}
|
||||
|
||||
void QWaylandTabletSeatV2::zwp_tablet_seat_v2_tablet_added(zwp_tablet_v2 *id)
|
||||
{
|
||||
auto *tablet = new QWaylandTabletV2(id);
|
||||
m_tablets.push_back(tablet);
|
||||
connect(tablet, &QWaylandTabletV2::destroyed, this, [this, tablet] { m_tablets.removeOne(tablet); });
|
||||
}
|
||||
|
||||
void QWaylandTabletSeatV2::zwp_tablet_seat_v2_tool_added(zwp_tablet_tool_v2 *id)
|
||||
{
|
||||
auto *tool = new QWaylandTabletToolV2(id);
|
||||
m_tools.push_back(tool);
|
||||
connect(tool, &QWaylandTabletToolV2::destroyed, this, [this, tool] { m_tools.removeOne(tool); });
|
||||
}
|
||||
|
||||
void QWaylandTabletSeatV2::zwp_tablet_seat_v2_pad_added(zwp_tablet_pad_v2 *id)
|
||||
{
|
||||
auto *pad = new QWaylandTabletPadV2(id);
|
||||
m_pads.push_back(pad);
|
||||
connect(pad, &QWaylandTabletPadV2::destroyed, this, [this, pad] { m_pads.removeOne(pad); });
|
||||
}
|
||||
|
||||
QWaylandTabletV2::QWaylandTabletV2(::zwp_tablet_v2 *tablet)
|
||||
: QtWayland::zwp_tablet_v2(tablet)
|
||||
{
|
||||
}
|
||||
|
||||
void QWaylandTabletV2::zwp_tablet_v2_removed()
|
||||
{
|
||||
destroy();
|
||||
delete this;
|
||||
}
|
||||
|
||||
QWaylandTabletToolV2::QWaylandTabletToolV2(::zwp_tablet_tool_v2 *tool)
|
||||
: QtWayland::zwp_tablet_tool_v2(tool)
|
||||
{
|
||||
}
|
||||
|
||||
void QWaylandTabletToolV2::zwp_tablet_tool_v2_type(uint32_t tool_type)
|
||||
{
|
||||
m_toolType = type(tool_type);
|
||||
}
|
||||
|
||||
void QWaylandTabletToolV2::zwp_tablet_tool_v2_hardware_serial(uint32_t hardware_serial_hi, uint32_t hardware_serial_lo)
|
||||
{
|
||||
m_uid = (quint64(hardware_serial_hi) << 32) + hardware_serial_lo;
|
||||
}
|
||||
|
||||
void QWaylandTabletToolV2::zwp_tablet_tool_v2_capability(uint32_t capability)
|
||||
{
|
||||
if (capability == capability_rotation)
|
||||
m_hasRotation = true;
|
||||
}
|
||||
|
||||
void QWaylandTabletToolV2::zwp_tablet_tool_v2_done()
|
||||
{
|
||||
switch (m_toolType) {
|
||||
case type::type_airbrush:
|
||||
case type::type_brush:
|
||||
case type::type_pencil:
|
||||
case type::type_pen:
|
||||
m_pointerType = QTabletEvent::PointerType::Pen;
|
||||
break;
|
||||
case type::type_eraser:
|
||||
m_pointerType = QTabletEvent::PointerType::Eraser;
|
||||
break;
|
||||
case type::type_mouse:
|
||||
case type::type_lens:
|
||||
m_pointerType = QTabletEvent::PointerType::Cursor;
|
||||
break;
|
||||
case type::type_finger:
|
||||
m_pointerType = QTabletEvent::PointerType::UnknownPointer;
|
||||
break;
|
||||
}
|
||||
switch (m_toolType) {
|
||||
case type::type_airbrush:
|
||||
m_tabletDevice = QTabletEvent::TabletDevice::Airbrush;
|
||||
break;
|
||||
case type::type_brush:
|
||||
case type::type_pencil:
|
||||
case type::type_pen:
|
||||
case type::type_eraser:
|
||||
m_tabletDevice = m_hasRotation ? QTabletEvent::TabletDevice::RotationStylus : QTabletEvent::TabletDevice::Stylus;
|
||||
break;
|
||||
case type::type_lens:
|
||||
m_tabletDevice = QTabletEvent::TabletDevice::Puck;
|
||||
break;
|
||||
case type::type_mouse:
|
||||
case type::type_finger:
|
||||
m_tabletDevice = QTabletEvent::TabletDevice::NoDevice;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void QWaylandTabletToolV2::zwp_tablet_tool_v2_removed()
|
||||
{
|
||||
destroy();
|
||||
delete this;
|
||||
}
|
||||
|
||||
void QWaylandTabletToolV2::zwp_tablet_tool_v2_proximity_in(uint32_t serial, zwp_tablet_v2 *tablet, wl_surface *surface)
|
||||
{
|
||||
Q_UNUSED(tablet);
|
||||
Q_UNUSED(serial);
|
||||
if (Q_UNLIKELY(!surface)) {
|
||||
qCDebug(lcQpaWayland) << "Ignoring zwp_tablet_tool_v2_proximity_v2 with no surface";
|
||||
return;
|
||||
}
|
||||
m_pending.enteredSurface = true;
|
||||
m_pending.proximitySurface = QWaylandSurface::fromWlSurface(surface);
|
||||
}
|
||||
|
||||
void QWaylandTabletToolV2::zwp_tablet_tool_v2_proximity_out()
|
||||
{
|
||||
m_pending.enteredSurface = false;
|
||||
m_pending.proximitySurface = nullptr;
|
||||
}
|
||||
|
||||
void QWaylandTabletToolV2::zwp_tablet_tool_v2_down(uint32_t serial)
|
||||
{
|
||||
Q_UNUSED(serial);
|
||||
m_pending.down = true;
|
||||
}
|
||||
|
||||
void QWaylandTabletToolV2::zwp_tablet_tool_v2_up()
|
||||
{
|
||||
m_pending.down = false;
|
||||
}
|
||||
|
||||
void QWaylandTabletToolV2::zwp_tablet_tool_v2_motion(wl_fixed_t x, wl_fixed_t y)
|
||||
{
|
||||
m_pending.surfacePosition = QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y));
|
||||
}
|
||||
|
||||
void QWaylandTabletToolV2::zwp_tablet_tool_v2_pressure(uint32_t pressure)
|
||||
{
|
||||
const int maxPressure = 65535;
|
||||
m_pending.pressure = qreal(pressure)/maxPressure;
|
||||
}
|
||||
|
||||
void QWaylandTabletToolV2::zwp_tablet_tool_v2_distance(uint32_t distance)
|
||||
{
|
||||
m_pending.distance = distance;
|
||||
}
|
||||
|
||||
void QWaylandTabletToolV2::zwp_tablet_tool_v2_tilt(wl_fixed_t tilt_x, wl_fixed_t tilt_y)
|
||||
{
|
||||
m_pending.xTilt = wl_fixed_to_double(tilt_x);
|
||||
m_pending.yTilt = wl_fixed_to_double(tilt_y);
|
||||
}
|
||||
|
||||
void QWaylandTabletToolV2::zwp_tablet_tool_v2_rotation(wl_fixed_t degrees)
|
||||
{
|
||||
m_pending.rotation = wl_fixed_to_double(degrees);
|
||||
}
|
||||
|
||||
void QWaylandTabletToolV2::zwp_tablet_tool_v2_slider(int32_t position)
|
||||
{
|
||||
m_pending.slider = qreal(position) / 65535;
|
||||
}
|
||||
|
||||
static Qt::MouseButton mouseButtonFromTablet(uint button)
|
||||
{
|
||||
switch (button) {
|
||||
case 0x110: return Qt::MouseButton::LeftButton; // BTN_LEFT
|
||||
case 0x14b: return Qt::MouseButton::MiddleButton; // BTN_STYLUS
|
||||
case 0x14c: return Qt::MouseButton::RightButton; // BTN_STYLUS2
|
||||
default:
|
||||
return Qt::NoButton;
|
||||
}
|
||||
}
|
||||
|
||||
void QWaylandTabletToolV2::zwp_tablet_tool_v2_button(uint32_t serial, uint32_t button, uint32_t state)
|
||||
{
|
||||
Q_UNUSED(serial);
|
||||
Qt::MouseButton mouseButton = mouseButtonFromTablet(button);
|
||||
if (state == button_state_pressed)
|
||||
m_pending.buttons |= mouseButton;
|
||||
else
|
||||
m_pending.buttons &= ~mouseButton;
|
||||
}
|
||||
|
||||
void QWaylandTabletToolV2::zwp_tablet_tool_v2_frame(uint32_t time)
|
||||
{
|
||||
if (m_pending.proximitySurface && !m_applied.proximitySurface) {
|
||||
QWindowSystemInterface::handleTabletEnterProximityEvent(m_tabletDevice, m_pointerType, m_uid);
|
||||
m_applied.proximitySurface = m_pending.proximitySurface;
|
||||
}
|
||||
|
||||
if (!(m_pending == m_applied) && m_pending.proximitySurface) {
|
||||
if (!m_pending.proximitySurface) {
|
||||
qCWarning(lcQpaWayland) << "Can't send tablet event with no proximity surface, ignoring";
|
||||
return;
|
||||
}
|
||||
QWaylandWindow *waylandWindow = QWaylandWindow::fromWlSurface(m_pending.proximitySurface->object());
|
||||
QWindow *window = waylandWindow->window();
|
||||
ulong timestamp = time;
|
||||
const QPointF localPosition = waylandWindow->mapFromWlSurface(m_pending.surfacePosition);
|
||||
|
||||
QPointF delta = localPosition - localPosition.toPoint();
|
||||
QPointF globalPosition = window->mapToGlobal(localPosition.toPoint());
|
||||
globalPosition += delta;
|
||||
|
||||
Qt::MouseButtons buttons = m_pending.down ? Qt::MouseButton::LeftButton : Qt::MouseButton::NoButton;
|
||||
buttons |= m_pending.buttons;
|
||||
qreal pressure = m_pending.pressure;
|
||||
int xTilt = int(m_pending.xTilt);
|
||||
int yTilt = int(m_pending.yTilt);
|
||||
qreal tangentialPressure = m_pending.slider;
|
||||
qreal rotation = m_pending.rotation;
|
||||
int z = int(m_pending.distance);
|
||||
QWindowSystemInterface::handleTabletEvent(window, timestamp, localPosition, globalPosition,
|
||||
m_tabletDevice, m_pointerType, buttons, pressure,
|
||||
xTilt, yTilt, tangentialPressure, rotation, z, m_uid);
|
||||
}
|
||||
|
||||
if (!m_pending.proximitySurface && m_applied.enteredSurface) {
|
||||
QWindowSystemInterface::handleTabletLeaveProximityEvent(m_tabletDevice, m_pointerType, m_uid);
|
||||
m_pending = State(); // Don't leave pressure etc. lying around when we enter the next surface
|
||||
}
|
||||
|
||||
m_applied = m_pending;
|
||||
}
|
||||
|
||||
// TODO: delete when upgrading to c++20
|
||||
bool QWaylandTabletToolV2::State::operator==(const QWaylandTabletToolV2::State &o) const {
|
||||
return
|
||||
down == o.down &&
|
||||
proximitySurface.data() == o.proximitySurface.data() &&
|
||||
enteredSurface == o.enteredSurface &&
|
||||
surfacePosition == o.surfacePosition &&
|
||||
distance == o.distance &&
|
||||
pressure == o.pressure &&
|
||||
rotation == o.rotation &&
|
||||
xTilt == o.xTilt &&
|
||||
yTilt == o.yTilt &&
|
||||
slider == o.slider &&
|
||||
buttons == o.buttons;
|
||||
}
|
||||
|
||||
QWaylandTabletPadV2::QWaylandTabletPadV2(::zwp_tablet_pad_v2 *pad)
|
||||
: QtWayland::zwp_tablet_pad_v2(pad)
|
||||
{
|
||||
}
|
||||
|
||||
void QWaylandTabletPadV2::zwp_tablet_pad_v2_removed()
|
||||
{
|
||||
destroy();
|
||||
delete this;
|
||||
}
|
||||
|
||||
} // namespace QtWaylandClient
|
||||
|
||||
QT_END_NAMESPACE
|
191
src/plugins/platforms/wayland/qwaylandtabletv2_p.h
Normal file
191
src/plugins/platforms/wayland/qwaylandtabletv2_p.h
Normal file
@ -0,0 +1,191 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the plugins 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QWAYLANDTABLETV2_P_H
|
||||
#define QWAYLANDTABLETV2_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtWaylandClient/private/qwayland-tablet-unstable-v2.h>
|
||||
|
||||
#include <QtWaylandClient/private/qtwaylandclientglobal_p.h>
|
||||
|
||||
#include <QtGui/QTabletEvent>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtCore/QPointF>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QtWaylandClient {
|
||||
|
||||
class QWaylandDisplay;
|
||||
class QWaylandInputDevice;
|
||||
class QWaylandSurface;
|
||||
|
||||
class QWaylandTabletSeatV2;
|
||||
class QWaylandTabletV2;
|
||||
class QWaylandTabletToolV2;
|
||||
class QWaylandTabletPadV2;
|
||||
|
||||
class Q_WAYLAND_CLIENT_EXPORT QWaylandTabletManagerV2 : public QtWayland::zwp_tablet_manager_v2
|
||||
{
|
||||
public:
|
||||
explicit QWaylandTabletManagerV2(QWaylandDisplay *display, uint id, uint version);
|
||||
QWaylandTabletSeatV2 *createTabletSeat(QWaylandInputDevice *seat);
|
||||
};
|
||||
|
||||
class Q_WAYLAND_CLIENT_EXPORT QWaylandTabletSeatV2 : public QObject, public QtWayland::zwp_tablet_seat_v2
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QWaylandTabletSeatV2(QWaylandTabletManagerV2 *manager, QWaylandInputDevice *seat);
|
||||
~QWaylandTabletSeatV2() override;
|
||||
|
||||
protected:
|
||||
void zwp_tablet_seat_v2_tablet_added(struct ::zwp_tablet_v2 *id) override;
|
||||
void zwp_tablet_seat_v2_tool_added(struct ::zwp_tablet_tool_v2 *id) override;
|
||||
void zwp_tablet_seat_v2_pad_added(struct ::zwp_tablet_pad_v2 *id) override;
|
||||
|
||||
private:
|
||||
QVector<QWaylandTabletV2 *> m_tablets;
|
||||
QVector<QWaylandTabletToolV2 *> m_tools;
|
||||
QVector<QWaylandTabletPadV2 *> m_pads;
|
||||
};
|
||||
|
||||
class Q_WAYLAND_CLIENT_EXPORT QWaylandTabletV2 : public QObject, public QtWayland::zwp_tablet_v2
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QWaylandTabletV2(::zwp_tablet_v2 *tablet);
|
||||
|
||||
protected:
|
||||
// void zwp_tablet_v2_name(const QString &name) override;
|
||||
// void zwp_tablet_v2_id(uint32_t vid, uint32_t pid) override;
|
||||
// void zwp_tablet_v2_path(const QString &path) override;
|
||||
// void zwp_tablet_v2_done() override;
|
||||
void zwp_tablet_v2_removed() override;
|
||||
};
|
||||
|
||||
class Q_WAYLAND_CLIENT_EXPORT QWaylandTabletToolV2 : public QObject, public QtWayland::zwp_tablet_tool_v2
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QWaylandTabletToolV2(::zwp_tablet_tool_v2 *tool);
|
||||
|
||||
protected:
|
||||
void zwp_tablet_tool_v2_type(uint32_t tool_type) override;
|
||||
void zwp_tablet_tool_v2_hardware_serial(uint32_t hardware_serial_hi, uint32_t hardware_serial_lo) override;
|
||||
// void zwp_tablet_tool_v2_hardware_id_wacom(uint32_t hardware_id_hi, uint32_t hardware_id_lo) override;
|
||||
void zwp_tablet_tool_v2_capability(uint32_t capability) override;
|
||||
void zwp_tablet_tool_v2_done() override;
|
||||
void zwp_tablet_tool_v2_removed() override;
|
||||
void zwp_tablet_tool_v2_proximity_in(uint32_t serial, struct ::zwp_tablet_v2 *tablet, struct ::wl_surface *surface) override;
|
||||
void zwp_tablet_tool_v2_proximity_out() override;
|
||||
void zwp_tablet_tool_v2_down(uint32_t serial) override;
|
||||
void zwp_tablet_tool_v2_up() override;
|
||||
void zwp_tablet_tool_v2_motion(wl_fixed_t x, wl_fixed_t y) override;
|
||||
void zwp_tablet_tool_v2_pressure(uint32_t pressure) override;
|
||||
void zwp_tablet_tool_v2_distance(uint32_t distance) override;
|
||||
void zwp_tablet_tool_v2_tilt(wl_fixed_t tilt_x, wl_fixed_t tilt_y) override;
|
||||
void zwp_tablet_tool_v2_rotation(wl_fixed_t degrees) override;
|
||||
void zwp_tablet_tool_v2_slider(int32_t position) override;
|
||||
// void zwp_tablet_tool_v2_wheel(wl_fixed_t degrees, int32_t clicks) override;
|
||||
void zwp_tablet_tool_v2_button(uint32_t serial, uint32_t button, uint32_t state) override;
|
||||
void zwp_tablet_tool_v2_frame(uint32_t time) override;
|
||||
|
||||
private:
|
||||
|
||||
// Static state (sent before done event)
|
||||
QTabletEvent::PointerType m_pointerType = QTabletEvent::PointerType::UnknownPointer;
|
||||
QTabletEvent::TabletDevice m_tabletDevice = QTabletEvent::TabletDevice::NoDevice;
|
||||
type m_toolType = type_pen;
|
||||
bool m_hasRotation = false;
|
||||
quint64 m_uid = 0;
|
||||
|
||||
// Accumulated state (applied on frame event)
|
||||
struct State {
|
||||
bool down = false;
|
||||
QPointer<QWaylandSurface> proximitySurface;
|
||||
bool enteredSurface = false; // Not enough with just proximitySurface, if the surface is deleted, we still want to send a leave event
|
||||
QPointF surfacePosition;
|
||||
uint distance = 0;
|
||||
qreal pressure = 0;
|
||||
qreal rotation = 0;
|
||||
qreal xTilt = 0;
|
||||
qreal yTilt = 0;
|
||||
qreal slider = 0;
|
||||
Qt::MouseButtons buttons = Qt::MouseButton::NoButton; // Actual buttons, down state -> left mouse is mapped inside the frame handler
|
||||
//auto operator<=>(const Point&) const = default; // TODO: use this when upgrading to C++20
|
||||
bool operator==(const State &o) const;
|
||||
} m_pending, m_applied;
|
||||
};
|
||||
|
||||
// We don't actually use this, but need to handle the "removed" event to comply with the protocol
|
||||
class Q_WAYLAND_CLIENT_EXPORT QWaylandTabletPadV2 : public QObject, public QtWayland::zwp_tablet_pad_v2
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QWaylandTabletPadV2(::zwp_tablet_pad_v2 *pad);
|
||||
|
||||
protected:
|
||||
// void zwp_tablet_pad_v2_group(struct ::zwp_tablet_pad_group_v2 *pad_group) override;
|
||||
// void zwp_tablet_pad_v2_path(const QString &path) override;
|
||||
// void zwp_tablet_pad_v2_buttons(uint32_t buttons) override;
|
||||
// void zwp_tablet_pad_v2_done() override;
|
||||
// void zwp_tablet_pad_v2_button(uint32_t time, uint32_t button, uint32_t state) override;
|
||||
// void zwp_tablet_pad_v2_enter(uint32_t serial, struct ::zwp_tablet_v2 *tablet, struct ::wl_surface *surface) override;
|
||||
// void zwp_tablet_pad_v2_leave(uint32_t serial, struct ::wl_surface *surface) override;
|
||||
void zwp_tablet_pad_v2_removed() override;
|
||||
};
|
||||
|
||||
} // namespace QtWaylandClient
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QWAYLANDTABLETV2_P_H
|
@ -11,6 +11,7 @@ SUBDIRS += \
|
||||
seatv4 \
|
||||
seatv5 \
|
||||
surface \
|
||||
tabletv2 \
|
||||
wl_connect \
|
||||
xdgdecorationv1 \
|
||||
xdgoutput \
|
||||
|
7
tests/auto/wayland/tabletv2/tabletv2.pro
Normal file
7
tests/auto/wayland/tabletv2/tabletv2.pro
Normal file
@ -0,0 +1,7 @@
|
||||
include (../shared/shared.pri)
|
||||
|
||||
WAYLANDSERVERSOURCES += \
|
||||
$$PWD/../../../../src/3rdparty/protocol/tablet-unstable-v2.xml
|
||||
|
||||
TARGET = tst_tabletv2
|
||||
SOURCES += tst_tabletv2.cpp
|
918
tests/auto/wayland/tabletv2/tst_tabletv2.cpp
Normal file
918
tests/auto/wayland/tabletv2/tst_tabletv2.cpp
Normal file
@ -0,0 +1,918 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** 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-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "mockcompositor.h"
|
||||
|
||||
#include <qwayland-server-tablet-unstable-v2.h>
|
||||
|
||||
#include <QtGui/QRasterWindow>
|
||||
|
||||
using namespace MockCompositor;
|
||||
|
||||
constexpr int tabletVersion = 1; // protocol VERSION, not the name suffix (_v2)
|
||||
|
||||
class TabletManagerV2;
|
||||
class TabletSeatV2;
|
||||
|
||||
class TabletV2 : public QObject, public QtWaylandServer::zwp_tablet_v2
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TabletV2(TabletSeatV2 *tabletSeat)
|
||||
: m_tabletSeat(tabletSeat)
|
||||
{
|
||||
}
|
||||
|
||||
void send_removed() = delete;
|
||||
void send_removed(struct ::wl_resource *resource) = delete;
|
||||
void sendRemoved();
|
||||
|
||||
QPointer<TabletSeatV2> m_tabletSeat; // destroy order is not guaranteed
|
||||
protected:
|
||||
void zwp_tablet_v2_destroy(Resource *resource) override;
|
||||
};
|
||||
|
||||
class TabletToolV2 : public QObject, public QtWaylandServer::zwp_tablet_tool_v2
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
using ToolType = QtWaylandServer::zwp_tablet_tool_v2::type;
|
||||
explicit TabletToolV2(TabletSeatV2 *tabletSeat, ToolType toolType, quint64 hardwareSerial)
|
||||
: m_tabletSeat(tabletSeat)
|
||||
, m_toolType(toolType)
|
||||
, m_hardwareSerial(hardwareSerial)
|
||||
{
|
||||
}
|
||||
|
||||
wl_resource *toolResource() // for convenience
|
||||
{
|
||||
Q_ASSERT(resourceMap().size() == 1);
|
||||
// Strictly speaking, there may be more than one resource for the tool, for intsance if
|
||||
// if there are multiple clients, or a client has called get_tablet_seat multiple times.
|
||||
// For now we'll pretend there can only be one resource.
|
||||
return resourceMap().first()->handle;
|
||||
}
|
||||
|
||||
void send_removed() = delete;
|
||||
void send_removed(struct ::wl_resource *resource) = delete;
|
||||
void sendRemoved();
|
||||
|
||||
uint sendProximityIn(TabletV2 *tablet, Surface *surface);
|
||||
void sendProximityOut();
|
||||
void sendMotion(QPointF position)
|
||||
{
|
||||
Q_ASSERT(m_proximitySurface);
|
||||
for (auto *resource : resourceMap())
|
||||
send_motion(resource->handle, wl_fixed_from_double(position.x()), wl_fixed_from_double(position.y()));
|
||||
}
|
||||
uint sendDown();
|
||||
void sendUp() { send_up(toolResource()); }
|
||||
void sendPressure(uint pressure);
|
||||
void sendTilt(qreal tiltX, qreal tiltY) { send_tilt(toolResource(), wl_fixed_from_double(tiltX), wl_fixed_from_double(tiltY)); }
|
||||
void sendRotation(qreal rotation) { send_rotation(toolResource(), wl_fixed_from_double(rotation)); }
|
||||
uint sendButton(uint button, bool pressed);
|
||||
uint sendFrame();
|
||||
|
||||
QPointer<TabletSeatV2> m_tabletSeat; // destruction order is not guaranteed
|
||||
ToolType m_toolType = ToolType::type_pen;
|
||||
quint64 m_hardwareSerial = 0;
|
||||
QPointer<Surface> m_proximitySurface;
|
||||
protected:
|
||||
void zwp_tablet_tool_v2_destroy(Resource *resource) override;
|
||||
};
|
||||
|
||||
class TabletPadV2 : public QObject, public QtWaylandServer::zwp_tablet_pad_v2
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TabletPadV2(TabletSeatV2 *tabletSeat)
|
||||
: m_tabletSeat(tabletSeat)
|
||||
{
|
||||
}
|
||||
|
||||
void send_removed() = delete;
|
||||
void send_removed(struct ::wl_resource *resource) = delete;
|
||||
void sendRemoved();
|
||||
|
||||
QPointer<TabletSeatV2> m_tabletSeat; // destroy order is not guaranteed
|
||||
protected:
|
||||
void zwp_tablet_pad_v2_destroy(Resource *resource) override;
|
||||
};
|
||||
|
||||
class TabletSeatV2 : public QObject, public QtWaylandServer::zwp_tablet_seat_v2
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TabletSeatV2(TabletManagerV2 *manager, Seat *seat)
|
||||
: m_manager(manager)
|
||||
, m_seat(seat)
|
||||
{}
|
||||
TabletV2 *addTablet()
|
||||
{
|
||||
auto *tablet = new TabletV2(this);
|
||||
m_tablets.append(tablet);
|
||||
for (auto *resource : resourceMap())
|
||||
sendTabletAdded(resource, tablet);
|
||||
return tablet;
|
||||
}
|
||||
|
||||
void sendTabletAdded(Resource *resource, TabletV2 *tablet)
|
||||
{
|
||||
// Although, not necessarily correct, assuming just one tablet_seat per client
|
||||
auto *tabletResource = tablet->add(resource->client(), resource->version());
|
||||
zwp_tablet_seat_v2::send_tablet_added(resource->handle, tabletResource->handle);
|
||||
// TODO: send extra stuff before done?
|
||||
tablet->send_done(tabletResource->handle);
|
||||
}
|
||||
|
||||
using ToolType = QtWaylandServer::zwp_tablet_tool_v2::type;
|
||||
TabletToolV2 *addTool(ToolType toolType = ToolType::type_pen, quint64 hardwareSerial = 0)
|
||||
{
|
||||
auto *tool = new TabletToolV2(this, toolType, hardwareSerial);
|
||||
m_tools.append(tool);
|
||||
for (auto *resource : resourceMap())
|
||||
sendToolAdded(resource, tool);
|
||||
return tool;
|
||||
}
|
||||
|
||||
void sendToolAdded(Resource *resource, TabletToolV2 *tool)
|
||||
{
|
||||
// Although, not necessarily correct, assuming just one tablet_seat per client
|
||||
auto *toolResource = tool->add(resource->client(), resource->version())->handle;
|
||||
zwp_tablet_seat_v2::send_tool_added(resource->handle, toolResource);
|
||||
tool->send_type(toolResource, tool->m_toolType);
|
||||
if (tool->m_hardwareSerial) {
|
||||
const uint hi = tool->m_hardwareSerial >> 32;
|
||||
const uint lo = tool->m_hardwareSerial & 0xffffffff;
|
||||
tool->send_hardware_serial(toolResource, hi, lo);
|
||||
}
|
||||
tool->send_done(toolResource);
|
||||
}
|
||||
|
||||
TabletPadV2 *addPad()
|
||||
{
|
||||
auto *pad = new TabletPadV2(this);
|
||||
m_pads.append(pad);
|
||||
for (auto *resource : resourceMap())
|
||||
sendPadAdded(resource, pad);
|
||||
return pad;
|
||||
}
|
||||
|
||||
void sendPadAdded(Resource *resource, TabletPadV2 *pad)
|
||||
{
|
||||
// Although, not necessarily correct, assuming just one tablet_seat per client
|
||||
auto *padResource = pad->add(resource->client(), resource->version())->handle;
|
||||
zwp_tablet_seat_v2::send_pad_added(resource->handle, padResource);
|
||||
pad->send_done(padResource);
|
||||
}
|
||||
|
||||
void removeAll()
|
||||
{
|
||||
const auto tools = m_tools;
|
||||
for (auto *tool : tools)
|
||||
tool->sendRemoved();
|
||||
|
||||
const auto tablets = m_tablets;
|
||||
for (auto *tablet : tablets)
|
||||
tablet->sendRemoved();
|
||||
|
||||
const auto pads = m_pads;
|
||||
for (auto *pad : pads)
|
||||
pad->sendRemoved();
|
||||
}
|
||||
|
||||
TabletManagerV2 *m_manager = nullptr;
|
||||
Seat *m_seat = nullptr;
|
||||
QVector<TabletV2 *> m_tablets;
|
||||
QVector<TabletV2 *> m_tabletsWaitingForDestroy;
|
||||
QVector<TabletToolV2 *> m_tools;
|
||||
QVector<TabletToolV2 *> m_toolsWaitingForDestroy;
|
||||
QVector<TabletPadV2 *> m_pads;
|
||||
QVector<TabletPadV2 *> m_padsWaitingForDestroy;
|
||||
|
||||
protected:
|
||||
void zwp_tablet_seat_v2_bind_resource(Resource *resource)
|
||||
{
|
||||
for (auto *tablet : m_tablets)
|
||||
sendTabletAdded(resource, tablet);
|
||||
for (auto *tool : m_tools)
|
||||
sendToolAdded(resource, tool);
|
||||
for (auto *pad : m_pads)
|
||||
sendPadAdded(resource, pad);
|
||||
}
|
||||
};
|
||||
|
||||
class TabletManagerV2 : public Global, public QtWaylandServer::zwp_tablet_manager_v2
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TabletManagerV2(CoreCompositor *compositor, int version = 1)
|
||||
: QtWaylandServer::zwp_tablet_manager_v2(compositor->m_display, version)
|
||||
, m_version(version)
|
||||
{}
|
||||
bool isClean() override
|
||||
{
|
||||
for (auto *seat : m_tabletSeats) {
|
||||
if (!seat->m_tabletsWaitingForDestroy.empty())
|
||||
return false;
|
||||
if (!seat->m_toolsWaitingForDestroy.empty())
|
||||
return false;
|
||||
if (!seat->m_padsWaitingForDestroy.empty())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TabletSeatV2 *tabletSeatFor(Seat *seat)
|
||||
{
|
||||
Q_ASSERT(seat);
|
||||
if (auto *tabletSeat = m_tabletSeats.value(seat, nullptr))
|
||||
return tabletSeat;
|
||||
|
||||
auto *tabletSeat = new TabletSeatV2(this, seat);
|
||||
m_tabletSeats[seat] = tabletSeat;
|
||||
return tabletSeat;
|
||||
}
|
||||
|
||||
int m_version = 1; // TODO: Remove on libwayland upgrade
|
||||
QMap<Seat *, TabletSeatV2 *> m_tabletSeats;
|
||||
|
||||
protected:
|
||||
void zwp_tablet_manager_v2_destroy(Resource *resource) override
|
||||
{
|
||||
// tablet_seats created from this object are unaffected and should be destroyed separately.
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void zwp_tablet_manager_v2_get_tablet_seat(Resource *resource, uint32_t id, ::wl_resource *seatResource) override
|
||||
{
|
||||
auto *seat = fromResource<Seat>(seatResource);
|
||||
QVERIFY(seat);
|
||||
auto *tabletSeat = tabletSeatFor(seat);
|
||||
tabletSeat->add(resource->client(), id, resource->version());
|
||||
}
|
||||
};
|
||||
|
||||
void TabletV2::sendRemoved()
|
||||
{
|
||||
for (auto *resource : resourceMap())
|
||||
zwp_tablet_v2_send_removed(resource->handle);
|
||||
bool removed = m_tabletSeat->m_tablets.removeOne(this);
|
||||
QVERIFY(removed);
|
||||
m_tabletSeat->m_tabletsWaitingForDestroy.append(this);
|
||||
}
|
||||
|
||||
void TabletV2::zwp_tablet_v2_destroy(QtWaylandServer::zwp_tablet_v2::Resource *resource)
|
||||
{
|
||||
Q_UNUSED(resource)
|
||||
if (m_tabletSeat) {
|
||||
bool removed = m_tabletSeat->m_tabletsWaitingForDestroy.removeOne(this);
|
||||
QVERIFY(removed);
|
||||
}
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void TabletToolV2::sendRemoved()
|
||||
{
|
||||
for (auto *resource : resourceMap())
|
||||
zwp_tablet_tool_v2_send_removed(resource->handle);
|
||||
bool removed = m_tabletSeat->m_tools.removeOne(this);
|
||||
QVERIFY(removed);
|
||||
m_tabletSeat->m_toolsWaitingForDestroy.append(this);
|
||||
}
|
||||
|
||||
uint TabletToolV2::sendProximityIn(TabletV2 *tablet, Surface *surface)
|
||||
{
|
||||
Q_ASSERT(!m_proximitySurface);
|
||||
m_proximitySurface = surface;
|
||||
uint serial = m_tabletSeat->m_seat->m_compositor->nextSerial();
|
||||
auto *client = surface->resource()->client();
|
||||
auto tabletResource = tablet->resourceMap().value(client)->handle;
|
||||
send_proximity_in(toolResource(), serial, tabletResource, surface->resource()->handle);
|
||||
return serial;
|
||||
}
|
||||
|
||||
void TabletToolV2::sendProximityOut()
|
||||
{
|
||||
Q_ASSERT(m_proximitySurface);
|
||||
send_proximity_out(toolResource());
|
||||
m_proximitySurface = nullptr;
|
||||
}
|
||||
|
||||
uint TabletToolV2::sendDown()
|
||||
{
|
||||
uint serial = m_tabletSeat->m_seat->m_compositor->nextSerial();
|
||||
send_down(toolResource(), serial);
|
||||
return serial;
|
||||
}
|
||||
|
||||
void TabletToolV2::sendPressure(uint pressure)
|
||||
{
|
||||
Q_ASSERT(m_proximitySurface);
|
||||
auto *client = m_proximitySurface->resource()->client();
|
||||
auto toolResource = resourceMap().value(client)->handle;
|
||||
send_pressure(toolResource, pressure);
|
||||
}
|
||||
|
||||
uint TabletToolV2::sendButton(uint button, bool pressed)
|
||||
{
|
||||
button_state state = pressed ? button_state_pressed : button_state_released;
|
||||
uint serial = m_tabletSeat->m_seat->m_compositor->nextSerial();
|
||||
send_button(toolResource(), serial, button, state);
|
||||
return serial;
|
||||
}
|
||||
|
||||
uint TabletToolV2::sendFrame()
|
||||
{
|
||||
uint time = m_tabletSeat->m_seat->m_compositor->currentTimeMilliseconds();
|
||||
for (auto *resource : resourceMap())
|
||||
send_frame(resource->handle, time);
|
||||
return time;
|
||||
}
|
||||
|
||||
void TabletToolV2::zwp_tablet_tool_v2_destroy(QtWaylandServer::zwp_tablet_tool_v2::Resource *resource)
|
||||
{
|
||||
if (m_tabletSeat) {
|
||||
bool removed = m_tabletSeat->m_toolsWaitingForDestroy.removeOne(this);
|
||||
QVERIFY(removed);
|
||||
}
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void TabletPadV2::sendRemoved()
|
||||
{
|
||||
for (auto *resource : resourceMap())
|
||||
zwp_tablet_pad_v2_send_removed(resource->handle);
|
||||
bool removed = m_tabletSeat->m_pads.removeOne(this);
|
||||
QVERIFY(removed);
|
||||
m_tabletSeat->m_padsWaitingForDestroy.append(this);
|
||||
}
|
||||
|
||||
void TabletPadV2::zwp_tablet_pad_v2_destroy(QtWaylandServer::zwp_tablet_pad_v2::Resource *resource)
|
||||
{
|
||||
if (m_tabletSeat) {
|
||||
bool removed = m_tabletSeat->m_padsWaitingForDestroy.removeOne(this);
|
||||
QVERIFY(removed);
|
||||
}
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
class TabletCompositor : public DefaultCompositor {
|
||||
public:
|
||||
explicit TabletCompositor()
|
||||
{
|
||||
exec([this] {
|
||||
m_config.autoConfigure = true;
|
||||
add<TabletManagerV2>(tabletVersion);
|
||||
});
|
||||
}
|
||||
TabletSeatV2 *tabletSeat(int i = 0)
|
||||
{
|
||||
return get<TabletManagerV2>()->tabletSeatFor(get<Seat>(i));
|
||||
}
|
||||
TabletV2 *tablet(int i = 0, int iSeat = 0)
|
||||
{
|
||||
if (auto *ts = tabletSeat(iSeat))
|
||||
return ts->m_tablets.value(i, nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
TabletToolV2 *tabletTool(int i = 0, int iSeat = 0)
|
||||
{
|
||||
if (auto *ts = tabletSeat(iSeat))
|
||||
return ts->m_tools.value(i, nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
TabletPadV2 *tabletPad(int i = 0, int iSeat = 0)
|
||||
{
|
||||
if (auto *ts = tabletSeat(iSeat))
|
||||
return ts->m_pads.value(i, nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(QtWaylandServer::zwp_tablet_tool_v2::type);
|
||||
Q_DECLARE_METATYPE(QTabletEvent::PointerType);
|
||||
Q_DECLARE_METATYPE(Qt::MouseButton);
|
||||
|
||||
class tst_tabletv2 : public QObject, private TabletCompositor
|
||||
{
|
||||
using ToolType = QtWaylandServer::zwp_tablet_tool_v2::type;
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void cleanup();
|
||||
void bindsToManager();
|
||||
void createsTabletSeat();
|
||||
void destroysTablet();
|
||||
void destroysTool();
|
||||
void destroysPad();
|
||||
void proximityEvents();
|
||||
void moveEvent();
|
||||
void pointerType_data();
|
||||
void pointerType();
|
||||
void hardwareSerial();
|
||||
void buttons_data();
|
||||
void buttons();
|
||||
void tabletEvents();
|
||||
};
|
||||
|
||||
class ProximityFilter : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ProximityFilter() { qApp->installEventFilter(this); }
|
||||
~ProximityFilter() override { qDeleteAll(m_events); }
|
||||
QVector<QTabletEvent *> m_events;
|
||||
|
||||
int nextEventIndex = 0;
|
||||
int numEvents() const { return m_events.size() - nextEventIndex; }
|
||||
QTabletEvent *popEvent()
|
||||
{
|
||||
auto *event = m_events.value(nextEventIndex, nullptr);
|
||||
if (event)
|
||||
++nextEventIndex;
|
||||
return event;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *object, QEvent *event) override
|
||||
{
|
||||
Q_UNUSED(object);
|
||||
switch (event->type()) {
|
||||
case QEvent::TabletEnterProximity:
|
||||
case QEvent::TabletLeaveProximity: {
|
||||
auto *e = static_cast<QTabletEvent *>(event);
|
||||
auto *ev = new QTabletEvent(e->type(), e->posF(), e->globalPosF(), e->device(),
|
||||
e->pointerType(), e->pressure(), e->xTilt(), e->yTilt(),
|
||||
e->tangentialPressure(), e->rotation(), e->z(),
|
||||
Qt::KeyboardModifier::NoModifier, e->uniqueId(),
|
||||
e->button(), e->buttons());
|
||||
m_events << ev;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
void tst_tabletv2::cleanup()
|
||||
{
|
||||
exec([&] {
|
||||
tabletSeat()->removeAll();
|
||||
});
|
||||
QCOMPOSITOR_COMPARE(get<TabletManagerV2>()->m_tabletSeats.size(), 1);
|
||||
QCOMPOSITOR_COMPARE(tabletSeat()->m_tablets.size(), 0);
|
||||
QCOMPOSITOR_COMPARE(tabletSeat()->m_tools.size(), 0);
|
||||
QCOMPOSITOR_COMPARE(tabletSeat()->m_pads.size(), 0);
|
||||
|
||||
QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage()));
|
||||
}
|
||||
|
||||
void tst_tabletv2::bindsToManager()
|
||||
{
|
||||
QCOMPOSITOR_TRY_COMPARE(get<TabletManagerV2>()->resourceMap().size(), 1);
|
||||
QCOMPOSITOR_TRY_COMPARE(get<TabletManagerV2>()->resourceMap().first()->version(), tabletVersion);
|
||||
}
|
||||
|
||||
void tst_tabletv2::createsTabletSeat()
|
||||
{
|
||||
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
|
||||
QCOMPOSITOR_TRY_VERIFY(tabletSeat()->resourceMap().contains(client()));
|
||||
QCOMPOSITOR_TRY_COMPARE(tabletSeat()->resourceMap().value(client())->version(), tabletVersion);
|
||||
//TODO: Maybe also assert some capability reported though qt APIs?
|
||||
}
|
||||
|
||||
void tst_tabletv2::destroysTablet()
|
||||
{
|
||||
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
|
||||
exec([&] {
|
||||
tabletSeat()->addTablet();
|
||||
});
|
||||
QCOMPOSITOR_TRY_VERIFY(tablet());
|
||||
|
||||
exec([&] {
|
||||
tablet()->sendRemoved();
|
||||
});
|
||||
|
||||
QCOMPOSITOR_TRY_VERIFY(!tablet());
|
||||
QCOMPOSITOR_TRY_VERIFY(tabletSeat()->m_tabletsWaitingForDestroy.empty());
|
||||
}
|
||||
|
||||
void tst_tabletv2::destroysTool()
|
||||
{
|
||||
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
|
||||
exec([&] {
|
||||
tabletSeat()->addTool();
|
||||
});
|
||||
QCOMPOSITOR_TRY_VERIFY(tabletTool());
|
||||
|
||||
exec([&] {
|
||||
tabletTool()->sendRemoved();
|
||||
});
|
||||
|
||||
QCOMPOSITOR_TRY_VERIFY(!tabletTool());
|
||||
QCOMPOSITOR_TRY_VERIFY(tabletSeat()->m_toolsWaitingForDestroy.empty());
|
||||
}
|
||||
|
||||
void tst_tabletv2::destroysPad()
|
||||
{
|
||||
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
|
||||
exec([&] {
|
||||
tabletSeat()->addPad();
|
||||
});
|
||||
QCOMPOSITOR_TRY_VERIFY(tabletPad());
|
||||
|
||||
exec([&] {
|
||||
tabletPad()->sendRemoved();
|
||||
});
|
||||
|
||||
QCOMPOSITOR_TRY_VERIFY(!tabletPad());
|
||||
QCOMPOSITOR_TRY_VERIFY(tabletSeat()->m_padsWaitingForDestroy.empty());
|
||||
}
|
||||
|
||||
void tst_tabletv2::proximityEvents()
|
||||
{
|
||||
ProximityFilter filter;
|
||||
|
||||
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
|
||||
exec([&] {
|
||||
tabletSeat()->addTablet();
|
||||
tabletSeat()->addTool();
|
||||
});
|
||||
|
||||
QRasterWindow window;
|
||||
window.resize(64, 64);
|
||||
window.show();
|
||||
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
||||
|
||||
QCOMPOSITOR_TRY_VERIFY(tablet());
|
||||
exec([&] {
|
||||
auto *surface = xdgSurface()->m_surface;
|
||||
auto *tool = tabletTool();
|
||||
tool->sendProximityIn(tablet(), surface);
|
||||
tool->sendFrame();
|
||||
});
|
||||
|
||||
QTRY_COMPARE(filter.numEvents(), 1);
|
||||
QTabletEvent *enterEvent = filter.popEvent();
|
||||
QCOMPARE(enterEvent->type(), QEvent::TabletEnterProximity);
|
||||
|
||||
exec([&] {
|
||||
auto *tool = tabletTool();
|
||||
tool->sendProximityOut();
|
||||
tool->sendFrame();
|
||||
});
|
||||
|
||||
QTRY_COMPARE(filter.numEvents(), 1);
|
||||
QTabletEvent *leaveEvent = filter.popEvent();
|
||||
QCOMPARE(leaveEvent->type(), QEvent::TabletLeaveProximity);
|
||||
}
|
||||
|
||||
class TabletWindow : public QRasterWindow {
|
||||
Q_OBJECT
|
||||
public:
|
||||
~TabletWindow() override { qDeleteAll(m_events); }
|
||||
|
||||
void tabletEvent(QTabletEvent *e) override
|
||||
{
|
||||
m_events << new QTabletEvent(e->type(), e->posF(), e->globalPosF(), e->device(),
|
||||
e->pointerType(), e->pressure(), e->xTilt(), e->yTilt(),
|
||||
e->tangentialPressure(), e->rotation(), e->z(),
|
||||
Qt::KeyboardModifier::NoModifier, e->uniqueId(), e->button(),
|
||||
e->buttons());
|
||||
emit tabletEventReceived(m_events.last());
|
||||
}
|
||||
int nextEventIndex = 0;
|
||||
int numEvents() const { return m_events.size() - nextEventIndex; }
|
||||
QTabletEvent *popEvent()
|
||||
{
|
||||
auto *event = m_events.value(nextEventIndex, nullptr);
|
||||
if (event)
|
||||
++nextEventIndex;
|
||||
return event;
|
||||
}
|
||||
|
||||
signals:
|
||||
void tabletEventReceived(QTabletEvent *event);
|
||||
|
||||
private:
|
||||
QVector<QTabletEvent *> m_events;
|
||||
};
|
||||
|
||||
void tst_tabletv2::moveEvent()
|
||||
{
|
||||
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
|
||||
exec([&] {
|
||||
tabletSeat()->addTablet();
|
||||
tabletSeat()->addTool();
|
||||
});
|
||||
|
||||
TabletWindow window;
|
||||
window.resize(64, 64);
|
||||
window.show();
|
||||
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
||||
|
||||
QCOMPOSITOR_TRY_VERIFY(tablet());
|
||||
exec([&] {
|
||||
auto *surface = xdgSurface()->m_surface;
|
||||
auto *tool = tabletTool();
|
||||
tool->sendProximityIn(tablet(), surface);
|
||||
QMargins margins = window.frameMargins();
|
||||
tool->sendMotion(QPointF(12 + margins.left(), 34 + margins.top()));
|
||||
tool->sendFrame();
|
||||
});
|
||||
QTRY_VERIFY(window.numEvents());
|
||||
QTabletEvent *event = window.popEvent();
|
||||
QCOMPARE(event->type(), QEvent::TabletMove);
|
||||
QCOMPARE(event->pressure(), 0);
|
||||
QCOMPARE(event->posF(), QPointF(12, 34));
|
||||
}
|
||||
|
||||
void tst_tabletv2::pointerType_data()
|
||||
{
|
||||
QTest::addColumn<ToolType>("toolType");
|
||||
QTest::addColumn<QTabletEvent::PointerType>("pointerType");
|
||||
QTest::addColumn<QTabletEvent::TabletDevice>("tabletDevice");
|
||||
|
||||
QTest::newRow("pen") << ToolType::type_pen << QTabletEvent::PointerType::Pen << QTabletEvent::TabletDevice::Stylus;
|
||||
QTest::newRow("eraser") << ToolType::type_eraser << QTabletEvent::PointerType::Eraser << QTabletEvent::TabletDevice::Stylus;
|
||||
QTest::newRow("pencil") << ToolType::type_pencil << QTabletEvent::PointerType::Pen << QTabletEvent::TabletDevice::Stylus;
|
||||
QTest::newRow("airbrush") << ToolType::type_airbrush << QTabletEvent::PointerType::Pen << QTabletEvent::TabletDevice::Airbrush;
|
||||
QTest::newRow("brush") << ToolType::type_brush << QTabletEvent::PointerType::Pen << QTabletEvent::TabletDevice::Stylus; // TODO: is TabletDevice::Stylus the right thing?
|
||||
QTest::newRow("lens") << ToolType::type_lens << QTabletEvent::PointerType::Cursor << QTabletEvent::TabletDevice::Puck;
|
||||
// TODO: also add tests for FourDMouse and RotationStylus (also need to send capabilities)
|
||||
|
||||
// TODO: should these rather be mapped to touch/mouse events?
|
||||
QTest::newRow("finger") << ToolType::type_finger << QTabletEvent::PointerType::UnknownPointer << QTabletEvent::TabletDevice::NoDevice;
|
||||
QTest::newRow("mouse") << ToolType::type_mouse << QTabletEvent::PointerType::Cursor << QTabletEvent::TabletDevice::NoDevice;
|
||||
}
|
||||
|
||||
void tst_tabletv2::pointerType()
|
||||
{
|
||||
using ToolType = QtWaylandServer::zwp_tablet_tool_v2::type;
|
||||
QFETCH(ToolType, toolType);
|
||||
QFETCH(QTabletEvent::PointerType, pointerType);
|
||||
QFETCH(QTabletEvent::TabletDevice, tabletDevice);
|
||||
|
||||
ProximityFilter filter;
|
||||
|
||||
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
|
||||
exec([&] {
|
||||
tabletSeat()->addTablet();
|
||||
tabletSeat()->addTool(toolType);
|
||||
});
|
||||
|
||||
TabletWindow window;
|
||||
window.resize(64, 64);
|
||||
window.show();
|
||||
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
||||
|
||||
QCOMPOSITOR_TRY_VERIFY(tablet());
|
||||
exec([&] {
|
||||
auto *surface = xdgSurface()->m_surface;
|
||||
auto *tool = tabletTool();
|
||||
tool->sendProximityIn(tablet(), surface);
|
||||
QMargins margins = window.frameMargins();
|
||||
tool->sendMotion(QPointF(12 + margins.left(), 34 + margins.top()));
|
||||
tool->sendFrame();
|
||||
});
|
||||
|
||||
QTRY_COMPARE(filter.numEvents(), 1);
|
||||
QTabletEvent *event = filter.popEvent();
|
||||
QCOMPARE(event->pointerType(), pointerType);
|
||||
QCOMPARE(event->device(), tabletDevice);
|
||||
|
||||
QTRY_VERIFY(window.numEvents());
|
||||
event = window.popEvent();
|
||||
QCOMPARE(event->pointerType(), pointerType);
|
||||
QCOMPARE(event->device(), tabletDevice);
|
||||
|
||||
exec([&] {
|
||||
tabletTool()->sendProximityOut();
|
||||
tabletTool()->sendFrame();
|
||||
});
|
||||
|
||||
QTRY_VERIFY(filter.numEvents());
|
||||
event = filter.popEvent();
|
||||
QCOMPARE(event->pointerType(), pointerType);
|
||||
QCOMPARE(event->device(), tabletDevice);
|
||||
}
|
||||
|
||||
void tst_tabletv2::hardwareSerial()
|
||||
{
|
||||
ProximityFilter filter;
|
||||
const quint64 uid = 0xbaba15dead15f00d;
|
||||
|
||||
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
|
||||
exec([&] {
|
||||
tabletSeat()->addTablet();
|
||||
tabletSeat()->addTool(ToolType::type_pen, uid);
|
||||
});
|
||||
|
||||
TabletWindow window;
|
||||
window.resize(64, 64);
|
||||
window.show();
|
||||
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
||||
|
||||
QCOMPOSITOR_TRY_VERIFY(tablet());
|
||||
exec([&] {
|
||||
auto *surface = xdgSurface()->m_surface;
|
||||
auto *tool = tabletTool();
|
||||
tool->sendProximityIn(tablet(), surface);
|
||||
QMargins margins = window.frameMargins();
|
||||
tool->sendMotion(QPointF(12 + margins.left(), 34 + margins.top()));
|
||||
tool->sendFrame();
|
||||
});
|
||||
|
||||
QTRY_COMPARE(filter.numEvents(), 1);
|
||||
QTabletEvent *event = filter.popEvent();
|
||||
QCOMPARE(event->uniqueId(), uid);
|
||||
|
||||
QTRY_VERIFY(window.numEvents());
|
||||
event = window.popEvent();
|
||||
QCOMPARE(event->uniqueId(), uid);
|
||||
|
||||
exec([&] {
|
||||
tabletTool()->sendProximityOut();
|
||||
tabletTool()->sendFrame();
|
||||
});
|
||||
|
||||
QTRY_VERIFY(filter.numEvents());
|
||||
event = filter.popEvent();
|
||||
QCOMPARE(event->uniqueId(), uid);
|
||||
}
|
||||
|
||||
// As defined in linux/input-event-codes.h
|
||||
#ifndef BTN_STYLUS
|
||||
#define BTN_STYLUS 0x14b
|
||||
#endif
|
||||
#ifndef BTN_STYLUS2
|
||||
#define BTN_STYLUS2 0x14c
|
||||
#endif
|
||||
|
||||
void tst_tabletv2::buttons_data()
|
||||
{
|
||||
QTest::addColumn<uint>("tabletButton");
|
||||
QTest::addColumn<Qt::MouseButton>("mouseButton");
|
||||
|
||||
QTest::newRow("BTN_STYLUS2") << uint(BTN_STYLUS2) << Qt::MouseButton::RightButton;
|
||||
QTest::newRow("BTN_STYLUS") << uint(BTN_STYLUS) << Qt::MouseButton::MiddleButton;
|
||||
}
|
||||
|
||||
void tst_tabletv2::buttons()
|
||||
{
|
||||
QFETCH(uint, tabletButton);
|
||||
QFETCH(Qt::MouseButton, mouseButton);
|
||||
|
||||
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
|
||||
exec([&] {
|
||||
tabletSeat()->addTablet();
|
||||
tabletSeat()->addTool();
|
||||
});
|
||||
|
||||
TabletWindow window;
|
||||
window.resize(64, 64);
|
||||
window.show();
|
||||
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
||||
|
||||
QCOMPOSITOR_TRY_VERIFY(tablet());
|
||||
exec([&] {
|
||||
tabletTool()->sendProximityIn(tablet(), xdgSurface()->m_surface);
|
||||
QMargins margins = window.frameMargins();
|
||||
tabletTool()->sendMotion(QPointF(12 + margins.left(), 34 + margins.top()));
|
||||
tabletTool()->sendFrame();
|
||||
});
|
||||
|
||||
QTRY_VERIFY(window.numEvents());
|
||||
window.popEvent();
|
||||
|
||||
QCOMPOSITOR_TRY_VERIFY(tablet());
|
||||
exec([&] {
|
||||
tabletTool()->sendButton(tabletButton, true);
|
||||
tabletTool()->sendFrame();
|
||||
tabletTool()->sendButton(tabletButton, false);
|
||||
tabletTool()->sendFrame();
|
||||
});
|
||||
|
||||
QTRY_VERIFY(window.numEvents());
|
||||
QTabletEvent *event = window.popEvent();
|
||||
QCOMPARE(event->buttons(), mouseButton);
|
||||
|
||||
exec([&] {
|
||||
tabletTool()->sendProximityOut();
|
||||
tabletTool()->sendFrame();
|
||||
});
|
||||
}
|
||||
|
||||
void tst_tabletv2::tabletEvents()
|
||||
{
|
||||
QCOMPOSITOR_TRY_VERIFY(tabletSeat());
|
||||
exec([&] {
|
||||
tabletSeat()->addTablet();
|
||||
tabletSeat()->addTool();
|
||||
});
|
||||
|
||||
TabletWindow window;
|
||||
window.resize(64, 64);
|
||||
window.show();
|
||||
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
||||
|
||||
const QPointF insideDecorations(window.frameMargins().left(), window.frameMargins().top());
|
||||
|
||||
QCOMPOSITOR_TRY_VERIFY(tablet());
|
||||
exec([&] {
|
||||
auto *surface = xdgSurface()->m_surface;
|
||||
auto *tool = tabletTool();
|
||||
// TODO: encapsulate this into a helper function?
|
||||
tool->sendProximityIn(tablet(), surface);
|
||||
tool->sendMotion(QPointF(12, 34) + insideDecorations);
|
||||
tool->sendDown();
|
||||
tool->sendPressure(65535);
|
||||
tool->sendFrame();
|
||||
});
|
||||
|
||||
QTRY_VERIFY(window.numEvents());
|
||||
QTabletEvent *event = window.popEvent();
|
||||
QCOMPARE(event->type(), QEvent::TabletPress);
|
||||
QCOMPARE(event->pressure(), 1.0);
|
||||
QCOMPARE(event->posF(), QPointF(12, 34));
|
||||
|
||||
// Values we didn't send should be 0
|
||||
QCOMPARE(event->rotation(), 0);
|
||||
QCOMPARE(event->xTilt(), 0);
|
||||
QCOMPARE(event->yTilt(), 0);
|
||||
|
||||
exec([&] {
|
||||
tabletTool()->sendMotion(QPointF(45, 56) + insideDecorations);
|
||||
tabletTool()->sendPressure(65535/2);
|
||||
tabletTool()->sendRotation(90);
|
||||
tabletTool()->sendTilt(13, 37);
|
||||
tabletTool()->sendFrame();
|
||||
});
|
||||
|
||||
QTRY_VERIFY(window.numEvents());
|
||||
event = window.popEvent();
|
||||
QCOMPARE(event->type(), QEvent::TabletMove);
|
||||
QVERIFY(qAbs(event->pressure() - 0.5) < 0.01);
|
||||
QVERIFY(qAbs(event->rotation() - 90) < 0.01);
|
||||
QVERIFY(qAbs(event->xTilt() - 13) < 0.01);
|
||||
QVERIFY(qAbs(event->yTilt() - 37) < 0.01);
|
||||
QCOMPARE(event->posF(), QPointF(45, 56));
|
||||
|
||||
// Verify that the values stay the same if we don't update them
|
||||
exec([&] {
|
||||
tabletTool()->sendMotion(QPointF(10, 11) + insideDecorations); // Change position only
|
||||
tabletTool()->sendFrame();
|
||||
});
|
||||
QTRY_VERIFY(window.numEvents());
|
||||
event = window.popEvent();
|
||||
QCOMPARE(event->type(), QEvent::TabletMove);
|
||||
QVERIFY(qAbs(event->pressure() - 0.5) < 0.01);
|
||||
QVERIFY(qAbs(event->rotation() - 90) < 0.01);
|
||||
QVERIFY(qAbs(event->xTilt() - 13) < 0.01);
|
||||
QVERIFY(qAbs(event->yTilt() - 37) < 0.01);
|
||||
QCOMPARE(event->posF(), QPointF(10, 11));
|
||||
|
||||
exec([&] {
|
||||
tabletTool()->sendPressure(0);
|
||||
tabletTool()->sendUp();
|
||||
tabletTool()->sendFrame();
|
||||
|
||||
tabletTool()->sendProximityOut();
|
||||
tabletTool()->sendFrame();
|
||||
});
|
||||
|
||||
QTRY_VERIFY(window.numEvents());
|
||||
event = window.popEvent();
|
||||
QCOMPARE(event->type(), QEvent::TabletRelease);
|
||||
QCOMPARE(event->pressure(), 0);
|
||||
QCOMPARE(event->posF(), QPointF(10, 11));
|
||||
}
|
||||
|
||||
QCOMPOSITOR_TEST_MAIN(tst_tabletv2)
|
||||
#include "tst_tabletv2.moc"
|
Loading…
x
Reference in New Issue
Block a user