~steef/snixembed

0cd2d89df586c4fa3cf1d4135ec961f9d88cc501 — Steef Hegeman 7 days ago b5bbe15
code quality, notably spaces for indentation

This is horrible for git blame and whatnot, but luckily I'm always to
blame so far. Better now than later.
M src/main.vala => src/main.vala +28 -28
@@ 1,39 1,39 @@
int fork_self(string[] argv) {
	Bus.watch_name(BusType.SESSION, StatusNotifierWatcher.NAME, BusNameWatcherFlags.NONE,
			() => Process.exit(0), null);
    Bus.watch_name(BusType.SESSION, StatusNotifierWatcher.NAME,
            BusNameWatcherFlags.NONE, () => Process.exit(0), null);

	for (int i = 0; i < argv.length; i++) {
		argv[i] = argv[i] == "--fork" ? "" : argv[i];
	}
    for (int i = 0; i < argv.length; i++) {
        argv[i] = argv[i] == "--fork" ? "" : argv[i];
    }

	Pid pid;
	Process.spawn_async(null, argv, null,
			SpawnFlags.CHILD_INHERITS_STDIN | SpawnFlags.SEARCH_PATH,
			null, out pid);
	ChildWatch.add(pid, (pid, status) => {
				Process.close_pid(pid);
				Process.exit(status);
			});
    Pid pid;
    Process.spawn_async(null, argv, null,
            SpawnFlags.CHILD_INHERITS_STDIN | SpawnFlags.SEARCH_PATH,
            null, out pid);
    ChildWatch.add(pid, (pid, status) => {
        Process.close_pid(pid);
        Process.exit(status);
    });

	new MainLoop().run();
	return 0;
    new MainLoop().run();
    return 0;
}

int main(string[] args) {
	if ("-v" in args || "--version" in args) {
		stdout.printf("version: %s\n", VERSION);
		return 0;
	}
	if ("--fork" in args) {
		string[] argv = args; // Copy since we're not allowed to mutate args
		return fork_self(argv);
	}
    if ("-v" in args || "--version" in args) {
        stdout.printf("version: %s\n", VERSION);
        return 0;
    }
    if ("--fork" in args) {
        string[] argv = args; // Copy since we're not allowed to mutate args
        return fork_self(argv);
    }

	Gtk.init(ref args);
    Gtk.init(ref args);

	var server = new ProxyServer();
	server.start();
    var server = new ProxyServer();
    server.start();

	Gtk.main();
	return 0;
    Gtk.main();
    return 0;
}

M src/proxyicon.vala => src/proxyicon.vala +103 -102
@@ 1,104 1,105 @@
class ProxyIcon : Object {
	StatusNotifierItem item;
	Gtk.StatusIcon icon;
	DbusmenuGtk.Menu menu;

	public ProxyIcon(string name, string object) throws IOError {
			init.begin(name, object);
		}

		async void init(string name, string object) {
			item = yield Bus.get_proxy(BusType.SESSION, name, object, DBusProxyFlags.NONE, null);
			icon = new Gtk.StatusIcon();

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

			icon.set_name(item.id);
			set_icon();
			set_tooltip();

			if (item.menu != null) {
				stdout.printf("%s set menu: %s\n", name, item.menu);
				menu = new DbusmenuGtk.Menu(name, item.menu);
			}

			icon.activate.connect(() => item.activate(0, 0)); // TODO: consider smarter coordinate hints
			icon.button_press_event.connect(on_button_press);
		}

	bool on_button_press(Gdk.EventButton event) {
		if (event.button == 3) {
			if (menu != null) {
				menu.popup_at_pointer(event);
				return true;
			}

			item.context_menu((int)event.x_root, (int)event.y_root);
			return true;
		}
		return false;
	}

	void set_icon() {
		var name = item.icon_name;
		if (name != null) {
			var theme = Gtk.IconTheme.get_for_screen(icon.screen);
			var path = item.icon_theme_path;
			if (path != null) {
				string[] paths;
				theme.get_search_path(out paths);
				if (!(path in paths)) {
					theme.append_search_path(path);
				}
			}
			theme.rescan_if_needed();

			set_icon_name(name);
			if (theme.has_icon(name)) {
				return;
			}
		}

		var pixmaps = item.icon_pixmap;
		if (pixmaps != null) {
			set_icon_pixmap(pixmaps);
		}
	}

	void set_icon_name(string name) {
		icon.set_from_icon_name(name);
	}

	void set_icon_pixmap(IconPixmap[] pixmaps) {
		var pixmap = pixmaps[0]; // TODO: choose the best one

		// Convert from ARGB to RGBA
		for (int i = 0; i < pixmap.width * pixmap.height * 4; i += 3) {
			var alpha = pixmap.data[i];
			pixmap.data[i] = pixmap.data[i + 1];
			pixmap.data[i + 1] = pixmap.data[i + 2];
			pixmap.data[i + 2] = pixmap.data[i + 3];
			pixmap.data[i + 3] = alpha;
		}

		var pixbuf = new Gdk.Pixbuf.from_data(pixmap.data, Gdk.Colorspace.RGB, true, 8, pixmap.width, pixmap.height, pixmap.width * 4);
		icon.set_from_pixbuf(pixbuf);
	}

	void set_tooltip() {
		var tooltip = item.tool_tip;
		string title = tooltip.title;
		string text = tooltip.text;

		if (text != null && text != "") {
			title += @"\n$(tooltip.text)";
		}
		icon.set_tooltip_markup(title);
	}
    StatusNotifierItem item;
    Gtk.StatusIcon icon;
    DbusmenuGtk.Menu menu;

    public ProxyIcon(string name, string object) throws IOError {
        init.begin(name, object);
    }

    async void init(string name, string object) {
        item = yield Bus.get_proxy(BusType.SESSION, name, object, DBusProxyFlags.NONE, null);
        icon = new Gtk.StatusIcon();

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

        icon.set_name(item.id);
        set_icon();
        set_tooltip();

        if (item.menu != null) {
            stdout.printf("%s set menu: %s\n", name, item.menu);
            menu = new DbusmenuGtk.Menu(name, item.menu);
        }

        icon.activate.connect(() => item.activate(0, 0)); // TODO: consider smarter coordinate hints
        icon.button_press_event.connect(on_button_press);
    }

    bool on_button_press(Gdk.EventButton event) {
        if (event.button == 3) {
            if (menu != null) {
                menu.popup_at_pointer(event);
                return true;
            }

            item.context_menu((int)event.x_root, (int)event.y_root);
            return true;
        }
        return false;
    }

    void set_icon() {
        var name = item.icon_name;
        if (name != null) {
            var theme = Gtk.IconTheme.get_for_screen(icon.screen);
            var path = item.icon_theme_path;
            if (path != null) {
                string[] paths;
                theme.get_search_path(out paths);
                if (!(path in paths)) {
                    theme.append_search_path(path);
                }
            }
            theme.rescan_if_needed();

            set_icon_name(name);
            if (theme.has_icon(name)) {
                return;
            }
        }

        var pixmaps = item.icon_pixmap;
        if (pixmaps != null) {
            set_icon_pixmap(pixmaps);
        }
    }

    void set_icon_name(string name) {
        icon.set_from_icon_name(name);
    }

    void set_icon_pixmap(IconPixmap[] pixmaps) {
        var pixmap = pixmaps[0]; // TODO: choose the best one

        // Convert from ARGB to RGBA
        for (int i = 0; i < pixmap.width * pixmap.height * 4; i += 3) {
            var alpha = pixmap.data[i];
            pixmap.data[i] = pixmap.data[i + 1];
            pixmap.data[i + 1] = pixmap.data[i + 2];
            pixmap.data[i + 2] = pixmap.data[i + 3];
            pixmap.data[i + 3] = alpha;
        }

        var pixbuf = new Gdk.Pixbuf.from_data(pixmap.data, Gdk.Colorspace.RGB,
                true, 8, pixmap.width, pixmap.height, pixmap.width * 4);
        icon.set_from_pixbuf(pixbuf);
    }

    void set_tooltip() {
        var tooltip = item.tool_tip;
        string title = tooltip.title;
        string text = tooltip.text;

        if (text != null && text != "") {
            title += @"\n$(tooltip.text)";
        }
        icon.set_tooltip_markup(title);
    }
}

M src/proxyserver.vala => src/proxyserver.vala +55 -53
@@ 1,55 1,57 @@
class ProxyServer : Object {
	StatusNotifierWatcher watcher;
	HashTable<string, ProxyIcon> icons;

	public ProxyServer() {
		icons = new HashTable<string, ProxyIcon>(str_hash, str_equal);
	}

	public void start() {
		// Register watcher
		Bus.own_name(BusType.SESSION, StatusNotifierWatcher.NAME,
				BusNameOwnerFlags.NONE, on_bus_acquired_watcher, () => { },
				() => {
					stderr.printf("error: could not acquire watcher name\n");
					Process.exit(1);
				});
	}

	bool on_item_registered(string service, BusName sender) {
		stdout.printf("%s appeared\n", service);
		var name = service;
		var object = StatusNotifierItem.OBJECT;
		if (!DBus.is_name(service)) {
			stdout.printf("%s is not a service name, falling back to appindicator on %s\n", service, sender);
			name = sender;
			object = service;
		}

		try {
			var icon = new ProxyIcon(name, object);
			icons.insert(name, icon);
		} catch (IOError e) {
			stderr.printf("error: %s\n", e.message);
		}
		return true;
	}

	bool on_item_unregistered(string service) {
		stdout.printf("%s vanished\n", service);
		icons.remove(service);
		return true;
	}

	void on_bus_acquired_watcher(DBusConnection conn) {
		try {
			watcher = new StatusNotifierWatcher();
			watcher.status_notifier_item_registered.connect(on_item_registered);
			watcher.status_notifier_item_unregistered.connect(on_item_unregistered);
			conn.register_object(StatusNotifierWatcher.OBJECT, watcher);
			watcher.status_notifier_host_registered();
		} catch (IOError e) {
			stderr.printf("could not register watcher service\n");
		}
	}
    StatusNotifierWatcher watcher;
    HashTable<string, ProxyIcon> icons;

    public ProxyServer() {
        icons = new HashTable<string, ProxyIcon>(str_hash, str_equal);
    }

    public void start() {
        // Register watcher
        Bus.own_name(BusType.SESSION, StatusNotifierWatcher.NAME,
                BusNameOwnerFlags.NONE, on_bus_acquired_watcher, () => { },
                () => {
                    stderr.printf("error: could not acquire watcher name\n");
                    Process.exit(1);
                });
    }

    bool on_item_registered(string service, BusName sender) {
        stdout.printf("%s appeared\n", service);

        var name = service;
        var object = StatusNotifierItem.OBJECT;
        if (!DBus.is_name(service)) {
            stdout.printf("%s is not a service name, falling back to appindicator on %s\n", service, sender);
            name = sender;
            object = service;
        }

        try {
            var icon = new ProxyIcon(name, object);
            icons.insert(name, icon);
        } catch (IOError e) {
            stderr.printf("error: %s\n", e.message);
        }
        return true;
    }

    bool on_item_unregistered(string service) {
        stdout.printf("%s vanished\n", service);
        icons.remove(service);
        return true;
    }

    void on_bus_acquired_watcher(DBusConnection conn) {
        try {
            watcher = new StatusNotifierWatcher();
            watcher.status_notifier_item_registered.connect(on_item_registered);
            watcher.status_notifier_item_unregistered.connect(on_item_unregistered);

            conn.register_object(StatusNotifierWatcher.OBJECT, watcher);
            watcher.status_notifier_host_registered();
        } catch (IOError e) {
            stderr.printf("could not register watcher service\n");
        }
    }
}

M src/statusnotifieritem.vala => src/statusnotifieritem.vala +52 -52
@@ 1,71 1,71 @@
struct IconPixmap {
	int32 width;
	int32 height;
	uint8[] data;
    int32 width;
    int32 height;
    uint8[] data;
}

struct ToolTip {
	string icon_title;
	IconPixmap[] icon_pixmap;
	string title;
	string text;
    string icon_title;
    IconPixmap[] icon_pixmap;
    string title;
    string text;
}

[DBus (name = "org.kde.StatusNotifierItem")]
interface StatusNotifierItem : Object {
	public const string OBJECT = "/StatusNotifierItem";
    public const string OBJECT = "/StatusNotifierItem";

	// Properties
	public abstract string id { owned get; }
	public abstract string title { owned get; }
	public abstract string icon_name { owned get; }
	public abstract IconPixmap[] icon_pixmap { owned get; }
	public abstract ToolTip tool_tip { owned get; }
	public abstract ObjectPath menu { owned get; }
    // Properties
    public abstract string id { owned get; }
    public abstract string title { owned get; }
    public abstract string icon_name { owned get; }
    public abstract IconPixmap[] icon_pixmap { owned get; }
    public abstract ToolTip tool_tip { owned get; }
    public abstract ObjectPath menu { owned get; }

	// AppIndicator TODO: split in separate interface
	public abstract string icon_theme_path { owned get; }
    // AppIndicator TODO: split in separate interface
    public abstract string icon_theme_path { owned get; }

	// Methods
	public abstract void activate(int x, int y);
	public abstract void context_menu(int x, int y);
    // Methods
    public abstract void activate(int x, int y);
    public abstract void context_menu(int x, int y);

	// Signals
	public virtual signal void new_title() {
		update_cache("Title", "title");
	}
    // Signals
    public virtual signal void new_title() {
        update_cache("Title", "title");
    }

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

	public virtual signal void new_tool_tip() {
		update_cache("ToolTip", "tool-tip");
	}
    public virtual signal void new_tool_tip() {
        update_cache("ToolTip", "tool-tip");
    }

	// Internal cache updates and signalling
	//
	// This looks like the most ugly implementation ever, however:
	// - Vala DBus abstraction is abstract
	// - GObject notify signals doesn't work
	// - g_properties_changed does not get called automatically when the cache is updated
	// - this is simple and just works
	internal abstract signal void changed(string property);
    // Internal cache updates and signalling
    //
    // This looks like the most ugly implementation ever, however:
    // - Vala DBus abstraction is abstract
    // - GObject notify signals doesn't work
    // - g_properties_changed does not get called automatically when the cache is updated
    // - this is simple and just works
    internal abstract signal void changed(string property);

	void update_cache(string property, string name) {
		var me = this as DBusProxy;
    void update_cache(string property, string name) {
        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) => {
			Variant res, v;
			res = me.call.end(result);
			res.get("(v)", out v);
        var par = new Variant("(ss)", me.get_interface_name(), property);
        me.call.begin("org.freedesktop.DBus.Properties.Get", par, 0, -1, null, (obj, result) => {
            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);
			}
		});
	}
            if (v != me.get_cached_property(property)) {
                me.set_cached_property(property, v);
                (me as StatusNotifierItem).changed(name);
            }
        });
    }
}

M src/statusnotifierwatcher.vala => src/statusnotifierwatcher.vala +46 -46
@@ 1,49 1,49 @@
[DBus (name = "org.kde.StatusNotifierWatcher")]
public class StatusNotifierWatcher : Object {
	public const string NAME = "org.kde.StatusNotifierWatcher";
	public const string OBJECT = "/StatusNotifierWatcher";

	// Methods
	public void register_status_notifier_item(string service, BusName sender) {
		var name = service;
		if (!DBus.is_name(service)) {
			// appindicator fallback
			name = sender;
		}

                if (watchers.contains(name)) {
                    stdout.printf("warning: %s (%s) is already registered; ignoring registration\n", name, service);
                    return;
                }

		watchers[name] = Bus.watch_name(BusType.SESSION, name, BusNameWatcherFlags.NONE,
				(conn, item, owner) => status_notifier_item_registered(service, sender),
				(conn, item) => {
					Bus.unwatch_name(watchers[name]);
					watchers.remove(name);
					status_notifier_item_unregistered(item);
				});
	}

	public void register_status_notifier_host(string service) {
		// We probably don't have to anything here, as we just proxy
	}

	// Properties
	public string[] registered_status_notifier_items { get; }
	public bool is_status_notifier_host_registered { get { return true; } }

	// This property is undocumented yet KDE implements it
	public int protocol_version { get { return 0; } }

	// Signals
	internal signal bool status_notifier_item_registered(string service, BusName sender);
	public signal bool status_notifier_item_unregistered(string service);
	public signal bool status_notifier_host_registered();

	// Internal
	HashTable<string, uint> watchers;
	construct {
		watchers = new HashTable<string, uint>(str_hash, str_equal);
	}
    public const string NAME = "org.kde.StatusNotifierWatcher";
    public const string OBJECT = "/StatusNotifierWatcher";

    // Methods
    public void register_status_notifier_item(string service, BusName sender) {
        var name = service;
        if (!DBus.is_name(service)) {
            // appindicator fallback
            name = sender;
        }

        if (watchers.contains(name)) {
            stdout.printf("warning: %s (%s) is already registered; ignoring registration\n", name, service);
            return;
        }

        watchers[name] = Bus.watch_name(BusType.SESSION, name, BusNameWatcherFlags.NONE,
            (conn, item, owner) => status_notifier_item_registered(service, sender),
            (conn, item) => {
                Bus.unwatch_name(watchers[name]);
                watchers.remove(name);
                status_notifier_item_unregistered(item);
            });
    }

    public void register_status_notifier_host(string service) {
        // We probably don't have to anything here, as we just proxy
    }

    // Properties
    public string[] registered_status_notifier_items { get; }
    public bool is_status_notifier_host_registered { get { return true; } }

    // This property is undocumented yet KDE implements it
    public int protocol_version { get { return 0; } }

    // Signals
    internal signal bool status_notifier_item_registered(string service, BusName sender);
    public signal bool status_notifier_item_unregistered(string service);
    public signal bool status_notifier_host_registered();

    // Internal
    HashTable<string, uint> watchers;
    construct {
        watchers = new HashTable<string, uint>(str_hash, str_equal);
    }
}