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
9QT_BEGIN_NAMESPACE
10
11QWaylandSurfaceRole QWaylandPointerPrivate::s_role("wl_pointer");
12
13QWaylandPointerPrivate::QWaylandPointerPrivate(QWaylandPointer *pointer, QWaylandSeat *seat)
14 : seat(seat)
15{
16 Q_UNUSED(pointer);
17}
18
19uint 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
33void 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
43void 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
61void 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
72void 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
84void QWaylandPointerPrivate::pointer_release(wl_pointer::Resource *resource)
85{
86 wl_resource_destroy(resource->handle);
87}
88
89void 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 */
126QWaylandPointer::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 */
136QWaylandSeat *QWaylandPointer::seat() const
137{
138 Q_D(const QWaylandPointer);
139 return d->seat;
140}
141
142/*!
143 * Returns the compositor for this QWaylandPointer.
144 */
145QWaylandCompositor *QWaylandPointer::compositor() const
146{
147 Q_D(const QWaylandPointer);
148 return d->compositor();
149}
150
151/*!
152 * Returns the output for this QWaylandPointer.
153 */
154QWaylandOutput *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 */
163void 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 */
176uint 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 */
192uint 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 */
207void 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 */
236void 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 */
253QWaylandView *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 */
262QPointF 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 */
271QPointF 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 */
280bool QWaylandPointer::isButtonPressed() const
281{
282 Q_D(const QWaylandPointer);
283 return d->buttonCount > 0;
284}
285
286/*!
287 * \internal
288 */
289void 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 */
306struct 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 */
320uint 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 */
333uint32_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 */
365void 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 */
383void 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
392QT_END_NAMESPACE
393
394#include "moc_qwaylandpointer.cpp"
395

source code of qtwayland/src/compositor/compositor_api/qwaylandpointer.cpp