aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore12
-rw-r--r--Makefile.am1
-rw-r--r--Makefile.am.marshal4
-rw-r--r--configure.ac1
-rw-r--r--debian/changelog49
-rw-r--r--example/Makefile.am19
-rw-r--r--example/simple-client.c41
-rw-r--r--src/Makefile.am17
-rw-r--r--src/custom-service-appstore.c300
-rw-r--r--src/custom-service-appstore.h40
-rw-r--r--src/custom-service-marshal.list1
-rw-r--r--src/custom-service-watcher.c183
-rw-r--r--src/custom-service-watcher.h40
-rw-r--r--src/custom-service.c52
-rw-r--r--src/custom-service.xml24
-rw-r--r--src/dbus-properties.xml23
-rw-r--r--src/dbus-shared.h10
-rw-r--r--src/indicator-custom.c306
-rw-r--r--src/libcustomindicator/custom-indicator.c51
-rw-r--r--src/notification-watcher.xml3
-rw-r--r--tests/Makefile.am19
-rw-r--r--tests/test-simple-app.c32
22 files changed, 1199 insertions, 29 deletions
diff --git a/.bzrignore b/.bzrignore
index 05932c9..672bbfe 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -24,4 +24,16 @@ tests/test-libcustomindicator-dbus-client
tests/test-libcustomindicator-dbus-server
tests/libcustomindicator-tests
tests/test-libcustomindicator-dbus
+src/custom-service-client.h
+src/custom-service-server.h
+src/custom-service-marshal.c
+src/custom-service-marshal.h
+src/stamp-marshal
+src/dbus-properties-client.h
+src/dbus-properties-server.h
+tests/test-simple-app
+example/.deps
+example/.libs
+example/simple-client
+src/libcustom_la-custom-service-marshal.lo
tests/libcustomindicator-tests-gtester
diff --git a/Makefile.am b/Makefile.am
index 4eb69d7..33f9f3e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,5 +1,6 @@
SUBDIRS = data \
src \
+ example \
tests
DISTCHECK_CONFIGURE_FLAGS = --enable-localinstall
diff --git a/Makefile.am.marshal b/Makefile.am.marshal
index 8b30d4f..a6ab024 100644
--- a/Makefile.am.marshal
+++ b/Makefile.am.marshal
@@ -26,7 +26,7 @@ stamp-marshal: $(glib_marshal_list)
$(QUIET_GEN)$(GLIB_GENMARSHAL) \
--prefix=$(glib_marshal_prefix) \
--header \
- $(glib_marshal_list) > xgen-mh \
+ $(srcdir)/$(glib_marshal_list) > xgen-mh \
&& (cmp -s xgen-mh $(marshal_h) || cp -f xgen-mh $(marshal_h)) \
&& rm -f xgen-mh \
&& echo timestamp > $(@F)
@@ -39,7 +39,7 @@ $(marshal_c): $(marshal_h)
$(GLIB_GENMARSHAL) \
--prefix=$(glib_marshal_prefix) \
--body \
- $(glib_marshal_list)) > xgen-mc \
+ $(srcdir)/$(glib_marshal_list)) > xgen-mc \
&& cp xgen-mc $(marshal_c) \
&& rm -f xgen-mc
diff --git a/configure.ac b/configure.ac
index 015677c..d76aec8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -81,6 +81,7 @@ Makefile
src/Makefile
data/Makefile
tests/Makefile
+example/Makefile
])
###########################
diff --git a/debian/changelog b/debian/changelog
index 146e584..011f168 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,52 @@
+indicator-custom (0.0.1-0ubuntu3~ppa1~service8) karmic; urgency=low
+
+ * Sync to trunk and fix distcheck by delaying connecting to
+ the service by checking for data.
+
+ -- Ted Gould <ted@ubuntu.com> Tue, 24 Nov 2009 17:08:34 -0600
+
+indicator-custom (0.0.1-0ubuntu3~ppa1~service7) karmic; urgency=low
+
+ * More updates and getting kinda stable-ish
+
+ -- Ted Gould <ted@ubuntu.com> Tue, 24 Nov 2009 16:31:53 -0600
+
+indicator-custom (0.0.1-0ubuntu3~ppa1~service6) karmic; urgency=low
+
+ * Debug messages
+
+ -- Ted Gould <ted@ubuntu.com> Mon, 23 Nov 2009 17:39:18 -0600
+
+indicator-custom (0.0.1-0ubuntu3~ppa1~service5) karmic; urgency=low
+
+ * Forgot return types.
+
+ -- Ted Gould <ted@ubuntu.com> Mon, 23 Nov 2009 16:52:16 -0600
+
+indicator-custom (0.0.1-0ubuntu3~ppa1~service4) karmic; urgency=low
+
+ * Adding marshallers
+
+ -- Ted Gould <ted@ubuntu.com> Mon, 23 Nov 2009 16:37:51 -0600
+
+indicator-custom (0.0.1-0ubuntu3~ppa1~service3) karmic; urgency=low
+
+ * Adding more fun. Now we're cookin'!
+
+ -- Ted Gould <ted@ubuntu.com> Mon, 23 Nov 2009 14:56:35 -0600
+
+indicator-custom (0.0.1-0ubuntu3~ppa1~service2) karmic; urgency=low
+
+ * Building the service and getting things in better shape.
+
+ -- Ted Gould <ted@ubuntu.com> Sun, 08 Nov 2009 23:04:48 -0600
+
+indicator-custom (0.0.1-0ubuntu3~ppa1~service1) karmic; urgency=low
+
+ * Grabbing from the service development branch
+
+ -- Ted Gould <ted@ubuntu.com> Mon, 02 Nov 2009 21:56:33 -0600
+
indicator-custom (0.0.1-0ubuntu3~ppa1) karmic; urgency=low
* Flesh out the library to have stuff working.
diff --git a/example/Makefile.am b/example/Makefile.am
new file mode 100644
index 0000000..954b04e
--- /dev/null
+++ b/example/Makefile.am
@@ -0,0 +1,19 @@
+
+check_PROGRAMS = \
+ simple-client
+
+#########################################
+## simple-client
+#########################################
+
+simple_client_SOURCES = \
+ simple-client.c
+
+simple_client_CFLAGS = \
+ $(INDICATOR_CFLAGS) \
+ -Wall -Werror \
+ -I$(top_srcdir)/src
+
+simple_client_LDADD = \
+ $(INDICATOR_LIBS) \
+ $(top_builddir)/src/libcustomindicator.la
diff --git a/example/simple-client.c b/example/simple-client.c
new file mode 100644
index 0000000..f1f53e1
--- /dev/null
+++ b/example/simple-client.c
@@ -0,0 +1,41 @@
+
+#include "libcustomindicator/custom-indicator.h"
+#include "libdbusmenu-glib/server.h"
+#include "libdbusmenu-glib/menuitem.h"
+
+GMainLoop * mainloop = NULL;
+
+int
+main (int argc, char ** argv)
+{
+ g_type_init();
+
+ CustomIndicator * ci = CUSTOM_INDICATOR(g_object_new(CUSTOM_INDICATOR_TYPE, NULL));
+ g_assert(ci != NULL);
+
+ custom_indicator_set_id(ci, "example-simple-client");
+ custom_indicator_set_category(ci, CUSTOM_INDICATOR_CATEGORY_APPLICATION_STATUS);
+ custom_indicator_set_status(ci, CUSTOM_INDICATOR_STATUS_ACTIVE);
+ custom_indicator_set_icon(ci, "indicator-messages");
+ custom_indicator_set_attention_icon(ci, "indicator-messages-new");
+
+ DbusmenuMenuitem * root = dbusmenu_menuitem_new();
+
+ DbusmenuMenuitem * item = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, "Item 1");
+ dbusmenu_menuitem_child_append(root, item);
+
+ item = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, "Item 2");
+ dbusmenu_menuitem_child_append(root, item);
+
+ DbusmenuServer * menuservice = dbusmenu_server_new ("/need/a/menu/path");
+ dbusmenu_server_set_root(menuservice, root);
+
+ custom_indicator_set_menu(ci, menuservice);
+
+ mainloop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(mainloop);
+
+ return 0;
+}
diff --git a/src/Makefile.am b/src/Makefile.am
index ff5b26e..102804e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,6 +4,7 @@ BUILT_SOURCES =
EXTRA_DIST =
include $(top_srcdir)/Makefile.am.enum
+include $(top_srcdir)/Makefile.am.marshal
##################################
# Indicator
@@ -12,6 +13,8 @@ include $(top_srcdir)/Makefile.am.enum
customlibdir = $(INDICATORDIR)
customlib_LTLIBRARIES = libcustom.la
libcustom_la_SOURCES = \
+ custom-service-marshal.c \
+ dbus-shared.h \
indicator-custom.c
libcustom_la_CFLAGS = $(INDICATOR_CFLAGS) \
-Wall \
@@ -30,6 +33,15 @@ libexec_PROGRAMS = indicator-custom-service
indicator_custom_service_SOURCES = \
custom-service.c \
+ custom-service-appstore.h \
+ custom-service-appstore.c \
+ custom-service-marshal.h \
+ custom-service-marshal.c \
+ custom-service-server.h \
+ custom-service-watcher.h \
+ custom-service-watcher.c \
+ dbus-properties-client.h \
+ dbus-shared.h \
notification-item-client.h \
notification-watcher-server.h
indicator_custom_service_CFLAGS = \
@@ -38,6 +50,9 @@ indicator_custom_service_CFLAGS = \
indicator_custom_service_LDADD = \
$(INDICATOR_LIBS)
+glib_marshal_list = custom-service-marshal.list
+glib_marshal_prefix = _custom_service_marshal
+
##################################
# Library
##################################
@@ -82,6 +97,8 @@ libcustomindicator_la_LIBADD = \
##################################
DBUS_SPECS = \
+ custom-service.xml \
+ dbus-properties.xml \
notification-item.xml \
notification-watcher.xml
diff --git a/src/custom-service-appstore.c b/src/custom-service-appstore.c
new file mode 100644
index 0000000..bdf8ffb
--- /dev/null
+++ b/src/custom-service-appstore.c
@@ -0,0 +1,300 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <dbus/dbus-glib.h>
+#include "custom-service-appstore.h"
+#include "custom-service-marshal.h"
+#include "dbus-properties-client.h"
+#include "dbus-shared.h"
+
+/* DBus Prototypes */
+static gboolean _custom_service_server_get_applications (CustomServiceAppstore * appstore, GArray ** apps);
+
+#include "custom-service-server.h"
+
+#define NOTIFICATION_ITEM_PROP_ID "Id"
+#define NOTIFICATION_ITEM_PROP_CATEGORY "Category"
+#define NOTIFICATION_ITEM_PROP_STATUS "Status"
+#define NOTIFICATION_ITEM_PROP_ICON_NAME "IconName"
+#define NOTIFICATION_ITEM_PROP_AICON_NAME "AttentionIconName"
+#define NOTIFICATION_ITEM_PROP_MENU "Menu"
+
+/* Private Stuff */
+typedef struct _CustomServiceAppstorePrivate CustomServiceAppstorePrivate;
+struct _CustomServiceAppstorePrivate {
+ DBusGConnection * bus;
+ GList * applications;
+};
+
+typedef struct _Application Application;
+struct _Application {
+ gchar * dbus_name;
+ gchar * dbus_object;
+ CustomServiceAppstore * appstore; /* not ref'd */
+ DBusGProxy * dbus_proxy;
+ DBusGProxy * prop_proxy;
+};
+
+#define CUSTOM_SERVICE_APPSTORE_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), CUSTOM_SERVICE_APPSTORE_TYPE, CustomServiceAppstorePrivate))
+
+/* Signals Stuff */
+enum {
+ APPLICATION_ADDED,
+ APPLICATION_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/* GObject stuff */
+static void custom_service_appstore_class_init (CustomServiceAppstoreClass *klass);
+static void custom_service_appstore_init (CustomServiceAppstore *self);
+static void custom_service_appstore_dispose (GObject *object);
+static void custom_service_appstore_finalize (GObject *object);
+
+G_DEFINE_TYPE (CustomServiceAppstore, custom_service_appstore, G_TYPE_OBJECT);
+
+static void
+custom_service_appstore_class_init (CustomServiceAppstoreClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (CustomServiceAppstorePrivate));
+
+ object_class->dispose = custom_service_appstore_dispose;
+ object_class->finalize = custom_service_appstore_finalize;
+
+ signals[APPLICATION_ADDED] = g_signal_new ("application-added",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CustomServiceAppstore, application_added),
+ NULL, NULL,
+ _custom_service_marshal_VOID__STRING_INT_STRING_STRING,
+ G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_NONE);
+ signals[APPLICATION_REMOVED] = g_signal_new ("application-removed",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CustomServiceAppstore, application_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE, 1, G_TYPE_INT, G_TYPE_NONE);
+
+
+ dbus_g_object_type_install_info(CUSTOM_SERVICE_APPSTORE_TYPE,
+ &dbus_glib__custom_service_server_object_info);
+
+ return;
+}
+
+static void
+custom_service_appstore_init (CustomServiceAppstore *self)
+{
+ CustomServiceAppstorePrivate * priv = CUSTOM_SERVICE_APPSTORE_GET_PRIVATE(self);
+
+ priv->applications = NULL;
+
+ GError * error = NULL;
+ priv->bus = dbus_g_bus_get(DBUS_BUS_STARTER, &error);
+ if (error != NULL) {
+ g_error("Unable to get session bus: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ dbus_g_connection_register_g_object(priv->bus,
+ INDICATOR_CUSTOM_DBUS_OBJ,
+ G_OBJECT(self));
+
+ return;
+}
+
+static void
+custom_service_appstore_dispose (GObject *object)
+{
+ CustomServiceAppstorePrivate * priv = CUSTOM_SERVICE_APPSTORE_GET_PRIVATE(object);
+
+ while (priv->applications != NULL) {
+ custom_service_appstore_application_remove(CUSTOM_SERVICE_APPSTORE(object),
+ ((Application *)priv->applications->data)->dbus_name,
+ ((Application *)priv->applications->data)->dbus_object);
+ }
+
+ G_OBJECT_CLASS (custom_service_appstore_parent_class)->dispose (object);
+ return;
+}
+
+static void
+custom_service_appstore_finalize (GObject *object)
+{
+
+ G_OBJECT_CLASS (custom_service_appstore_parent_class)->finalize (object);
+ return;
+}
+
+static void
+get_all_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError * error, gpointer data)
+{
+ if (error != NULL) {
+ g_warning("Unable to get properties: %s", error->message);
+ return;
+ }
+
+ Application * app = (Application *)data;
+
+ if (g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_MENU) == NULL ||
+ g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_ICON_NAME) == NULL) {
+ g_warning("Notification Item on object %s of %s doesn't have enough properties.", app->dbus_object, app->dbus_name);
+ g_free(app); // Need to do more than this, but it gives the idea of the flow we're going for.
+ return;
+ }
+
+ CustomServiceAppstorePrivate * priv = CUSTOM_SERVICE_APPSTORE_GET_PRIVATE(app->appstore);
+ priv->applications = g_list_prepend(priv->applications, app);
+
+ /* TODO: We need to have the position determined better. This
+ would involve looking at the name and category and sorting
+ it with the other entries. */
+
+ g_signal_emit(G_OBJECT(app->appstore),
+ signals[APPLICATION_ADDED], 0,
+ g_value_get_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_ICON_NAME)),
+ 0, /* Position */
+ app->dbus_name,
+ g_value_get_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_MENU)),
+ TRUE);
+
+ return;
+}
+
+/* A simple global function for dealing with freeing the information
+ in an Application structure */
+static void
+application_free (Application * app)
+{
+ if (app == NULL) return;
+
+ if (app->dbus_name != NULL) {
+ g_free(app->dbus_name);
+ }
+ if (app->dbus_object != NULL) {
+ g_free(app->dbus_object);
+ }
+
+ g_free(app);
+ return;
+}
+
+/* Gets called when the proxy is destroyed, which is usually when it
+ drops off of the bus. */
+static void
+application_removed_cb (DBusGProxy * proxy, gpointer userdata)
+{
+ Application * app = (Application *)userdata;
+ CustomServiceAppstore * appstore = app->appstore;
+ CustomServiceAppstorePrivate * priv = CUSTOM_SERVICE_APPSTORE_GET_PRIVATE(appstore);
+
+ GList * applistitem = g_list_find(priv->applications, app);
+ if (applistitem == NULL) {
+ g_warning("Removing an application that isn't in the application list?");
+ return;
+ }
+
+ gint position = g_list_position(priv->applications, applistitem);
+
+ g_signal_emit(G_OBJECT(appstore),
+ signals[APPLICATION_REMOVED], 0,
+ position, TRUE);
+
+ priv->applications = g_list_remove(priv->applications, app);
+
+ application_free(app);
+ return;
+}
+
+/* Adding a new NotificationItem object from DBus in to the
+ appstore. First, we need to get the information on it
+ though. */
+void
+custom_service_appstore_application_add (CustomServiceAppstore * appstore, const gchar * dbus_name, const gchar * dbus_object)
+{
+ g_debug("Adding new application: %s:%s", dbus_name, dbus_object);
+
+ /* Make sure we got a sensible request */
+ g_return_if_fail(IS_CUSTOM_SERVICE_APPSTORE(appstore));
+ g_return_if_fail(dbus_name != NULL && dbus_name[0] != '\0');
+ g_return_if_fail(dbus_object != NULL && dbus_object[0] != '\0');
+ CustomServiceAppstorePrivate * priv = CUSTOM_SERVICE_APPSTORE_GET_PRIVATE(appstore);
+
+ /* Build the application entry. This will be carried
+ along until we're sure we've got everything. */
+ Application * app = g_new(Application, 1);
+
+ app->dbus_name = g_strdup(dbus_name);
+ app->dbus_object = g_strdup(dbus_object);
+ app->appstore = appstore;
+
+ /* Get the DBus proxy for the NotificationItem interface */
+ GError * error = NULL;
+ app->dbus_proxy = dbus_g_proxy_new_for_name_owner(priv->bus,
+ app->dbus_name,
+ app->dbus_object,
+ NOTIFICATION_ITEM_DBUS_IFACE,
+ &error);
+
+ if (error != NULL) {
+ g_warning("Unable to get notification item proxy for object '%s' on host '%s': %s", dbus_object, dbus_name, error->message);
+ g_error_free(error);
+ g_free(app);
+ return;
+ }
+
+ /* We've got it, let's watch it for destruction */
+ g_signal_connect(G_OBJECT(app->dbus_proxy), "destroy", G_CALLBACK(application_removed_cb), app);
+
+ /* Grab the property proxy interface */
+ app->prop_proxy = dbus_g_proxy_new_for_name_owner(priv->bus,
+ app->dbus_name,
+ app->dbus_object,
+ DBUS_INTERFACE_PROPERTIES,
+ &error);
+
+ if (error != NULL) {
+ g_warning("Unable to get property proxy for object '%s' on host '%s': %s", dbus_object, dbus_name, error->message);
+ g_error_free(error);
+ g_object_unref(app->dbus_proxy);
+ g_free(app);
+ return;
+ }
+
+ /* Get all the propertiees */
+ org_freedesktop_DBus_Properties_get_all_async(app->prop_proxy,
+ NOTIFICATION_ITEM_DBUS_IFACE,
+ get_all_properties_cb,
+ app);
+
+ /* We're returning, nothing is yet added until the properties
+ come back and give us more info. */
+ return;
+}
+
+void
+custom_service_appstore_application_remove (CustomServiceAppstore * appstore, const gchar * dbus_name, const gchar * dbus_object)
+{
+ g_return_if_fail(IS_CUSTOM_SERVICE_APPSTORE(appstore));
+ g_return_if_fail(dbus_name != NULL && dbus_name[0] != '\0');
+ g_return_if_fail(dbus_object != NULL && dbus_object[0] != '\0');
+
+
+ return;
+}
+
+/* DBus Interface */
+static gboolean
+_custom_service_server_get_applications (CustomServiceAppstore * appstore, GArray ** apps)
+{
+
+ return FALSE;
+}
+
diff --git a/src/custom-service-appstore.h b/src/custom-service-appstore.h
new file mode 100644
index 0000000..7263617
--- /dev/null
+++ b/src/custom-service-appstore.h
@@ -0,0 +1,40 @@
+#ifndef __CUSTOM_SERVICE_APPSTORE_H__
+#define __CUSTOM_SERVICE_APPSTORE_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define CUSTOM_SERVICE_APPSTORE_TYPE (custom_service_appstore_get_type ())
+#define CUSTOM_SERVICE_APPSTORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CUSTOM_SERVICE_APPSTORE_TYPE, CustomServiceAppstore))
+#define CUSTOM_SERVICE_APPSTORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CUSTOM_SERVICE_APPSTORE_TYPE, CustomServiceAppstoreClass))
+#define IS_CUSTOM_SERVICE_APPSTORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CUSTOM_SERVICE_APPSTORE_TYPE))
+#define IS_CUSTOM_SERVICE_APPSTORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CUSTOM_SERVICE_APPSTORE_TYPE))
+#define CUSTOM_SERVICE_APPSTORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CUSTOM_SERVICE_APPSTORE_TYPE, CustomServiceAppstoreClass))
+
+typedef struct _CustomServiceAppstore CustomServiceAppstore;
+typedef struct _CustomServiceAppstoreClass CustomServiceAppstoreClass;
+
+struct _CustomServiceAppstoreClass {
+ GObjectClass parent_class;
+};
+
+struct _CustomServiceAppstore {
+ GObject parent;
+
+ void (*application_added) (CustomServiceAppstore * appstore, gchar *, gint, gchar *, gchar *, gpointer);
+ void (*application_removed) (CustomServiceAppstore * appstore, gint, gpointer);
+};
+
+GType custom_service_appstore_get_type (void);
+void custom_service_appstore_application_add (CustomServiceAppstore * appstore,
+ const gchar * dbus_name,
+ const gchar * dbus_object);
+void custom_service_appstore_application_remove (CustomServiceAppstore * appstore,
+ const gchar * dbus_name,
+ const gchar * dbus_object);
+
+G_END_DECLS
+
+#endif
diff --git a/src/custom-service-marshal.list b/src/custom-service-marshal.list
new file mode 100644
index 0000000..4056f53
--- /dev/null
+++ b/src/custom-service-marshal.list
@@ -0,0 +1 @@
+VOID: STRING, INT, STRING, STRING
diff --git a/src/custom-service-watcher.c b/src/custom-service-watcher.c
new file mode 100644
index 0000000..7cc15ee
--- /dev/null
+++ b/src/custom-service-watcher.c
@@ -0,0 +1,183 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include "custom-service-watcher.h"
+#include "dbus-shared.h"
+
+static gboolean _notification_watcher_server_register_service (CustomServiceWatcher * appwatcher, const gchar * service, DBusGMethodInvocation * method);
+static gboolean _notification_watcher_server_registered_services (CustomServiceWatcher * appwatcher, GArray ** apps);
+static gboolean _notification_watcher_server_protocol_version (CustomServiceWatcher * appwatcher, char ** version);
+static gboolean _notification_watcher_server_register_notification_host (CustomServiceWatcher * appwatcher, const gchar * host);
+static gboolean _notification_watcher_server_is_notification_host_registered (CustomServiceWatcher * appwatcher, gboolean * haveHost);
+
+#include "notification-watcher-server.h"
+
+/* Private Stuff */
+typedef struct _CustomServiceWatcherPrivate CustomServiceWatcherPrivate;
+struct _CustomServiceWatcherPrivate {
+ CustomServiceAppstore * appstore;
+};
+
+#define CUSTOM_SERVICE_WATCHER_GET_PRIVATE(o) \
+(G_TYPE_INSTANCE_GET_PRIVATE ((o), CUSTOM_SERVICE_WATCHER_TYPE, CustomServiceWatcherPrivate))
+
+/* Signals Stuff */
+enum {
+ SERVICE_REGISTERED,
+ SERVICE_UNREGISTERED,
+ NOTIFICATION_HOST_REGISTERED,
+ NOTIFICATION_HOST_UNREGISTERED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/* GObject stuff */
+static void custom_service_watcher_class_init (CustomServiceWatcherClass *klass);
+static void custom_service_watcher_init (CustomServiceWatcher *self);
+static void custom_service_watcher_dispose (GObject *object);
+static void custom_service_watcher_finalize (GObject *object);
+
+G_DEFINE_TYPE (CustomServiceWatcher, custom_service_watcher, G_TYPE_OBJECT);
+
+static void
+custom_service_watcher_class_init (CustomServiceWatcherClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (CustomServiceWatcherPrivate));
+
+ object_class->dispose = custom_service_watcher_dispose;
+ object_class->finalize = custom_service_watcher_finalize;
+
+ signals[SERVICE_REGISTERED] = g_signal_new ("service-registered",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CustomServiceWatcherClass, service_registered),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING, G_TYPE_NONE);
+ signals[SERVICE_UNREGISTERED] = g_signal_new ("service-unregistered",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CustomServiceWatcherClass, service_unregistered),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING, G_TYPE_NONE);
+ signals[NOTIFICATION_HOST_REGISTERED] = g_signal_new ("notification-host-registered",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CustomServiceWatcherClass, notification_host_registered),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0, G_TYPE_NONE);
+ signals[NOTIFICATION_HOST_UNREGISTERED] = g_signal_new ("notification-host-unregistered",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CustomServiceWatcherClass, notification_host_unregistered),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0, G_TYPE_NONE);
+
+ dbus_g_object_type_install_info(CUSTOM_SERVICE_WATCHER_TYPE,
+ &dbus_glib__notification_watcher_server_object_info);
+
+ return;
+}
+
+static void
+custom_service_watcher_init (CustomServiceWatcher *self)
+{
+ CustomServiceWatcherPrivate * priv = CUSTOM_SERVICE_WATCHER_GET_PRIVATE(self);
+
+ priv->appstore = NULL;
+
+ GError * error = NULL;
+ DBusGConnection * session_bus = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+ if (error != NULL) {
+ g_error("Unable to get session bus: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ dbus_g_connection_register_g_object(session_bus,
+ NOTIFICATION_WATCHER_DBUS_OBJ,
+ G_OBJECT(self));
+
+ return;
+}
+
+static void
+custom_service_watcher_dispose (GObject *object)
+{
+ CustomServiceWatcherPrivate * priv = CUSTOM_SERVICE_WATCHER_GET_PRIVATE(object);
+
+ if (priv->appstore != NULL) {
+ g_object_unref(G_OBJECT(priv->appstore));
+ priv->appstore = NULL;
+ }
+
+ G_OBJECT_CLASS (custom_service_watcher_parent_class)->dispose (object);
+ return;
+}
+
+static void
+custom_service_watcher_finalize (GObject *object)
+{
+
+ G_OBJECT_CLASS (custom_service_watcher_parent_class)->finalize (object);
+ return;
+}
+
+CustomServiceWatcher *
+custom_service_watcher_new (CustomServiceAppstore * appstore)
+{
+ GObject * obj = g_object_new(CUSTOM_SERVICE_WATCHER_TYPE, NULL);
+ CustomServiceWatcherPrivate * priv = CUSTOM_SERVICE_WATCHER_GET_PRIVATE(obj);
+ priv->appstore = appstore;
+ g_object_ref(G_OBJECT(priv->appstore));
+ return CUSTOM_SERVICE_WATCHER(obj);
+}
+
+static gboolean
+_notification_watcher_server_register_service (CustomServiceWatcher * appwatcher, const gchar * service, DBusGMethodInvocation * method)
+{
+ CustomServiceWatcherPrivate * priv = CUSTOM_SERVICE_WATCHER_GET_PRIVATE(appwatcher);
+
+ custom_service_appstore_application_add(priv->appstore, dbus_g_method_get_sender(method), service);
+
+ dbus_g_method_return(method, G_TYPE_NONE);
+ return TRUE;
+}
+
+static gboolean
+_notification_watcher_server_registered_services (CustomServiceWatcher * appwatcher, GArray ** apps)
+{
+
+ return FALSE;
+}
+
+static gboolean
+_notification_watcher_server_protocol_version (CustomServiceWatcher * appwatcher, char ** version)
+{
+ *version = g_strdup("Ayatana Version 1");
+ return TRUE;
+}
+
+static gboolean
+_notification_watcher_server_register_notification_host (CustomServiceWatcher * appwatcher, const gchar * host)
+{
+
+ return FALSE;
+}
+
+static gboolean
+_notification_watcher_server_is_notification_host_registered (CustomServiceWatcher * appwatcher, gboolean * haveHost)
+{
+ *haveHost = TRUE;
+ return TRUE;
+}
+
diff --git a/src/custom-service-watcher.h b/src/custom-service-watcher.h
new file mode 100644
index 0000000..1a037be
--- /dev/null
+++ b/src/custom-service-watcher.h
@@ -0,0 +1,40 @@
+#ifndef __CUSTOM_SERVICE_WATCHER_H__
+#define __CUSTOM_SERVICE_WATCHER_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "custom-service-appstore.h"
+
+G_BEGIN_DECLS
+
+#define CUSTOM_SERVICE_WATCHER_TYPE (custom_service_watcher_get_type ())
+#define CUSTOM_SERVICE_WATCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CUSTOM_SERVICE_WATCHER_TYPE, CustomServiceWatcher))
+#define CUSTOM_SERVICE_WATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CUSTOM_SERVICE_WATCHER_TYPE, CustomServiceWatcherClass))
+#define IS_CUSTOM_SERVICE_WATCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CUSTOM_SERVICE_WATCHER_TYPE))
+#define IS_CUSTOM_SERVICE_WATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CUSTOM_SERVICE_WATCHER_TYPE))
+#define CUSTOM_SERVICE_WATCHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CUSTOM_SERVICE_WATCHER_TYPE, CustomServiceWatcherClass))
+
+typedef struct _CustomServiceWatcher CustomServiceWatcher;
+typedef struct _CustomServiceWatcherClass CustomServiceWatcherClass;
+
+struct _CustomServiceWatcherClass {
+ GObjectClass parent_class;
+
+ /* Signals */
+ void (*service_registered) (CustomServiceWatcher * watcher, gchar * object, gpointer data);
+ void (*service_unregistered) (CustomServiceWatcher * watcher, gchar * object, gpointer data);
+ void (*notification_host_registered) (CustomServiceWatcher * watcher, gpointer data);
+ void (*notification_host_unregistered) (CustomServiceWatcher * watcher, gpointer data);
+};
+
+struct _CustomServiceWatcher {
+ GObject parent;
+};
+
+GType custom_service_watcher_get_type (void);
+CustomServiceWatcher * custom_service_watcher_new (CustomServiceAppstore * appstore);
+
+G_END_DECLS
+
+#endif
diff --git a/src/custom-service.c b/src/custom-service.c
index d96a9de..5bd9b96 100644
--- a/src/custom-service.c
+++ b/src/custom-service.c
@@ -1,16 +1,56 @@
+
+#include "libindicator/indicator-service.h"
#include "notification-item-client.h"
+#include "custom-service-appstore.h"
+#include "custom-service-watcher.h"
+#include "dbus-shared.h"
-void _notification_watcher_server_register_service (void) { };
-void _notification_watcher_server_registered_services (void) { };
-void _notification_watcher_server_protocol_version (void) { };
-void _notification_watcher_server_register_notification_host (void) { };
-void _notification_watcher_server_is_notification_host_registered (void) { };
+/* The base main loop */
+static GMainLoop * mainloop = NULL;
+/* Where the application registry lives */
+static CustomServiceAppstore * appstore = NULL;
+/* Interface for applications */
+static CustomServiceWatcher * watcher = NULL;
+/* The service management interface */
+static IndicatorService * service = NULL;
-#include "notification-watcher-server.h"
+/* Recieves the disonnection signal from the service
+ object and closes the mainloop. */
+static void
+service_disconnected (IndicatorService * service, gpointer data)
+{
+ g_debug("Service disconnected");
+ if (mainloop != NULL) {
+ g_main_loop_quit(mainloop);
+ }
+ return;
+}
+/* Builds up the core objects and puts us spinning into
+ a main loop. */
int
main (int argc, char ** argv)
{
+ g_type_init();
+
+ /* Bring us up as a basic indicator service */
+ service = indicator_service_new(INDICATOR_CUSTOM_DBUS_ADDR);
+ g_signal_connect(G_OBJECT(service), "disconnected", G_CALLBACK(service_disconnected), NULL);
+
+ /* Building our app store */
+ appstore = CUSTOM_SERVICE_APPSTORE(g_object_new(CUSTOM_SERVICE_APPSTORE_TYPE, NULL));
+
+ /* Adding a watcher for the Apps coming up */
+ watcher = custom_service_watcher_new(appstore);
+
+ /* Building and executing our main loop */
+ mainloop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(mainloop);
+
+ /* Unref'ing all the objects */
+ g_object_unref(G_OBJECT(watcher));
+ g_object_unref(G_OBJECT(appstore));
+ g_object_unref(G_OBJECT(service));
return 0;
}
diff --git a/src/custom-service.xml b/src/custom-service.xml
new file mode 100644
index 0000000..3d943a2
--- /dev/null
+++ b/src/custom-service.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/">
+ <interface name="org.ayatana.indicator.custom.service">
+<!-- Properties -->
+ <!-- None currently -->
+
+<!-- Methods -->
+ <method name="GetApplications">
+ <arg type="a(siso)" name="applications" direction="out" />
+ </method>
+
+<!-- Signals -->
+ <signal name="ApplicationAdded">
+ <arg type="s" name="iconname" direction="out" />
+ <arg type="i" name="position" direction="out" />
+ <arg type="s" name="dbusaddress" direction="out" />
+ <arg type="o" name="dbusobject" direction="out" />
+ </signal>
+ <signal name="ApplicationRemoved">
+ <arg type="i" name="position" direction="out" />
+ </signal>
+
+ </interface>
+</node>
diff --git a/src/dbus-properties.xml b/src/dbus-properties.xml
new file mode 100644
index 0000000..c172895
--- /dev/null
+++ b/src/dbus-properties.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/">
+ <interface name="org.freedesktop.DBus.Properties">
+
+ <method name="Get">
+ <arg direction="in" type="s" name="Interface_Name"/>
+ <arg direction="in" type="s" name="Property_Name"/>
+ <arg direction="out" type="v" name="Value"/>
+ </method>
+
+ <method name="Set">
+ <arg direction="in" type="s" name="Interface_Name"/>
+ <arg direction="in" type="s" name="Property_Name"/>
+ <arg direction="in" type="v" name="Value"/>
+ </method>
+
+ <method name="GetAll">
+ <arg direction="in" type="s" name="Interface_Name"/>
+ <arg direction="out" type="a{sv}" name="Properties"/>
+ </method>
+
+ </interface>
+</node>
diff --git a/src/dbus-shared.h b/src/dbus-shared.h
new file mode 100644
index 0000000..364ac46
--- /dev/null
+++ b/src/dbus-shared.h
@@ -0,0 +1,10 @@
+
+#define INDICATOR_CUSTOM_DBUS_ADDR "org.ayatana.indicator.custom"
+#define INDICATOR_CUSTOM_DBUS_OBJ "/org/ayatana/indicator/custom/service"
+#define INDICATOR_CUSTOM_DBUS_IFACE "org.ayatana.indicator.custom.service"
+
+#define NOTIFICATION_WATCHER_DBUS_OBJ "/org/ayatana/indicator/custom/NotificationWatcher"
+#define NOTIFICATION_WATCHER_DBUS_IFACE "org.ayatana.indicator.custom.NotificationWatcher"
+
+#define NOTIFICATION_ITEM_DBUS_IFACE "org.ayatana.indicator.custom.NotificationItem"
+
diff --git a/src/indicator-custom.c b/src/indicator-custom.c
index 1a09a9a..da89c30 100644
--- a/src/indicator-custom.c
+++ b/src/indicator-custom.c
@@ -1,28 +1,304 @@
-#include "libindicator/indicator.h"
+/* G Stuff */
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+/* DBus Stuff */
+#include <dbus/dbus-glib.h>
+#include <libdbusmenu-gtk/menu.h>
+
+/* Indicator Stuff */
+#include <libindicator/indicator.h>
+#include <libindicator/indicator-object.h>
+#include <libindicator/indicator-service-manager.h>
+
+/* Local Stuff */
+#include "dbus-shared.h"
+#include "custom-service-client.h"
+#include "custom-service-marshal.h"
+
+#define INDICATOR_CUSTOM_TYPE (indicator_custom_get_type ())
+#define INDICATOR_CUSTOM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_CUSTOM_TYPE, IndicatorCustom))
+#define INDICATOR_CUSTOM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_CUSTOM_TYPE, IndicatorCustomClass))
+#define IS_INDICATOR_CUSTOM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_CUSTOM_TYPE))
+#define IS_INDICATOR_CUSTOM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_CUSTOM_TYPE))
+#define INDICATOR_CUSTOM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_CUSTOM_TYPE, IndicatorCustomClass))
+
+typedef struct _IndicatorCustom IndicatorCustom;
+typedef struct _IndicatorCustomClass IndicatorCustomClass;
+
+struct _IndicatorCustomClass {
+ IndicatorObjectClass parent_class;
+};
+
+struct _IndicatorCustom {
+ IndicatorObject parent;
+};
+
+GType indicator_custom_get_type (void);
INDICATOR_SET_VERSION
-INDICATOR_SET_NAME("indicator-custom")
+INDICATOR_SET_TYPE(INDICATOR_CUSTOM_TYPE)
+
-GtkLabel *
-get_label (void)
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+typedef struct _IndicatorCustomPrivate IndicatorCustomPrivate;
+struct _IndicatorCustomPrivate {
+ IndicatorServiceManager * sm;
+ DBusGConnection * bus;
+ DBusGProxy * service_proxy;
+ GList * applications;
+};
+
+typedef struct _ApplicationEntry ApplicationEntry;
+struct _ApplicationEntry {
+ IndicatorObjectEntry entry;
+};
+
+#define INDICATOR_CUSTOM_GET_PRIVATE(o) \
+(G_TYPE_INSTANCE_GET_PRIVATE ((o), INDICATOR_CUSTOM_TYPE, IndicatorCustomPrivate))
+
+static void indicator_custom_class_init (IndicatorCustomClass *klass);
+static void indicator_custom_init (IndicatorCustom *self);
+static void indicator_custom_dispose (GObject *object);
+static void indicator_custom_finalize (GObject *object);
+static GList * get_entries (IndicatorObject * io);
+static void connected (IndicatorServiceManager * sm, gboolean connected, IndicatorCustom * custom);
+static void application_added (DBusGProxy * proxy, const gchar * iconname, gint position, const gchar * dbusaddress, const gchar * dbusobject, IndicatorCustom * custom);
+static void application_removed (DBusGProxy * proxy, gint position , IndicatorCustom * custom);
+static void get_applications (DBusGProxy *proxy, GPtrArray *OUT_applications, GError *error, gpointer userdata);
+
+G_DEFINE_TYPE (IndicatorCustom, indicator_custom, INDICATOR_OBJECT_TYPE);
+
+static void
+indicator_custom_class_init (IndicatorCustomClass *klass)
{
- return NULL;
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (IndicatorCustomPrivate));
+
+ object_class->dispose = indicator_custom_dispose;
+ object_class->finalize = indicator_custom_finalize;
+
+ IndicatorObjectClass * io_class = INDICATOR_OBJECT_CLASS(klass);
+
+ io_class->get_entries = get_entries;
+
+ dbus_g_object_register_marshaller(_custom_service_marshal_VOID__STRING_INT_STRING_STRING,
+ G_TYPE_NONE,
+ G_TYPE_STRING,
+ G_TYPE_INT,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
+
+ return;
}
-GtkImage *
-get_icon (void)
+static void
+indicator_custom_init (IndicatorCustom *self)
{
- return GTK_IMAGE(gtk_image_new());
+ IndicatorCustomPrivate * priv = INDICATOR_CUSTOM_GET_PRIVATE(self);
+
+ /* These are built in the connection phase */
+ priv->bus = NULL;
+ priv->service_proxy = NULL;
+
+ priv->sm = indicator_service_manager_new(INDICATOR_CUSTOM_DBUS_ADDR);
+ g_signal_connect(G_OBJECT(priv->sm), INDICATOR_SERVICE_MANAGER_SIGNAL_CONNECTION_CHANGE, G_CALLBACK(connected), self);
+
+ priv->applications = NULL;
+
+ return;
+}
+
+static void
+indicator_custom_dispose (GObject *object)
+{
+ IndicatorCustomPrivate * priv = INDICATOR_CUSTOM_GET_PRIVATE(object);
+
+ while (priv->applications != NULL) {
+ application_removed(priv->service_proxy,
+ 0,
+ INDICATOR_CUSTOM(object));
+ }
+
+ if (priv->sm != NULL) {
+ g_object_unref(priv->sm);
+ priv->sm = NULL;
+ }
+
+ if (priv->bus != NULL) {
+ /* We're not incrementing the ref count on this one. */
+ priv->bus = NULL;
+ }
+
+ if (priv->service_proxy != NULL) {
+ g_object_unref(G_OBJECT(priv->service_proxy));
+ priv->service_proxy = NULL;
+ }
+
+ G_OBJECT_CLASS (indicator_custom_parent_class)->dispose (object);
+ return;
+}
+
+static void
+indicator_custom_finalize (GObject *object)
+{
+
+ G_OBJECT_CLASS (indicator_custom_parent_class)->finalize (object);
+ return;
+}
+
+void
+connected (IndicatorServiceManager * sm, gboolean connected, IndicatorCustom * custom)
+{
+ IndicatorCustomPrivate * priv = INDICATOR_CUSTOM_GET_PRIVATE(custom);
+ g_debug("Connected to Custom Indicator Service.");
+
+ GError * error = NULL;
+
+ /* Grab the session bus */
+ if (priv->bus == NULL) {
+ priv->bus = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+
+ if (error != NULL) {
+ g_error("Unable to get session bus: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+ }
+
+ /* Build the service proxy */
+ priv->service_proxy = dbus_g_proxy_new_for_name_owner(priv->bus,
+ INDICATOR_CUSTOM_DBUS_ADDR,
+ INDICATOR_CUSTOM_DBUS_OBJ,
+ INDICATOR_CUSTOM_DBUS_IFACE,
+ &error);
+
+ /* Set up proxy signals */
+ g_debug("Setup proxy signals");
+ dbus_g_proxy_add_signal(priv->service_proxy,
+ "ApplicationAdded",
+ G_TYPE_STRING,
+ G_TYPE_INT,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_add_signal(priv->service_proxy,
+ "ApplicationRemoved",
+ G_TYPE_INT,
+ G_TYPE_INVALID);
+
+ /* Connect to them */
+ g_debug("Connect to them.");
+ dbus_g_proxy_connect_signal(priv->service_proxy,
+ "ApplicationAdded",
+ G_CALLBACK(application_added),
+ custom,
+ NULL /* Disconnection Signal */);
+ dbus_g_proxy_connect_signal(priv->service_proxy,
+ "ApplicationRemoved",
+ G_CALLBACK(application_removed),
+ custom,
+ NULL /* Disconnection Signal */);
+
+ /* Query it for existing applications */
+ g_debug("Request current apps");
+ org_ayatana_indicator_custom_service_get_applications_async(priv->service_proxy,
+ get_applications,
+ custom);
+
+ return;
+}
+
+/* Goes through the list of applications that we're maintaining and
+ pulls out the IndicatorObjectEntry and returns that in a list
+ for the caller. */
+static GList *
+get_entries (IndicatorObject * io)
+{
+ g_return_val_if_fail(IS_INDICATOR_CUSTOM(io), NULL);
+
+ IndicatorCustomPrivate * priv = INDICATOR_CUSTOM_GET_PRIVATE(io);
+ GList * retval = NULL;
+ GList * apppointer = NULL;
+
+ for (apppointer = priv->applications; apppointer != NULL; apppointer = g_list_next(apppointer)) {
+ IndicatorObjectEntry * entry = &(((ApplicationEntry *)apppointer->data)->entry);
+ retval = g_list_prepend(retval, entry);
+ }
+
+ if (retval != NULL) {
+ retval = g_list_reverse(retval);
+ }
+
+ return retval;
+}
+
+/* Here we respond to new applications by building up the
+ ApplicationEntry and signaling the indicator host that
+ we've got a new indicator. */
+static void
+application_added (DBusGProxy * proxy, const gchar * iconname, gint position, const gchar * dbusaddress, const gchar * dbusobject, IndicatorCustom * custom)
+{
+ g_debug("Building new application entry: %s with icon: %s", dbusaddress, iconname);
+ IndicatorCustomPrivate * priv = INDICATOR_CUSTOM_GET_PRIVATE(custom);
+ ApplicationEntry * app = g_new(ApplicationEntry, 1);
+
+ app->entry.image = GTK_IMAGE(gtk_image_new_from_icon_name(iconname, GTK_ICON_SIZE_MENU));
+ app->entry.label = NULL;
+ app->entry.menu = GTK_MENU(dbusmenu_gtkmenu_new((gchar *)dbusaddress, (gchar *)dbusobject));
+
+ gtk_widget_show(GTK_WIDGET(app->entry.image));
+
+ priv->applications = g_list_insert(priv->applications, app, position);
+
+ /* TODO: Need to deal with position here somehow */
+ g_signal_emit(G_OBJECT(custom), INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED_ID, 0, &(app->entry), TRUE);
+ return;
+}
+
+/* This removes the application from the list and free's all
+ of the memory associated with it. */
+static void
+application_removed (DBusGProxy * proxy, gint position, IndicatorCustom * custom)
+{
+ IndicatorCustomPrivate * priv = INDICATOR_CUSTOM_GET_PRIVATE(custom);
+ ApplicationEntry * app = (ApplicationEntry *)g_list_nth_data(priv->applications, position);
+
+ if (app == NULL) {
+ g_warning("Unable to find application at position: %d", position);
+ return;
+ }
+
+ priv->applications = g_list_remove(priv->applications, app);
+ g_signal_emit(G_OBJECT(custom), INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED_ID, 0, &(app->entry), TRUE);
+
+ if (app->entry.image != NULL) {
+ g_object_unref(G_OBJECT(app->entry.image));
+ }
+ if (app->entry.label != NULL) {
+ g_warning("Odd, an application indicator with a label?");
+ g_object_unref(G_OBJECT(app->entry.label));
+ }
+ if (app->entry.menu != NULL) {
+ g_object_unref(G_OBJECT(app->entry.menu));
+ }
+ g_free(app);
+
+ return;
}
-GtkMenu *
-get_menu (void)
+/* This repsonds to the list of applications that the service
+ has and calls application_added on each one of them. */
+static void
+get_applications (DBusGProxy *proxy, GPtrArray *OUT_applications, GError *error, gpointer userdata)
{
- GtkMenu * main_menu = GTK_MENU(gtk_menu_new());
- GtkWidget * loading_item = gtk_menu_item_new_with_label("Loading...");
- gtk_menu_shell_append(GTK_MENU_SHELL(main_menu), loading_item);
- gtk_widget_show(GTK_WIDGET(loading_item));
- return main_menu;
+ return;
}
diff --git a/src/libcustomindicator/custom-indicator.c b/src/libcustomindicator/custom-indicator.c
index 639d304..8d6633a 100644
--- a/src/libcustomindicator/custom-indicator.c
+++ b/src/libcustomindicator/custom-indicator.c
@@ -11,6 +11,8 @@
#include "notification-item-server.h"
#include "notification-watcher-client.h"
+#include "dbus-shared.h"
+
/**
CustomIndicatorPrivate:
@id: The ID of the indicator. Maps to CustomIndicator::id.
@@ -36,6 +38,7 @@ struct _CustomIndicatorPrivate {
/* Fun stuff */
DBusGProxy * watcher_proxy;
+ DBusGConnection * connection;
};
/* Signals Stuff */
@@ -91,6 +94,7 @@ static void custom_indicator_set_property (GObject * object, guint prop_id, cons
static void custom_indicator_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
/* Other stuff */
static void check_connect (CustomIndicator * self);
+static void register_service_cb (DBusGProxy * proxy, GError * error, gpointer data);
/* GObject type */
G_DEFINE_TYPE (CustomIndicator, custom_indicator, G_TYPE_OBJECT);
@@ -267,16 +271,18 @@ custom_indicator_init (CustomIndicator *self)
priv->menu = NULL;
priv->watcher_proxy = NULL;
+ priv->connection = NULL;
/* Put the object on DBus */
GError * error = NULL;
- DBusGConnection * connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+ priv->connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
if (error != NULL) {
g_error("Unable to connect to the session bus when creating custom indicator: %s", error->message);
g_error_free(error);
return;
}
- dbus_g_connection_register_g_object(connection,
+
+ dbus_g_connection_register_g_object(priv->connection,
"/need/a/path",
G_OBJECT(self));
@@ -303,8 +309,7 @@ custom_indicator_dispose (GObject *object)
}
if (priv->watcher_proxy != NULL) {
- DBusGConnection * session_bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
- dbus_g_connection_flush(session_bus);
+ dbus_g_connection_flush(priv->connection);
g_object_unref(G_OBJECT(priv->watcher_proxy));
priv->watcher_proxy = NULL;
}
@@ -421,6 +426,7 @@ custom_indicator_set_property (GObject * object, guint prop_id, const GValue * v
} else {
WARN_BAD_TYPE(PROP_ICON_NAME_S, value);
}
+ check_connect(self);
break;
/* *********************** */
case PROP_ATTENTION_ICON_NAME:
@@ -455,6 +461,7 @@ custom_indicator_set_property (GObject * object, guint prop_id, const GValue * v
} else {
WARN_BAD_TYPE(PROP_MENU_S, value);
}
+ check_connect(self);
break;
/* *********************** */
default:
@@ -588,9 +595,45 @@ custom_indicator_get_property (GObject * object, guint prop_id, GValue * value,
static void
check_connect (CustomIndicator * self)
{
+ CustomIndicatorPrivate * priv = CUSTOM_INDICATOR_GET_PRIVATE(self);
+
+ /* We're alreadying connecting or trying to connect. */
+ if (priv->watcher_proxy != NULL) return;
+ /* Do we have enough information? */
+ if (priv->menu == NULL) return;
+ if (priv->icon_name == NULL) return;
+ if (priv->id == NULL) return;
+
+ GError * error = NULL;
+ priv->watcher_proxy = dbus_g_proxy_new_for_name_owner(priv->connection,
+ INDICATOR_CUSTOM_DBUS_ADDR,
+ NOTIFICATION_WATCHER_DBUS_OBJ,
+ NOTIFICATION_WATCHER_DBUS_IFACE,
+ &error);
+ if (error != NULL) {
+ g_warning("Unable to create Ayatana Watcher proxy! %s", error->message);
+ /* TODO: This is where we should start looking at fallbacks */
+ g_error_free(error);
+ return;
+ }
+ org_ayatana_indicator_custom_NotificationWatcher_register_service_async(priv->watcher_proxy, "/need/a/path", register_service_cb, self);
+ return;
+}
+
+static void
+register_service_cb (DBusGProxy * proxy, GError * error, gpointer data)
+{
+ CustomIndicatorPrivate * priv = CUSTOM_INDICATOR_GET_PRIVATE(data);
+
+ if (error != NULL) {
+ g_warning("Unable to connect to the Notification Watcher: %s", error->message);
+ g_object_unref(G_OBJECT(priv->watcher_proxy));
+ priv->watcher_proxy = NULL;
+ }
+ return;
}
diff --git a/src/notification-watcher.xml b/src/notification-watcher.xml
index 93acf74..7b034e7 100644
--- a/src/notification-watcher.xml
+++ b/src/notification-watcher.xml
@@ -7,6 +7,7 @@
<!-- Methods -->
<method name="RegisterService">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value="true" />
<arg type="s" name="service" direction="in" />
</method>
<method name="RegisteredServices">
@@ -31,7 +32,7 @@
</signal>
<signal name="NotificationHostRegistered">
</signal>
- <signal name="NotificationHostUnegistered">
+ <signal name="NotificationHostUnregistered">
</signal>
</interface>
diff --git a/tests/Makefile.am b/tests/Makefile.am
index dfdb397..0b771db 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -2,7 +2,8 @@
check_PROGRAMS = \
test-libcustomindicator \
test-libcustomindicator-dbus-client \
- test-libcustomindicator-dbus-server
+ test-libcustomindicator-dbus-server \
+ test-simple-app
TESTS =
DISTCLEANFILES = $(TESTS)
@@ -87,3 +88,19 @@ test-libcustomindicator-dbus: test-libcustomindicator-dbus-client test-libcustom
TESTS += test-libcustomindicator-dbus
+#########################################
+## test-simple-app
+#########################################
+
+test_simple_app_SOURCES = \
+ test-simple-app.c
+
+test_simple_app_CFLAGS = \
+ $(INDICATOR_CFLAGS) \
+ -Wall -Werror \
+ -I$(top_srcdir)/src
+
+test_simple_app_LDADD = \
+ $(INDICATOR_LIBS) \
+ $(top_builddir)/src/libcustomindicator.la
+
diff --git a/tests/test-simple-app.c b/tests/test-simple-app.c
new file mode 100644
index 0000000..9e779ec
--- /dev/null
+++ b/tests/test-simple-app.c
@@ -0,0 +1,32 @@
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <glib.h>
+#include <libcustomindicator/custom-indicator.h>
+
+static GMainLoop * mainloop = NULL;
+
+int
+main (int argc, char ** argv)
+{
+ g_type_init();
+
+ DbusmenuServer * dms = dbusmenu_server_new("/menu");
+ DbusmenuMenuitem * dmi = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set(dmi, "label", "Bob");
+
+ CustomIndicator * ci = CUSTOM_INDICATOR(g_object_new(CUSTOM_INDICATOR_TYPE,
+ "id", "test-application",
+ "status-enum", CUSTOM_INDICATOR_STATUS_ACTIVE,
+ "icon-name", "system-shutdown",
+ "menu-object", dms,
+ NULL));
+
+ mainloop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(mainloop);
+
+ g_object_unref(G_OBJECT(ci));
+ g_debug("Quiting");
+
+ return 0;
+}