mirror of
https://github.com/zoriya/astal.git
synced 2026-05-29 17:02:00 +00:00
implement tray items
This commit is contained in:
+26
-2
@@ -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',
|
||||
)
|
||||
|
||||
|
||||
+12
-4
@@ -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);
|
||||
}
|
||||
|
||||
+159
-18
@@ -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);
|
||||
|
||||
removed();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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;
|
||||
|
||||
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)
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user