Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qeglfskmsgbmcursor.cpp
Go to the documentation of this file.
1// Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
2// Copyright (C) 2016 The Qt Company Ltd.
3// Copyright (C) 2016 Pelagicore AG
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5
9
10#include <QtCore/QFile>
11#include <QtCore/QJsonDocument>
12#include <QtCore/QJsonObject>
13#include <QtCore/QJsonArray>
14#include <QtCore/QLoggingCategory>
15#include <QtGui/QPainter>
16#include <QtGui/private/qguiapplication_p.h>
17
18#include <xf86drm.h>
19#include <xf86drmMode.h>
20
21#ifndef DRM_CAP_CURSOR_WIDTH
22#define DRM_CAP_CURSOR_WIDTH 0x8
23#endif
24
25#ifndef DRM_CAP_CURSOR_HEIGHT
26#define DRM_CAP_CURSOR_HEIGHT 0x9
27#endif
28
30
31using namespace Qt::StringLiterals;
32
33Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
34
36 : m_screen(screen)
37 , m_cursorSize(64, 64) // 64x64 is the old standard size, we now try to query the real size below
38 , m_bo(nullptr)
39 , m_cursorImage(nullptr, nullptr, 0, 0, 0, 0)
40 , m_state(CursorPendingVisible)
41 , m_deviceListener(nullptr)
42{
43 QByteArray hideCursorVal = qgetenv("QT_QPA_EGLFS_HIDECURSOR");
44 if (!hideCursorVal.isEmpty() && hideCursorVal.toInt()) {
45 m_state = CursorDisabled;
46 return;
47 }
48
49 uint64_t width, height;
50 if ((drmGetCap(m_screen->device()->fd(), DRM_CAP_CURSOR_WIDTH, &width) == 0)
51 && (drmGetCap(m_screen->device()->fd(), DRM_CAP_CURSOR_HEIGHT, &height) == 0)) {
52 m_cursorSize.setWidth(width);
53 m_cursorSize.setHeight(height);
54 }
55
56 m_bo = gbm_bo_create(static_cast<QEglFSKmsGbmDevice *>(m_screen->device())->gbmDevice(), m_cursorSize.width(), m_cursorSize.height(),
57 GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE);
58 if (!m_bo) {
59 qWarning("Could not create buffer for cursor!");
60 } else {
61 initCursorAtlas();
62 }
63
64 m_deviceListener = new QEglFSKmsGbmCursorDeviceListener(this);
67 if (!m_deviceListener->hasMouse())
68 m_state = CursorPendingHidden;
69
70#ifndef QT_NO_CURSOR
72 changeCursor(&cursor, nullptr);
73#endif
74 setPos(QPoint(0, 0));
75}
76
78{
79 delete m_deviceListener;
80
81 for (QPlatformScreen *screen : m_screen->virtualSiblings()) {
82 QEglFSKmsScreen *kmsScreen = static_cast<QEglFSKmsScreen *>(screen);
83 drmModeSetCursor(kmsScreen->device()->fd(), kmsScreen->output().crtc_id, 0, 0, 0);
84 drmModeMoveCursor(kmsScreen->device()->fd(), kmsScreen->output().crtc_id, 0, 0);
85 }
86
87 if (m_bo) {
88 gbm_bo_destroy(m_bo);
89 m_bo = nullptr;
90 }
91}
92
94{
95 const bool wasVisible = m_state == CursorVisible;
96 const bool visible = m_deviceListener->hasMouse();
97 if (visible == wasVisible)
98 return;
99
100 m_state = visible ? CursorPendingVisible : CursorPendingHidden;
101
102#ifndef QT_NO_CURSOR
103 changeCursor(nullptr, m_screen->topLevelAt(pos()));
104#endif
105}
106
111
117
119{
120 setPos(event.globalPosition().toPoint());
121}
122
123#ifndef QT_NO_CURSOR
125{
127
128 if (!m_bo)
129 return;
130
131 if (m_state == CursorPendingHidden) {
132 m_state = CursorHidden;
133 for (QPlatformScreen *screen : m_screen->virtualSiblings()) {
134 QEglFSKmsScreen *kmsScreen = static_cast<QEglFSKmsScreen *>(screen);
135 drmModeSetCursor(kmsScreen->device()->fd(), kmsScreen->output().crtc_id, 0, 0, 0);
136 }
137 }
138
139 if (m_state == CursorHidden || m_state == CursorDisabled)
140 return;
141
142 const Qt::CursorShape newShape = windowCursor ? windowCursor->shape() : Qt::ArrowCursor;
143 if (newShape == Qt::BitmapCursor) {
144 m_cursorImage.set(windowCursor->pixmap().toImage(),
145 windowCursor->hotSpot().x(),
146 windowCursor->hotSpot().y());
147 } else {
148 // Standard cursor, look up in atlas
149 const int width = m_cursorAtlas.cursorWidth;
150 const int height = m_cursorAtlas.cursorHeight;
151 const qreal ws = (qreal)m_cursorAtlas.cursorWidth / m_cursorAtlas.width;
152 const qreal hs = (qreal)m_cursorAtlas.cursorHeight / m_cursorAtlas.height;
153
154 QRect textureRect(ws * (newShape % m_cursorAtlas.cursorsPerRow) * m_cursorAtlas.width,
155 hs * (newShape / m_cursorAtlas.cursorsPerRow) * m_cursorAtlas.height,
156 width,
157 height);
158 QPoint hotSpot = m_cursorAtlas.hotSpots[newShape];
159 m_cursorImage.set(m_cursorAtlas.image.copy(textureRect),
160 hotSpot.x(),
161 hotSpot.y());
162 }
163
164 if (m_cursorImage.image()->width() > m_cursorSize.width() || m_cursorImage.image()->height() > m_cursorSize.height())
165 qWarning("Cursor larger than %dx%d, cursor will be clipped.", m_cursorSize.width(), m_cursorSize.height());
166
167 QImage cursorImage(m_cursorSize, QImage::Format_ARGB32);
168 cursorImage.fill(Qt::transparent);
169
171 painter.begin(&cursorImage);
172 painter.drawImage(0, 0, *m_cursorImage.image());
173 painter.end();
174
175 gbm_bo_write(m_bo, cursorImage.constBits(), cursorImage.sizeInBytes());
176
177 uint32_t handle = gbm_bo_get_handle(m_bo).u32;
178
179 if (m_state == CursorPendingVisible)
180 m_state = CursorVisible;
181
182 for (QPlatformScreen *screen : m_screen->virtualSiblings()) {
183 QEglFSKmsScreen *kmsScreen = static_cast<QEglFSKmsScreen *>(screen);
184 if (kmsScreen->isCursorOutOfRange())
185 continue;
186 int status = drmModeSetCursor(kmsScreen->device()->fd(), kmsScreen->output().crtc_id, handle,
187 m_cursorSize.width(), m_cursorSize.height());
188 if (status != 0)
189 qWarning("Could not set cursor on screen %s: %d", kmsScreen->name().toLatin1().constData(), status);
190 }
191}
192#endif // QT_NO_CURSOR
193
195{
196 return m_pos;
197}
198
200{
201 for (QPlatformScreen *screen : m_screen->virtualSiblings()) {
202 QEglFSKmsScreen *kmsScreen = static_cast<QEglFSKmsScreen *>(screen);
203 const QRect screenGeom = kmsScreen->geometry();
204 const QPoint origin = screenGeom.topLeft();
205 const QPoint localPos = pos - origin;
206 const QPoint adjustedLocalPos = localPos - m_cursorImage.hotspot();
207
208 if (localPos.x() < 0 || localPos.y() < 0
209 || localPos.x() >= screenGeom.width() || localPos.y() >= screenGeom.height())
210 {
211 if (!kmsScreen->isCursorOutOfRange()) {
212 kmsScreen->setCursorOutOfRange(true);
213 drmModeSetCursor(kmsScreen->device()->fd(), kmsScreen->output().crtc_id, 0, 0, 0);
214 }
215 } else {
216 int ret;
217 if (kmsScreen->isCursorOutOfRange() && m_bo) {
218 kmsScreen->setCursorOutOfRange(false);
219 uint32_t handle = gbm_bo_get_handle(m_bo).u32;
220 ret = drmModeSetCursor(kmsScreen->device()->fd(), kmsScreen->output().crtc_id,
221 handle, m_cursorSize.width(), m_cursorSize.height());
222 } else {
223 ret = drmModeMoveCursor(kmsScreen->device()->fd(), kmsScreen->output().crtc_id,
224 adjustedLocalPos.x(), adjustedLocalPos.y());
225 }
226 if (ret == 0)
227 m_pos = pos;
228 else
229 qWarning("Failed to move cursor on screen %s: %d", kmsScreen->name().toLatin1().constData(), ret);
230
231 kmsScreen->handleCursorMove(pos);
232 }
233 }
234}
235
236void QEglFSKmsGbmCursor::initCursorAtlas()
237{
238 static QByteArray json = qgetenv("QT_QPA_EGLFS_CURSOR");
239 if (json.isEmpty())
240 json = ":/cursor.json";
241
242 qCDebug(qLcEglfsKmsDebug) << "Initializing cursor atlas from" << json;
243
245 if (!file.open(QFile::ReadOnly)) {
246 for (QPlatformScreen *screen : m_screen->virtualSiblings()) {
247 QEglFSKmsScreen *kmsScreen = static_cast<QEglFSKmsScreen *>(screen);
248 drmModeSetCursor(kmsScreen->device()->fd(), kmsScreen->output().crtc_id, 0, 0, 0);
249 drmModeMoveCursor(kmsScreen->device()->fd(), kmsScreen->output().crtc_id, 0, 0);
250 }
251 m_state = CursorDisabled;
252 return;
253 }
254
256 QJsonObject object = doc.object();
257
258 QString atlas = object.value("image"_L1).toString();
259 Q_ASSERT(!atlas.isEmpty());
260
261 const int cursorsPerRow = object.value("cursorsPerRow"_L1).toDouble();
262 Q_ASSERT(cursorsPerRow);
263 m_cursorAtlas.cursorsPerRow = cursorsPerRow;
264
265 const QJsonArray hotSpots = object.value("hotSpots"_L1).toArray();
266 Q_ASSERT(hotSpots.count() == Qt::LastCursor + 1);
267 for (int i = 0; i < hotSpots.count(); i++) {
268 QPoint hotSpot(hotSpots[i].toArray()[0].toDouble(), hotSpots[i].toArray()[1].toDouble());
269 m_cursorAtlas.hotSpots << hotSpot;
270 }
271
273 m_cursorAtlas.cursorWidth = image.width() / m_cursorAtlas.cursorsPerRow;
274 m_cursorAtlas.cursorHeight = image.height() / ((Qt::LastCursor + cursorsPerRow) / cursorsPerRow);
275 m_cursorAtlas.width = image.width();
276 m_cursorAtlas.height = image.height();
277 m_cursorAtlas.image = image;
278}
279
\inmodule QtCore
Definition qbytearray.h:57
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
The QCursor class provides a mouse cursor with an arbitrary shape.
Definition qcursor.h:45
void onDeviceListChanged(QInputDeviceManager::DeviceType type)
void pointerEvent(const QMouseEvent &event) override
This method is called by Qt whenever a QMouseEvent is generated by the underlying pointer input.
void changeCursor(QCursor *windowCursor, QWindow *window) override
This method is called by Qt whenever the cursor graphic should be changed.
void setPos(const QPoint &pos) override
QPoint pos() const override
gbm_device * gbmDevice() const
QList< QPlatformScreen * > virtualSiblings() const override
Returns a list of all the platform screens that are part of the same virtual desktop.
QWindow * topLevelAt(const QPoint &point) const override
Return the given top level window for a given position.
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
static QInputDeviceManager * inputDeviceManager()
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
\inmodule QtGui
Definition qimage.h:37
qsizetype sizeInBytes() const
Definition qimage.cpp:1548
int width() const
Returns the width of the image.
int height() const
Returns the height of the image.
@ Format_ARGB32
Definition qimage.h:47
void fill(uint pixel)
Fills the entire image with the given pixelValue.
Definition qimage.cpp:1758
const uchar * constBits() const
Returns a pointer to the first pixel data.
Definition qimage.cpp:1733
QImage convertToFormat(Format f, Qt::ImageConversionFlags flags=Qt::AutoColor) const &
Definition qimage.h:125
void deviceListChanged(QInputDeviceManager::DeviceType type)
\inmodule QtCore\reentrant
Definition qjsonarray.h:18
qsizetype count() const
Same as size().
Definition qjsonarray.h:42
\inmodule QtCore\reentrant
QJsonObject object() const
Returns the QJsonObject contained in the document.
static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error=nullptr)
Parses json as a UTF-8 encoded JSON document, and creates a QJsonDocument from it.
\inmodule QtCore\reentrant
Definition qjsonobject.h:20
\inmodule QtGui
Definition qevent.h:196
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
bool begin(QPaintDevice *)
Begins painting the paint device and returns true if successful; otherwise returns false.
void drawImage(const QRectF &targetRect, const QImage &image, const QRectF &sourceRect, Qt::ImageConversionFlags flags=Qt::AutoColor)
Draws the rectangular portion source of the given image into the target rectangle in the paint device...
bool end()
Ends painting.
QPoint hotspot() const
Return the cursor's hotspot.
void set(const uchar *data, const uchar *mask, int width, int height, int hotX, int hotY)
Sets the cursor image to the graphic represented by the combination of data and mask,...
QImage * image()
Return the cursor graphic as a pointer to a QImage.
The QPlatformScreen class provides an abstraction for visual displays.
\inmodule QtCore\reentrant
Definition qpoint.h:25
constexpr int x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:130
constexpr int y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:135
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
\inmodule QtGui
Definition qwindow.h:63
QCursor cursor
Combined button and popup list for selecting options.
CursorShape
@ BitmapCursor
@ LastCursor
@ ArrowCursor
@ transparent
Definition qnamespace.h:47
Definition image.cpp:4
#define DRM_CAP_CURSOR_HEIGHT
#define DRM_CAP_CURSOR_WIDTH
#define qWarning
Definition qlogging.h:166
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
return ret
GLuint64 GLenum void * handle
GLint GLsizei GLsizei height
GLint GLsizei width
GLenum type
struct _cl_event * event
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QScreen * screen
[1]
Definition main.cpp:29
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
#define Q_UNUSED(x)
double qreal
Definition qtypes.h:187
static double toDouble(Value v)
QFile file
[0]
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
rect setPos(100, 100)
QPainter painter(this)
[7]
aWidget window() -> setWindowTitle("New Window Title")
[2]