diff options
-rw-r--r-- | lib/tray/trayItem.vala | 395 |
1 files changed, 277 insertions, 118 deletions
diff --git a/lib/tray/trayItem.vala b/lib/tray/trayItem.vala index b335eb6..787e9ae 100644 --- a/lib/tray/trayItem.vala +++ b/lib/tray/trayItem.vala @@ -3,6 +3,34 @@ public struct Pixmap { int width; int height; uint8[] bytes; + + internal static Pixmap from_variant(GLib.Variant variant) { + Pixmap pixmap = new Pixmap(); + + int width, height; + Variant data; + + variant.get("(ii@ay)", out width, out height, out data); + pixmap.width = width; + pixmap.height = height; + pixmap.bytes = data.get_data_as_bytes().get_data(); + + + return pixmap; + } + + internal static Pixmap[] array_from_variant(GLib.Variant variant) { + Pixmap[] icons = new Pixmap[0]; + + GLib.VariantIter iter = variant.iterator(); + + GLib.Variant child; + while ((child = iter.next_value()) != null) { + Pixmap pm = Pixmap.from_variant(child); + icons += pm; + } + return icons; + } } public struct Tooltip { @@ -10,6 +38,36 @@ public struct Tooltip { Pixmap[] icon; string title; string description; + + + internal static Tooltip from_variant(GLib.Variant variant) { + Tooltip tooltip = Tooltip(); + + string icon_name; + GLib.VariantIter iter; + string title; + string description; + + variant.get("(sa(iiay)ss)", out icon_name, out iter, out title, out description); + tooltip.icon_name = icon_name; + tooltip.title = title; + tooltip.description = description; + + int x, y; + uint8[] data; + GLib.Variant child; + + Pixmap[] icons = new Pixmap[0]; + + while ((child = iter.next_value()) != null) { + Pixmap pm = Pixmap.from_variant(child); + icons += pm; + } + + tooltip.icon = icons; + + return tooltip; + } } [DBus (use_string_marshalling = true)] @@ -31,6 +89,8 @@ public enum Category { unowned var eval = enumc.get_value(this); return eval.value_nick; } + + public static extern Category from_string(string value) throws Error; } @@ -50,24 +110,27 @@ public enum Status { unowned var eval = enumc.get_value(this); return eval.value_nick; } + + public static extern Status from_string(string value) throws Error; } [DBus (name="org.kde.StatusNotifierItem")] internal interface IItem : DBusProxy { - public abstract string Title { owned get; } - public abstract Category Category { get; } - public abstract Status Status { get; } - public abstract Tooltip? ToolTip { owned get; } - public abstract string Id { owned get; } - public abstract string? IconThemePath { owned get; } - public abstract bool ItemIsMenu { get; } - public abstract ObjectPath? Menu { owned get; } - public abstract string IconName { owned get; } - public abstract Pixmap[] IconPixmap { owned get; } - public abstract string AttentionIconName { owned get; } - public abstract Pixmap[] AttentionIconPixmap { owned get; } - public abstract string OverlayIconName { owned get; } - public abstract Pixmap[] OverlayIconPixmap { owned get; } + + // public abstract string Id { owned get; } + // public abstract Category Category { get; } + // public abstract string Title { owned get; } + // public abstract Status Status { get; } + // public abstract Tooltip? ToolTip { owned get; } + // public abstract string IconThemePath { owned get; } + // public abstract bool ItemIsMenu { get; } + // public abstract ObjectPath? Menu { owned get; } + // public abstract string IconName { owned get; } + // public abstract Pixmap[] IconPixmap { owned get; } + // public abstract string AttentionIconName { owned get; } + // public abstract Pixmap[] AttentionIconPixmap { owned get; } + // public abstract string OverlayIconName { owned get; } + // public abstract Pixmap[] OverlayIconPixmap { owned get; } public abstract void ContexMenu(int x, int y) throws DBusError, IOError; public abstract void Activate(int x, int y) throws DBusError, IOError; @@ -84,19 +147,20 @@ internal interface IItem : DBusProxy { public class TrayItem : Object { private IItem proxy; - private List<ulong> connection_ids; + private bool needs_update = false; + /** The Title of the TrayItem */ - public string title { owned get { return proxy.Title; } } + public string title { get; private set; } /** The category this item belongs to */ - public Category category { get { return proxy.Category; } } + public Category category { get; private set; } /** The current status of this item */ - public Status status { get { return proxy.Status; } } + public Status status { get; private set; } /** The tooltip of this item */ - public Tooltip? tooltip { owned get { return proxy.ToolTip; } } + public Tooltip? tooltip { get; private set; } /** * A markup representation of the tooltip. This is basically equvivalent @@ -104,36 +168,40 @@ public class TrayItem : Object { */ public string tooltip_markup { owned get { - if (proxy.ToolTip == null) + if (tooltip == null) return ""; - var tt = proxy.ToolTip.title; - if (proxy.ToolTip.description != "") - tt += "\n" + proxy.ToolTip.description; + var tt = tooltip.title; + if (tooltip.description != "") + tt += "\n" + tooltip.description; return tt; } } /** the id of the item. This id is specified by the tray app.*/ - public string id { owned get { return proxy.Id ;} } + public string id { get; private set; } /** * If set, this only supports the menu, so showing the menu should be prefered * over calling [[email protected]]. */ - public bool is_menu { - get { - return proxy.ItemIsMenu || proxy.get_cached_property("ItemIsMenu") == null; - } - } + public bool is_menu { get; private set; default = true; } /** * The icon theme path, where to look for the [[email protected]:icon-name]. * It is recommended to use the [[email protected]:gicon] property, * which does the icon lookups for you. */ - public string icon_theme_path { owned get { return proxy.IconThemePath ;} } + public string icon_theme_path { get; private set; } + + // icon properties from the dbus for internal use only + private string IconName; + private Pixmap[] IconPixmap; + private string AttentionIconName; + private Pixmap[] AttentionIconPixmap; + private string OverlayIconName; + private Pixmap[] OverlayIconPixmap; /** * The name of the icon. This should be looked up in the [[email protected]:icon-theme-path] @@ -143,9 +211,9 @@ public class TrayItem : Object { */ public string icon_name { owned get { - return proxy.Status == Status.NEEDS_ATTENTION - ? proxy.AttentionIconName - : proxy.IconName; + return status == Status.NEEDS_ATTENTION + ? AttentionIconName + : IconName; } } @@ -167,6 +235,9 @@ public class TrayItem : Object { /** The id of the item used to uniquely identify the TrayItems by this lib.*/ public string item_id { get; private set; } + /** The object path to the dbusmenu */ + public ObjectPath menu_path { get; private set; } + private DBusMenu.Importer menu_importer; /** @@ -196,7 +267,6 @@ public class TrayItem : Object { public signal void ready(); internal TrayItem(string service, string path) { - connection_ids = new List<ulong>(); item_id = service + path; setup_proxy.begin(service, path, (_, res) => setup_proxy.end(res)); } @@ -209,109 +279,198 @@ public class TrayItem : Object { path ); - proxy.g_default_timeout = 1000; - - connection_ids.append(proxy.NewStatus.connect(refresh_all_properties)); - connection_ids.append(proxy.NewToolTip.connect(refresh_all_properties)); - connection_ids.append(proxy.NewTitle.connect(refresh_all_properties)); - connection_ids.append(proxy.NewIcon.connect(refresh_all_properties)); - - proxy.notify["g-name-owner"].connect(() => { - if (proxy.g_name_owner == null) { - foreach (var id in connection_ids) - SignalHandler.disconnect(proxy, id); - } - }); - - Variant? menuVariant = proxy.get_cached_property("Menu"); - if (proxy.Menu != null && menuVariant != null && menuVariant.is_of_type(VariantType.OBJECT_PATH)) { - this.menu_importer = new DBusMenu.Importer(proxy.get_name_owner(), proxy.Menu); - this.menu_importer.notify["model"].connect(() => { - notify_property("menu-model"); - notify_property("action-group"); - }); - } + proxy.g_signal.connect(handle_signal); - update_gicon(); + yield refresh_all_properties(); ready(); } catch (Error err) { critical(err.message); } } - private void _notify() { - string[] props = { - "category", - "id", - "title", - "status", - "is-menu", - "tooltip-markup", - "icon-name", - "icon-pixbuf" - }; - - foreach (string prop in props) { - notify_property(prop); - } - - changed(); - } - private void update_gicon() { if (icon_name != null && icon_name != "") { if (GLib.FileUtils.test(icon_name, GLib.FileTest.EXISTS)) { gicon = new GLib.FileIcon(GLib.File.new_for_path(icon_name)); } else if(icon_theme_path != null && icon_theme_path != "") { - gicon = new GLib.FileIcon(GLib.File.new_for_path( - find_icon_in_theme(icon_name, icon_theme_path) - )); + string path = find_icon_in_theme(icon_name, icon_theme_path); + if(path != null) gicon = new GLib.FileIcon(GLib.File.new_for_path(path)); + else gicon = new GLib.ThemedIcon(icon_name); } else { gicon = new GLib.ThemedIcon(icon_name); } } else { - Pixmap[] pixmaps = proxy.Status == Status.NEEDS_ATTENTION - ? proxy.AttentionIconPixmap - : proxy.IconPixmap; + Pixmap[] pixmaps = status == Status.NEEDS_ATTENTION + ? AttentionIconPixmap + : IconPixmap; gicon = pixmap_to_pixbuf(pixmaps); } } + private void handle_signal(DBusProxy proxy, string? sender_name, string signal_name, Variant parameters) { + if (needs_update) return; + needs_update = true; + GLib.Timeout.add_once(10, () => { + needs_update = false; + refresh_all_properties.begin(); + }); + } - private void refresh_all_properties() { - proxy.g_connection.call.begin( - proxy.g_name, - proxy.g_object_path, - "org.freedesktop.DBus.Properties", - "GetAll", - new Variant("(s)", proxy.g_interface_name), - new VariantType("(a{sv})"), - DBusCallFlags.NONE, - -1, - null, - (_, result) => { - try { - Variant parameters = proxy.g_connection.call.end(result); - VariantIter prop_iter; - parameters.get("(a{sv})", out prop_iter); - - string prop_key; - Variant prop_value; - - while (prop_iter.next ("{sv}", out prop_key, out prop_value)) { - proxy.set_cached_property(prop_key, prop_value); - } + private void set_dbus_property(string prop_name, Variant prop_value) { + try { + switch(prop_name) { + case "Category": { + var new_category = Category.from_string(prop_value.get_string()); + if (category != new_category) { + category = new_category; + } + break; + } + case "Id": { + var new_id = prop_value.get_string(); + if (id != new_id) { + id = new_id; + } + break; + } + case "Title": { + var new_title = prop_value.get_string(); + if (title != new_title) { + title = new_title; + } + break; + } + case "Status": { + var new_status = Status.from_string(prop_value.get_string()); + if (status != new_status) { + status = new_status; + update_gicon(); + } + break; + } + case "ToolTip": { + tooltip = Tooltip.from_variant(prop_value); + break; + } + case "IconThemePath": { + var new_path = prop_value.get_string(); + if (icon_theme_path != new_path) { + icon_theme_path = new_path; + update_gicon(); + } + break; + } + case "ItemIsMenu": { + var new_is_menu = prop_value.get_boolean(); + if (is_menu != new_is_menu) { + is_menu = new_is_menu; + } + break; + } + case "Menu": { + if(!prop_value.is_of_type(VariantType.OBJECT_PATH)) break; + var new_menu_path = (ObjectPath) prop_value.get_string(); + if (new_menu_path != menu_path) { + menu_path = new_menu_path; + if (menu_path != null) { + this.menu_importer = new DBusMenu.Importer(proxy.get_name_owner(), menu_path); + this.menu_importer.notify["model"].connect(() => { + notify_property("menu-model"); + notify_property("action-group"); + }); + } else { + this.menu_importer = null; + notify_property("menu-model"); + notify_property("action-group"); + } + } + break; + } + case "IconName": { + var new_icon_name = prop_value.get_string(); + if (IconName != new_icon_name) { + IconName = new_icon_name; + notify_property("icon-name"); + update_gicon(); + } + break; + } + case "IconPixmap": { + IconPixmap = Pixmap.array_from_variant(prop_value); update_gicon(); - - _notify(); - } catch(Error e) { - //silently ignore + notify_property("icon-pixbuf"); + break; + } + case "AttentionIconName": { + var new_attention_icon_name = prop_value.get_string(); + if (AttentionIconName != new_attention_icon_name) { + AttentionIconName = new_attention_icon_name; + update_gicon(); + notify_property("icon-name"); + } + break; + } + case "AttentionIconPixmap": { + AttentionIconPixmap = Pixmap.array_from_variant(prop_value); + update_gicon(); + notify_property("icon-pixbuf"); + break; + } + case "OverlayIconName": { + var new_overlay_icon_name = prop_value.get_string(); + if (OverlayIconName != new_overlay_icon_name) { + OverlayIconName = new_overlay_icon_name; + update_gicon(); + notify_property("icon-name"); + } + break; + } + case "OverlayIconPixmap": { + OverlayIconPixmap = Pixmap.array_from_variant(prop_value); + update_gicon(); + notify_property("icon-pixbuf"); + break; } } - ); + } + catch(Error e) { + //silently ignore + } + } + + + private async void refresh_all_properties() { + this.freeze_notify(); + try { + Variant parameters = yield proxy.g_connection.call( + proxy.g_name, + proxy.g_object_path, + "org.freedesktop.DBus.Properties", + "GetAll", + new Variant("(s)", proxy.g_interface_name), + new VariantType("(a{sv})"), + DBusCallFlags.NONE, + -1, + null); + + VariantIter prop_iter; + parameters.get("(a{sv})", out prop_iter); + + string prop_key; + Variant prop_value; + + while (prop_iter.next ("{sv}", out prop_key, out prop_value)) { + set_dbus_property(prop_key, prop_value); + } + } + catch(Error e) { + //silently ignode + } + this.thaw_notify(); + this.changed(); } /** @@ -320,11 +479,11 @@ public class TrayItem : Object { * before openening the menu. */ public void about_to_show() { - if(proxy.Menu == null) return; + if(menu_path == null) return; try { Bus.get_sync(BusType.SESSION).call_sync( this.proxy.g_name_owner, - this.proxy.Menu, + menu_path, "com.canonical.dbusmenu", "AboutToShow", new Variant("(i)", 0), @@ -411,15 +570,15 @@ public class TrayItem : Object { } private Gdk.Pixbuf? _get_icon_pixbuf() { - Pixmap[] pixmaps = proxy.Status == Status.NEEDS_ATTENTION - ? proxy.AttentionIconPixmap - : proxy.IconPixmap; + Pixmap[] pixmaps = status == Status.NEEDS_ATTENTION + ? AttentionIconPixmap + : IconPixmap; return pixmap_to_pixbuf(pixmaps); } private Gdk.Pixbuf? pixmap_to_pixbuf(Pixmap[] pixmaps) { - if (pixmaps == null || pixmaps.length == 0) + if (pixmaps == null || pixmaps.length <= 0) return null; Pixmap pixmap = pixmaps[0]; @@ -467,9 +626,9 @@ public class TrayItem : Object { .set_member_name("status").add_string_value(status.to_nick()) .set_member_name("category").add_string_value(category.to_nick()) .set_member_name("tooltip").add_string_value(tooltip_markup) - .set_member_name("icon_theme_path").add_string_value(proxy.IconThemePath) + .set_member_name("icon_theme_path").add_string_value(icon_theme_path) .set_member_name("icon_name").add_string_value(icon_name) - .set_member_name("menu_path").add_string_value(proxy.Menu) + .set_member_name("menu_path").add_string_value(menu_path) .set_member_name("is_menu").add_boolean_value(is_menu) .end_object() .get_root(); |