/* * dbus-spy.c - A gobject subclass to watch dbus for org.freedesktop.Notification.Notify messages. */ #include "dbus-spy.h" enum { MESSAGE_RECEIVED, LAST_SIGNAL }; typedef struct _IdleMessage IdleMessage; struct _IdleMessage { DBusSpy *spy; Notification *note; }; 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); static gboolean idle_message_emit(gpointer user_data); #define MATCH_STRING "eavesdrop=true,type='method_call',interface='org.freedesktop.Notifications',member='Notify'" G_DEFINE_TYPE_WITH_PRIVATE(DBusSpy, dbus_spy, G_TYPE_OBJECT); static void dbus_spy_class_init(DBusSpyClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); object_class->dispose = dbus_spy_dispose; signals[MESSAGE_RECEIVED] = g_signal_new(DBUS_SPY_SIGNAL_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); IdleMessage *im = g_new0(IdleMessage, 1); im->spy = spy; im->note = note; g_idle_add(idle_message_emit, im); g_object_unref(message); message = NULL; } return message; } static gboolean idle_message_emit(gpointer user_data) { IdleMessage *message = (IdleMessage *)user_data; g_signal_emit(message->spy, signals[MESSAGE_RECEIVED], 0, message->note); g_free(message); return FALSE; } static void dbus_spy_init(DBusSpy *self) { self->priv = dbus_spy_get_instance_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)); }