summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkotontrion <[email protected]>2024-05-23 14:30:34 +0200
committerkotontrion <[email protected]>2024-05-23 14:30:34 +0200
commitc9d7394bcce12e761e1372fbd18649023aad729d (patch)
treea8adb3e044ad226defa1f8c9802e32ef15c6ed9c
parent7c2bdd44cd7d2c4d50a13a706b6cc13e29340426 (diff)
implement tray items
-rw-r--r--src/meson.build28
-rw-r--r--src/tray.vala16
-rw-r--r--src/trayItem.vala175
-rw-r--r--src/watcherProxy.vala49
4 files changed, 196 insertions, 72 deletions
diff --git a/src/meson.build b/src/meson.build
index af71701..9110030 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -10,8 +10,29 @@ deps = [
dependency('gio-2.0'),
dependency('json-glib-1.0'),
dependency('gdk-pixbuf-2.0'),
+ dependency('gtk+-3.0'),
]
+dbusmenu_cflags = run_command(
+ find_program('pkg-config', required: true),
+ '--cflags',
+ 'dbusmenu-gtk3-0.4',
+ 'gobject-introspection-1.0',
+ 'gobject-2.0', 'glib-2.0',
+ capture: true,
+ check: true,
+).stdout().strip()
+
+dbusmenu_libs = run_command(
+ find_program('pkg-config', required: true),
+ '--libs',
+ 'dbusmenu-gtk3-0.4',
+ 'gobject-introspection-1.0',
+ 'gobject-2.0', 'glib-2.0',
+ capture: true,
+ check: true,
+).stdout().strip()
+
sources = files(
'tray.vala',
'watcher.vala',
@@ -21,11 +42,14 @@ sources = files(
libtray = library(
meson.project_name(),
sources,
- dependencies: deps,
+ dependencies: deps + meson.get_compiler('vala').find_library('posix'),
vala_header: meson.project_name() + '.h',
vala_vapi: meson.project_name() + '.vapi',
vala_gir: tray_gir,
+ vala_args: ['--pkg', 'DbusmenuGtk3-0.4', '--pkg', 'Dbusmenu-0.4'],
version: meson.project_version(),
+ c_args: dbusmenu_cflags.split(' '),
+ link_args: dbusmenu_libs.split(' '),
install: true,
install_dir: [true, true, true, true],
)
@@ -37,7 +61,7 @@ import('pkgconfig').generate(
filebase: meson.project_name() + '-' + api_version,
version: meson.project_version(),
subdirs: meson.project_name(),
- requires: 'gio-2.0',
+ requires: deps + [dependency('dbusmenu-gtk3-0.4')],
install_dir: get_option('libdir') / 'pkgconfig',
)
diff --git a/src/tray.vala b/src/tray.vala
index 380eaa1..5bf9270 100644
--- a/src/tray.vala
+++ b/src/tray.vala
@@ -22,7 +22,7 @@ public class Tray : Object {
private IWatcher proxy;
private HashTable<string, TrayItem> _items;
- public TrayItem[] items { get { return _items.get_values_as_ptr_array().data; }}
+ public List<weak TrayItem> items { owned get { return _items.get_values(); }}
public signal void item_added(string service);
public signal void item_removed(string service);
@@ -38,6 +38,9 @@ public class Tray : Object {
start_watcher,
() => {
if (proxy != null) {
+ //foreach (string item in proxy.RegisteredStatusNotifierItems) {
+ // on_item_unregister(item);
+ //}
proxy = null;
}
},
@@ -68,18 +71,23 @@ public class Tray : Object {
proxy.StatusNotifierItemRegistered.connect(on_item_register);
proxy.StatusNotifierItemUnregistered.connect(on_item_unregister);
+
+ foreach (string item in proxy.RegisteredStatusNotifierItems) {
+ on_item_register(item);
+ }
}
private void on_item_register(string service) {
if(_items.contains(service)) return;
string[] parts = service.split("/", 2);
TrayItem item = new TrayItem(parts[0], "/" + parts[1]);
- _items.set(service, item);
- item_added(service);
+ item.ready.connect(() => {
+ _items.set(service, item);
+ item_added(service);
+ });
}
private void on_item_unregister(string service) {
- string[] parts = service.split("/", 2);
_items.remove(service);
item_removed(service);
}
diff --git a/src/trayItem.vala b/src/trayItem.vala
index b4f8207..6bee7ed 100644
--- a/src/trayItem.vala
+++ b/src/trayItem.vala
@@ -1,3 +1,6 @@
+using DbusmenuGtk;
+using Posix;
+
namespace AstalTray {
public struct Pixmap {
@@ -8,7 +11,7 @@ namespace AstalTray {
public struct Tooltip {
string icon_name;
- Pixmap icon;
+ Pixmap[] icon;
string title;
string description;
}
@@ -41,11 +44,11 @@ internal interface IItem : DBusProxy {
public abstract string Title { owned get; }
public abstract Category Category { owned get; }
public abstract Status Status { owned get; }
- public abstract Tooltip Tooltip { owned get; }
+ public abstract Tooltip? ToolTip { owned get; }
public abstract string Id { owned get; }
- public abstract string IconThemePath { owned get; }
+ public abstract string? IconThemePath { owned get; }
public abstract bool ItemIsMenu { owned get; }
- public abstract ObjectPath Menu { owned 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; }
@@ -75,42 +78,180 @@ public class TrayItem : Object {
public string title { owned get { return proxy.Title; } }
public Category category { get { return proxy.Category; } }
public Status status { get { return proxy.Status; } }
- public Tooltip tooltip { owned get { return proxy.Tooltip; } }
- public string tooltip_string { owned get { return proxy.Tooltip.title; } }
+ public Tooltip? tooltip { owned get { return proxy.ToolTip; } }
+ public string tooltip_markup {
+ owned get {
+ if(proxy.ToolTip == null) return "";
+
+ string tt = proxy.ToolTip.title;
+ if (proxy.ToolTip.description != "")
+ tt += "\n" + proxy.ToolTip.description;
+
+ return tt;
+ }
+ }
public string id { owned get { return proxy.Id ;} }
public string icon_theme_path { owned get { return proxy.IconThemePath ;} }
public bool is_menu { get { return proxy.ItemIsMenu ;} }
- public signal void removed();
+ public DbusmenuGtk.Menu? menu { get; private set;}
+ public string icon_name {
+ owned get {
+ if(proxy.Status == Status.NEEDS_ATTENTION)
+ return proxy.AttentionIconName;
+ else return proxy.IconName;
+ }
+ }
+
+ public Gdk.Pixbuf icon_pixbuf { owned get { return _get_icon_pixbuf(); } }
+
+ public signal void changed();
+ public signal void ready();
public TrayItem(string service, string path) {
connection_ids = new List<ulong>();
+ setup_proxy(service, path);
+
+ }
- proxy = Bus.get_proxy_sync(
+ private async void setup_proxy(string service, string path) {
+
+ proxy = yield Bus.get_proxy(
BusType.SESSION,
service,
path
);
-
- //connection_ids.append(proxy.NewIcon.connect(() => notify(icon)));
- connection_ids.append(proxy.NewTitle.connect(() => notify_property("title")));
- connection_ids.append(proxy.NewToolTip.connect(() => {
- notify_property("tooltip");
- notify_property("tooltip_string");
- }));
- connection_ids.append(proxy.NewStatus.connect(() => notify_property("status")));
+
+ if(proxy.Menu != null) {
+ menu = new DbusmenuGtk.Menu(
+ proxy.get_name_owner(),
+ proxy.Menu);
+ }
+
+ 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);
+ }
+ }
+ );
+
+ ready();
+
+ }
+
+ private void _notify() {
+ string[] props = {
+ "category", "id", "title", "status", "is-menu",
+ "tooltip-markup"
+ };
+
+ foreach (string prop in props)
+ notify_property(prop);
+
+ changed();
+ }
+
+ 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;
- removed();
+ while (prop_iter.next ("{sv}", out prop_key, out prop_value)) {
+ proxy.set_cached_property(prop_key, prop_value);
}
+ _notify();
+
+ } catch(Error e) {
+ //silently ignore
}
+ });
+ }
+
+
+ public Gdk.Pixbuf? _get_icon_pixbuf() {
+ Pixmap[] pixmaps = proxy.Status == Status.NEEDS_ATTENTION
+ ? proxy.AttentionIconPixmap
+ : proxy.IconPixmap;
+
+
+ string icon_name = proxy.Status == Status.NEEDS_ATTENTION
+ ? proxy.AttentionIconName
+ : proxy.IconName;
+
+ Gdk.Pixbuf pixbuf = null;
+
+
+ if(icon_name != null && proxy.IconThemePath != null)
+ pixbuf = load_from_theme(icon_name, proxy.IconThemePath);
+ if (pixbuf == null) pixbuf = pixmap_to_pixbuf(pixmaps);
+
+ return pixbuf;
+
+ }
+
+ private Gdk.Pixbuf? load_from_theme(string icon_name, string theme_path) {
+ if(theme_path == "" || theme_path == null) return null;
+ if(icon_name == "" || icon_name == null) return null;
+
+ Gtk.IconTheme icon_theme = new Gtk.IconTheme();
+ string[] paths = {theme_path};
+ icon_theme.set_search_path(paths);
+
+ int size = icon_theme.get_icon_sizes(icon_name)[0];
+ Gtk.IconInfo icon_info = icon_theme.lookup_icon(
+ icon_name, size, Gtk.IconLookupFlags.FORCE_SIZE);
+
+ if (icon_info != null)
+ return icon_info.load_icon();
+ return null;
+ }
+
+ private Gdk.Pixbuf? pixmap_to_pixbuf(Pixmap[] pixmaps) {
+ if(pixmaps == null || pixmaps.length == 0) return null;
+ Pixmap pixmap = pixmaps[0];
+ uint8[] image_data = pixmap.bytes.copy();
+
+ for (int i = 0; i < pixmap.width * pixmap.height * 4; i += 4) {
+ uint8 alpha = image_data[i];
+ image_data[i] = image_data[i + 1];
+ image_data[i + 1] = image_data[i + 2];
+ image_data[i + 2] = image_data[i + 3];
+ image_data[i + 3] = alpha;
+ }
+
+ return new Gdk.Pixbuf.from_bytes(
+ new Bytes(image_data),
+ Gdk.Colorspace.RGB,
+ true,
+ 8,
+ (int)pixmap.width,
+ (int)pixmap.height,
+ (int)(pixmap.width * 4)
);
+
}
}
}
diff --git a/src/watcherProxy.vala b/src/watcherProxy.vala
deleted file mode 100644
index 18cb296..0000000
--- a/src/watcherProxy.vala
+++ /dev/null
@@ -1,49 +0,0 @@
-namespace AstalTray {
-
-[DBus (name="org.kde.StatusNotifierWatcher")]
-internal interface IWatcher : Object {
-
- public abstract string[] RegisteredStatusNotifierItems { get; }
- public abstract int ProtocolVersion { get; }
-
- public abstract void RegisterStatusNotifierItem(string service, BusName sender) throws DBusError, IOError;
- public abstract void RegisterStatusNotifierHost(string service) throws DBusError, IOError;
-
- public signal void StatusNotifierItemRegistered(string service);
- public signal void StatusNotifierItemUnregistered(string service);
- public signal void StatusNotifierHostRegistered();
- public signal void StatusNotifierHostUnregistered();
-
-}
-
-
-internal class StatusNotifierWatcherProxy : Object {
-
-
- private IWatcher proxy;
-
- public string[] RegisteredStatusNotifierItems { get { return proxy.RegisteredStatusNotifierItems; } }
- public int ProtocolVersion { get {return proxy.ProtocolVersion;} }
-
- public signal void StatusNotifierItemRegistered(string service);
- public signal void StatusNotifierItemUnregistered(string service);
-
- construct {
-
- proxy = Bus.get_proxy_sync(
- BusType.SESSION,
- "org.kde.StatusNotifierWatcher",
- "/StatusNotifierWatcher"
- );
-
- foreach (string item in proxy.RegisteredStatusNotifierItems) {
- StatusNotifierItemRegistered(item);
- }
-
- proxy.StatusNotifierItemRegistered.connect((s) => StatusNotifierItemRegistered(s));
- proxy.StatusNotifierItemUnregistered.connect((s) => StatusNotifierItemUnregistered(s));
-
- }
-
-}
-}