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