Codebase list gnome-shell-extension-appindicator / debian/33-3 iconCache.js
debian/33-3

Tree @debian/33-3 (Download .tar.gz)

iconCache.js @debian/33-3

e2de1b7
2d2d1f7
 
 
 
 
ef1c3b8
2d2d1f7
 
 
 
 
 
 
 
 
90c376a
da06740
90c376a
 
2d2d1f7
7e7a328
 
2d2d1f7
 
 
 
 
 
3e62054
12df268
3e62054
2d2d1f7
f7d9118
 
0383431
 
3e62054
f7d9118
e2de1b7
f7d9118
3e62054
 
 
 
0383431
3e62054
444c796
3e62054
 
da06740
3e62054
 
 
 
 
 
 
03d52f9
3e62054
0383431
f7d9118
e2de1b7
f7d9118
3e62054
 
 
444c796
3e62054
da06740
 
 
 
 
 
 
3e62054
0383431
 
3e62054
03d52f9
 
f7d9118
3e62054
f7d9118
3e62054
 
f7d9118
e2de1b7
f7d9118
0383431
f7d9118
7e7a328
 
f7d9118
7e7a328
 
03d52f9
 
f7d9118
e2de1b7
7e7a328
f7d9118
0383431
444c796
3e62054
0383431
 
3e62054
 
f7d9118
e2de1b7
f7d9118
03d52f9
 
 
444c796
03d52f9
 
 
444c796
03d52f9
ca76158
03d52f9
f7d9118
03d52f9
f7d9118
0383431
 
 
444c796
0383431
 
 
 
444c796
0383431
 
12df268
03d52f9
f7d9118
e2de1b7
f7d9118
35e3e1c
0383431
f7d9118
// This file is part of the AppIndicator/KStatusNotifierItem GNOME Shell extension
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

const GLib = imports.gi.GLib
const GObject = imports.gi.GObject

const Mainloop = imports.mainloop

const Util = imports.misc.extensionUtils.getCurrentExtension().imports.util;

// The icon cache caches icon objects in case they're reused shortly aftwerwards.
// This is necessary for some indicators like skype which rapidly switch between serveral icons.
// Without caching, the garbage collection would never be able to handle the amount of new icon data.
// If the lifetime of an icon is over, the cache will destroy the icon. (!)
// The presence of an inUse property set to true on the icon will extend the lifetime.

const LIFETIME_TIMESPAN = 5000; // milli-seconds
const GC_INTERVAL = 10; // seconds

// how to use: see IconCache.add, IconCache.get
var IconCache = class AppIndicators_IconCache {
    constructor() {
        this._cache = {};
        this._lifetime = {}; //we don't want to attach lifetime to the object
        this._destroyNotify = {};
    }

    add(id, o) {
        if (!(o && id))
            return null;

        if (!(id in this._cache) || this._cache[id] !== o) {
            this._remove(id);

            Util.Logger.debug("IconCache: adding "+id,o);
            this._cache[id] = o;

            if ((o instanceof GObject.Object) && GObject.signal_lookup('destroy', o)) {
                this._destroyNotify[id] = o.connect('destroy', () => {
                    this._remove(id);
                });
            }
        }

        this._renewLifetime(id);
        this._checkGC();

        return o;
    }

    _remove(id) {
        if (!(id in this._cache))
            return;

        Util.Logger.debug('IconCache: removing '+id);

        let object = this._cache[id];

        if ((object instanceof GObject.Object) && GObject.signal_lookup('destroy', object))
            object.disconnect(this._destroyNotify[id]);

        if (typeof object.destroy === 'function')
            object.destroy();

        delete this._cache[id];
        delete this._lifetime[id];
        delete this._destroyNotify[id];

        this._checkGC();
    }

    _renewLifetime(id) {
        if (id in this._cache)
            this._lifetime[id] = new Date().getTime() + LIFETIME_TIMESPAN;
    }

    forceDestroy(id) {
        this._remove(id);
    }

    // removes everything from the cache
    clear() {
        for (let id in this._cache)
            this._remove(id)

        this._checkGC();
    }

    // returns an object from the cache, or null if it can't be found.
    get(id) {
        if (id in this._cache) {
            Util.Logger.debug('IconCache: retrieving '+id);
            this._renewLifetime(id);
            return this._cache[id];
        }

        return null;
    }

    _checkGC() {
        let cacheIsEmpty = (Object.keys(this._cache).length === 0);

        if (!cacheIsEmpty && !this._gcTimeout) {
            Util.Logger.debug("IconCache: garbage collector started");
            this._gcTimeout = Mainloop.timeout_add_seconds(GC_INTERVAL,
                                                           this._gc.bind(this));
        } else if (cacheIsEmpty && this._gcTimeout) {
            Util.Logger.debug("IconCache: garbage collector stopped");
            GLib.Source.remove(this._gcTimeout);
            delete this._gcTimeout;
        }
    }

    _gc() {
        var time = new Date().getTime();
        for (var id in this._cache) {
            if (this._cache[id].inUse) {
                Util.Logger.debug("IconCache: " + id + " is in use.");
                continue;
            } else if (this._lifetime[id] < time) {
                this._remove(id);
            } else {
                Util.Logger.debug("IconCache: " + id + " survived this round.");
            }
        }

        return true;
    }

    destroy() {
        this.clear();
    }
};