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