Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qwaylandcursor.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2023 David Edmundson <davidedmundson@kde.org>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qwaylandcursor_p.h"
6
7#include "qwaylanddisplay_p.h"
10
11#include <QtGui/QImageReader>
12#include <QDebug>
13
14#include <wayland-cursor.h>
15
16#include <algorithm>
17
19
20namespace QtWaylandClient {
21
22std::unique_ptr<QWaylandCursorTheme> QWaylandCursorTheme::create(QWaylandShm *shm, int size, const QString &themeName)
23{
24 QByteArray nameBytes = themeName.toLocal8Bit();
25 struct ::wl_cursor_theme *theme = wl_cursor_theme_load(nameBytes.constData(), size, shm->object());
26
27 if (!theme) {
28 qCWarning(lcQpaWayland) << "Could not load cursor theme" << themeName << "size" << size;
29 return nullptr;
30 }
31
32 return std::unique_ptr<QWaylandCursorTheme>{new QWaylandCursorTheme(theme)};
33}
34
35QWaylandCursorTheme::~QWaylandCursorTheme()
36{
37 wl_cursor_theme_destroy(m_theme);
38}
39
40wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape)
41{
42 if (struct wl_cursor *cursor = m_cursors[shape])
43 return cursor;
44
45 static Q_CONSTEXPR struct ShapeAndName {
46 WaylandCursor shape;
47 const char name[33];
48 } cursorNamesMap[] = {
49 {ArrowCursor, "left_ptr"},
50 {ArrowCursor, "default"},
51 {ArrowCursor, "top_left_arrow"},
52 {ArrowCursor, "left_arrow"},
53
54 {UpArrowCursor, "up_arrow"},
55
56 {CrossCursor, "cross"},
57
58 {WaitCursor, "wait"},
59 {WaitCursor, "watch"},
60 {WaitCursor, "0426c94ea35c87780ff01dc239897213"},
61
62 {IBeamCursor, "ibeam"},
63 {IBeamCursor, "text"},
64 {IBeamCursor, "xterm"},
65
66 {SizeVerCursor, "size_ver"},
67 {SizeVerCursor, "ns-resize"},
68 {SizeVerCursor, "v_double_arrow"},
69 {SizeVerCursor, "00008160000006810000408080010102"},
70
71 {SizeHorCursor, "size_hor"},
72 {SizeHorCursor, "ew-resize"},
73 {SizeHorCursor, "h_double_arrow"},
74 {SizeHorCursor, "028006030e0e7ebffc7f7070c0600140"},
75
76 {SizeBDiagCursor, "size_bdiag"},
77 {SizeBDiagCursor, "nesw-resize"},
78 {SizeBDiagCursor, "50585d75b494802d0151028115016902"},
79 {SizeBDiagCursor, "fcf1c3c7cd4491d801f1e1c78f100000"},
80
81 {SizeFDiagCursor, "size_fdiag"},
82 {SizeFDiagCursor, "nwse-resize"},
83 {SizeFDiagCursor, "38c5dff7c7b8962045400281044508d2"},
84 {SizeFDiagCursor, "c7088f0f3e6c8088236ef8e1e3e70000"},
85
86 {SizeAllCursor, "size_all"},
87
88 {BlankCursor, "blank"},
89
90 {SplitVCursor, "split_v"},
91 {SplitVCursor, "row-resize"},
92 {SplitVCursor, "sb_v_double_arrow"},
93 {SplitVCursor, "2870a09082c103050810ffdffffe0204"},
94 {SplitVCursor, "c07385c7190e701020ff7ffffd08103c"},
95
96 {SplitHCursor, "split_h"},
97 {SplitHCursor, "col-resize"},
98 {SplitHCursor, "sb_h_double_arrow"},
99 {SplitHCursor, "043a9f68147c53184671403ffa811cc5"},
100 {SplitHCursor, "14fef782d02440884392942c11205230"},
101
102 {PointingHandCursor, "pointing_hand"},
103 {PointingHandCursor, "pointer"},
104 {PointingHandCursor, "hand1"},
105 {PointingHandCursor, "e29285e634086352946a0e7090d73106"},
106
107 {ForbiddenCursor, "forbidden"},
108 {ForbiddenCursor, "not-allowed"},
109 {ForbiddenCursor, "crossed_circle"},
110 {ForbiddenCursor, "circle"},
111 {ForbiddenCursor, "03b6e0fcb3499374a867c041f52298f0"},
112
113 {WhatsThisCursor, "whats_this"},
114 {WhatsThisCursor, "help"},
115 {WhatsThisCursor, "question_arrow"},
116 {WhatsThisCursor, "5c6cd98b3f3ebcb1f9c7f1c204630408"},
117 {WhatsThisCursor, "d9ce0ab605698f320427677b458ad60b"},
118
119 {BusyCursor, "left_ptr_watch"},
120 {BusyCursor, "half-busy"},
121 {BusyCursor, "progress"},
122 {BusyCursor, "00000000000000020006000e7e9ffc3f"},
123 {BusyCursor, "08e8e1c95fe2fc01f976f1e063a24ccd"},
124
125 {OpenHandCursor, "openhand"},
126 {OpenHandCursor, "fleur"},
127 {OpenHandCursor, "5aca4d189052212118709018842178c0"},
128 {OpenHandCursor, "9d800788f1b08800ae810202380a0822"},
129
130 {ClosedHandCursor, "closedhand"},
131 {ClosedHandCursor, "grabbing"},
132 {ClosedHandCursor, "208530c400c041818281048008011002"},
133
134 {DragCopyCursor, "dnd-copy"},
135 {DragCopyCursor, "copy"},
136
137 {DragMoveCursor, "dnd-move"},
138 {DragMoveCursor, "move"},
139
140 {DragLinkCursor, "dnd-link"},
141 {DragLinkCursor, "link"},
142
143 {ResizeNorthCursor, "n-resize"},
144 {ResizeNorthCursor, "top_side"},
145
146 {ResizeSouthCursor, "s-resize"},
147 {ResizeSouthCursor, "bottom_side"},
148
149 {ResizeEastCursor, "e-resize"},
150 {ResizeEastCursor, "right_side"},
151
152 {ResizeWestCursor, "w-resize"},
153 {ResizeWestCursor, "left_side"},
154
155 {ResizeNorthWestCursor, "nw-resize"},
156 {ResizeNorthWestCursor, "top_left_corner"},
157
158 {ResizeSouthEastCursor, "se-resize"},
159 {ResizeSouthEastCursor, "bottom_right_corner"},
160
161 {ResizeNorthEastCursor, "ne-resize"},
162 {ResizeNorthEastCursor, "top_right_corner"},
163
164 {ResizeSouthWestCursor, "sw-resize"},
165 {ResizeSouthWestCursor, "bottom_left_corner"},
166 };
167
168 const auto byShape = [](ShapeAndName lhs, ShapeAndName rhs) {
169 return lhs.shape < rhs.shape;
170 };
171 Q_ASSERT(std::is_sorted(std::begin(cursorNamesMap), std::end(cursorNamesMap), byShape));
172 const auto p = std::equal_range(std::begin(cursorNamesMap), std::end(cursorNamesMap),
173 ShapeAndName{shape, ""}, byShape);
174 for (auto it = p.first; it != p.second; ++it) {
175 if (wl_cursor *cursor = wl_cursor_theme_get_cursor(m_theme, it->name)) {
176 m_cursors[shape] = cursor;
177 return cursor;
178 }
179 }
180
181 // Fallback to arrow cursor
182 if (shape != ArrowCursor)
183 return requestCursor(ArrowCursor);
184
185 // Give up
186 return nullptr;
187}
188
189::wl_cursor *QWaylandCursorTheme::cursor(Qt::CursorShape shape)
190{
191 struct wl_cursor *waylandCursor = nullptr;
192
193 if (shape < Qt::BitmapCursor) {
194 waylandCursor = requestCursor(WaylandCursor(shape));
195 } else if (shape == Qt::BitmapCursor) {
196 qCWarning(lcQpaWayland) << "cannot create a wl_cursor_image for a CursorShape";
197 return nullptr;
198 } else {
199 //TODO: Custom cursor logic (for resize arrows)
200 }
201
202 if (!waylandCursor) {
203 qCWarning(lcQpaWayland) << "Could not find cursor for shape" << shape;
204 return nullptr;
205 }
206
207 return waylandCursor;
208}
209
210QWaylandCursorShape::QWaylandCursorShape(::wp_cursor_shape_device_v1 *object)
211 : QtWayland::wp_cursor_shape_device_v1(object)
212{}
213
214QWaylandCursorShape::~QWaylandCursorShape()
215{
216 destroy();
217}
218
219static QtWayland::wp_cursor_shape_device_v1::shape qtCursorShapeToWaylandShape(Qt::CursorShape cursorShape)
220{
221 using QtWayland::wp_cursor_shape_device_v1;
222
223 switch (cursorShape) {
224 case Qt::BlankCursor:
225 case Qt::CustomCursor:
226 case Qt::BitmapCursor:
227 // these should have been handled separately before using the shape protocol
228 Q_ASSERT(false);
229 break;
230 case Qt::ArrowCursor:
231 return wp_cursor_shape_device_v1::shape_default;
233 return wp_cursor_shape_device_v1::shape_ns_resize;
235 return wp_cursor_shape_device_v1::shape_n_resize;
237 return wp_cursor_shape_device_v1::shape_ew_resize;
238 case Qt::CrossCursor:
239 return wp_cursor_shape_device_v1::shape_crosshair;
241 return wp_cursor_shape_device_v1::shape_nesw_resize;
242 case Qt::IBeamCursor:
243 return wp_cursor_shape_device_v1::shape_text;
245 return wp_cursor_shape_device_v1::shape_nwse_resize;
246 case Qt::WaitCursor:
247 return wp_cursor_shape_device_v1::shape_progress;
249 return wp_cursor_shape_device_v1::shape_all_scroll;
250 case Qt::BusyCursor:
251 return wp_cursor_shape_device_v1::shape_wait;
252 case Qt::SplitVCursor:
253 return wp_cursor_shape_device_v1::shape_row_resize;
255 return wp_cursor_shape_device_v1::shape_not_allowed;
256 case Qt::SplitHCursor:
257 return wp_cursor_shape_device_v1::shape_col_resize;
259 return wp_cursor_shape_device_v1::shape_pointer;
261 return wp_cursor_shape_device_v1::shape_grab;
263 return wp_cursor_shape_device_v1::shape_help;
265 return wp_cursor_shape_device_v1::shape_grabbing;
269 // drags on wayland are different, the compositor knows
270 // the drag type and can do something custom
271 return wp_cursor_shape_device_v1::shape_grab;
272 }
273 return wp_cursor_shape_device_v1::shape_default;
274}
275
276void QWaylandCursorShape::setShape(uint32_t serial, Qt::CursorShape shape)
277{
278 set_shape(serial, qtCursorShapeToWaylandShape(shape));
279}
280
281QWaylandCursor::QWaylandCursor(QWaylandDisplay *display)
282 : mDisplay(display)
283{
284}
285
286QSharedPointer<QWaylandBuffer> QWaylandCursor::cursorBitmapBuffer(QWaylandDisplay *display, const QCursor *cursor)
287{
290
291 // convert to supported format if necessary
292 if (!display->shm()->formatSupported(img.format())) {
293 if (cursor->mask().isNull()) {
295 } else {
296 // preserve mask
297 img.convertTo(QImage::Format_ARGB32);
299 pixmap.setMask(cursor->mask());
300 img = pixmap.toImage();
301 }
302 }
303
304 QSharedPointer<QWaylandShmBuffer> buffer(new QWaylandShmBuffer(display, img.size(), img.format()));
305 memcpy(buffer->image()->bits(), img.bits(), size_t(img.sizeInBytes()));
306 return buffer;
307}
308
309void QWaylandCursor::changeCursor(QCursor *cursor, QWindow *window)
310{
312 // Create the buffer here so we don't have to create one per input device
314 if (cursor && cursor->shape() == Qt::BitmapCursor)
315 bitmapBuffer = cursorBitmapBuffer(mDisplay, cursor);
316
317 int fallbackOutputScale = int(window->devicePixelRatio());
318 const auto seats = mDisplay->inputDevices();
319 for (auto *seat : seats)
320 seat->setCursor(cursor, bitmapBuffer, fallbackOutputScale);
321}
322
323void QWaylandCursor::pointerEvent(const QMouseEvent &event)
324{
325 mLastPos = event.globalPosition().toPoint();
326}
327
328QPoint QWaylandCursor::pos() const
329{
330 return mLastPos;
331}
332
333void QWaylandCursor::setPos(const QPoint &pos)
334{
335 Q_UNUSED(pos);
336 qCWarning(lcQpaWayland) << "Setting cursor position is not possible on wayland";
337}
338
339} // namespace QtWaylandClient
340
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
The QCursor class provides a mouse cursor with an arbitrary shape.
Definition qcursor.h:45
QBitmap bitmap() const
Returns the cursor bitmap, or a null bitmap if it is one of the standard cursors.
Definition qcursor.cpp:548
QPixmap pixmap() const
Returns the cursor pixmap.
Definition qcursor.cpp:589
Qt::CursorShape shape() const
Returns the cursor shape identifier.
Definition qcursor.cpp:499
QBitmap mask() const
Returns the cursor bitmap mask, or a null bitmap if it is one of the standard cursors.
Definition qcursor.cpp:575
\inmodule QtGui
Definition qimage.h:37
void convertTo(Format f, Qt::ImageConversionFlags flags=Qt::AutoColor)
Definition qimage.cpp:2384
@ Format_RGB32
Definition qimage.h:46
@ Format_ARGB32
Definition qimage.h:47
\inmodule QtGui
Definition qevent.h:195
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
QImage toImage() const
Converts the pixmap to a QImage.
Definition qpixmap.cpp:412
bool isNull() const
Returns true if this is a null pixmap; otherwise returns false.
Definition qpixmap.cpp:460
static QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags=Qt::AutoColor)
Converts the given image to a pixmap using the specified flags to control the conversion.
Definition qpixmap.cpp:1445
\inmodule QtCore\reentrant
Definition qpoint.h:23
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QByteArray toLocal8Bit() const &
Definition qstring.h:567
\inmodule QtGui
Definition qwindow.h:63
QCursor cursor
QSet< QString >::iterator it
struct wl_display * display
Definition linuxdmabuf.h:41
Combined button and popup list for selecting options.
static QtWayland::wp_cursor_shape_device_v1::shape qtCursorShapeToWaylandShape(Qt::CursorShape cursorShape)
CursorShape
@ BlankCursor
@ CrossCursor
@ DragCopyCursor
@ BitmapCursor
@ PointingHandCursor
@ SizeHorCursor
@ SizeAllCursor
@ CustomCursor
@ WaitCursor
@ SizeVerCursor
@ DragLinkCursor
@ OpenHandCursor
@ SizeFDiagCursor
@ WhatsThisCursor
@ ArrowCursor
@ SplitVCursor
@ UpArrowCursor
@ ClosedHandCursor
@ DragMoveCursor
@ IBeamCursor
@ SizeBDiagCursor
@ ForbiddenCursor
@ BusyCursor
@ SplitHCursor
#define Q_CONSTEXPR
#define qCWarning(category,...)
static QString themeName()
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint object
[3]
GLenum GLuint buffer
GLuint name
struct _cl_event * event
GLint void * img
Definition qopenglext.h:233
GLfloat GLfloat p
[1]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_UNUSED(x)
item setCursor(Qt::IBeamCursor)
[1]
widget render & pixmap
aWidget window() -> setWindowTitle("New Window Title")
[2]