11#include <KConfigGroup>
18#include <QStandardPaths>
28 std::chrono::milliseconds
delay;
34 void load(
const QString &themeName,
int size, qreal devicePixelRatio);
35 void loadCursors(
const QString &packagePath,
int size, qreal devicePixelRatio);
37 QHash<QByteArray, QList<KXcursorSprite>>
registry;
61 const std::chrono::milliseconds &delay)
84static QList<KXcursorSprite> loadCursor(
const QString &filePath,
int desiredSize, qreal devicePixelRatio)
91 QList<KXcursorSprite> sprites;
92 for (
int i = 0; i < images->
nimage; ++i) {
94 const qreal scale = std::max(qreal(1), qreal(nativeCursorImage->
size) / desiredSize);
95 const QPoint hotspot(nativeCursorImage->
xhot, nativeCursorImage->
yhot);
96 const std::chrono::milliseconds delay(nativeCursorImage->
delay);
98 QImage data(nativeCursorImage->
width, nativeCursorImage->
height, QImage::Format_ARGB32_Premultiplied);
99 data.setDevicePixelRatio(scale);
100 memcpy(data.bits(), nativeCursorImage->
pixels, data.sizeInBytes());
102 sprites.append(KXcursorSprite(data, hotspot / scale, delay));
111 const QDir dir(packagePath);
112 QFileInfoList entries = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
113 std::partition(entries.begin(), entries.end(), [](
const QFileInfo &fileInfo) {
114 return !fileInfo.isSymLink();
117 for (
const QFileInfo &entry : std::as_const(entries)) {
118 const QByteArray shape = QFile::encodeName(entry.fileName());
122 if (entry.isSymLink()) {
123 const QFileInfo symLinkInfo(entry.symLinkTarget());
124 if (symLinkInfo.absolutePath() == entry.absolutePath()) {
125 const auto sprites =
registry.value(QFile::encodeName(symLinkInfo.fileName()));
126 if (!sprites.isEmpty()) {
132 const QList<KXcursorSprite> sprites = loadCursor(entry.absoluteFilePath(), size, devicePixelRatio);
133 if (!sprites.isEmpty()) {
139static QStringList searchPaths()
141 static QStringList paths;
142 if (paths.isEmpty()) {
143 if (
const QString env = qEnvironmentVariable(
"XCURSOR_PATH"); !env.isEmpty()) {
144 paths.append(env.split(
':', Qt::SkipEmptyParts));
146 const QString home = QDir::homePath();
147 if (!home.isEmpty()) {
148 paths.append(home + QLatin1String(
"/.icons"));
150 const QStringList dataDirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
151 for (
const QString &dataDir : dataDirs) {
152 paths.append(dataDir + QLatin1String(
"/icons"));
161 const QStringList paths = searchPaths();
163 QStack<QString> stack;
164 QSet<QString> loaded;
166 stack.push(themeName);
168 while (!stack.isEmpty()) {
169 const QString themeName = stack.pop();
170 if (loaded.contains(themeName)) {
174 QStringList inherits;
176 for (
const QString &path : paths) {
177 const QDir dir(path + QLatin1Char(
'/') + themeName);
181 loadCursors(dir.filePath(QStringLiteral(
"cursors")), size, devicePixelRatio);
182 if (inherits.isEmpty()) {
183 const KConfig config(dir.filePath(QStringLiteral(
"index.theme")), KConfig::NoGlobals);
184 inherits << KConfigGroup(&config, QStringLiteral(
"Icon Theme")).readEntry(
"Inherits", QStringList());
188 loaded.insert(themeName);
189 for (
auto it = inherits.crbegin(); it != inherits.crend(); ++it) {
203 d->load(themeName, size, devicePixelRatio);
228 return !(*
this == other);
233 return d->registry.isEmpty();
238 return d->registry.value(name);
std::chrono::milliseconds delay() const
KXcursorSprite & operator=(const KXcursorSprite &other)
std::chrono::milliseconds delay
bool operator==(const KXcursorTheme &other)
QList< KXcursorSprite > shape(const QByteArray &name) const
bool operator!=(const KXcursorTheme &other)
KXcursorTheme & operator=(const KXcursorTheme &other)
void loadCursors(const QString &packagePath, int size, qreal devicePixelRatio)
void load(const QString &themeName, int size, qreal devicePixelRatio)
QHash< QByteArray, QList< KXcursorSprite > > registry
void XcursorImagesDestroy(XcursorImages *images)
XcursorImages * XcursorFileLoadImages(const char *file, int size)