17#include "CursorImageProvider.h"
23#include <QSvgRenderer>
25CursorImageProvider *CursorImageProvider::m_instance =
nullptr;
30BuiltInCursorImage::BuiltInCursorImage(
int cursorHeight)
32 const char *svgString =
33 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
35 " xmlns:dc=\"http://purl.org/dc/elements/1.1/\""
36 " xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\""
37 " xmlns:svg=\"http://www.w3.org/2000/svg\""
38 " xmlns=\"http://www.w3.org/2000/svg\""
41 " style=\"fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:40;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1\""
42 " d=\"M 20.504,50.94931 460.42533,518.14486 266.47603,515.61948 366.48114,719.16522 274.05218,770.68296 172.53185,559.56112 20.504,716.13476 Z\" />"
46 qimage = QImage((20./32.)*cursorHeight, cursorHeight, QImage::Format_ARGB32);
47 qimage.fill(Qt::transparent);
48 QPainter imagePainter(&qimage);
50 frameWidth = qimage.width();
51 frameHeight = qimage.height();
52 requestedHeight = cursorHeight;
54 QSvgRenderer *svgRenderer =
new QSvgRenderer(QByteArray(svgString));
55 svgRenderer->render(&imagePainter);
63BlankCursorImage::BlankCursorImage()
65 qimage = QImage(1, 1, QImage::Format_ARGB32);
66 qimage.fill(Qt::transparent);
67 frameWidth = qimage.width();
68 frameHeight = qimage.height();
75CustomCursorImage::CustomCursorImage(
const QCursor &cursor)
77 qimage = cursor.pixmap().toImage();
78 hotspot = cursor.hotSpot();
79 frameWidth = qimage.width();
80 frameHeight = qimage.height();
86XCursorImage::XCursorImage(
const QString &theme,
const QString &file,
int preferredCursorHeightPx)
88 requestedHeight = preferredCursorHeightPx;
90 XcursorImages *xcursorImages = XcursorLibraryLoadImages(QFile::encodeName(file), QFile::encodeName(theme),
91 preferredCursorHeightPx);
92 if (!xcursorImages || xcursorImages->nimage == 0) {
96 frameCount = xcursorImages->nimage;
98 for (
int i = 0; i < xcursorImages->nimage; ++i) {
99 XcursorImage *xcursorImage = xcursorImages->images[i];
100 if (frameWidth < (
int)xcursorImage->width) {
101 frameWidth = xcursorImage->width;
103 if (frameHeight < (
int)xcursorImage->height) {
104 frameHeight = xcursorImage->height;
107 frameDuration = (int)xcursorImage->delay;
109 if (frameDuration != (
int)xcursorImage->delay) {
110 qWarning().nospace() <<
"CursorImageProvider: XCursorImage("<<theme<<
","<<file<<
") has"
111 " varying delays in its animation. Animation won't look right.";
118 XcursorImage *xcursorImage = xcursorImages->images[0];
119 hotspot.setX(xcursorImage->xhot);
120 hotspot.setY(xcursorImage->yhot);
124 qimage = QImage(frameWidth*frameCount, frameHeight, QImage::Format_ARGB32);
125 qimage.fill(Qt::transparent);
128 QPainter painter(&qimage);
130 for (
int i = 0; i < xcursorImages->nimage; ++i) {
131 XcursorImage *xcursorImage = xcursorImages->images[i];
133 auto frameImage = QImage((uchar*)xcursorImage->pixels,
134 xcursorImage->width, xcursorImage->height, QImage::Format_ARGB32);
136 painter.drawImage(QPoint(i*frameWidth, 0), frameImage);
140 XcursorImagesDestroy(xcursorImages);
143XCursorImage::~XCursorImage()
150CursorImageProvider::CursorImageProvider()
151 : QQuickImageProvider(QQuickImageProvider::Image)
154 qFatal(
"Cannot have multiple CursorImageProvider instances");
158 m_fallbackNames[QStringLiteral(
"closedhand")].append(QStringLiteral(
"grabbing"));
159 m_fallbackNames[QStringLiteral(
"closedhand")].append(QStringLiteral(
"dnd-none"));
161 m_fallbackNames[QStringLiteral(
"dnd-copy")].append(QStringLiteral(
"dnd-none"));
162 m_fallbackNames[QStringLiteral(
"dnd-copy")].append(QStringLiteral(
"grabbing"));
163 m_fallbackNames[QStringLiteral(
"dnd-copy")].append(QStringLiteral(
"closedhand"));
165 m_fallbackNames[QStringLiteral(
"dnd-move")].append(QStringLiteral(
"dnd-none"));
166 m_fallbackNames[QStringLiteral(
"dnd-move")].append(QStringLiteral(
"grabbing"));
167 m_fallbackNames[QStringLiteral(
"dnd-move")].append(QStringLiteral(
"closedhand"));
169 m_fallbackNames[QStringLiteral(
"dnd-link")].append(QStringLiteral(
"dnd-none"));
170 m_fallbackNames[QStringLiteral(
"dnd-link")].append(QStringLiteral(
"grabbing"));
171 m_fallbackNames[QStringLiteral(
"dnd-link")].append(QStringLiteral(
"closedhand"));
173 m_fallbackNames[QStringLiteral(
"forbidden")].append(QStringLiteral(
"crossed_circle"));
174 m_fallbackNames[QStringLiteral(
"forbidden")].append(QStringLiteral(
"not-allowed"));
175 m_fallbackNames[QStringLiteral(
"forbidden")].append(QStringLiteral(
"circle"));
177 m_fallbackNames[QStringLiteral(
"grabbing")].append(QStringLiteral(
"closedhand"));
179 m_fallbackNames[QStringLiteral(
"hand")].append(QStringLiteral(
"pointing_hand"));
180 m_fallbackNames[QStringLiteral(
"hand")].append(QStringLiteral(
"pointer"));
182 m_fallbackNames[QStringLiteral(
"ibeam")].append(QStringLiteral(
"xterm"));
183 m_fallbackNames[QStringLiteral(
"ibeam")].append(QStringLiteral(
"text"));
185 m_fallbackNames[QStringLiteral(
"left_ptr")].append(QStringLiteral(
"default"));
186 m_fallbackNames[QStringLiteral(
"left_ptr")].append(QStringLiteral(
"top_left_arrow"));
187 m_fallbackNames[QStringLiteral(
"left_ptr")].append(QStringLiteral(
"left_arrow"));
189 m_fallbackNames[QStringLiteral(
"left_ptr_watch")].append(QStringLiteral(
"half-busy"));
190 m_fallbackNames[QStringLiteral(
"left_ptr_watch")].append(QStringLiteral(
"progress"));
192 m_fallbackNames[QStringLiteral(
"size_bdiag")].append(QStringLiteral(
"fd_double_arrow"));
193 m_fallbackNames[QStringLiteral(
"size_bdiag")].append(QStringLiteral(
"nesw-resize"));
195 m_fallbackNames[QStringLiteral(
"size_fdiag")].append(QStringLiteral(
"bd_double_arrow"));
196 m_fallbackNames[QStringLiteral(
"size_fdiag")].append(QStringLiteral(
"nwse-resize"));
198 m_fallbackNames[QStringLiteral(
"size_hor")].append(QStringLiteral(
"sb_h_double_arrow"));
199 m_fallbackNames[QStringLiteral(
"size_hor")].append(QStringLiteral(
"ew-resize"));
200 m_fallbackNames[QStringLiteral(
"size_hor")].append(QStringLiteral(
"h_double_arrow"));
202 m_fallbackNames[QStringLiteral(
"size_ver")].append(QStringLiteral(
"sb_v_double_arrow"));
203 m_fallbackNames[QStringLiteral(
"size_ver")].append(QStringLiteral(
"ns-resize"));
204 m_fallbackNames[QStringLiteral(
"size_ver")].append(QStringLiteral(
"v_double_arrow"));
206 m_fallbackNames[QStringLiteral(
"split_h")].append(QStringLiteral(
"sb_h_double_arrow"));
207 m_fallbackNames[QStringLiteral(
"split_h")].append(QStringLiteral(
"col-resize"));
209 m_fallbackNames[QStringLiteral(
"split_v")].append(QStringLiteral(
"sb_v_double_arrow"));
210 m_fallbackNames[QStringLiteral(
"split_v")].append(QStringLiteral(
"row-resize"));
212 m_fallbackNames[QStringLiteral(
"up_arrow")].append(QStringLiteral(
"sb_up_arrow"));
214 m_fallbackNames[QStringLiteral(
"watch")].append(QStringLiteral(
"wait"));
216 m_fallbackNames[QStringLiteral(
"whats_this")].append(QStringLiteral(
"left_ptr_help"));
217 m_fallbackNames[QStringLiteral(
"whats_this")].append(QStringLiteral(
"help"));
218 m_fallbackNames[QStringLiteral(
"whats_this")].append(QStringLiteral(
"question_arrow"));
220 m_fallbackNames[QStringLiteral(
"xterm")].append(QStringLiteral(
"ibeam"));
223CursorImageProvider::~CursorImageProvider()
226 QList< QMap<QString, CursorImage*> > cursorList = m_cursors.values();
228 for (
int i = 0; i < cursorList.count(); ++i) {
229 QList<CursorImage*> cursorImageList = cursorList[i].values();
230 for (
int j = 0; j < cursorImageList.count(); ++j) {
231 delete cursorImageList[j];
237 m_instance =
nullptr;
240QImage CursorImageProvider::requestImage(
const QString &cursorThemeAndNameAndHeight, QSize *size,
const QSize & )
242 CursorImage *cursorImage = fetchCursor(cursorThemeAndNameAndHeight);
243 size->setWidth(cursorImage->qimage.width());
244 size->setHeight(cursorImage->qimage.height());
246 return cursorImage->qimage;
249CursorImage *CursorImageProvider::fetchCursor(
const QString &cursorThemeAndNameAndHeight)
255 QStringList themeAndNameList = cursorThemeAndNameAndHeight.split(
'/');
256 if (themeAndNameList.size() != 3) {
259 themeName = themeAndNameList[0];
260 cursorName = themeAndNameList[1];
263 cursorHeight = themeAndNameList[2].toInt(&ok);
266 qWarning().nospace() <<
"CursorImageProvider: invalid cursor height ("<<themeAndNameList[2]<<
")."
267 " Falling back to "<<cursorHeight<<
" pixels";
271 return fetchCursor(themeName, cursorName, cursorHeight);
274CursorImage *CursorImageProvider::fetchCursor(
const QString &themeName,
const QString &cursorName,
int cursorHeight)
276 CursorImage *cursorImage = fetchCursorHelper(themeName, cursorName, cursorHeight);
279 if (cursorImage->qimage.isNull()) {
280 if (m_fallbackNames.contains(cursorName)) {
281 const QStringList &fallbackNames = m_fallbackNames[cursorName];
283 while (cursorImage->qimage.isNull() && i < fallbackNames.count()) {
284 qDebug().nospace() <<
"CursorImageProvider: "<< cursorName <<
" not found, trying " << fallbackNames.at(i);
285 cursorImage = fetchCursorHelper(themeName, fallbackNames.at(i), cursorHeight);
292 if (cursorImage->qimage.isNull() && cursorName != QLatin1String(
"left_ptr")) {
293 qDebug() <<
"CursorImageProvider:" << cursorName
294 <<
"not found (nor its fallbacks, if any). Going for \"left_ptr\" as a last resort.";
295 cursorImage = fetchCursorHelper(themeName, QStringLiteral(
"left_ptr"), cursorHeight);
298 if (cursorImage->qimage.isNull()) {
300 qWarning() <<
"CursorImageProvider: couldn't find any cursors. Using the built-in one";
301 if (!m_builtInCursorImage || m_builtInCursorImage->requestedHeight != cursorHeight) {
302 m_builtInCursorImage.reset(
new BuiltInCursorImage(cursorHeight));
304 cursorImage = m_builtInCursorImage.data();
310CursorImage *CursorImageProvider::fetchCursorHelper(
const QString &themeName,
const QString &cursorName,
int cursorHeight)
312 if (cursorName == QLatin1String(
"blank")) {
313 return &m_blankCursorImage;
314 }
else if (cursorName.startsWith(QLatin1String(
"custom"))) {
315 return m_customCursorImage.data();
317 QMap<QString, CursorImage*> &themeCursors = m_cursors[themeName];
319 if (!themeCursors.contains(cursorName)) {
320 themeCursors[cursorName] =
new XCursorImage(themeName, cursorName, cursorHeight);
321 }
else if (themeCursors[cursorName]->requestedHeight != cursorHeight) {
322 delete themeCursors.take(cursorName);
323 themeCursors[cursorName] =
new XCursorImage(themeName, cursorName, cursorHeight);
326 return themeCursors[cursorName];
330void CursorImageProvider::setCustomCursor(
const QCursor &customCursor)
332 if (customCursor.pixmap().isNull()) {
333 m_customCursorImage.reset();
335 m_customCursorImage.reset(
new CustomCursorImage(customCursor));