~steef/snixembed

0d047dade7836636b8919920eb66317ac43bd86f — Steef Hegeman 2 years ago 6589038
improve new_icon handling (less buggy, more async)
2 files changed, 42 insertions(+), 19 deletions(-)

M src/proxyicon.vala
M src/statusnotifieritem.vala
M src/proxyicon.vala => src/proxyicon.vala +1 -2
@@ 13,11 13,10 @@ class ProxyIcon : Object {

        item.changed.connect(property => {
            switch(property) {
            case "icon-name":
            case "icon-pixmap": set_icon(); break;
            case "tool-tip": set_tooltip(); break;
            }
        });
        item.icon_changed.connect(set_icon);

        set_icon();
        icon.set_name(item.id); // MUST be called after an icon is set due to Gdk internals

M src/statusnotifieritem.vala => src/statusnotifieritem.vala +41 -17
@@ 11,6 11,12 @@ struct ToolTip {
    string text;
}

// StatusNotifierItem proxy
//
// Do not use the D-Bus signals directly, instead rely on icon_changed for icon changes
// and the property changed signals for the rest (title, tooltip, ...).
//
// TODO: handle race conditions
[DBus (name = "org.kde.StatusNotifierItem")]
interface StatusNotifierItem : Object {
    public const string OBJECT = "/StatusNotifierItem";


@@ 36,11 42,29 @@ interface StatusNotifierItem : Object {
    }

    public virtual signal void new_icon() {
        update_cache("IconName", "icon-name");
        update_cache("IconPixmap", "icon-pixmap");
        handle_new_icon();
    }

    public virtual signal void icon_changed();

        // AppIndicator only
        update_cache("IconThemePath", "icon-theme-path");
    // Asynchronously update icon data and then emit icon_changed if necessary
    async void handle_new_icon() {
        var changes = 0;

        var done = 0;
        AsyncReadyCallback handler = (obj, res) => {
            try {
                if (update_cache.end(res)) changes++;
            } finally {
                if (++done == 3) handle_new_icon.callback();
            }
        };
        update_cache.begin("IconName", "icon-name", handler);
        update_cache.begin("IconPixmap", "icon-pixmap", handler);
        update_cache.begin("IconThemePath", "icon-theme-path", handler); // AppIndicator only
        yield;

        if (changes != 0) icon_changed();
    }

    public virtual signal void new_tool_tip() {


@@ 56,21 80,21 @@ interface StatusNotifierItem : Object {
    // - this is simple and just works
    internal abstract signal void changed(string property);

    void update_cache(string property, string name) {
    async bool update_cache(string property, string name) throws Error {
        var me = this as DBusProxy;

        var par = new Variant("(ss)", me.get_interface_name(), property);
        me.call.begin("org.freedesktop.DBus.Properties.Get", par, 0, -1, null, (obj, result) => {
            try {
                Variant res, v;
                res = me.call.end(result);
                res.get("(v)", out v);

                if (v != me.get_cached_property(property)) {
                    me.set_cached_property(property, v);
                    (me as StatusNotifierItem).changed(name);
                }
            } catch (DBusError.INVALID_ARGS e) { /* Ignore properties that don't exist */ }
        });
        try {
            var res = yield me.call("org.freedesktop.DBus.Properties.Get", par, 0, -1, null);
            Variant v;
            res.get("(v)", out v);

            if (v != me.get_cached_property(property)) {
                me.set_cached_property(property, v);
                (me as StatusNotifierItem).changed(name);
                return true;
            }
        } catch (DBusError.UNKNOWN_PROPERTY e) {} catch (DBusError.INVALID_ARGS e) {}
        return false;
    }
}