Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwaylandpointer.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
6#include <QtWaylandCompositor/QWaylandClient>
7#include <QtWaylandCompositor/QWaylandCompositor>
8
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(surface->client(), 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(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(nullptr, 0, 0, QWaylandClient::fromWlClient(compositor(), resource->client()));
95 return;
96 }
97
98 QWaylandSurface *s = QWaylandSurface::fromResource(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(&QWaylandPointerPrivate::s_role, displayRes, WL_DISPLAY_ERROR_INVALID_OBJECT)) {
108 s->markAsCursorSurface(true);
109 seat->cursorSurfaceRequested(s, hotspot_x, hotspot_y, QWaylandClient::fromWlClient(compositor(), 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(&d_func()->enteredSurfaceDestroyListener, &QWaylandDestroyListener::fired, this, &QWaylandPointer::enteredSurfaceDestroyed);
130 connect(seat, &QWaylandSeat::mouseFocusChanged, this, &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, 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, 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(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 * \property QWaylandPointer::isButtonPressed
279 *
280 * This property holds whether any mouse button is currently pressed.
281 *
282 * The value is \c true if at least one mouse button is pressed; otherwise
283 * \c false.
284 */
285
286/*!
287 * Returns true if any button is currently pressed. Otherwise returns false.
288 */
289bool QWaylandPointer::isButtonPressed() const
290{
291 Q_D(const QWaylandPointer);
292 return d->buttonCount > 0;
293}
294
295/*!
296 * \internal
297 */
298void QWaylandPointer::addClient(QWaylandClient *client, uint32_t id, uint32_t version)
299{
300 Q_D(QWaylandPointer);
301 wl_resource *resource = d->add(client->client(), id, qMin<uint32_t>(QtWaylandServer::wl_pointer::interfaceVersion(), version))->handle;
302 if (d->enteredSurface && client == d->enteredSurface->client()) {
303 d->send_enter(resource, d->enterSerial, d->enteredSurface->resource(),
304 wl_fixed_from_double(d->localPosition.x()),
305 wl_fixed_from_double(d->localPosition.y()));
306 }
307}
308
309/*!
310 * Returns a Wayland resource for this QWaylandPointer.
311 *
312 * This API doesn't actually make sense, since there may be many pointer resources per client
313 * It's here for compatibility reasons.
314 */
315struct wl_resource *QWaylandPointer::focusResource() const
316{
317 Q_D(const QWaylandPointer);
318 QWaylandView *focus = d->seat->mouseFocus();
319 if (!focus)
320 return nullptr;
321
322 // Just return the first resource we can find.
323 return d->resourceMap().value(focus->surface()->waylandClient())->handle;
324}
325
326/*!
327 * \internal
328 */
329uint QWaylandPointer::sendButton(struct wl_resource *resource, uint32_t time, Qt::MouseButton button, uint32_t state)
330{
331 // This method is here for compatibility reasons only, since it usually doesn't make sense to
332 // send button events to just one of the pointer resources for a client.
333 Q_D(QWaylandPointer);
334 uint32_t serial = d->compositor()->nextSerial();
335 d->send_button(resource, serial, time, toWaylandButton(button), state);
336 return serial;
337}
338
339/*!
340 * \internal
341 */
342uint32_t QWaylandPointer::toWaylandButton(Qt::MouseButton button)
343{
344#ifndef BTN_LEFT
345 uint32_t BTN_LEFT = 0x110;
346#endif
347 // the range of valid buttons (evdev module) is from 0x110
348 // through 0x11f. 0x120 is the first 'Joystick' button.
349 switch (button) {
350 case Qt::LeftButton: return BTN_LEFT;
351 case Qt::RightButton: return uint32_t(0x111);
352 case Qt::MiddleButton: return uint32_t(0x112);
353 case Qt::ExtraButton1: return uint32_t(0x113); // AKA Qt::BackButton, Qt::XButton1
354 case Qt::ExtraButton2: return uint32_t(0x114); // AKA Qt::ForwardButton, Qt::XButton2
355 case Qt::ExtraButton3: return uint32_t(0x115);
356 case Qt::ExtraButton4: return uint32_t(0x116);
357 case Qt::ExtraButton5: return uint32_t(0x117);
358 case Qt::ExtraButton6: return uint32_t(0x118);
359 case Qt::ExtraButton7: return uint32_t(0x119);
360 case Qt::ExtraButton8: return uint32_t(0x11a);
361 case Qt::ExtraButton9: return uint32_t(0x11b);
362 case Qt::ExtraButton10: return uint32_t(0x11c);
363 case Qt::ExtraButton11: return uint32_t(0x11d);
364 case Qt::ExtraButton12: return uint32_t(0x11e);
365 case Qt::ExtraButton13: return uint32_t(0x11f);
366 // default should not occur; but if it does, then return Wayland's highest possible button number.
367 default: return uint32_t(0x11f);
368 }
369}
370
371/*!
372 * \internal
373 */
374void QWaylandPointer::enteredSurfaceDestroyed(void *data)
375{
376 Q_D(QWaylandPointer);
377 Q_UNUSED(data);
378 d->enteredSurfaceDestroyListener.reset();
379 d->enteredSurface = nullptr;
380
381 d->seat->setMouseFocus(nullptr);
382
383 if (d->buttonCount != 0) {
384 d->buttonCount = 0;
385 emit buttonPressedChanged();
386 }
387}
388
389/*!
390 * \internal
391 */
392void QWaylandPointer::pointerFocusChanged(QWaylandView *newFocus, QWaylandView *oldFocus)
393{
394 Q_D(QWaylandPointer);
395 Q_UNUSED(oldFocus);
396 bool wasSameSurface = newFocus && newFocus->surface() == d->enteredSurface;
397 if (d->enteredSurface && !wasSameSurface)
398 d->sendLeave();
399}
400
401QT_END_NAMESPACE
402
403#include "moc_qwaylandpointer.cpp"
Combined button and popup list for selecting options.