aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dbus-spy.c166
-rw-r--r--src/dbus-spy.h53
-rw-r--r--src/notification.c132
-rw-r--r--src/notification.h62
4 files changed, 413 insertions, 0 deletions
diff --git a/src/dbus-spy.c b/src/dbus-spy.c
new file mode 100644
index 0000000..af3e492
--- /dev/null
+++ b/src/dbus-spy.c
@@ -0,0 +1,166 @@
+/*
+ * dbus-spy.c - A gobject subclass to watch dbus for org.freedesktop.Notification.Notify messages.
+ */
+
+#include "dbus-spy.h"
+
+enum {
+ MESSAGE_RECEIVED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+static void dbus_spy_class_init(DBusSpyClass *klass);
+static void dbus_spy_init(DBusSpy *self);
+static void dbus_spy_dispose(GObject *object);
+
+static void add_filter(DBusSpy *self);
+
+static void bus_get_cb(GObject *source_object, GAsyncResult *res, gpointer user_data);
+
+static GDBusMessage *message_filter(GDBusConnection *connection, GDBusMessage *message,
+ gboolean incoming, gpointer user_data);
+
+#define MATCH_STRING "type='method_call',interface='org.freedesktop.Notifications',member='Notify'"
+
+G_DEFINE_TYPE (DBusSpy, dbus_spy, G_TYPE_OBJECT);
+
+static void
+dbus_spy_class_init(DBusSpyClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ g_type_class_add_private(klass, sizeof(DBusSpyPrivate));
+
+ object_class->dispose = dbus_spy_dispose;
+
+ signals[MESSAGE_RECEIVED] =
+ g_signal_new("message-received",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(DBusSpyClass, message_received),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1, NOTIFICATION_TYPE);
+}
+
+static void
+bus_get_cb(GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ GError *error = NULL;
+
+ GDBusConnection *connection = g_bus_get_finish(res, &error);
+
+ if(error != NULL) {
+ g_warning("Could not get a connection to the dbus session bus: %s\n", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ DBusSpy *self = DBUS_SPY(user_data);
+ g_return_if_fail(self != NULL);
+
+ if(self->priv->connection_cancel != NULL) {
+ g_object_unref(self->priv->connection_cancel);
+ self->priv->connection_cancel = NULL;
+ }
+
+ self->priv->connection = connection;
+
+ add_filter(self);
+}
+
+static void
+add_filter(DBusSpy *self)
+{
+ GDBusMessage *message;
+ GVariant *body;
+ GError *error = NULL;
+
+ message = g_dbus_message_new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus",
+ "org.freedesktop.DBus", "AddMatch");
+
+ body = g_variant_new_parsed("(%s,)", MATCH_STRING);
+
+ g_dbus_message_set_body(message, body);
+
+ g_dbus_connection_send_message(self->priv->connection,
+ message,
+ G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+ NULL,
+ &error);
+ if(error != NULL) {
+ g_warning("Failed to send AddMatch message: %s\n", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ g_dbus_connection_add_filter(self->priv->connection, message_filter, self, NULL);
+}
+
+static GDBusMessage*
+message_filter(GDBusConnection *connection, GDBusMessage *message, gboolean incoming, gpointer user_data)
+{
+ if(!incoming) return message;
+
+ GDBusMessageType type = g_dbus_message_get_message_type(message);
+ const gchar *interface = g_dbus_message_get_interface(message);
+ const gchar *member = g_dbus_message_get_member(message);
+
+ if((type == G_DBUS_MESSAGE_TYPE_METHOD_CALL)
+ && (g_strcmp0(interface, "org.freedesktop.Notifications") == 0)
+ && (g_strcmp0(member, "Notify") == 0))
+ {
+ DBusSpy *spy = DBUS_SPY(user_data);
+ Notification *note = notification_new_from_dbus_message(message);
+ g_signal_emit(spy, signals[MESSAGE_RECEIVED], 0, note);
+ g_object_unref(note);
+ g_object_unref(message);
+ message = NULL;
+ }
+
+ return message;
+}
+
+static void
+dbus_spy_init(DBusSpy *self)
+{
+ self->priv = DBUS_SPY_GET_PRIVATE(self);
+
+ self->priv->connection = NULL;
+ self->priv->connection_cancel = g_cancellable_new();
+
+ g_bus_get(G_BUS_TYPE_SESSION,
+ self->priv->connection_cancel,
+ bus_get_cb,
+ self);
+}
+
+static void
+dbus_spy_dispose(GObject *object)
+{
+ DBusSpy *self = DBUS_SPY(object);
+
+ if(self->priv->connection_cancel != NULL) {
+ g_cancellable_cancel(self->priv->connection_cancel);
+ g_object_unref(self->priv->connection_cancel);
+ self->priv->connection_cancel = NULL;
+ }
+
+ if(self->priv->connection != NULL) {
+ g_dbus_connection_close(self->priv->connection, NULL, NULL, NULL);
+ g_object_unref(self->priv->connection);
+ self->priv->connection = NULL;
+ }
+
+ G_OBJECT_CLASS(dbus_spy_parent_class)->dispose(object);
+}
+
+DBusSpy*
+dbus_spy_new(void)
+{
+ return DBUS_SPY(g_object_new(DBUS_SPY_TYPE, NULL));
+}
+
diff --git a/src/dbus-spy.h b/src/dbus-spy.h
new file mode 100644
index 0000000..1f9f79e
--- /dev/null
+++ b/src/dbus-spy.h
@@ -0,0 +1,53 @@
+/*
+ * dbus-spy.h - A gobject subclass to watch dbus for org.freedesktop.Notification.Notify messages.
+ */
+
+#ifndef __DBUS_SPY_H__
+#define __DBUS_SPY_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "notification.h"
+
+G_BEGIN_DECLS
+
+#define DBUS_SPY_TYPE (dbus_spy_get_type ())
+#define DBUS_SPY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUS_SPY_TYPE, DBusSpy))
+#define DBUS_SPY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUS_SPY_TYPE, DBusSpyClass))
+#define IS_DBUS_SPY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUS_SPY_TYPE))
+#define IS_DBUS_SPY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUS_SPY_TYPE))
+
+typedef struct _DBusSpy DBusSpy;
+typedef struct _DBusSpyClass DBusSpyClass;
+typedef struct _DBusSpyPrivate DBusSpyPrivate;
+
+struct _DBusSpy
+{
+ GObject parent;
+ DBusSpyPrivate *priv;
+};
+
+struct _DBusSpyClass
+{
+ GObjectClass parent_class;
+
+ void (* message_received) (DBusSpy *spy,
+ Notification *note);
+};
+
+struct _DBusSpyPrivate {
+ GDBusConnection *connection;
+ GCancellable *connection_cancel;
+};
+
+#define DBUS_SPY_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUS_SPY_TYPE, DBusSpyPrivate))
+
+GType dbus_spy_get_type(void);
+DBusSpy* dbus_spy_new(void);
+
+G_END_DECLS
+
+#endif /* __DBUS_SPY_H__ */
diff --git a/src/notification.c b/src/notification.c
new file mode 100644
index 0000000..9b0a3c9
--- /dev/null
+++ b/src/notification.c
@@ -0,0 +1,132 @@
+/*
+ * notification.c - A gobject subclass to represent a org.freedesktop.Notification.Notify message.
+ */
+
+#include "notification.h"
+
+#define COLUMN_APP_NAME 0
+#define COLUMN_REPLACES_ID 1
+#define COLUMN_APP_ICON 2
+#define COLUMN_SUMMARY 3
+#define COLUMN_BODY 4
+#define COLUMN_ACTIONS 5
+#define COLUMN_HINTS 6
+#define COLUMN_EXPIRE_TIMEOUT 7
+
+#define COLUMN_COUNT 8
+
+static void notification_class_init(NotificationClass *klass);
+static void notification_init(Notification *self);
+static void notification_dispose(GObject *object);
+
+G_DEFINE_TYPE (Notification, notification, G_TYPE_OBJECT);
+
+static void
+notification_class_init(NotificationClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ g_type_class_add_private(klass, sizeof(NotificationPrivate));
+
+ object_class->dispose = notification_dispose;
+}
+
+static void
+notification_init(Notification *self)
+{
+ self->priv = NOTIFICATION_GET_PRIVATE(self);
+
+ self->priv->app_name = NULL;
+ self->priv->replaces_id = 0;
+ self->priv->app_icon = NULL;
+ self->priv->summary = NULL;
+ self->priv->body = NULL;
+ self->priv->expire_timeout = 0;
+}
+
+static void
+notification_dispose(GObject *object)
+{
+ Notification *self = NOTIFICATION(object);
+
+ if(self->priv->app_name != NULL) {
+ g_free(self->priv->app_name);
+ self->priv->app_name = NULL;
+ }
+
+ if(self->priv->app_icon != NULL) {
+ g_free(self->priv->app_icon);
+ self->priv->app_icon = NULL;
+ }
+
+ if(self->priv->summary != NULL) {
+ g_free(self->priv->summary);
+ self->priv->summary = NULL;
+ }
+
+ if(self->priv->body != NULL) {
+ g_free(self->priv->body);
+ self->priv->body = NULL;
+ }
+
+ G_OBJECT_CLASS(notification_parent_class)->dispose(object);
+}
+
+Notification*
+notification_new(void)
+{
+ return NOTIFICATION(g_object_new(NOTIFICATION_TYPE, NULL));
+}
+
+Notification*
+notification_new_from_dbus_message(GDBusMessage *message)
+{
+ Notification *self = notification_new();
+
+ GVariant *body = g_dbus_message_get_body(message);
+ GVariant *child = NULL;
+ g_assert(g_variant_is_of_type(body, G_VARIANT_TYPE_TUPLE));
+ g_assert(g_variant_n_children(body) == COLUMN_COUNT);
+
+ /* app_name */
+ child = g_variant_get_child_value(body, COLUMN_APP_NAME);
+ g_assert(g_variant_is_of_type(child, G_VARIANT_TYPE_STRING));
+ self->priv->app_name = g_variant_dup_string(child,
+ &(self->priv->app_name_length));
+
+ /* replaces_id */
+ child = g_variant_get_child_value(body, COLUMN_REPLACES_ID);
+ g_assert(g_variant_is_of_type(child, G_VARIANT_TYPE_UINT32));
+ self->priv->replaces_id = g_variant_get_uint32(child);
+
+ /* app_icon */
+ child = g_variant_get_child_value(body, COLUMN_APP_ICON);
+ g_assert(g_variant_is_of_type(child, G_VARIANT_TYPE_STRING));
+ self->priv->app_icon = g_variant_dup_string(child,
+ &(self->priv->app_icon_length));
+
+ /* summary */
+ child = g_variant_get_child_value(body, COLUMN_SUMMARY);
+ g_assert(g_variant_is_of_type(child, G_VARIANT_TYPE_STRING));
+ self->priv->summary = g_variant_dup_string(child,
+ &(self->priv->summary_length));
+
+ /* body */
+ child = g_variant_get_child_value(body, COLUMN_BODY);
+ g_assert(g_variant_is_of_type(child, G_VARIANT_TYPE_STRING));
+ self->priv->body = g_variant_dup_string(child,
+ &(self->priv->body_length));
+
+ child = NULL;
+
+ return self;
+}
+
+void
+notification_print(Notification *self)
+{
+ g_print("app_name = %s\n", self->priv->app_name);
+ g_print("app_icon = %s\n", self->priv->app_icon);
+ g_print("summary = %s\n", self->priv->summary);
+ g_print("body = %s\n", self->priv->body);
+}
diff --git a/src/notification.h b/src/notification.h
new file mode 100644
index 0000000..d31a76d
--- /dev/null
+++ b/src/notification.h
@@ -0,0 +1,62 @@
+/*
+ * notification.h - A gobject subclass to represent a org.freedesktop.Notification.Notify message.
+ */
+
+#ifndef __NOTIFICATION_H__
+#define __NOTIFICATION_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define NOTIFICATION_TYPE (notification_get_type ())
+#define NOTIFICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NOTIFICATION_TYPE, Notification))
+#define NOTIFICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NOTIFICATION_TYPE, NotificationClass))
+#define IS_NOTIFICATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NOTIFICATION_TYPE))
+#define IS_NOTIFICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NOTIFICATION_TYPE))
+
+typedef struct _Notification Notification;
+typedef struct _NotificationClass NotificationClass;
+typedef struct _NotificationPrivate NotificationPrivate;
+
+struct _Notification
+{
+ GObject parent;
+ NotificationPrivate *priv;
+};
+
+struct _NotificationClass
+{
+ GObjectClass parent_class;
+};
+
+struct _NotificationPrivate {
+ gchar *app_name;
+ gsize app_name_length;
+ guint32 replaces_id;
+ gchar *app_icon;
+ gsize app_icon_length;
+ gchar *summary;
+ gsize summary_length;
+ gchar *body;
+ gsize body_length;
+ gint expire_timeout;
+};
+
+#define NOTIFICATION_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), NOTIFICATION_TYPE, NotificationPrivate))
+
+GType notification_get_type(void);
+Notification *notification_new(void);
+Notification *notification_new_from_dbus_message(GDBusMessage *);
+gchar *notification_get_app_name(Notification *);
+gchar *notification_get_app_icon(Notification *);
+gchar *notification_get_summary(Notification *);
+gchar *notification_get_body(Notification *);
+void notification_print(Notification *);
+
+G_END_DECLS
+
+#endif /* __NOTIFICATION_H__ */