From 7d39b2e82f46777efa67224f078c1cec9e827654 Mon Sep 17 00:00:00 2001 From: Colomban Wendling Date: Thu, 19 Jan 2017 18:46:10 +0100 Subject: Add StatusNotifier support to the Notification Area applet The StatusNotifier part of the implementation is based off gnome-panel's status-notifier applet. --- .../libstatus-notifier-watcher/Makefile.am | 45 +++ .../libstatus-notifier-watcher/README | 2 + .../libstatus-notifier-watcher/gf-sn-watcher-v0.c | 395 +++++++++++++++++++++ .../libstatus-notifier-watcher/gf-sn-watcher-v0.h | 33 ++ .../gf-status-notifier-watcher.c | 64 ++++ .../gf-status-notifier-watcher.h | 33 ++ .../org.kde.StatusNotifierWatcher.xml | 42 +++ 7 files changed, 614 insertions(+) create mode 100644 applets/notification_area/libstatus-notifier-watcher/Makefile.am create mode 100644 applets/notification_area/libstatus-notifier-watcher/README create mode 100644 applets/notification_area/libstatus-notifier-watcher/gf-sn-watcher-v0.c create mode 100644 applets/notification_area/libstatus-notifier-watcher/gf-sn-watcher-v0.h create mode 100644 applets/notification_area/libstatus-notifier-watcher/gf-status-notifier-watcher.c create mode 100644 applets/notification_area/libstatus-notifier-watcher/gf-status-notifier-watcher.h create mode 100644 applets/notification_area/libstatus-notifier-watcher/org.kde.StatusNotifierWatcher.xml (limited to 'applets/notification_area/libstatus-notifier-watcher') diff --git a/applets/notification_area/libstatus-notifier-watcher/Makefile.am b/applets/notification_area/libstatus-notifier-watcher/Makefile.am new file mode 100644 index 00000000..eb06ce83 --- /dev/null +++ b/applets/notification_area/libstatus-notifier-watcher/Makefile.am @@ -0,0 +1,45 @@ +NULL = + +noinst_LTLIBRARIES = \ + libstatus-notifier-watcher.la \ + $(NULL) + +AM_CPPFLAGS = \ + $(NOTIFICATION_AREA_CFLAGS) \ + -DG_LOG_DOMAIN=\"status-notifier-watcher\" \ + $(DISABLE_DEPRECATED_CFLAGS) + +AM_CFLAGS = $(WARN_CFLAGS) + +libstatus_notifier_watcher_la_SOURCES = \ + gf-sn-watcher-v0.c \ + gf-sn-watcher-v0.h \ + gf-status-notifier-watcher.c \ + gf-status-notifier-watcher.h \ + $(BUILT_SOURCES) \ + $(NULL) + +libstatus_notifier_watcher_la_LIBADD = \ + $(NOTIFICATION_AREA_LIBS) \ + $(NULL) + +gf-sn-watcher-v0-gen.h: +gf-sn-watcher-v0-gen.c: org.kde.StatusNotifierWatcher.xml + $(AM_V_GEN) $(GDBUS_CODEGEN) --c-namespace Gf \ + --generate-c-code gf-sn-watcher-v0-gen \ + $(srcdir)/org.kde.StatusNotifierWatcher.xml + +BUILT_SOURCES = \ + gf-sn-watcher-v0-gen.c \ + gf-sn-watcher-v0-gen.h \ + $(NULL) + +EXTRA_DIST = \ + org.kde.StatusNotifierWatcher.xml \ + $(NULL) + +CLEANFILES = \ + $(BUILT_SOURCES) \ + $(NULL) + +-include $(top_srcdir)/git.mk diff --git a/applets/notification_area/libstatus-notifier-watcher/README b/applets/notification_area/libstatus-notifier-watcher/README new file mode 100644 index 00000000..706b2a82 --- /dev/null +++ b/applets/notification_area/libstatus-notifier-watcher/README @@ -0,0 +1,2 @@ +Borrowed from gnome-flashback: +https://git.gnome.org/browse/gnome-flashback/tree/gnome-flashback/libstatus-notifier-watcher diff --git a/applets/notification_area/libstatus-notifier-watcher/gf-sn-watcher-v0.c b/applets/notification_area/libstatus-notifier-watcher/gf-sn-watcher-v0.c new file mode 100644 index 00000000..8e82c10f --- /dev/null +++ b/applets/notification_area/libstatus-notifier-watcher/gf-sn-watcher-v0.c @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2016 Alberts Muktupāvels + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include "gf-sn-watcher-v0.h" + +struct _GfSnWatcherV0 +{ + GfSnWatcherV0GenSkeleton parent; + + guint bus_name_id; + + GSList *hosts; + GSList *items; +}; + +typedef enum +{ + GF_WATCH_TYPE_HOST, + GF_WATCH_TYPE_ITEM +} GfWatchType; + +typedef struct +{ + GfSnWatcherV0 *v0; + GfWatchType type; + + gchar *service; + gchar *bus_name; + gchar *object_path; + guint watch_id; +} GfWatch; + +static void gf_sn_watcher_v0_gen_init (GfSnWatcherV0GenIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GfSnWatcherV0, gf_sn_watcher_v0, GF_TYPE_SN_WATCHER_V0_GEN_SKELETON, + G_IMPLEMENT_INTERFACE (GF_TYPE_SN_WATCHER_V0_GEN, gf_sn_watcher_v0_gen_init)) + +static void +update_registered_items (GfSnWatcherV0 *v0) +{ + GVariantBuilder builder; + GSList *l; + GVariant *variant; + const gchar **items; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("as")); + + for (l = v0->items; l != NULL; l = g_slist_next (l)) + { + GfWatch *watch; + gchar *item; + + watch = (GfWatch *) l->data; + + item = g_strdup_printf ("%s%s", watch->bus_name, watch->object_path); + g_variant_builder_add (&builder, "s", item); + g_free (item); + } + + variant = g_variant_builder_end (&builder); + items = g_variant_get_strv (variant, NULL); + + gf_sn_watcher_v0_gen_set_registered_items (GF_SN_WATCHER_V0_GEN (v0), items); + g_variant_unref (variant); +} + +static void +gf_watch_free (gpointer data) +{ + GfWatch *watch; + + watch = (GfWatch *) data; + + if (watch->watch_id > 0) + g_bus_unwatch_name (watch->watch_id); + + g_free (watch->service); + g_free (watch->bus_name); + g_free (watch->object_path); + + g_free (watch); +} + +static void +name_vanished_cb (GDBusConnection *connection, + const char *name, + gpointer user_data) +{ + GfWatch *watch; + GfSnWatcherV0 *v0; + GfSnWatcherV0Gen *gen; + + watch = (GfWatch *) user_data; + v0 = watch->v0; + gen = GF_SN_WATCHER_V0_GEN (v0); + + if (watch->type == GF_WATCH_TYPE_HOST) + { + v0->hosts = g_slist_remove (v0->hosts, watch); + + if (v0->hosts == NULL) + { + gf_sn_watcher_v0_gen_set_is_host_registered (gen, FALSE); + gf_sn_watcher_v0_gen_emit_host_registered (gen); + } + } + else if (watch->type == GF_WATCH_TYPE_ITEM) + { + gchar *tmp; + + v0->items = g_slist_remove (v0->items, watch); + + update_registered_items (v0); + + tmp = g_strdup_printf ("%s%s", watch->bus_name, watch->object_path); + gf_sn_watcher_v0_gen_emit_item_unregistered (gen, tmp); + g_free (tmp); + } + else + { + g_assert_not_reached (); + } + + gf_watch_free (watch); +} + +static GfWatch * +gf_watch_new (GfSnWatcherV0 *v0, + GfWatchType type, + const gchar *service, + const gchar *bus_name, + const gchar *object_path) +{ + GfWatch *watch; + + watch = g_new0 (GfWatch, 1); + + watch->v0 = v0; + watch->type = type; + + watch->service = g_strdup (service); + watch->bus_name = g_strdup (bus_name); + watch->object_path = g_strdup (object_path); + watch->watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, bus_name, + G_BUS_NAME_WATCHER_FLAGS_NONE, NULL, + name_vanished_cb, watch, NULL); + + return watch; +} + +static GfWatch * +gf_watch_find (GSList *list, + const gchar *bus_name, + const gchar *object_path) +{ + GSList *l; + + for (l = list; l != NULL; l = g_slist_next (l)) + { + GfWatch *watch; + + watch = (GfWatch *) l->data; + + if (g_strcmp0 (watch->bus_name, bus_name) == 0 && + g_strcmp0 (watch->object_path, object_path) == 0) + { + return watch; + } + } + + return NULL; +} + +static gboolean +gf_sn_watcher_v0_handle_register_host (GfSnWatcherV0Gen *object, + GDBusMethodInvocation *invocation, + const gchar *service) +{ + GfSnWatcherV0 *v0; + const gchar *bus_name; + const gchar *object_path; + GfWatch *watch; + + v0 = GF_SN_WATCHER_V0 (object); + + if (*service == '/') + { + bus_name = g_dbus_method_invocation_get_sender (invocation); + object_path = service; + } + else + { + bus_name = service; + object_path = "/StatusNotifierHost"; + } + + if (g_dbus_is_name (bus_name) == FALSE) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, + "D-Bus bus name '%s' is not valid", + bus_name); + + return TRUE; + } + + watch = gf_watch_find (v0->hosts, bus_name, object_path); + + if (watch != NULL) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, + "Status Notifier Host with bus name '%s' and object path '%s' is already registered", + bus_name, object_path); + + return TRUE; + } + + watch = gf_watch_new (v0, GF_WATCH_TYPE_HOST, service, bus_name, object_path); + v0->hosts = g_slist_prepend (v0->hosts, watch); + + if (!gf_sn_watcher_v0_gen_get_is_host_registered (object)) + { + gf_sn_watcher_v0_gen_set_is_host_registered (object, TRUE); + gf_sn_watcher_v0_gen_emit_host_registered (object); + } + + gf_sn_watcher_v0_gen_complete_register_host (object, invocation); + + return TRUE; +} + +static gboolean +gf_sn_watcher_v0_handle_register_item (GfSnWatcherV0Gen *object, + GDBusMethodInvocation *invocation, + const gchar *service) +{ + GfSnWatcherV0 *v0; + const gchar *bus_name; + const gchar *object_path; + GfWatch *watch; + gchar *tmp; + + v0 = GF_SN_WATCHER_V0 (object); + + if (*service == '/') + { + bus_name = g_dbus_method_invocation_get_sender (invocation); + object_path = service; + } + else + { + bus_name = service; + object_path = "/StatusNotifierItem"; + } + + if (g_dbus_is_name (bus_name) == FALSE) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, + "D-Bus bus name '%s' is not valid", + bus_name); + + return TRUE; + } + + watch = gf_watch_find (v0->items, bus_name, object_path); + + if (watch != NULL) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, + "Status Notifier Item with bus name '%s' and object path '%s' is already registered", + bus_name, object_path); + + return TRUE; + } + + watch = gf_watch_new (v0, GF_WATCH_TYPE_ITEM, service, bus_name, object_path); + v0->items = g_slist_prepend (v0->items, watch); + + update_registered_items (v0); + + tmp = g_strdup_printf ("%s%s", bus_name, object_path); + gf_sn_watcher_v0_gen_emit_item_registered (object, tmp); + g_free (tmp); + + gf_sn_watcher_v0_gen_complete_register_item (object, invocation); + + return TRUE; +} + +static void +gf_sn_watcher_v0_gen_init (GfSnWatcherV0GenIface *iface) +{ + iface->handle_register_host = gf_sn_watcher_v0_handle_register_host; + iface->handle_register_item = gf_sn_watcher_v0_handle_register_item; +} + +static void +bus_acquired_cb (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + GfSnWatcherV0 *v0; + GDBusInterfaceSkeleton *skeleton; + GError *error; + + v0 = GF_SN_WATCHER_V0 (user_data); + skeleton = G_DBUS_INTERFACE_SKELETON (v0); + + error = NULL; + g_dbus_interface_skeleton_export (skeleton, connection, + "/StatusNotifierWatcher", &error); + + if (error != NULL) + { + g_warning ("%s", error->message); + g_error_free (error); + return; + } +} + +static void +gf_sn_watcher_v0_dispose (GObject *object) +{ + GfSnWatcherV0 *v0; + + v0 = GF_SN_WATCHER_V0 (object); + + if (v0->bus_name_id > 0) + { + g_bus_unown_name (v0->bus_name_id); + v0->bus_name_id = 0; + } + + if (v0->hosts != NULL) + { + g_slist_free_full (v0->hosts, gf_watch_free); + v0->hosts = NULL; + } + + if (v0->items != NULL) + { + g_slist_free_full (v0->items, gf_watch_free); + v0->items = NULL; + } + + G_OBJECT_CLASS (gf_sn_watcher_v0_parent_class)->dispose (object); +} + +static void +gf_sn_watcher_v0_class_init (GfSnWatcherV0Class *v0_class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (v0_class); + + object_class->dispose = gf_sn_watcher_v0_dispose; +} + +static void +gf_sn_watcher_v0_init (GfSnWatcherV0 *v0) +{ + GBusNameOwnerFlags flags; + + flags = G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | + G_BUS_NAME_OWNER_FLAGS_REPLACE; + + v0->bus_name_id = g_bus_own_name (G_BUS_TYPE_SESSION, + "org.kde.StatusNotifierWatcher", flags, + bus_acquired_cb, NULL, NULL, v0, NULL); +} + +GfSnWatcherV0 * +gf_sn_watcher_v0_new (void) +{ + return g_object_new (GF_TYPE_SN_WATCHER_V0, NULL); +} diff --git a/applets/notification_area/libstatus-notifier-watcher/gf-sn-watcher-v0.h b/applets/notification_area/libstatus-notifier-watcher/gf-sn-watcher-v0.h new file mode 100644 index 00000000..73bf6d04 --- /dev/null +++ b/applets/notification_area/libstatus-notifier-watcher/gf-sn-watcher-v0.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016 Alberts Muktupāvels + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef GF_SN_WATCHER_V0_H +#define GF_SN_WATCHER_V0_H + +#include "gf-sn-watcher-v0-gen.h" + +G_BEGIN_DECLS + +#define GF_TYPE_SN_WATCHER_V0 gf_sn_watcher_v0_get_type () +G_DECLARE_FINAL_TYPE (GfSnWatcherV0, gf_sn_watcher_v0, + GF, SN_WATCHER_V0, GfSnWatcherV0GenSkeleton) + +GfSnWatcherV0 *gf_sn_watcher_v0_new (void); + +G_END_DECLS + +#endif diff --git a/applets/notification_area/libstatus-notifier-watcher/gf-status-notifier-watcher.c b/applets/notification_area/libstatus-notifier-watcher/gf-status-notifier-watcher.c new file mode 100644 index 00000000..e8b2c654 --- /dev/null +++ b/applets/notification_area/libstatus-notifier-watcher/gf-status-notifier-watcher.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 Alberts Muktupāvels + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include "gf-sn-watcher-v0.h" +#include "gf-status-notifier-watcher.h" + +struct _GfStatusNotifierWatcher +{ + GObject parent; + + GfSnWatcherV0 *v0; +}; + +G_DEFINE_TYPE (GfStatusNotifierWatcher, gf_status_notifier_watcher, G_TYPE_OBJECT) + +static void +gf_status_notifier_watcher_dispose (GObject *object) +{ + GfStatusNotifierWatcher *watcher; + + watcher = GF_STATUS_NOTIFIER_WATCHER (object); + + g_clear_object (&watcher->v0); + + G_OBJECT_CLASS (gf_status_notifier_watcher_parent_class)->dispose (object); +} + +static void +gf_status_notifier_watcher_class_init (GfStatusNotifierWatcherClass *watcher_class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (watcher_class); + + object_class->dispose = gf_status_notifier_watcher_dispose; +} + +static void +gf_status_notifier_watcher_init (GfStatusNotifierWatcher *watcher) +{ + watcher->v0 = gf_sn_watcher_v0_new (); +} + +GfStatusNotifierWatcher * +gf_status_notifier_watcher_new (void) +{ + return g_object_new (GF_TYPE_STATUS_NOTIFIER_WATCHER, NULL); +} diff --git a/applets/notification_area/libstatus-notifier-watcher/gf-status-notifier-watcher.h b/applets/notification_area/libstatus-notifier-watcher/gf-status-notifier-watcher.h new file mode 100644 index 00000000..fd871a42 --- /dev/null +++ b/applets/notification_area/libstatus-notifier-watcher/gf-status-notifier-watcher.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016 Alberts Muktupāvels + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef GF_STATUS_NOTIFIER_WATCHER_H +#define GF_STATUS_NOTIFIER_WATCHER_H + +#include + +G_BEGIN_DECLS + +#define GF_TYPE_STATUS_NOTIFIER_WATCHER gf_status_notifier_watcher_get_type () +G_DECLARE_FINAL_TYPE (GfStatusNotifierWatcher, gf_status_notifier_watcher, + GF, STATUS_NOTIFIER_WATCHER, GObject) + +GfStatusNotifierWatcher *gf_status_notifier_watcher_new (void); + +G_END_DECLS + +#endif diff --git a/applets/notification_area/libstatus-notifier-watcher/org.kde.StatusNotifierWatcher.xml b/applets/notification_area/libstatus-notifier-watcher/org.kde.StatusNotifierWatcher.xml new file mode 100644 index 00000000..30d3f774 --- /dev/null +++ b/applets/notification_area/libstatus-notifier-watcher/org.kde.StatusNotifierWatcher.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.1