1 | // Copyright (C) 2017 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "qwaylandpointer.h" |
5 | #include "qwaylandpointer_p.h" |
6 | #include <QtWaylandCompositor/QWaylandClient> |
7 | #include <QtWaylandCompositor/QWaylandCompositor> |
8 | |
9 | QT_BEGIN_NAMESPACE |
10 | |
11 | QWaylandSurfaceRole QWaylandPointerPrivate::s_role("wl_pointer" ); |
12 | |
13 | QWaylandPointerPrivate::QWaylandPointerPrivate(QWaylandPointer *pointer, QWaylandSeat *seat) |
14 | : seat(seat) |
15 | { |
16 | Q_UNUSED(pointer); |
17 | } |
18 | |
19 | uint QWaylandPointerPrivate::sendButton(Qt::MouseButton button, uint32_t state) |
20 | { |
21 | Q_Q(QWaylandPointer); |
22 | if (!q->mouseFocus() || !q->mouseFocus()->surface()) |
23 | return 0; |
24 | |
25 | wl_client *client = q->mouseFocus()->surface()->waylandClient(); |
26 | uint32_t time = compositor()->currentTimeMsecs(); |
27 | uint32_t serial = compositor()->nextSerial(); |
28 | for (auto resource : resourceMap().values(client)) |
29 | send_button(resource->handle, serial, time, q->toWaylandButton(button), state); |
30 | return serial; |
31 | } |
32 | |
33 | void QWaylandPointerPrivate::sendMotion() |
34 | { |
35 | Q_ASSERT(enteredSurface); |
36 | uint32_t time = compositor()->currentTimeMsecs(); |
37 | wl_fixed_t x = wl_fixed_from_double(localPosition.x()); |
38 | wl_fixed_t y = wl_fixed_from_double(localPosition.y()); |
39 | for (auto resource : resourceMap().values(enteredSurface->waylandClient())) |
40 | wl_pointer_send_motion(resource->handle, time, x, y); |
41 | } |
42 | |
43 | void QWaylandPointerPrivate::sendEnter(QWaylandSurface *surface) |
44 | { |
45 | Q_ASSERT(surface && !enteredSurface); |
46 | enterSerial = compositor()->nextSerial(); |
47 | |
48 | QWaylandKeyboard *keyboard = seat->keyboard(); |
49 | if (keyboard) |
50 | keyboard->sendKeyModifiers(client: surface->client(), serial: enterSerial); |
51 | |
52 | wl_fixed_t x = wl_fixed_from_double(localPosition.x()); |
53 | wl_fixed_t y = wl_fixed_from_double(localPosition.y()); |
54 | for (auto resource : resourceMap().values(surface->waylandClient())) |
55 | send_enter(resource->handle, enterSerial, surface->resource(), x, y); |
56 | |
57 | enteredSurface = surface; |
58 | enteredSurfaceDestroyListener.listenForDestruction(resource: surface->resource()); |
59 | } |
60 | |
61 | void QWaylandPointerPrivate::sendLeave() |
62 | { |
63 | Q_ASSERT(enteredSurface); |
64 | uint32_t serial = compositor()->nextSerial(); |
65 | for (auto resource : resourceMap().values(enteredSurface->waylandClient())) |
66 | send_leave(resource->handle, serial, enteredSurface->resource()); |
67 | localPosition = QPointF(); |
68 | enteredSurfaceDestroyListener.reset(); |
69 | enteredSurface = nullptr; |
70 | } |
71 | |
72 | void QWaylandPointerPrivate::ensureEntered(QWaylandSurface *surface) |
73 | { |
74 | if (enteredSurface == surface) |
75 | return; |
76 | |
77 | if (enteredSurface) |
78 | sendLeave(); |
79 | |
80 | if (surface) |
81 | sendEnter(surface); |
82 | } |
83 | |
84 | void QWaylandPointerPrivate::pointer_release(wl_pointer::Resource *resource) |
85 | { |
86 | wl_resource_destroy(resource->handle); |
87 | } |
88 | |
89 | void QWaylandPointerPrivate::pointer_set_cursor(wl_pointer::Resource *resource, uint32_t serial, wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y) |
90 | { |
91 | Q_UNUSED(serial); |
92 | |
93 | if (!surface) { |
94 | seat->cursorSurfaceRequested(surface: nullptr, hotspotX: 0, hotspotY: 0, client: QWaylandClient::fromWlClient(compositor: compositor(), wlClient: resource->client())); |
95 | return; |
96 | } |
97 | |
98 | QWaylandSurface *s = QWaylandSurface::fromResource(resource: surface); |
99 | // XXX FIXME |
100 | // The role concept was formalized in wayland 1.7, so that release adds one error |
101 | // code for each interface that implements a role, and we are supposed to pass here |
102 | // the newly constructed resource and the correct error code so that if setting the |
103 | // role fails, a proper error can be sent to the client. |
104 | // However we're still using wayland 1.4, which doesn't have interface specific role |
105 | // errors, so the best we can do is to use wl_display's object_id error. |
106 | wl_resource *displayRes = wl_client_get_object(resource->client(), 1); |
107 | if (s->setRole(role: &QWaylandPointerPrivate::s_role, errorResource: displayRes, errorCode: WL_DISPLAY_ERROR_INVALID_OBJECT)) { |
108 | s->markAsCursorSurface(cursorSurface: true); |
109 | seat->cursorSurfaceRequested(surface: s, hotspotX: hotspot_x, hotspotY: hotspot_y, client: QWaylandClient::fromWlClient(compositor: compositor(), wlClient: resource->client())); |
110 | } |
111 | } |
112 | |
113 | /*! |
114 | * \class QWaylandPointer |
115 | * \inmodule QtWaylandCompositor |
116 | * \since 5.8 |
117 | * \brief The QWaylandPointer class represents a pointer device. |
118 | * |
119 | * This class provides access to the pointer device in a QWaylandSeat. It corresponds to |
120 | * the Wayland interface wl_pointer. |
121 | */ |
122 | |
123 | /*! |
124 | * Constructs a QWaylandPointer for the given \a seat and with the given \a parent. |
125 | */ |
126 | QWaylandPointer::QWaylandPointer(QWaylandSeat *seat, QObject *parent) |
127 | : QWaylandObject(* new QWaylandPointerPrivate(this, seat), parent) |
128 | { |
129 | connect(sender: &d_func()->enteredSurfaceDestroyListener, signal: &QWaylandDestroyListener::fired, context: this, slot: &QWaylandPointer::enteredSurfaceDestroyed); |
130 | connect(sender: seat, signal: &QWaylandSeat::mouseFocusChanged, context: this, slot: &QWaylandPointer::pointerFocusChanged); |
131 | } |
132 | |
133 | /*! |
134 | * Returns the input device for this QWaylandPointer. |
135 | */ |
136 | QWaylandSeat *QWaylandPointer::seat() const |
137 | { |
138 | Q_D(const QWaylandPointer); |
139 | return d->seat; |
140 | } |
141 | |
142 | /*! |
143 | * Returns the compositor for this QWaylandPointer. |
144 | */ |
145 | QWaylandCompositor *QWaylandPointer::compositor() const |
146 | { |
147 | Q_D(const QWaylandPointer); |
148 | return d->compositor(); |
149 | } |
150 | |
151 | /*! |
152 | * Returns the output for this QWaylandPointer. |
153 | */ |
154 | QWaylandOutput *QWaylandPointer::output() const |
155 | { |
156 | Q_D(const QWaylandPointer); |
157 | return d->output; |
158 | } |
159 | |
160 | /*! |
161 | * Sets the output for this QWaylandPointer to \a output. |
162 | */ |
163 | void QWaylandPointer::setOutput(QWaylandOutput *output) |
164 | { |
165 | Q_D(QWaylandPointer); |
166 | if (d->output == output) return; |
167 | d->output = output; |
168 | outputChanged(); |
169 | } |
170 | |
171 | /*! |
172 | * Sends a mouse press event for \a button to the view currently holding mouse focus. |
173 | * |
174 | * Returns the serial number of the press event. |
175 | */ |
176 | uint QWaylandPointer::sendMousePressEvent(Qt::MouseButton button) |
177 | { |
178 | Q_D(QWaylandPointer); |
179 | d->buttonCount++; |
180 | |
181 | if (d->buttonCount == 1) |
182 | emit buttonPressedChanged(); |
183 | |
184 | return d->sendButton(button, state: WL_POINTER_BUTTON_STATE_PRESSED); |
185 | } |
186 | |
187 | /*! |
188 | * Sends a mouse release event for \a button to the view currently holding mouse focus. |
189 | * |
190 | * Returns the serial number of the release event. |
191 | */ |
192 | uint QWaylandPointer::sendMouseReleaseEvent(Qt::MouseButton button) |
193 | { |
194 | Q_D(QWaylandPointer); |
195 | d->buttonCount--; |
196 | |
197 | if (d->buttonCount == 0) |
198 | emit buttonPressedChanged(); |
199 | |
200 | return d->sendButton(button, state: WL_POINTER_BUTTON_STATE_RELEASED); |
201 | } |
202 | |
203 | /*! |
204 | * Sets the current mouse focus to \a view and sends a mouse move event to it with the |
205 | * local position \a localPos in surface coordinates and output space position \a outputSpacePos. |
206 | */ |
207 | void QWaylandPointer::sendMouseMoveEvent(QWaylandView *view, const QPointF &localPos, const QPointF &outputSpacePos) |
208 | { |
209 | Q_D(QWaylandPointer); |
210 | if (view && (!view->surface() || view->surface()->isCursorSurface())) |
211 | view = nullptr; |
212 | d->seat->setMouseFocus(view); |
213 | d->localPosition = localPos; |
214 | d->spacePosition = outputSpacePos; |
215 | |
216 | if (view) { |
217 | // We adjust if the mouse position is on the edge |
218 | // to work around Qt's event propagation |
219 | QSizeF size(view->surface()->destinationSize()); |
220 | if (d->localPosition.x() == size.width()) |
221 | d->localPosition.rx() -= 0.01; |
222 | if (d->localPosition.y() == size.height()) |
223 | d->localPosition.ry() -= 0.01; |
224 | |
225 | d->ensureEntered(surface: view->surface()); |
226 | d->sendMotion(); |
227 | |
228 | if (view->output()) |
229 | setOutput(view->output()); |
230 | } |
231 | } |
232 | |
233 | /*! |
234 | * Sends a mouse wheel event with the given \a orientation and \a delta to the view that currently holds mouse focus. |
235 | */ |
236 | void QWaylandPointer::sendMouseWheelEvent(Qt::Orientation orientation, int delta) |
237 | { |
238 | Q_D(QWaylandPointer); |
239 | if (!d->enteredSurface) |
240 | return; |
241 | |
242 | uint32_t time = d->compositor()->currentTimeMsecs(); |
243 | uint32_t axis = orientation == Qt::Horizontal ? WL_POINTER_AXIS_HORIZONTAL_SCROLL |
244 | : WL_POINTER_AXIS_VERTICAL_SCROLL; |
245 | |
246 | for (auto resource : d->resourceMap().values(d->enteredSurface->waylandClient())) |
247 | d->send_axis(resource->handle, time, axis, wl_fixed_from_int(-delta / 12)); |
248 | } |
249 | |
250 | /*! |
251 | * Returns the view that currently holds mouse focus. |
252 | */ |
253 | QWaylandView *QWaylandPointer::mouseFocus() const |
254 | { |
255 | Q_D(const QWaylandPointer); |
256 | return d->seat->mouseFocus(); |
257 | } |
258 | |
259 | /*! |
260 | * Returns the current local position of the QWaylandPointer in surface coordinates. |
261 | */ |
262 | QPointF QWaylandPointer::currentLocalPosition() const |
263 | { |
264 | Q_D(const QWaylandPointer); |
265 | return d->localPosition; |
266 | } |
267 | |
268 | /*! |
269 | * Returns the current output space position of the QWaylandPointer. |
270 | */ |
271 | QPointF QWaylandPointer::currentSpacePosition() const |
272 | { |
273 | Q_D(const QWaylandPointer); |
274 | return d->spacePosition; |
275 | } |
276 | |
277 | /*! |
278 | * Returns true if any button is currently pressed. Otherwise returns false. |
279 | */ |
280 | bool QWaylandPointer::isButtonPressed() const |
281 | { |
282 | Q_D(const QWaylandPointer); |
283 | return d->buttonCount > 0; |
284 | } |
285 | |
286 | /*! |
287 | * \internal |
288 | */ |
289 | void QWaylandPointer::addClient(QWaylandClient *client, uint32_t id, uint32_t version) |
290 | { |
291 | Q_D(QWaylandPointer); |
292 | wl_resource *resource = d->add(client->client(), id, qMin<uint32_t>(QtWaylandServer::wl_pointer::interfaceVersion(), version))->handle; |
293 | if (d->enteredSurface && client == d->enteredSurface->client()) { |
294 | d->send_enter(resource, d->enterSerial, d->enteredSurface->resource(), |
295 | wl_fixed_from_double(d->localPosition.x()), |
296 | wl_fixed_from_double(d->localPosition.y())); |
297 | } |
298 | } |
299 | |
300 | /*! |
301 | * Returns a Wayland resource for this QWaylandPointer. |
302 | * |
303 | * This API doesn't actually make sense, since there may be many pointer resources per client |
304 | * It's here for compatibility reasons. |
305 | */ |
306 | struct wl_resource *QWaylandPointer::focusResource() const |
307 | { |
308 | Q_D(const QWaylandPointer); |
309 | QWaylandView *focus = d->seat->mouseFocus(); |
310 | if (!focus) |
311 | return nullptr; |
312 | |
313 | // Just return the first resource we can find. |
314 | return d->resourceMap().value(focus->surface()->waylandClient())->handle; |
315 | } |
316 | |
317 | /*! |
318 | * \internal |
319 | */ |
320 | uint QWaylandPointer::sendButton(struct wl_resource *resource, uint32_t time, Qt::MouseButton button, uint32_t state) |
321 | { |
322 | // This method is here for compatibility reasons only, since it usually doesn't make sense to |
323 | // send button events to just one of the pointer resources for a client. |
324 | Q_D(QWaylandPointer); |
325 | uint32_t serial = d->compositor()->nextSerial(); |
326 | d->send_button(resource, serial, time, toWaylandButton(button), state); |
327 | return serial; |
328 | } |
329 | |
330 | /*! |
331 | * \internal |
332 | */ |
333 | uint32_t QWaylandPointer::toWaylandButton(Qt::MouseButton button) |
334 | { |
335 | #ifndef BTN_LEFT |
336 | uint32_t BTN_LEFT = 0x110; |
337 | #endif |
338 | // the range of valid buttons (evdev module) is from 0x110 |
339 | // through 0x11f. 0x120 is the first 'Joystick' button. |
340 | switch (button) { |
341 | case Qt::LeftButton: return BTN_LEFT; |
342 | case Qt::RightButton: return uint32_t(0x111); |
343 | case Qt::MiddleButton: return uint32_t(0x112); |
344 | case Qt::ExtraButton1: return uint32_t(0x113); // AKA Qt::BackButton, Qt::XButton1 |
345 | case Qt::ExtraButton2: return uint32_t(0x114); // AKA Qt::ForwardButton, Qt::XButton2 |
346 | case Qt::ExtraButton3: return uint32_t(0x115); |
347 | case Qt::ExtraButton4: return uint32_t(0x116); |
348 | case Qt::ExtraButton5: return uint32_t(0x117); |
349 | case Qt::ExtraButton6: return uint32_t(0x118); |
350 | case Qt::ExtraButton7: return uint32_t(0x119); |
351 | case Qt::ExtraButton8: return uint32_t(0x11a); |
352 | case Qt::ExtraButton9: return uint32_t(0x11b); |
353 | case Qt::ExtraButton10: return uint32_t(0x11c); |
354 | case Qt::ExtraButton11: return uint32_t(0x11d); |
355 | case Qt::ExtraButton12: return uint32_t(0x11e); |
356 | case Qt::ExtraButton13: return uint32_t(0x11f); |
357 | // default should not occur; but if it does, then return Wayland's highest possible button number. |
358 | default: return uint32_t(0x11f); |
359 | } |
360 | } |
361 | |
362 | /*! |
363 | * \internal |
364 | */ |
365 | void QWaylandPointer::enteredSurfaceDestroyed(void *data) |
366 | { |
367 | Q_D(QWaylandPointer); |
368 | Q_UNUSED(data); |
369 | d->enteredSurfaceDestroyListener.reset(); |
370 | d->enteredSurface = nullptr; |
371 | |
372 | d->seat->setMouseFocus(nullptr); |
373 | |
374 | if (d->buttonCount != 0) { |
375 | d->buttonCount = 0; |
376 | emit buttonPressedChanged(); |
377 | } |
378 | } |
379 | |
380 | /*! |
381 | * \internal |
382 | */ |
383 | void QWaylandPointer::pointerFocusChanged(QWaylandView *newFocus, QWaylandView *oldFocus) |
384 | { |
385 | Q_D(QWaylandPointer); |
386 | Q_UNUSED(oldFocus); |
387 | bool wasSameSurface = newFocus && newFocus->surface() == d->enteredSurface; |
388 | if (d->enteredSurface && !wasSameSurface) |
389 | d->sendLeave(); |
390 | } |
391 | |
392 | QT_END_NAMESPACE |
393 | |
394 | #include "moc_qwaylandpointer.cpp" |
395 | |