diff options
Diffstat (limited to 'applets/notification_area/libstatus-notifier-watcher/gf-sn-watcher-v0.c')
-rw-r--r-- | applets/notification_area/libstatus-notifier-watcher/gf-sn-watcher-v0.c | 395 |
1 files changed, 395 insertions, 0 deletions
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 <http://www.gnu.org/licenses/>. + */ + +#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); +} |