1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qwaylandtabletv2_p.h"
41 #include "qwaylandinputdevice_p.h"
42 #include "qwaylanddisplay_p.h"
43 #include "qwaylandsurface_p.h"
44 
45 QT_BEGIN_NAMESPACE
46 
47 namespace QtWaylandClient {
48 
QWaylandTabletManagerV2(QWaylandDisplay * display,uint id,uint version)49 QWaylandTabletManagerV2::QWaylandTabletManagerV2(QWaylandDisplay *display, uint id, uint version)
50     : zwp_tablet_manager_v2(display->wl_registry(), id, qMin(version, uint(1)))
51 {
52     // Create tabletSeats for all seats.
53     // This only works if we get the manager after all seats
54     const auto seats = display->inputDevices();
55     for (auto *seat : seats)
56         createTabletSeat(seat);
57 }
58 
createTabletSeat(QWaylandInputDevice * seat)59 QWaylandTabletSeatV2 *QWaylandTabletManagerV2::createTabletSeat(QWaylandInputDevice *seat)
60 {
61     return new QWaylandTabletSeatV2(this, seat);
62 }
63 
QWaylandTabletSeatV2(QWaylandTabletManagerV2 * manager,QWaylandInputDevice * seat)64 QWaylandTabletSeatV2::QWaylandTabletSeatV2(QWaylandTabletManagerV2 *manager, QWaylandInputDevice *seat)
65     : QtWayland::zwp_tablet_seat_v2(manager->get_tablet_seat(seat->wl_seat()))
66 {
67 }
68 
~QWaylandTabletSeatV2()69 QWaylandTabletSeatV2::~QWaylandTabletSeatV2()
70 {
71     for (auto *tablet : m_tablets)
72         tablet->destroy();
73     for (auto *tool : m_tools)
74         tool->destroy();
75     for (auto *pad : m_pads)
76         pad->destroy();
77     destroy();
78 }
79 
zwp_tablet_seat_v2_tablet_added(zwp_tablet_v2 * id)80 void QWaylandTabletSeatV2::zwp_tablet_seat_v2_tablet_added(zwp_tablet_v2 *id)
81 {
82     auto *tablet = new QWaylandTabletV2(id);
83     m_tablets.push_back(tablet);
84     connect(tablet, &QWaylandTabletV2::destroyed, this, [this, tablet] { m_tablets.removeOne(tablet); });
85 }
86 
zwp_tablet_seat_v2_tool_added(zwp_tablet_tool_v2 * id)87 void QWaylandTabletSeatV2::zwp_tablet_seat_v2_tool_added(zwp_tablet_tool_v2 *id)
88 {
89     auto *tool = new QWaylandTabletToolV2(id);
90     m_tools.push_back(tool);
91     connect(tool, &QWaylandTabletToolV2::destroyed, this, [this, tool] { m_tools.removeOne(tool); });
92 }
93 
zwp_tablet_seat_v2_pad_added(zwp_tablet_pad_v2 * id)94 void QWaylandTabletSeatV2::zwp_tablet_seat_v2_pad_added(zwp_tablet_pad_v2 *id)
95 {
96     auto *pad = new QWaylandTabletPadV2(id);
97     m_pads.push_back(pad);
98     connect(pad, &QWaylandTabletPadV2::destroyed, this, [this, pad] { m_pads.removeOne(pad); });
99 }
100 
QWaylandTabletV2(::zwp_tablet_v2 * tablet)101 QWaylandTabletV2::QWaylandTabletV2(::zwp_tablet_v2 *tablet)
102     : QtWayland::zwp_tablet_v2(tablet)
103 {
104 }
105 
zwp_tablet_v2_removed()106 void QWaylandTabletV2::zwp_tablet_v2_removed()
107 {
108     destroy();
109     delete this;
110 }
111 
QWaylandTabletToolV2(::zwp_tablet_tool_v2 * tool)112 QWaylandTabletToolV2::QWaylandTabletToolV2(::zwp_tablet_tool_v2 *tool)
113     : QtWayland::zwp_tablet_tool_v2(tool)
114 {
115 }
116 
zwp_tablet_tool_v2_type(uint32_t tool_type)117 void QWaylandTabletToolV2::zwp_tablet_tool_v2_type(uint32_t tool_type)
118 {
119     m_toolType = type(tool_type);
120 }
121 
zwp_tablet_tool_v2_hardware_serial(uint32_t hardware_serial_hi,uint32_t hardware_serial_lo)122 void QWaylandTabletToolV2::zwp_tablet_tool_v2_hardware_serial(uint32_t hardware_serial_hi, uint32_t hardware_serial_lo)
123 {
124     m_uid = (quint64(hardware_serial_hi) << 32) + hardware_serial_lo;
125 }
126 
zwp_tablet_tool_v2_capability(uint32_t capability)127 void QWaylandTabletToolV2::zwp_tablet_tool_v2_capability(uint32_t capability)
128 {
129     if (capability == capability_rotation)
130         m_hasRotation = true;
131 }
132 
zwp_tablet_tool_v2_done()133 void QWaylandTabletToolV2::zwp_tablet_tool_v2_done()
134 {
135     switch (m_toolType) {
136     case type::type_airbrush:
137     case type::type_brush:
138     case type::type_pencil:
139     case type::type_pen:
140         m_pointerType = QTabletEvent::PointerType::Pen;
141         break;
142     case type::type_eraser:
143         m_pointerType = QTabletEvent::PointerType::Eraser;
144         break;
145     case type::type_mouse:
146     case type::type_lens:
147         m_pointerType = QTabletEvent::PointerType::Cursor;
148         break;
149     case type::type_finger:
150         m_pointerType = QTabletEvent::PointerType::UnknownPointer;
151         break;
152     }
153     switch (m_toolType) {
154     case type::type_airbrush:
155         m_tabletDevice = QTabletEvent::TabletDevice::Airbrush;
156         break;
157     case type::type_brush:
158     case type::type_pencil:
159     case type::type_pen:
160     case type::type_eraser:
161         m_tabletDevice = m_hasRotation ? QTabletEvent::TabletDevice::RotationStylus : QTabletEvent::TabletDevice::Stylus;
162         break;
163     case type::type_lens:
164         m_tabletDevice = QTabletEvent::TabletDevice::Puck;
165         break;
166     case type::type_mouse:
167     case type::type_finger:
168         m_tabletDevice = QTabletEvent::TabletDevice::NoDevice;
169         break;
170     }
171 }
172 
zwp_tablet_tool_v2_removed()173 void QWaylandTabletToolV2::zwp_tablet_tool_v2_removed()
174 {
175     destroy();
176     delete this;
177 }
178 
zwp_tablet_tool_v2_proximity_in(uint32_t serial,zwp_tablet_v2 * tablet,wl_surface * surface)179 void QWaylandTabletToolV2::zwp_tablet_tool_v2_proximity_in(uint32_t serial, zwp_tablet_v2 *tablet, wl_surface *surface)
180 {
181     Q_UNUSED(tablet);
182     Q_UNUSED(serial);
183     if (Q_UNLIKELY(!surface)) {
184         qCDebug(lcQpaWayland) << "Ignoring zwp_tablet_tool_v2_proximity_v2 with no surface";
185         return;
186     }
187     m_pending.enteredSurface = true;
188     m_pending.proximitySurface = QWaylandSurface::fromWlSurface(surface);
189 }
190 
zwp_tablet_tool_v2_proximity_out()191 void QWaylandTabletToolV2::zwp_tablet_tool_v2_proximity_out()
192 {
193     m_pending.enteredSurface = false;
194     m_pending.proximitySurface = nullptr;
195 }
196 
zwp_tablet_tool_v2_down(uint32_t serial)197 void QWaylandTabletToolV2::zwp_tablet_tool_v2_down(uint32_t serial)
198 {
199     Q_UNUSED(serial);
200     m_pending.down = true;
201 }
202 
zwp_tablet_tool_v2_up()203 void QWaylandTabletToolV2::zwp_tablet_tool_v2_up()
204 {
205     m_pending.down = false;
206 }
207 
zwp_tablet_tool_v2_motion(wl_fixed_t x,wl_fixed_t y)208 void QWaylandTabletToolV2::zwp_tablet_tool_v2_motion(wl_fixed_t x, wl_fixed_t y)
209 {
210     m_pending.surfacePosition = QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y));
211 }
212 
zwp_tablet_tool_v2_pressure(uint32_t pressure)213 void QWaylandTabletToolV2::zwp_tablet_tool_v2_pressure(uint32_t pressure)
214 {
215     const int maxPressure = 65535;
216     m_pending.pressure = qreal(pressure)/maxPressure;
217 }
218 
zwp_tablet_tool_v2_distance(uint32_t distance)219 void QWaylandTabletToolV2::zwp_tablet_tool_v2_distance(uint32_t distance)
220 {
221     m_pending.distance = distance;
222 }
223 
zwp_tablet_tool_v2_tilt(wl_fixed_t tilt_x,wl_fixed_t tilt_y)224 void QWaylandTabletToolV2::zwp_tablet_tool_v2_tilt(wl_fixed_t tilt_x, wl_fixed_t tilt_y)
225 {
226     m_pending.xTilt = wl_fixed_to_double(tilt_x);
227     m_pending.yTilt = wl_fixed_to_double(tilt_y);
228 }
229 
zwp_tablet_tool_v2_rotation(wl_fixed_t degrees)230 void QWaylandTabletToolV2::zwp_tablet_tool_v2_rotation(wl_fixed_t degrees)
231 {
232     m_pending.rotation = wl_fixed_to_double(degrees);
233 }
234 
zwp_tablet_tool_v2_slider(int32_t position)235 void QWaylandTabletToolV2::zwp_tablet_tool_v2_slider(int32_t position)
236 {
237     m_pending.slider = qreal(position) / 65535;
238 }
239 
mouseButtonFromTablet(uint button)240 static Qt::MouseButton mouseButtonFromTablet(uint button)
241 {
242     switch (button) {
243     case 0x110: return Qt::MouseButton::LeftButton; // BTN_LEFT
244     case 0x14b: return Qt::MouseButton::MiddleButton; // BTN_STYLUS
245     case 0x14c: return Qt::MouseButton::RightButton; // BTN_STYLUS2
246     default:
247         return Qt::NoButton;
248     }
249 }
250 
zwp_tablet_tool_v2_button(uint32_t serial,uint32_t button,uint32_t state)251 void QWaylandTabletToolV2::zwp_tablet_tool_v2_button(uint32_t serial, uint32_t button, uint32_t state)
252 {
253     Q_UNUSED(serial);
254     Qt::MouseButton mouseButton = mouseButtonFromTablet(button);
255     if (state == button_state_pressed)
256         m_pending.buttons |= mouseButton;
257     else
258         m_pending.buttons &= ~mouseButton;
259 }
260 
zwp_tablet_tool_v2_frame(uint32_t time)261 void QWaylandTabletToolV2::zwp_tablet_tool_v2_frame(uint32_t time)
262 {
263     if (m_pending.proximitySurface && !m_applied.proximitySurface) {
264         QWindowSystemInterface::handleTabletEnterProximityEvent(m_tabletDevice, m_pointerType, m_uid);
265         m_applied.proximitySurface = m_pending.proximitySurface;
266     }
267 
268     if (!(m_pending == m_applied) && m_pending.proximitySurface) {
269         if (!m_pending.proximitySurface) {
270             qCWarning(lcQpaWayland) << "Can't send tablet event with no proximity surface, ignoring";
271             return;
272         }
273         QWaylandWindow *waylandWindow = QWaylandWindow::fromWlSurface(m_pending.proximitySurface->object());
274         QWindow *window = waylandWindow->window();
275         ulong timestamp = time;
276         const QPointF localPosition = waylandWindow->mapFromWlSurface(m_pending.surfacePosition);
277 
278         QPointF delta = localPosition - localPosition.toPoint();
279         QPointF globalPosition = window->mapToGlobal(localPosition.toPoint());
280         globalPosition += delta;
281 
282         Qt::MouseButtons buttons = m_pending.down ? Qt::MouseButton::LeftButton : Qt::MouseButton::NoButton;
283         buttons |= m_pending.buttons;
284         qreal pressure = m_pending.pressure;
285         int xTilt = int(m_pending.xTilt);
286         int yTilt = int(m_pending.yTilt);
287         qreal tangentialPressure = m_pending.slider;
288         qreal rotation = m_pending.rotation;
289         int z = int(m_pending.distance);
290         QWindowSystemInterface::handleTabletEvent(window, timestamp, localPosition, globalPosition,
291                                                   m_tabletDevice, m_pointerType, buttons, pressure,
292                                                   xTilt, yTilt, tangentialPressure, rotation, z, m_uid);
293     }
294 
295     if (!m_pending.proximitySurface && m_applied.enteredSurface) {
296         QWindowSystemInterface::handleTabletLeaveProximityEvent(m_tabletDevice, m_pointerType, m_uid);
297         m_pending = State(); // Don't leave pressure etc. lying around when we enter the next surface
298     }
299 
300     m_applied = m_pending;
301 }
302 
303 // TODO: delete when upgrading to c++20
operator ==(const QWaylandTabletToolV2::State & o) const304 bool QWaylandTabletToolV2::State::operator==(const QWaylandTabletToolV2::State &o) const {
305     return
306             down == o.down &&
307             proximitySurface.data() == o.proximitySurface.data() &&
308             enteredSurface == o.enteredSurface &&
309             surfacePosition == o.surfacePosition &&
310             distance == o.distance &&
311             pressure == o.pressure &&
312             rotation == o.rotation &&
313             xTilt == o.xTilt &&
314             yTilt == o.yTilt &&
315             slider == o.slider &&
316             buttons == o.buttons;
317 }
318 
QWaylandTabletPadV2(::zwp_tablet_pad_v2 * pad)319 QWaylandTabletPadV2::QWaylandTabletPadV2(::zwp_tablet_pad_v2 *pad)
320     : QtWayland::zwp_tablet_pad_v2(pad)
321 {
322 }
323 
zwp_tablet_pad_v2_removed()324 void QWaylandTabletPadV2::zwp_tablet_pad_v2_removed()
325 {
326     destroy();
327     delete this;
328 }
329 
330 } // namespace QtWaylandClient
331 
332 QT_END_NAMESPACE
333