Unity 8
CursorImageProvider.cpp
1 /*
2  * Copyright (C) 2015 Canonical, Ltd.
3  *
4  * This program is free software: you can redistribute it and/or modify it under
5  * the terms of the GNU Lesser General Public License version 3, as published by
6  * the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10  * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include "CursorImageProvider.h"
18 
19 #include <QDebug>
20 #include <QFile>
21 #include <QPainter>
22 #include <QSvgRenderer>
23 
24 CursorImageProvider *CursorImageProvider::m_instance = nullptr;
25 
27 // BuiltInCursorImage
28 
29 BuiltInCursorImage::BuiltInCursorImage()
30 {
31  const char *svgString =
32  "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
33  "<svg"
34  " xmlns:dc=\"http://purl.org/dc/elements/1.1/\""
35  " xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\""
36  " xmlns:svg=\"http://www.w3.org/2000/svg\""
37  " xmlns=\"http://www.w3.org/2000/svg\""
38  " version=\"1.1\">"
39  " <path"
40  " 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\""
41  " 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\" />"
42  "</svg>";
43 
44  qimage = QImage(20, 32, QImage::Format_ARGB32);
45  QPainter imagePainter(&qimage);
46 
47  QSvgRenderer *svgRenderer = new QSvgRenderer(QByteArray(svgString));
48  svgRenderer->render(&imagePainter);
49  delete svgRenderer;
50 }
51 
53 // XCursorImage
54 
55 XCursorImage::XCursorImage(const QString &theme, const QString &file)
56  : xcursorImages(nullptr)
57 {
58  xcursorImages = XcursorLibraryLoadImages(QFile::encodeName(file), QFile::encodeName(theme), 32);
59  if (!xcursorImages) {
60  return;
61  }
62 
63  bool loaded = false;
64  for (int i = 0; i < xcursorImages->nimage && !loaded; ++i) {
65  XcursorImage *xcursorImage = xcursorImages->images[i];
66  if (xcursorImage->size == 32) {
67 
68  qimage = QImage((uchar*)xcursorImage->pixels,
69  xcursorImage->width, xcursorImage->height, QImage::Format_ARGB32);
70 
71  hotspot.setX(xcursorImage->xhot);
72  hotspot.setY(xcursorImage->yhot);
73 
74  loaded = true;
75  }
76  }
77 }
78 
79 XCursorImage::~XCursorImage()
80 {
81  XcursorImagesDestroy(xcursorImages);
82 }
83 
85 // CursorImageProvider
86 
87 CursorImageProvider::CursorImageProvider()
88  : QQuickImageProvider(QQuickImageProvider::Image)
89 {
90  if (m_instance) {
91  qFatal("Cannot have multiple CursorImageProvider instances");
92  }
93  m_instance = this;
94 }
95 
96 CursorImageProvider::~CursorImageProvider()
97 {
98  {
99  QList< QMap<QString, CursorImage*> > cursorList = m_cursors.values();
100 
101  for (int i = 0; i < cursorList.count(); ++i) {
102  QList<CursorImage*> cursorImageList = cursorList[i].values();
103  for (int j = 0; j < cursorImageList.count(); ++j) {
104  delete cursorImageList[j];
105  }
106  }
107  }
108 
109  m_cursors.clear();
110  m_instance = nullptr;
111 }
112 
113 QImage CursorImageProvider::requestImage(const QString &cursorThemeAndName, QSize *size, const QSize & /*requestedSize*/)
114 {
115  CursorImage *cursorImage = fetchCursor(cursorThemeAndName);
116  size->setWidth(cursorImage->qimage.width());
117  size->setHeight(cursorImage->qimage.height());
118 
119  return cursorImage->qimage;
120 }
121 
122 QPoint CursorImageProvider::hotspot(const QString &themeName, const QString &cursorName)
123 {
124  CursorImage *cursorImage = fetchCursor(themeName, cursorName);
125  if (cursorImage) {
126  return cursorImage->hotspot;
127  } else {
128  return QPoint(0,0);
129  }
130 }
131 
132 CursorImage *CursorImageProvider::fetchCursor(const QString &cursorThemeAndName)
133 {
134  QString themeName;
135  QString cursorName;
136  {
137  QStringList themeAndNameList = cursorThemeAndName.split("/");
138  if (themeAndNameList.size() != 2) {
139  return nullptr;
140  }
141  themeName = themeAndNameList[0];
142  cursorName = themeAndNameList[1];
143  }
144 
145  return fetchCursor(themeName, cursorName);
146 }
147 
148 CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QString &cursorName)
149 {
150  CursorImage *cursorImage = fetchCursorHelper(themeName, cursorName);
151 
152  // Try some fallbacks
153  if (cursorImage->qimage.isNull()) {
154  if (cursorName == "ibeam") {
155  qDebug() << "CursorImageProvider: \"ibeam\" not found, falling back to \"xterm\"";
156  cursorImage = fetchCursorHelper(themeName, "xterm");
157  } else if (cursorName == "xterm") {
158  qDebug() << "CursorImageProvider: \"xterm\" not found, falling back to \"ibeam\"";
159  cursorImage = fetchCursorHelper(themeName, "ibeam");
160  }
161  }
162 
163  // if it all fails, there must be at least a left_ptr
164  if (cursorImage->qimage.isNull() && cursorName != "left_ptr") {
165  qDebug() << "CursorImageProvider:" << cursorName
166  << "not found (nor its fallbacks, if any). Going for \"left_ptr\" as a last resort.";
167  cursorImage = fetchCursorHelper(themeName, "left_ptr");
168  }
169 
170  if (cursorImage->qimage.isNull()) {
171  // finally, go for the built-in cursor
172  qWarning() << "CursorImageProvider: couldn't find any cursors. Using the built-in one";
173  if (!m_builtInCursorImage) {
174  m_builtInCursorImage.reset(new BuiltInCursorImage);
175  }
176  cursorImage = m_builtInCursorImage.data();
177  }
178 
179  return cursorImage;
180 }
181 
182 CursorImage *CursorImageProvider::fetchCursorHelper(const QString &themeName, const QString &cursorName)
183 {
184  QMap<QString, CursorImage*> &themeCursors = m_cursors[themeName];
185 
186  if (!themeCursors.contains(cursorName)) {
187  themeCursors[cursorName] = new XCursorImage(themeName, cursorName);
188  }
189 
190  return themeCursors[cursorName];
191 }