diff --git a/src/kiconeffect.h b/src/kiconeffect.h --- a/src/kiconeffect.h +++ b/src/kiconeffect.h @@ -31,6 +31,7 @@ #include #include +class QMutex; class KIconEffectPrivate; /** @@ -57,6 +58,9 @@ * iconEffect() function. */ KIconEffect(); + + explicit KIconEffect(QMutex *mutex); + ~KIconEffect(); /** diff --git a/src/kiconeffect.cpp b/src/kiconeffect.cpp --- a/src/kiconeffect.cpp +++ b/src/kiconeffect.cpp @@ -37,12 +37,15 @@ #include #include +#include "mutexguard.h" + class KIconEffectPrivate { public: // http://en.cppreference.com/w/cpp/language/zero_initialization - KIconEffectPrivate() - : effect{{}} + KIconEffectPrivate(QMutex *mutex = nullptr) + : m_mutex(mutex) + , effect{{}} , value{{}} , color{{}} , trans{{}} @@ -52,6 +55,7 @@ } public: + QMutex *m_mutex; int effect[KIconLoader::LastGroup][KIconLoader::LastState]; float value[KIconLoader::LastGroup][KIconLoader::LastState]; QColor color[KIconLoader::LastGroup][KIconLoader::LastState]; @@ -66,6 +70,12 @@ init(); } +KIconEffect::KIconEffect(QMutex *mutex) + : d(new KIconEffectPrivate(mutex)) +{ + init(); +} + KIconEffect::~KIconEffect() { delete d; @@ -73,6 +83,7 @@ void KIconEffect::init() { + MutexGuard guard(d->m_mutex); KSharedConfig::Ptr config = KSharedConfig::openConfig(); int i, j, effect = -1; @@ -154,7 +165,7 @@ state < 0 || state >= KIconLoader::LastState) { return false; } - + MutexGuard guard(d->m_mutex); return d->effect[group][state] != NoEffect; } @@ -164,7 +175,7 @@ state < 0 || state >= KIconLoader::LastState) { return QString(); } - + MutexGuard guard(d->m_mutex); QString cached = d->key[group][state]; if (cached.isEmpty()) { QString tmp; @@ -199,6 +210,7 @@ qWarning() << "Illegal icon group: " << group; return image; } + MutexGuard guard(d->m_mutex); return apply(image, d->effect[group][state], d->value[group][state], d->color[group][state], d->color2[group][state], d->trans[group][state]); } @@ -256,6 +268,7 @@ qWarning() << "Illegal icon group: " << group; return pixmap; } + MutexGuard guard(d->m_mutex); return apply(pixmap, d->effect[group][state], d->value[group][state], d->color[group][state], d->color2[group][state], d->trans[group][state]); } diff --git a/src/kiconloader.cpp b/src/kiconloader.cpp --- a/src/kiconloader.cpp +++ b/src/kiconloader.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include // kdecore @@ -58,6 +59,7 @@ #include "kicontheme.h" #include "kiconeffect.h" #include "debug.h" +#include "mutexguard.h" //kwidgetsaddons #include @@ -208,7 +210,9 @@ public: KIconLoaderPrivate(KIconLoader *q) : q(q) + , m_mutex(QMutex::Recursive) , mpGroups(nullptr) + , mpEffect(nullptr) , mIconCache(nullptr) { } @@ -225,6 +229,8 @@ qDeleteAll(links); delete[] mpGroups; delete mIconCache; + delete mpEffect; + mpEffect = nullptr; mpGroups = nullptr; mIconCache = nullptr; mPixmapCache.clear(); @@ -233,6 +239,7 @@ links.clear(); mIconThemeInited = false; mThemesInTree.clear(); + mIconAvailability.clear(); } /** @@ -320,7 +327,7 @@ * icon metadata. Ensure the metadata is normalized first. */ QString makeCacheKey(const QString &name, KIconLoader::Group group, const QStringList &overlays, - int size, qreal scale, int state) const; + int size, qreal scale, int state); /** * @internal @@ -355,7 +362,7 @@ /** * Find the given file in the search paths. */ - QString locate(const QString &fileName); + QString locate(const QString &fileName) const; /** * @internal @@ -372,13 +379,54 @@ return true; } + void addAppDir(const QString &appname, const QString &themeBaseDir); + + QString iconPath(const QString &_name, int group_or_size, bool canReturnNull); + + QString iconPath(const QString &_name, int group_or_size, bool canReturnNull, qreal scale); + + QPixmap loadMimeTypeIcon(const QString &_iconName, KIconLoader::Group group, int size, + int state, const QStringList &overlays, QString *path_store); + + QPixmap loadIcon(const QString &_name, KIconLoader::Group group, int size, + int state, const QStringList &overlays, + QString *path_store, bool canReturnNull); + + QPixmap loadScaledIcon(const QString &_name, KIconLoader::Group group, qreal scale, + int size, int state, const QStringList &overlays, + QString *path_store, bool canReturnNull); + + QString moviePath(const QString &name, KIconLoader::Group group, int size); + + QStringList loadAnimated(const QString &name, KIconLoader::Group group, int size); + + KIconTheme *theme(); + + int currentSize(KIconLoader::Group group) const; + + QStringList queryIconsByContext(int group_or_size, KIconLoader::Context context); + + QStringList queryIcons(int group_or_size, KIconLoader::Context context); + + bool hasContext(KIconLoader::Context context); + + bool alphaBlending(KIconLoader::Group group) const; + + bool hasIcon(const QString &name); + + void setCustomPalette(const QPalette &palette); + + void resetPalette(); + + QMutex m_mutex; + KIconLoader *const q; QStringList mThemesInTree; KIconGroup *mpGroups; KIconThemeNode *mpThemeRoot; QStringList searchPaths; - KIconEffect mpEffect; + KIconEffect *mpEffect; QList links; // This shares the icons across all processes @@ -393,7 +441,7 @@ bool mIconThemeInited : 1; QString appname; - void drawOverlays(const KIconLoader *loader, KIconLoader::Group group, int state, QPixmap &pix, const QStringList &overlays); + void drawOverlays(const QStringList &overlays, QPixmap &pixmap, KIconLoader::Group group, int state); QHash mIconAvailability; // icon name -> true (known to be available) or false (known to be unavailable) QElapsedTimer mLastUnknownIconCheck; // recheck for unknown icons after kiconloader_ms_between_checks @@ -470,7 +518,7 @@ Q_GLOBAL_STATIC(KIconLoaderGlobalData, s_globalData) -void KIconLoaderPrivate::drawOverlays(const KIconLoader *iconLoader, KIconLoader::Group group, int state, QPixmap &pix, const QStringList &overlays) +void KIconLoaderPrivate::drawOverlays(const QStringList &overlays, QPixmap &pix, KIconLoader::Group group, int state) { if (overlays.isEmpty()) { return; @@ -508,7 +556,7 @@ //TODO: should we pass in the kstate? it results in a slower // path, and perhaps emblems should remain in the default state // anyways? - QPixmap pixmap = iconLoader->loadIcon(overlay, group, overlaySize, state, QStringList(), nullptr, true); + QPixmap pixmap = loadIcon(overlay, group, overlaySize, state, QStringList(), nullptr, true); if (pixmap.isNull()) { continue; @@ -565,7 +613,6 @@ } q->newIconLoader(); - mIconAvailability.clear(); emit q->iconChanged(group); } @@ -575,19 +622,21 @@ setObjectName(_appname); d = new KIconLoaderPrivate(this); - connect(s_globalData, SIGNAL(iconChanged(int)), SLOT(_k_refreshIcons(int))); + connect(s_globalData, &KIconLoaderGlobalData::iconChanged, this, [this](int group) { + d->_k_refreshIcons(group); + }, Qt::QueuedConnection); d->init(_appname, extraSearchPaths); } void KIconLoader::reconfigure(const QString &_appname, const QStringList &extraSearchPaths) { - d->mIconCache->clear(); - d->clear(); + MutexGuard guard(&d->m_mutex); d->init(_appname, extraSearchPaths); } void KIconLoaderPrivate::init(const QString &_appname, const QStringList &extraSearchPaths) { + clear(); extraDesktopIconsLoaded = false; mIconThemeInited = false; mpThemeRoot = nullptr; @@ -599,6 +648,9 @@ appname = QCoreApplication::applicationName(); } + // Protect shared pointer + mpEffect = new KIconEffect(&m_mutex); + // Initialize icon cache mIconCache = new KSharedDataCache(QStringLiteral("icon-cache"), 10 * 1024 * 1024); // Cost here is number of pixels, not size. So this is actually a bit @@ -637,12 +689,12 @@ mIconThemeInited = true; // Add the default theme and its base themes to the theme tree - KIconTheme *def = new KIconTheme(KIconTheme::current(), appname); + KIconTheme *def = new KIconTheme(&m_mutex, KIconTheme::current(), appname); if (!def->isValid()) { delete def; // warn, as this is actually a small penalty hit qCDebug(KICONTHEMES) << "Couldn't find current icon theme, falling back to default."; - def = new KIconTheme(KIconTheme::defaultThemeName(), appname); + def = new KIconTheme(&m_mutex, KIconTheme::defaultThemeName(), appname); if (!def->isValid()) { qWarning() << "Error: standard icon theme" << KIconTheme::defaultThemeName() << "not found!"; delete def; @@ -672,15 +724,21 @@ QStringList KIconLoader::searchPaths() const { + MutexGuard guard(&d->m_mutex); return d->searchPaths; } void KIconLoader::addAppDir(const QString &appname, const QString &themeBaseDir) { - d->initIconThemes(); + MutexGuard guard(&d->m_mutex); + d->addAppDir(appname, themeBaseDir); +} - d->searchPaths.append(appname + QStringLiteral("/pics")); - d->addAppThemes(appname, themeBaseDir); +void KIconLoaderPrivate::addAppDir(const QString &appname, const QString &themeBaseDir) +{ + initIconThemes(); + searchPaths.append(appname + QStringLiteral("/pics")); + addAppThemes(appname, themeBaseDir); } void KIconLoaderPrivate::addAppThemes(const QString &appname, const QString &themeBaseDir) @@ -690,7 +748,7 @@ KIconTheme *def = new KIconTheme(QStringLiteral("hicolor"), appname, themeBaseDir); if (!def->isValid()) { delete def; - def = new KIconTheme(KIconTheme::defaultThemeName(), appname, themeBaseDir); + def = new KIconTheme(&m_mutex, KIconTheme::defaultThemeName(), appname, themeBaseDir); } KIconThemeNode *node = new KIconThemeNode(def); bool addedToLinks = false; @@ -744,7 +802,7 @@ if (mThemesInTree.contains(themename + appname)) { return; } - KIconTheme *theme = new KIconTheme(themename, appname); + KIconTheme *theme = new KIconTheme(&m_mutex, themename, appname); if (!theme->isValid()) { delete theme; return; @@ -808,7 +866,8 @@ void KIconLoader::drawOverlays(const QStringList &overlays, QPixmap &pixmap, KIconLoader::Group group, int state) const { - d->drawOverlays(this, group, state, pixmap, overlays); + MutexGuard guard(&d->m_mutex); + d->drawOverlays(overlays, pixmap, group, state); } QString KIconLoaderPrivate::removeIconExtension(const QString &name) const @@ -857,7 +916,7 @@ } QString KIconLoaderPrivate::makeCacheKey(const QString &name, KIconLoader::Group group, - const QStringList &overlays, int size, qreal scale, int state) const + const QStringList &overlays, int size, qreal scale, int state) { // The KSharedDataCache is shared so add some namespacing. The following code // uses QStringBuilder (new in Qt 4.6) @@ -872,11 +931,11 @@ % QString::number(scale, 'f', 1) % QLatin1Char('_') % overlays.join(QStringLiteral("_")) - % (group >= 0 ? mpEffect.fingerprint(group, state) + % (group >= 0 ? mpEffect->fingerprint(group, state) : NULL_EFFECT_FINGERPRINT()) % QLatin1Char('_') % paletteId(mCustomPalette ? mPalette : qApp->palette()) - % (q->theme() && q->theme()->followsColorScheme() && state == KIconLoader::SelectedState ? QStringLiteral("_selected") : QString()); + % (theme() && theme()->followsColorScheme() && state == KIconLoader::SelectedState ? QStringLiteral("_selected") : QString()); } QByteArray KIconLoaderPrivate::processSvg(const QString &path, KIconLoader::States state) const @@ -935,7 +994,7 @@ QScopedPointer reader; QBuffer buffer; - if (q->theme()->followsColorScheme() && (path.endsWith(QLatin1String("svg")) || path.endsWith(QLatin1String("svgz")))) { + if (theme()->followsColorScheme() && (path.endsWith(QLatin1String("svg")) || path.endsWith(QLatin1String("svgz")))) { buffer.setData(processSvg(path, state)); reader.reset(new QImageReader(&buffer)); } else { @@ -1142,7 +1201,7 @@ return path; } -QString KIconLoaderPrivate::locate(const QString &fileName) +QString KIconLoaderPrivate::locate(const QString &fileName) const { Q_FOREACH (const QString &dir, searchPaths) { const QString path = dir + QLatin1Char('/') + fileName; @@ -1164,6 +1223,13 @@ QString KIconLoader::iconPath(const QString &_name, int group_or_size, bool canReturnNull) const +{ + MutexGuard guard(&d->m_mutex); + return d->iconPath(_name, group_or_size, canReturnNull, 1 /*scale*/); +} + +QString KIconLoaderPrivate::iconPath(const QString &_name, int group_or_size, + bool canReturnNull) { return iconPath(_name, group_or_size, canReturnNull, 1 /*scale*/); } @@ -1171,7 +1237,14 @@ QString KIconLoader::iconPath(const QString &_name, int group_or_size, bool canReturnNull, qreal scale) const { - if (!d->initIconThemes()) { + MutexGuard guard(&d->m_mutex); + return d->iconPath(_name, group_or_size, canReturnNull, scale); +} + +QString KIconLoaderPrivate::iconPath(const QString &_name, int group_or_size, + bool canReturnNull, qreal scale) +{ + if (!initIconThemes()) { return QString(); } @@ -1180,19 +1253,19 @@ return _name; } - QString name = d->removeIconExtension(_name); + QString name = removeIconExtension(_name); QString path; if (group_or_size == KIconLoader::User) { - path = d->locate(name + QLatin1String(".png")); + path = locate(name + QLatin1String(".png")); if (path.isEmpty()) { - path = d->locate(name + QLatin1String(".svgz")); + path = locate(name + QLatin1String(".svgz")); } if (path.isEmpty()) { - path = d->locate(name + QLatin1String(".svg")); + path = locate(name + QLatin1String(".svg")); } if (path.isEmpty()) { - path = d->locate(name + QLatin1String(".xpm")); + path = locate(name + QLatin1String(".xpm")); } return path; } @@ -1204,7 +1277,7 @@ int size; if (group_or_size >= 0) { - size = d->mpGroups[group_or_size].size; + size = mpGroups[group_or_size].size; } else { size = -group_or_size; } @@ -1213,11 +1286,11 @@ if (canReturnNull) { return QString(); } else { - return d->unknownIconPath(size, scale); + return unknownIconPath(size, scale); } } - path = d->findMatchingIconWithGenericFallbacks(name, size, scale); + path = findMatchingIconWithGenericFallbacks(name, size, scale); if (path.isEmpty()) { // Try "User" group too. @@ -1226,13 +1299,20 @@ return path; } - return d->unknownIconPath(size, scale); + return unknownIconPath(size, scale); } return path; } QPixmap KIconLoader::loadMimeTypeIcon(const QString &_iconName, KIconLoader::Group group, int size, int state, const QStringList &overlays, QString *path_store) const +{ + MutexGuard guard(&d->m_mutex); + return d->loadMimeTypeIcon(_iconName, group, size, state, overlays, path_store); +} + +QPixmap KIconLoaderPrivate::loadMimeTypeIcon(const QString &_iconName, KIconLoader::Group group, int size, + int state, const QStringList &overlays, QString *path_store) { QString iconName = _iconName; const int slashindex = iconName.indexOf(QLatin1Char('/')); @@ -1240,12 +1320,12 @@ iconName[slashindex] = QLatin1Char('-'); } - if (!d->extraDesktopIconsLoaded) { + if (!extraDesktopIconsLoaded) { const QPixmap pixmap = loadIcon(iconName, group, size, state, overlays, path_store, true); if (!pixmap.isNull()) { return pixmap; } - d->addExtraDesktopThemes(); + addExtraDesktopThemes(); } const QPixmap pixmap = loadIcon(iconName, group, size, state, overlays, path_store, true); if (pixmap.isNull()) { @@ -1255,18 +1335,32 @@ return pixmap; } - - QPixmap KIconLoader::loadIcon(const QString &_name, KIconLoader::Group group, int size, int state, const QStringList &overlays, QString *path_store, bool canReturnNull) const { -return loadScaledIcon(_name, group, 1.0 /*scale*/, size, state, overlays, path_store, canReturnNull); + MutexGuard guard(&d->m_mutex); + return d->loadIcon(_name, group, size, state, overlays, path_store, canReturnNull); +} + +QPixmap KIconLoaderPrivate::loadIcon(const QString &_name, KIconLoader::Group group, int size, + int state, const QStringList &overlays, + QString *path_store, bool canReturnNull) +{ + return loadScaledIcon(_name, group, 1.0 /*scale*/, size, state, overlays, path_store, canReturnNull); } QPixmap KIconLoader::loadScaledIcon(const QString &_name, KIconLoader::Group group, qreal scale, int size, int state, const QStringList &overlays, QString *path_store, bool canReturnNull) const +{ + MutexGuard guard(&d->m_mutex); + return d->loadScaledIcon(_name, group, scale, size, state, overlays, path_store, canReturnNull); +} + +QPixmap KIconLoaderPrivate::loadScaledIcon(const QString &_name, KIconLoader::Group group, qreal scale, + int size, int state, const QStringList &overlays, + QString *path_store, bool canReturnNull) { QString name = _name; bool favIconOverlay = false; @@ -1293,7 +1387,7 @@ bool absolutePath = !pathIsRelative(name); if (!absolutePath) { - name = d->removeIconExtension(name); + name = removeIconExtension(name); } // Don't bother looking for an icon with no name. @@ -1303,16 +1397,16 @@ // May modify group, size, or state. This function puts them into sane // states. - d->normalizeIconMetadata(group, size, state); + normalizeIconMetadata(group, size, state); // See if the image is already cached. - QString key = d->makeCacheKey(name, group, overlays, size, scale, state); + QString key = makeCacheKey(name, group, overlays, size, scale, state); QPixmap pix; bool iconWasUnknown = false; QString path; - if (d->findCachedPixmapWithPath(key, pix, path)) { + if (findCachedPixmapWithPath(key, pix, path)) { pix.setDevicePixelRatio(scale); if (path_store) { @@ -1324,14 +1418,14 @@ } else { // path is empty for "unknown" icons, which should be searched for // anew regularly - if (!d->shouldCheckForUnknownIcons()) { + if (!shouldCheckForUnknownIcons()) { return canReturnNull ? QPixmap() : pix; } } } // Image is not cached... go find it and apply effects. - if (!d->initIconThemes()) { + if (!initIconThemes()) { return QPixmap(); } @@ -1343,7 +1437,7 @@ if (absolutePath && !favIconOverlay) { path = name; } else { - path = d->findMatchingIconWithGenericFallbacks(favIconOverlay ? QStringLiteral("text-html") : name, size, scale); + path = findMatchingIconWithGenericFallbacks(favIconOverlay ? QStringLiteral("text-html") : name, size, scale); } } @@ -1356,17 +1450,17 @@ // Still can't find it? Use "unknown" if we can't return null. // We keep going in the function so we can ensure this result gets cached. if (path.isEmpty() && !canReturnNull) { - path = d->unknownIconPath(size, scale); + path = unknownIconPath(size, scale); iconWasUnknown = true; } QImage img; if (!path.isEmpty()) { - img = d->createIconImage(path, size, scale, static_cast(state)); + img = createIconImage(path, size, scale, static_cast(state)); } if (group >= 0) { - img = d->mpEffect.apply(img, group, state); + img = mpEffect->apply(img, group, state); } if (favIconOverlay) { @@ -1389,7 +1483,7 @@ // TODO: If we make a loadIcon that returns the image we can convert // drawOverlays to use the image instead of pixmaps as well so we don't // have to transfer so much to the graphics card. - d->drawOverlays(this, group, state, pix, overlays); + drawOverlays(overlays, pix, group, state); // Don't add the path to our unknown icon to the cache, only cache the // actual image. @@ -1397,7 +1491,7 @@ path.clear(); } - d->insertCachedPixmapWithPath(key, pix, path); + insertCachedPixmapWithPath(key, pix, path); if (path_store) { *path_store = path; @@ -1432,11 +1526,17 @@ QString KIconLoader::moviePath(const QString &name, KIconLoader::Group group, int size) const { - if (!d->mpGroups) { + MutexGuard guard(&d->m_mutex); + return d->moviePath(name, group, size); +} + +QString KIconLoaderPrivate::moviePath(const QString &name, KIconLoader::Group group, int size) +{ + if (!mpGroups) { return QString(); } - d->initIconThemes(); + initIconThemes(); if ((group < -1 || group >= KIconLoader::LastGroup) && group != KIconLoader::User) { qCDebug(KICONTHEMES) << "Illegal icon group:" << group; @@ -1449,15 +1549,15 @@ QString file = name + QStringLiteral(".mng"); if (group == KIconLoader::User) { - file = d->locate(file); + file = locate(file); } else { if (size == 0) { - size = d->mpGroups[group].size; + size = mpGroups[group].size; } QString path; - foreach (KIconThemeNode *themeNode, d->links) { + foreach (KIconThemeNode *themeNode, links) { path = themeNode->theme->iconPath(file, size, KIconLoader::MatchExact); if (!path.isEmpty()) { break; @@ -1465,7 +1565,7 @@ } if (path.isEmpty()) { - foreach (KIconThemeNode *themeNode, d->links) { + foreach (KIconThemeNode *themeNode, links) { path = themeNode->theme->iconPath(file, size, KIconLoader::MatchBest); if (!path.isEmpty()) { break; @@ -1479,14 +1579,20 @@ } QStringList KIconLoader::loadAnimated(const QString &name, KIconLoader::Group group, int size) const +{ + MutexGuard guard(&d->m_mutex); + return d->loadAnimated(name, group, size); +} + +QStringList KIconLoaderPrivate::loadAnimated(const QString &name, KIconLoader::Group group, int size) { QStringList lst; - if (!d->mpGroups) { + if (!mpGroups) { return lst; } - d->initIconThemes(); + initIconThemes(); if ((group < -1) || (group >= KIconLoader::LastGroup)) { qCDebug(KICONTHEMES) << "Illegal icon group: " << group; @@ -1499,12 +1605,12 @@ QString file = name + QStringLiteral("/0001"); if (group == KIconLoader::User) { - file = d->locate(file + QStringLiteral(".png")); + file = locate(file + QStringLiteral(".png")); } else { if (size == 0) { - size = d->mpGroups[group].size; + size = mpGroups[group].size; } - file = d->findMatchingIcon(file, size, 1); // FIXME scale + file = findMatchingIcon(file, size, 1); // FIXME scale } if (file.isEmpty()) { return lst; @@ -1529,16 +1635,28 @@ KIconTheme *KIconLoader::theme() const { - d->initIconThemes(); - if (d->mpThemeRoot) { - return d->mpThemeRoot->theme; + MutexGuard guard(&d->m_mutex); + return d->theme(); +} + +KIconTheme *KIconLoaderPrivate::theme() +{ + initIconThemes(); + if (mpThemeRoot) { + return mpThemeRoot->theme; } return nullptr; } int KIconLoader::currentSize(KIconLoader::Group group) const { - if (!d->mpGroups) { + MutexGuard guard(&d->m_mutex); + return d->currentSize(group); +} + +int KIconLoaderPrivate::currentSize(KIconLoader::Group group) const +{ + if (!mpGroups) { return -1; } @@ -1546,7 +1664,7 @@ qCDebug(KICONTHEMES) << "Illegal icon group:" << group; return -1; } - return d->mpGroups[group].size; + return mpGroups[group].size; } QStringList KIconLoader::queryIconsByDir(const QString &iconsDir) const @@ -1561,10 +1679,15 @@ return result; } -QStringList KIconLoader::queryIconsByContext(int group_or_size, - KIconLoader::Context context) const +QStringList KIconLoader::queryIconsByContext(int group_or_size, KIconLoader::Context context) const +{ + MutexGuard guard(&d->m_mutex); + return d->queryIconsByContext(group_or_size, context); +} + +QStringList KIconLoaderPrivate::queryIconsByContext(int group_or_size, KIconLoader::Context context) { - d->initIconThemes(); + initIconThemes(); QStringList result; if (group_or_size >= KIconLoader::LastGroup) { @@ -1573,12 +1696,12 @@ } int size; if (group_or_size >= 0) { - size = d->mpGroups[group_or_size].size; + size = mpGroups[group_or_size].size; } else { size = -group_or_size; } - foreach (KIconThemeNode *themeNode, d->links) { + foreach (KIconThemeNode *themeNode, links) { themeNode->queryIconsByContext(&result, size, context); } @@ -1593,19 +1716,24 @@ } else { name = (*it).mid(n + 1); } - name = d->removeIconExtension(name); + name = removeIconExtension(name); if (!entries.contains(name)) { entries += name; res2 += *it; } } return res2; - } QStringList KIconLoader::queryIcons(int group_or_size, KIconLoader::Context context) const { - d->initIconThemes(); + MutexGuard guard(&d->m_mutex); + return d->queryIcons(group_or_size, context); +} + +QStringList KIconLoaderPrivate::queryIcons(int group_or_size, KIconLoader::Context context) +{ + initIconThemes(); QStringList result; if (group_or_size >= KIconLoader::LastGroup) { @@ -1614,12 +1742,12 @@ } int size; if (group_or_size >= 0) { - size = d->mpGroups[group_or_size].size; + size = mpGroups[group_or_size].size; } else { size = -group_or_size; } - foreach (KIconThemeNode *themeNode, d->links) { + foreach (KIconThemeNode *themeNode, links) { themeNode->queryIcons(&result, size, context); } @@ -1634,7 +1762,7 @@ } else { name = (*it).mid(n + 1); } - name = d->removeIconExtension(name); + name = removeIconExtension(name); if (!entries.contains(name)) { entries += name; res2 += *it; @@ -1646,9 +1774,15 @@ // used by KIconDialog to find out which contexts to offer in a combobox bool KIconLoader::hasContext(KIconLoader::Context context) const { - d->initIconThemes(); + MutexGuard guard(&d->m_mutex); + return d->hasContext(context); +} + +bool KIconLoaderPrivate::hasContext(KIconLoader::Context context) +{ + initIconThemes(); - foreach (KIconThemeNode *themeNode, d->links) + foreach (KIconThemeNode *themeNode, links) if (themeNode->theme->hasContext(context)) { return true; } @@ -1657,12 +1791,19 @@ KIconEffect *KIconLoader::iconEffect() const { - return &d->mpEffect; + // it's save to share + return d->mpEffect; } bool KIconLoader::alphaBlending(KIconLoader::Group group) const { - if (!d->mpGroups) { + MutexGuard guard(&d->m_mutex); + return d->alphaBlending(group); +} + +bool KIconLoaderPrivate::alphaBlending(KIconLoader::Group group) const +{ + if (!mpGroups) { return false; } @@ -1794,9 +1935,15 @@ bool KIconLoader::hasIcon(const QString &name) const { - auto it = d->mIconAvailability.constFind(name); - const auto end = d->mIconAvailability.constEnd(); - if (it != end && !it.value() && !d->shouldCheckForUnknownIcons()) { + MutexGuard guard(&d->m_mutex); + return d->hasIcon(name); +} + +bool KIconLoaderPrivate::hasIcon(const QString &name) +{ + auto it = mIconAvailability.constFind(name); + const auto end = mIconAvailability.constEnd(); + if (it != end && !it.value() && !shouldCheckForUnknownIcons()) { return false; // known to be unavailable } bool found = it != end && it.value(); @@ -1804,26 +1951,39 @@ if (!iconPath(name, KIconLoader::Desktop, KIconLoader::MatchBest).isEmpty()) { found = true; } - d->mIconAvailability.insert(name, found); // remember whether the icon is available or not + mIconAvailability.insert(name, found); // remember whether the icon is available or not } return found; } void KIconLoader::setCustomPalette(const QPalette &palette) { - d->mCustomPalette = true; - d->mPalette = palette; + MutexGuard guard(&d->m_mutex); + d->setCustomPalette(palette); +} + +void KIconLoaderPrivate::setCustomPalette(const QPalette &palette) +{ + mCustomPalette = true; + mPalette = palette; } QPalette KIconLoader::customPalette() const { + MutexGuard guard(&d->m_mutex); return d->mPalette; } void KIconLoader::resetPalette() { - d->mCustomPalette = false; - d->mPalette = QPalette(); + MutexGuard guard(&d->m_mutex); + d->resetPalette(); +} + +void KIconLoaderPrivate::resetPalette() +{ + mCustomPalette = false; + mPalette = QPalette(); } /*** the global icon loader ***/ diff --git a/src/kicontheme.h b/src/kicontheme.h --- a/src/kicontheme.h +++ b/src/kicontheme.h @@ -31,6 +31,7 @@ #include "kiconloader.h" +class QMutex; class QAction; class KIconThemeDir; @@ -54,6 +55,9 @@ * This is appended at the end of the search paths */ explicit KIconTheme(const QString &name, const QString &appName = QString(), const QString &basePathHint = QString()); + + KIconTheme(QMutex *mutex, const QString &name, const QString &appName = QString(), const QString &basePathHint = QString()); + ~KIconTheme(); /** diff --git a/src/kicontheme.cpp b/src/kicontheme.cpp --- a/src/kicontheme.cpp +++ b/src/kicontheme.cpp @@ -34,6 +34,8 @@ #include #include +#include "mutexguard.h" + #include // KLocalizedString::localizedFilePath. Need such functionality in, hmm, QLocale? QStandardPaths? #include @@ -73,6 +75,7 @@ class Q_DECL_HIDDEN KIconTheme::KIconThemePrivate { public: + KIconThemePrivate(QMutex *mutex = nullptr) : m_mutex(mutex) {} QString example, screenshot; bool hidden; KSharedConfig::Ptr sharedConfig; @@ -81,6 +84,7 @@ QList mSizes[6]; int mDepth; + QMutex *m_mutex; QString mDir, mName, mInternalName, mDesc; QStringList mInherits; QStringList mExtensions; @@ -88,6 +92,23 @@ QVector mScaledDirs; bool followsColorScheme : 1; + bool isValid() const + { + return !mDirs.isEmpty() || !mScaledDirs.isEmpty(); + } + + void init(const QString &name, const QString &appName, const QString &basePathHint); + + QStringList queryIcons(int size, KIconLoader::Context context) const; + + QStringList queryIconsByContext(int size, KIconLoader::Context context) const; + + bool hasContext(KIconLoader::Context context) const; + + QString iconPathByName(const QString &iconName, int size, KIconLoader::MatchType match, qreal scale) const; + + QString iconPath(const QString &name, int size, KIconLoader::MatchType match, qreal scale) const; + /// Searches the given dirs vector for a matching icon QString iconPath(const QVector &dirs, const QString &name, int size, qreal scale, KIconLoader::MatchType match) const; }; @@ -248,10 +269,22 @@ return path; } +KIconTheme::KIconTheme(QMutex *mutex, const QString &name, const QString &appName, const QString &basePathHint) + : d(new KIconThemePrivate(mutex)) +{ + MutexGuard guard(d->m_mutex); + d->init(name, appName, basePathHint); +} + KIconTheme::KIconTheme(const QString &name, const QString &appName, const QString &basePathHint) - : d(new KIconThemePrivate) + : d(new KIconThemePrivate()) +{ + d->init(name, appName, basePathHint); +} + +void KIconTheme::KIconThemePrivate::init(const QString &name, const QString &appName, const QString &basePathHint) { - d->mInternalName = name; + mInternalName = name; QStringList themeDirs; QSet addedDirs; // Used for avoiding duplicates. @@ -293,52 +326,52 @@ const QString cDir = *it + QLatin1Char('/') + name + QLatin1Char('/'); if (QDir(cDir).exists()) { themeDirs += cDir; - if (d->mDir.isEmpty()) { + if (mDir.isEmpty()) { if (QFileInfo::exists(cDir + QStringLiteral("index.theme"))) { - d->mDir = cDir; - fileName = d->mDir + QStringLiteral("index.theme"); + mDir = cDir; + fileName = mDir + QStringLiteral("index.theme"); mainSection = QStringLiteral("Icon Theme"); } else if (QFileInfo::exists(cDir + QStringLiteral("index.desktop"))) { - d->mDir = cDir; - fileName = d->mDir + QStringLiteral("index.desktop"); + mDir = cDir; + fileName = mDir + QStringLiteral("index.desktop"); mainSection = QStringLiteral("KDE Icon Theme"); } } } } - if (d->mDir.isEmpty()) { + if (mDir.isEmpty()) { qWarning() << "Icon theme" << name << "not found."; return; } // Use KSharedConfig to avoid parsing the file many times, from each component. // Need to keep a ref to it to make this useful - d->sharedConfig = KSharedConfig::openConfig(fileName, KConfig::NoGlobals); + sharedConfig = KSharedConfig::openConfig(fileName, KConfig::NoGlobals); - KConfigGroup cfg(d->sharedConfig, mainSection); - d->mName = cfg.readEntry("Name"); - d->mDesc = cfg.readEntry("Comment"); - d->mDepth = cfg.readEntry("DisplayDepth", 32); - d->mInherits = cfg.readEntry("Inherits", QStringList()); + KConfigGroup cfg(sharedConfig, mainSection); + mName = cfg.readEntry("Name"); + mDesc = cfg.readEntry("Comment"); + mDepth = cfg.readEntry("DisplayDepth", 32); + mInherits = cfg.readEntry("Inherits", QStringList()); if (name != defaultThemeName()) { - for (QStringList::Iterator it = d->mInherits.begin(), total = d->mInherits.end(); it != total; ++it) { + for (QStringList::Iterator it = mInherits.begin(), total = mInherits.end(); it != total; ++it) { if (*it == QLatin1String("default")) { *it = defaultThemeName(); } } } - d->hidden = cfg.readEntry("Hidden", false); - d->followsColorScheme = cfg.readEntry("FollowsColorScheme", false); - d->example = cfg.readPathEntry("Example", QString()); - d->screenshot = cfg.readPathEntry("ScreenShot", QString()); - d->mExtensions = cfg.readEntry("KDE-Extensions", QStringList{ QStringLiteral(".png"), QStringLiteral(".svgz"), QStringLiteral(".svg"), QStringLiteral(".xpm") }); + hidden = cfg.readEntry("Hidden", false); + followsColorScheme = cfg.readEntry("FollowsColorScheme", false); + example = cfg.readPathEntry("Example", QString()); + screenshot = cfg.readPathEntry("ScreenShot", QString()); + mExtensions = cfg.readEntry("KDE-Extensions", QStringList{ QStringLiteral(".png"), QStringLiteral(".svgz"), QStringLiteral(".svg"), QStringLiteral(".xpm") }); const QStringList dirs = cfg.readPathEntry("Directories", QStringList()) + cfg.readPathEntry("ScaledDirectories", QStringList()); for (QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) { - KConfigGroup cg(d->sharedConfig, *it); + KConfigGroup cg(sharedConfig, *it); for (QStringList::ConstIterator itDir = themeDirs.constBegin(); itDir != themeDirs.constEnd(); ++itDir) { const QString currentDir(*itDir + *it + QLatin1Char('/')); if (!addedDirs.contains(currentDir) && QDir(currentDir).exists()) { @@ -346,9 +379,9 @@ KIconThemeDir *dir = new KIconThemeDir(*itDir, *it, cg); if (dir->isValid()) { if (dir->scale() > 1) { - d->mScaledDirs.append(dir); + mScaledDirs.append(dir); } else { - d->mDirs.append(dir); + mDirs.append(dir); } } else { delete dir; @@ -365,11 +398,11 @@ groups += QStringLiteral("Panel"); groups += QStringLiteral("Dialog"); const int defDefSizes[] = { 32, 22, 22, 16, 48, 32 }; - KConfigGroup cg(d->sharedConfig, mainSection); + KConfigGroup cg(sharedConfig, mainSection); for (int i = 0; i < groups.size(); ++i) { const QString group = groups.at(i); - d->mDefSize[i] = cg.readEntry(group + QStringLiteral("Default"), defDefSizes[i]); - d->mSizes[i] = cg.readEntry(group + QStringLiteral("Sizes"), QList()); + mDefSize[i] = cg.readEntry(group + QStringLiteral("Default"), defDefSizes[i]); + mSizes[i] = cg.readEntry(group + QStringLiteral("Sizes"), QList()); } } @@ -382,51 +415,61 @@ QString KIconTheme::name() const { + MutexGuard guard(d->m_mutex); return d->mName; } QString KIconTheme::internalName() const { + MutexGuard guard(d->m_mutex); return d->mInternalName; } QString KIconTheme::description() const { + MutexGuard guard(d->m_mutex); return d->mDesc; } QString KIconTheme::example() const { + MutexGuard guard(d->m_mutex); return d->example; } QString KIconTheme::screenshot() const { + MutexGuard guard(d->m_mutex); return d->screenshot; } QString KIconTheme::dir() const { + MutexGuard guard(d->m_mutex); return d->mDir; } QStringList KIconTheme::inherits() const { + MutexGuard guard(d->m_mutex); return d->mInherits; } bool KIconTheme::isValid() const { - return !d->mDirs.isEmpty() || !d->mScaledDirs.isEmpty(); + MutexGuard guard(d->m_mutex); + return d->isValid(); } bool KIconTheme::isHidden() const { + MutexGuard guard(d->m_mutex); return d->hidden; } int KIconTheme::depth() const { + MutexGuard guard(d->m_mutex); return d->mDepth; } @@ -436,6 +479,7 @@ qWarning() << "Illegal icon group: " << group; return -1; } + MutexGuard guard(d->m_mutex); return d->mDefSize[group]; } @@ -445,14 +489,21 @@ qWarning() << "Illegal icon group: " << group; return QList(); } + MutexGuard guard(d->m_mutex); return d->mSizes[group]; } QStringList KIconTheme::queryIcons(int size, KIconLoader::Context context) const +{ + MutexGuard guard(d->m_mutex); + return d->queryIcons(size, context); +} + +QStringList KIconTheme::KIconThemePrivate::queryIcons(int size, KIconLoader::Context context) const { // Try to find exact match QStringList result; - foreach (KIconThemeDir* dir, d->mDirs + d->mScaledDirs) { + foreach (KIconThemeDir* dir, mDirs + mScaledDirs) { if ((context != KIconLoader::Any) && (context != dir->context())) { continue; } @@ -478,8 +529,8 @@ // Find close match KIconThemeDir *best = 0L; - for(int i=0; imDirs.size(); ++i) { - dir = d->mDirs.at(i); + for(int i=0; icontext())) { continue; } @@ -498,6 +549,12 @@ } QStringList KIconTheme::queryIconsByContext(int size, KIconLoader::Context context) const +{ + MutexGuard guard(d->m_mutex); + return d->queryIconsByContext(size, context); +} + +QStringList KIconTheme::KIconThemePrivate::queryIconsByContext(int size, KIconLoader::Context context) const { int dw; @@ -509,7 +566,7 @@ // 26 (48-22) and 32 (48-16) will be used, but who knows if someone // will make icon themes with different icon sizes. - foreach (KIconThemeDir *dir, d->mDirs + d->mScaledDirs) { + foreach (KIconThemeDir *dir, mDirs + mScaledDirs) { if ((context != KIconLoader::Any) && (context != dir->context())) { continue; } @@ -527,7 +584,13 @@ bool KIconTheme::hasContext(KIconLoader::Context context) const { - foreach (KIconThemeDir *dir, d->mDirs + d->mScaledDirs) { + MutexGuard guard(d->m_mutex); + return d->hasContext(context); +} + +bool KIconTheme::KIconThemePrivate::hasContext(KIconLoader::Context context) const +{ + foreach (KIconThemeDir *dir, mDirs + mScaledDirs) { if ((context == KIconLoader::Any) || (context == dir->context())) { return true; } @@ -542,7 +605,13 @@ QString KIconTheme::iconPathByName(const QString &iconName, int size, KIconLoader::MatchType match, qreal scale) const { - foreach(const QString ¤t, d->mExtensions) { + MutexGuard guard(d->m_mutex); + return d->iconPathByName(iconName, size, match, scale); +} + +QString KIconTheme::KIconThemePrivate::iconPathByName(const QString &iconName, int size, KIconLoader::MatchType match, qreal scale) const +{ + foreach(const QString ¤t, mExtensions) { const QString path = iconPath(iconName + current, size, match, scale); if (!path.isEmpty()) return path; @@ -552,6 +621,7 @@ bool KIconTheme::followsColorScheme() const { + MutexGuard guard(d->m_mutex); return d->followsColorScheme; } @@ -561,13 +631,19 @@ } QString KIconTheme::iconPath(const QString &name, int size, KIconLoader::MatchType match, qreal scale) const +{ + MutexGuard guard(d->m_mutex); + return d->iconPath(name, size, match, scale); +} + +QString KIconTheme::KIconThemePrivate::iconPath(const QString &name, int size, KIconLoader::MatchType match, qreal scale) const { // first look for a scaled image at exactly the requested size - QString path = d->iconPath(d->mScaledDirs, name, size, scale, KIconLoader::MatchExact); + QString path = iconPath(mScaledDirs, name, size, scale, KIconLoader::MatchExact); // then look for an unscaled one but request it at larger size so it doesn't become blurry if (path.isEmpty()) { - path = d->iconPath(d->mDirs, name, size * scale, 1, match); + path = iconPath(mDirs, name, size * scale, 1, match); } return path; } diff --git a/src/mutexguard.h b/src/mutexguard.h new file mode 100644 --- /dev/null +++ b/src/mutexguard.h @@ -0,0 +1,41 @@ +/* This file is part of the KDE project. + * Copyright (C) 2018 Anthony Fieroni + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef MUTEXGUARD_H +#define MUTEXGUARD_H + +#include + +class MutexGuard +{ + QMutex *m_mutex; + bool m_aquireMutex; +public: + MutexGuard(QMutex *mutex) : m_mutex(mutex) + { + m_aquireMutex = mutex && mutex->tryLock(-1); + } + ~MutexGuard() + { + if (m_aquireMutex) { + m_mutex->unlock(); + } + } +}; + +#endif // MUTEXGUARD_H diff --git a/tests/kiconloadertest.cpp b/tests/kiconloadertest.cpp --- a/tests/kiconloadertest.cpp +++ b/tests/kiconloadertest.cpp @@ -4,32 +4,54 @@ #include #include #include +#include +#include +#include #include #include +class Worker : public QThread +{ +private: + void run() override + { + KIconLoader *mpLoader = KIconLoader::global(); + KIconLoader::Context mContext = KIconLoader::Application; + QTime dt; + dt.start(); + int count = 0; + for (int mGroup = 0; mGroup < KIconLoader::LastGroup; ++mGroup) { + const QStringList filelist = mpLoader->queryIcons(mGroup, mContext); + for (QStringList::ConstIterator it = filelist.begin(); it != filelist.end(); ++it) { + mpLoader->loadIcon((*it), (KIconLoader::Group)mGroup); + ++count; + } + } + QString str; + QTextStream stream(&str); + stream << "Loading " << count << " icons took " << (float)(dt.elapsed()) / 1000 << " seconds"; + qDebug() << stream.readAll(); + } +}; + int main(int argc, char *argv[]) { QApplication app(argc, argv); + QAtomicInt count; - KIconLoader *mpLoader = KIconLoader::global(); - KIconLoader::Context mContext = KIconLoader::Application; - QTime dt; - dt.start(); - int count = 0; - for (int mGroup = 0; mGroup < KIconLoader::LastGroup; ++mGroup) { - qDebug() << "queryIcons " << mGroup << "," << mContext; - const QStringList filelist = mpLoader->queryIcons(mGroup, mContext); - qDebug() << " -> found " << filelist.count() << " icons."; - int i = 0; - for (QStringList::ConstIterator it = filelist.begin(); - it != filelist.end() /*&& i<10*/; - ++it, ++i) { - //qDebug() << ( i==9 ? "..." : (*it) ); - mpLoader->loadIcon((*it), (KIconLoader::Group)mGroup); - ++count; - } + for (int i=0; i<32; i++) { + Worker *w = new Worker; + QObject::connect(w, &QThread::finished, [&count, w]() { + w->deleteLater(); + int num = ++count; + if (num == 32) { + qApp->quit(); + } + }); + w->start(); } - qDebug() << "Loading " << count << " icons took " << (float)(dt.elapsed()) / 1000 << " seconds"; + + return app.exec(); }