1 | // Copyright (C) 2019 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 "qwaylandtabletv2_p.h" |
5 | #include "qwaylandinputdevice_p.h" |
6 | #include "qwaylanddisplay_p.h" |
7 | #include "qwaylandsurface_p.h" |
8 | |
9 | QT_BEGIN_NAMESPACE |
10 | |
11 | namespace QtWaylandClient { |
12 | |
13 | QWaylandTabletManagerV2::QWaylandTabletManagerV2(QWaylandDisplay *display, uint id, uint version) |
14 | : zwp_tablet_manager_v2(display->wl_registry(), id, qMin(version, uint(1))) |
15 | { |
16 | // Create tabletSeats for all seats. |
17 | // This only works if we get the manager after all seats |
18 | const auto seats = display->inputDevices(); |
19 | for (auto *seat : seats) |
20 | createTabletSeat(seat); |
21 | } |
22 | |
23 | QWaylandTabletSeatV2 *QWaylandTabletManagerV2::createTabletSeat(QWaylandInputDevice *seat) |
24 | { |
25 | return new QWaylandTabletSeatV2(this, seat); |
26 | } |
27 | |
28 | QWaylandTabletSeatV2::QWaylandTabletSeatV2(QWaylandTabletManagerV2 *manager, QWaylandInputDevice *seat) |
29 | : QtWayland::zwp_tablet_seat_v2(manager->get_tablet_seat(seat->wl_seat())) |
30 | , m_seat(seat) |
31 | { |
32 | } |
33 | |
34 | QWaylandTabletSeatV2::~QWaylandTabletSeatV2() |
35 | { |
36 | for (auto *tablet : m_tablets) |
37 | tablet->destroy(); |
38 | for (auto *tool : m_tools) |
39 | tool->destroy(); |
40 | for (auto *pad : m_pads) |
41 | pad->destroy(); |
42 | qDeleteAll(c: m_tablets); |
43 | qDeleteAll(c: m_tools); |
44 | qDeleteAll(c: m_pads); |
45 | destroy(); |
46 | } |
47 | |
48 | void QWaylandTabletSeatV2::zwp_tablet_seat_v2_tablet_added(zwp_tablet_v2 *id) |
49 | { |
50 | auto *tablet = new QWaylandTabletV2(id); |
51 | m_tablets.push_back(tablet); |
52 | connect(tablet, &QWaylandTabletV2::destroyed, this, [this, tablet] { m_tablets.removeOne(tablet); }); |
53 | } |
54 | |
55 | void QWaylandTabletSeatV2::zwp_tablet_seat_v2_tool_added(zwp_tablet_tool_v2 *id) |
56 | { |
57 | auto *tool = new QWaylandTabletToolV2(this, id); |
58 | m_tools.push_back(tool); |
59 | connect(tool, &QWaylandTabletToolV2::destroyed, this, [this, tool] { m_tools.removeOne(tool); }); |
60 | } |
61 | |
62 | void QWaylandTabletSeatV2::zwp_tablet_seat_v2_pad_added(zwp_tablet_pad_v2 *id) |
63 | { |
64 | auto *pad = new QWaylandTabletPadV2(id); |
65 | m_pads.push_back(pad); |
66 | connect(pad, &QWaylandTabletPadV2::destroyed, this, [this, pad] { m_pads.removeOne(pad); }); |
67 | } |
68 | |
69 | QWaylandTabletV2::QWaylandTabletV2(::zwp_tablet_v2 *tablet) |
70 | : QtWayland::zwp_tablet_v2(tablet) |
71 | { |
72 | } |
73 | |
74 | void QWaylandTabletV2::zwp_tablet_v2_removed() |
75 | { |
76 | destroy(); |
77 | delete this; |
78 | } |
79 | |
80 | QWaylandTabletToolV2::QWaylandTabletToolV2(QWaylandTabletSeatV2 *tabletSeat, ::zwp_tablet_tool_v2 *tool) |
81 | : QtWayland::zwp_tablet_tool_v2(tool) |
82 | , m_tabletSeat(tabletSeat) |
83 | { |
84 | } |
85 | |
86 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_type(uint32_t tool_type) |
87 | { |
88 | m_toolType = type(tool_type); |
89 | } |
90 | |
91 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_hardware_serial(uint32_t hardware_serial_hi, uint32_t hardware_serial_lo) |
92 | { |
93 | m_uid = (quint64(hardware_serial_hi) << 32) + hardware_serial_lo; |
94 | } |
95 | |
96 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_capability(uint32_t capability) |
97 | { |
98 | if (capability == capability_rotation) |
99 | m_hasRotation = true; |
100 | } |
101 | |
102 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_done() |
103 | { |
104 | switch (m_toolType) { |
105 | case type::type_airbrush: |
106 | case type::type_brush: |
107 | case type::type_pencil: |
108 | case type::type_pen: |
109 | m_pointerType = QPointingDevice::PointerType::Pen; |
110 | break; |
111 | case type::type_eraser: |
112 | m_pointerType = QPointingDevice::PointerType::Eraser; |
113 | break; |
114 | case type::type_mouse: |
115 | case type::type_lens: |
116 | m_pointerType = QPointingDevice::PointerType::Cursor; |
117 | break; |
118 | case type::type_finger: |
119 | m_pointerType = QPointingDevice::PointerType::Unknown; |
120 | break; |
121 | } |
122 | switch (m_toolType) { |
123 | case type::type_airbrush: |
124 | m_tabletDevice = QInputDevice::DeviceType::Airbrush; |
125 | break; |
126 | case type::type_brush: |
127 | case type::type_pencil: |
128 | case type::type_pen: |
129 | case type::type_eraser: |
130 | m_tabletDevice = QInputDevice::DeviceType::Stylus; |
131 | break; |
132 | case type::type_lens: |
133 | m_tabletDevice = QInputDevice::DeviceType::Puck; |
134 | break; |
135 | case type::type_mouse: |
136 | case type::type_finger: |
137 | m_tabletDevice = QInputDevice::DeviceType::Unknown; |
138 | break; |
139 | } |
140 | } |
141 | |
142 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_removed() |
143 | { |
144 | destroy(); |
145 | delete this; |
146 | } |
147 | |
148 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_proximity_in(uint32_t serial, zwp_tablet_v2 *tablet, wl_surface *surface) |
149 | { |
150 | Q_UNUSED(tablet); |
151 | Q_UNUSED(serial); |
152 | if (Q_UNLIKELY(!surface)) { |
153 | qCDebug(lcQpaWayland) << "Ignoring zwp_tablet_tool_v2_proximity_v2 with no surface" ; |
154 | return; |
155 | } |
156 | m_pending.enteredSurface = true; |
157 | m_pending.proximitySurface = QWaylandSurface::fromWlSurface(surface); |
158 | } |
159 | |
160 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_proximity_out() |
161 | { |
162 | m_pending.enteredSurface = false; |
163 | m_pending.proximitySurface = nullptr; |
164 | } |
165 | |
166 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_down(uint32_t serial) |
167 | { |
168 | m_pending.down = true; |
169 | |
170 | if (m_pending.proximitySurface) { |
171 | if (QWaylandWindow *window = m_pending.proximitySurface->waylandWindow()) { |
172 | QWaylandInputDevice *seat = m_tabletSeat->seat(); |
173 | seat->display()->setLastInputDevice(device: seat, serial, window); |
174 | } |
175 | } |
176 | } |
177 | |
178 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_up() |
179 | { |
180 | m_pending.down = false; |
181 | } |
182 | |
183 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_motion(wl_fixed_t x, wl_fixed_t y) |
184 | { |
185 | m_pending.surfacePosition = QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y)); |
186 | } |
187 | |
188 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_pressure(uint32_t pressure) |
189 | { |
190 | const int maxPressure = 65535; |
191 | m_pending.pressure = qreal(pressure)/maxPressure; |
192 | } |
193 | |
194 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_distance(uint32_t distance) |
195 | { |
196 | m_pending.distance = distance; |
197 | } |
198 | |
199 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_tilt(wl_fixed_t tilt_x, wl_fixed_t tilt_y) |
200 | { |
201 | m_pending.xTilt = wl_fixed_to_double(tilt_x); |
202 | m_pending.yTilt = wl_fixed_to_double(tilt_y); |
203 | } |
204 | |
205 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_rotation(wl_fixed_t degrees) |
206 | { |
207 | m_pending.rotation = wl_fixed_to_double(degrees); |
208 | } |
209 | |
210 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_slider(int32_t position) |
211 | { |
212 | m_pending.slider = qreal(position) / 65535; |
213 | } |
214 | |
215 | static Qt::MouseButton mouseButtonFromTablet(uint button) |
216 | { |
217 | switch (button) { |
218 | case 0x110: return Qt::MouseButton::LeftButton; // BTN_LEFT |
219 | case 0x14b: return Qt::MouseButton::MiddleButton; // BTN_STYLUS |
220 | case 0x14c: return Qt::MouseButton::RightButton; // BTN_STYLUS2 |
221 | default: |
222 | return Qt::NoButton; |
223 | } |
224 | } |
225 | |
226 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_button(uint32_t serial, uint32_t button, uint32_t state) |
227 | { |
228 | Q_UNUSED(serial); |
229 | Qt::MouseButton mouseButton = mouseButtonFromTablet(button); |
230 | if (state == button_state_pressed) |
231 | m_pending.buttons |= mouseButton; |
232 | else |
233 | m_pending.buttons &= ~mouseButton; |
234 | } |
235 | |
236 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_frame(uint32_t time) |
237 | { |
238 | if (m_pending.proximitySurface && !m_applied.proximitySurface) { |
239 | QWindowSystemInterface::handleTabletEnterProximityEvent(deviceType: int(m_tabletDevice), pointerType: int(m_pointerType), uid: m_uid); |
240 | m_applied.proximitySurface = m_pending.proximitySurface; |
241 | } |
242 | |
243 | if (!(m_pending == m_applied) && m_pending.proximitySurface) { |
244 | if (!m_pending.proximitySurface) { |
245 | qCWarning(lcQpaWayland) << "Can't send tablet event with no proximity surface, ignoring" ; |
246 | return; |
247 | } |
248 | QWaylandWindow *waylandWindow = QWaylandWindow::fromWlSurface(surface: m_pending.proximitySurface->object()); |
249 | QWindow *window = waylandWindow->window(); |
250 | ulong timestamp = time; |
251 | const QPointF localPosition = waylandWindow->mapFromWlSurface(surfacePosition: m_pending.surfacePosition); |
252 | |
253 | QPointF delta = localPosition - localPosition.toPoint(); |
254 | QPointF globalPosition = window->mapToGlobal(pos: localPosition.toPoint()); |
255 | globalPosition += delta; |
256 | |
257 | Qt::MouseButtons buttons = m_pending.down ? Qt::MouseButton::LeftButton : Qt::MouseButton::NoButton; |
258 | buttons |= m_pending.buttons; |
259 | qreal pressure = m_pending.pressure; |
260 | int xTilt = int(m_pending.xTilt); |
261 | int yTilt = int(m_pending.yTilt); |
262 | qreal tangentialPressure = m_pending.slider; |
263 | qreal rotation = m_pending.rotation; |
264 | int z = int(m_pending.distance); |
265 | QWindowSystemInterface::handleTabletEvent(window, timestamp, local: localPosition, global: globalPosition, |
266 | device: int(m_tabletDevice), pointerType: int(m_pointerType), buttons, pressure, |
267 | xTilt, yTilt, tangentialPressure, rotation, z, uid: m_uid); |
268 | } |
269 | |
270 | if (!m_pending.proximitySurface && m_applied.enteredSurface) { |
271 | QWindowSystemInterface::handleTabletLeaveProximityEvent(deviceType: int(m_tabletDevice), pointerType: int(m_pointerType), uid: m_uid); |
272 | m_pending = State(); // Don't leave pressure etc. lying around when we enter the next surface |
273 | } |
274 | |
275 | m_applied = m_pending; |
276 | } |
277 | |
278 | // TODO: delete when upgrading to c++20 |
279 | bool QWaylandTabletToolV2::State::operator==(const QWaylandTabletToolV2::State &o) const { |
280 | return |
281 | down == o.down && |
282 | proximitySurface.data() == o.proximitySurface.data() && |
283 | enteredSurface == o.enteredSurface && |
284 | surfacePosition == o.surfacePosition && |
285 | distance == o.distance && |
286 | pressure == o.pressure && |
287 | rotation == o.rotation && |
288 | xTilt == o.xTilt && |
289 | yTilt == o.yTilt && |
290 | slider == o.slider && |
291 | buttons == o.buttons; |
292 | } |
293 | |
294 | QWaylandTabletPadV2::QWaylandTabletPadV2(::zwp_tablet_pad_v2 *pad) |
295 | : QtWayland::zwp_tablet_pad_v2(pad) |
296 | { |
297 | } |
298 | |
299 | void QWaylandTabletPadV2::zwp_tablet_pad_v2_removed() |
300 | { |
301 | destroy(); |
302 | delete this; |
303 | } |
304 | |
305 | } // namespace QtWaylandClient |
306 | |
307 | QT_END_NAMESPACE |
308 | |
309 | #include "moc_qwaylandtabletv2_p.cpp" |
310 | |