[GnomeMeeting-devel-list] DBUS component



Hi,

the patch adds a DBUS component to gnomemeeting ; this component is much
less featureful than the previous one, but is using the recently
committed events code (which means it will soon be better than the
previous).

The remote allows to:
* launch gnomemeeting ;
* begin a call (type the url in the entry, then enter) ;
* stop a call (well, current gm only knows of one call, and will end
that one whatever it is told to do, but still).

Snark

PS: the remote is crashy, but its purpose is to test the code in gm, not
to be used!
diff -urN gnomemeeting/configure.in gnomemeeting.dbus/configure.in
--- gnomemeeting/configure.in	2004-08-26 23:13:40.000000000 +0200
+++ gnomemeeting.dbus/configure.in	2004-08-31 16:32:43.000000000 +0200
@@ -406,6 +406,27 @@
 AC_CHECK_TYPES(xmlSAXHandlerV1,,, [#include <libxml/SAX.h>])
 CPPFLAGS="$CPPFLAGS_save"
 
+dnl #########################################################################
+dnl   Check for DBUS
+dnl #########################################################################
+AC_ARG_ENABLE(dbus,
+	[  --enable-dbus       Enable the DBUS component.],
+	enable_dbus=yes, enable_dbus=no)
+
+AC_MSG_CHECKING(whether the DBUS component should be compiled in)
+if test x"${enable_dbus}" = xyes ; then
+   AC_MSG_RESULT([yes])
+else
+   AC_MSG_RESULT([no])
+fi
+
+if test x"${enable_dbus}" = xyes ; then
+   PKG_CHECK_MODULES(DBUS,  dbus-1 = 0.22 dbus-glib-1 = 0.22)
+   GNOMEMEETING_CFLAGS="$GNOMEMEETING_CFLAGS $DBUS_CFLAGS -DHAS_DBUS"
+   GNOMEMEETING_LIBS="$GNOMEMEETING_LIBS $DBUS_LIBS"
+fi
+AM_CONDITIONAL(HAS_DBUS, test x"${enable_dbus}" = xyes)
+AM_CONDITIONAL(DBUS_SERVICES_INSTALL, test x"${enable_dbus}" = xyes)
 
 dnl ###########################################################################
 dnl  The various CFLAGS are merged into GNOMEMEETING_CFLAGS and 
@@ -463,6 +484,7 @@
 Makefile
 src/gnomemeeting-config-tool
 gnomemeeting.schemas.in
+gnomemeeting.service
 lib/Makefile
 lib/about/Makefile
 lib/druid/Makefile
diff -urN gnomemeeting/gnomemeeting.service.in gnomemeeting.dbus/gnomemeeting.service.in
--- gnomemeeting/gnomemeeting.service.in	1970-01-01 01:00:00.000000000 +0100
+++ gnomemeeting.dbus/gnomemeeting.service.in	2004-08-31 16:32:43.000000000 +0200
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=org.gnomemeeting.CallService
+Exec= prefix@/bin/gnomemeeting
+
diff -urN gnomemeeting/Makefile.am gnomemeeting.dbus/Makefile.am
--- gnomemeeting/Makefile.am	2004-08-14 16:00:31.000000000 +0200
+++ gnomemeeting.dbus/Makefile.am	2004-08-31 16:34:27.000000000 +0200
@@ -36,8 +36,15 @@
 	GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) gconftool-2 --makefile-install-rule $(SCHEMAS_FILE) 2>&1 > /dev/null
 	gconftool-2 --shutdown
 
+install-services: gnomemeeting.service
+	cp gnomemeeting.service $(libdir)/dbus-1.0/services/
+
 if GCONF_SCHEMAS_INSTALL
 install-data-local: install-schemas 
 else
 install-data-local:
 endif
+
+if DBUS_SERVICES_INSTALL
+install-data-local: install-services
+endif
diff -urN gnomemeeting/src/dbus_component.cpp gnomemeeting.dbus/src/dbus_component.cpp
--- gnomemeeting/src/dbus_component.cpp	1970-01-01 01:00:00.000000000 +0100
+++ gnomemeeting.dbus/src/dbus_component.cpp	2004-08-31 16:32:43.000000000 +0200
@@ -0,0 +1,634 @@
+#include "dbus_component.h"
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "gm_conf.h"
+
+#include "common.h"
+#include "gnomemeeting.h"
+#include "endpoint.h"
+
+/* declaration of the GObject */
+
+
+#define DBUS_COMPONENT_TYPE dbus_component_get_type ()
+
+
+#define DBUS_COMPONENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+                             DBUS_COMPONENT_TYPE, DBusComponent))
+
+
+#define DBUS_COMPONENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
+                                     DBUS_COMPONENT_TYPE, DBusComponentClass))
+
+
+#define IS_DBUS_COMPONENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+			        DBUS_COMPONENT_TYPE))
+
+
+#define DBUS_COMPONENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+                                       DBUS_COMPONENT_TYPE, \
+                                       DBusComponentClass)) 
+
+
+typedef struct _DBusComponent DBusComponent;
+
+
+typedef struct _DBusComponentClass DBusComponentClass;
+
+
+struct _DBusComponent
+{
+  GObject parent;
+
+  GMH323EndPoint *endpoint;
+  DBusConnection *connection;
+  gboolean is_registered;
+  gboolean owns_the_service;
+};
+
+
+struct _DBusComponentClass
+{
+  GObjectClass parent;
+};
+
+
+static GType dbus_component_get_type();
+
+
+static void dbus_component_finalize (GObject *self);
+
+
+static void dbus_component_init (DBusComponent *self);
+
+
+static void dbus_component_class_init (DBusComponentClass *klass);
+
+
+/* declaration of various helpers */
+
+
+static const gchar *state_to_string (GMH323EndPoint::CallingState);
+
+
+static gboolean connect_component (gpointer user_data);
+
+
+/* declaration of the signal callbacks */
+
+
+static void call_begin_cb (GObject *self,
+			   gchar *call_token,
+			   gpointer user_data);
+
+
+static void call_end_cb (GObject *self,
+			 gchar *call_token,
+			 gpointer user_data);
+
+
+static void endpoint_state_changed_cb (GObject *self,
+				       GMH323EndPoint::CallingState new_state,
+				       gpointer user_data);
+
+
+/* declaration of the DBUS helper functions */
+
+
+/* this is a sort of callback, called by DBUS when a watched type of message
+ * arrives.
+ */
+static DBusHandlerResult filter_func (DBusConnection *connection,
+				      DBusMessage *message,
+				      void *user_data);
+
+
+
+/* this function is called by DBUS when a message directed at the
+ * GM_DBUS_OBJECT_PATH arrives (provided we're the registered instance!)
+ */
+static DBusHandlerResult path_message_func (DBusConnection *connection,
+					    DBusMessage *message,
+					    void *user_data);
+
+/* the rest of those DBUS helpers, with name "handle_<method call>_message",
+ * are used to handle the various method calls. They get their arguments
+ * directly from path_message_func.
+ */
+static void handle_connect_to_message (DBusConnection *connection,
+				       DBusMessage *message);
+
+
+static void handle_get_state_message (DBusConnection *connection,
+				      DBusMessage *message);
+
+
+static void handle_disconnect_message (DBusConnection *connection,
+				       DBusMessage *message);
+
+
+static void handle_get_calls_list_message (DBusConnection *connection,
+					   DBusMessage *message);
+
+
+static void handle_get_call_info_message (DBusConnection *connection,
+					  DBusMessage *message);
+
+
+/* definition of some helper DBUS-related data */
+
+
+static DBusObjectPathVTable call_vtable = {
+  NULL,
+  path_message_func,
+  NULL,
+};
+
+
+/* Implementation of the GObject */
+
+
+static GType
+dbus_component_get_type()
+{
+  static GType my_type = 0; 
+  
+  if (!my_type) {
+    static const GTypeInfo my_info = {
+      sizeof (DBusComponentClass),
+      NULL,
+      NULL,
+      (GClassInitFunc) dbus_component_class_init,
+      NULL,
+      NULL,
+      sizeof(DBusComponent),
+      0,
+      (GInstanceInitFunc) dbus_component_init
+    };
+    my_type = g_type_register_static (G_TYPE_OBJECT ,
+				      "DBusComponent", &my_info, 
+				      (GTypeFlags)0);
+  }
+  
+  return my_type;
+}
+
+
+static void
+dbus_component_finalize (GObject *object)
+{
+  DBusComponent *self = NULL;
+  GObjectClass *parent_class = NULL;
+
+  g_return_if_fail (IS_DBUS_COMPONENT (object));
+
+  self = DBUS_COMPONENT (object);
+
+  if (self->connection != NULL) {
+    dbus_connection_disconnect (self->connection);
+    dbus_connection_unref (self->connection);
+  }
+ 
+  parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (DBUS_COMPONENT_GET_CLASS (self)));
+
+  parent_class->finalize (G_OBJECT(self));
+}
+
+
+static void
+dbus_component_init (DBusComponent *self)
+{
+  self->endpoint = NULL;
+  self->connection = NULL;
+  self->is_registered = FALSE;
+  self->owns_the_service = FALSE;
+
+  g_signal_connect (G_OBJECT (self),
+		    "call-begin",
+		    G_CALLBACK (call_begin_cb),
+		    NULL);
+  g_signal_connect (G_OBJECT (self),
+		    "call-end",
+		    G_CALLBACK (call_end_cb),
+		    NULL);
+  g_signal_connect (G_OBJECT (self),
+		    "endpoint-state-changed",
+		    G_CALLBACK (endpoint_state_changed_cb),
+		    NULL);
+}
+
+
+static void dbus_component_class_init (DBusComponentClass *klass)
+{
+  GObjectClass *object_klass = G_OBJECT_CLASS (klass);
+
+  object_klass->finalize = dbus_component_finalize;
+}
+
+
+/* implementation of various helpers */
+
+
+static const gchar*
+state_to_string (GMH323EndPoint::CallingState state)
+{
+  const gchar *result;
+
+  switch (state) {
+  case GMH323EndPoint::Standby:
+    result = "Standby";
+    break;
+  case GMH323EndPoint::Calling:
+    result = "Calling";
+    break;
+  case GMH323EndPoint::Connected:
+    result  = "Connected";
+    break;
+  case GMH323EndPoint::Called:
+    result = "Called";
+    break;
+  default:
+    result = "Bogus";
+  }
+  
+  return result;
+} 
+
+
+static gboolean
+connect_component (gpointer user_data)
+{
+  DBusComponent *self = NULL;
+  
+  g_return_val_if_fail (IS_DBUS_COMPONENT (user_data), TRUE);
+
+  self = DBUS_COMPONENT (user_data);
+
+  if (self->connection == NULL) { /* we lost contact with the server */
+    self->connection = dbus_bus_get (DBUS_BUS_SESSION, NULL);
+    if (self->connection != NULL) {
+      if (dbus_connection_add_filter (self->connection, 
+				      filter_func,
+				      self, NULL))
+ 	dbus_connection_setup_with_g_main (self->connection, NULL);
+      else {
+	dbus_connection_disconnect (self->connection);
+	self->connection = NULL;
+      }
+    }
+  }
+
+  if (self->connection != NULL) {  
+    /* we have a contact with the server, check the rest */
+    if (self->is_registered == FALSE)
+      self->is_registered 
+	= dbus_connection_register_object_path (self->connection,
+						GM_DBUS_OBJECT_PATH,
+						&call_vtable, self);
+    
+    if (self->owns_the_service == FALSE)
+      self->owns_the_service 
+	= (dbus_bus_acquire_service (self->connection, 
+				     GM_DBUS_SERVICE, 0, NULL) >= 0);
+    
+ 
+  }
+
+  return self->connection != NULL && self->is_registered && self->owns_the_service;
+}
+
+
+/* implementation of the signal callbacks */
+
+
+static void
+call_begin_cb (GObject *object,
+	       gchar *call_token,
+	       gpointer user_data)
+{
+  DBusComponent *self = NULL;
+  DBusMessage *message = NULL;
+
+  g_return_if_fail (IS_DBUS_COMPONENT (object));
+
+  self = DBUS_COMPONENT (object);
+
+  if (self->connection == NULL)
+    return;
+
+  message = dbus_message_new_signal (GM_DBUS_OBJECT_PATH,
+				     GM_DBUS_INTERFACE, "AddCall");
+
+  if (dbus_message_append_args (message,
+				DBUS_TYPE_STRING, call_token,
+				DBUS_TYPE_INVALID)) {
+    (void)dbus_connection_send (self->connection, message, NULL);
+    dbus_connection_flush (self->connection);
+  }
+  dbus_message_unref (message);
+}
+
+
+static void
+call_end_cb (GObject *object,
+	     gchar *call_token,
+	     gpointer user_data)
+{
+  DBusComponent *self = NULL;
+  DBusMessage *message = NULL;
+
+  g_return_if_fail (IS_DBUS_COMPONENT (object));
+
+  self = DBUS_COMPONENT (object);
+
+  if (self->connection == NULL)
+    return;
+
+  message = dbus_message_new_signal (GM_DBUS_OBJECT_PATH,
+				     GM_DBUS_INTERFACE, "DeleteCall");
+
+  if (dbus_message_append_args (message,
+				DBUS_TYPE_STRING, call_token,
+				DBUS_TYPE_INVALID)) {
+    (void)dbus_connection_send (self->connection, message, NULL);
+    dbus_connection_flush (self->connection);
+  }
+  dbus_message_unref (message);
+}
+
+
+static void
+endpoint_state_changed_cb (GObject *object,
+			   GMH323EndPoint::CallingState new_state,
+			   gpointer user_data)
+{
+  DBusComponent *self = NULL;
+  DBusMessage *message = NULL;
+
+  g_return_if_fail (IS_DBUS_COMPONENT (object));
+
+  self = DBUS_COMPONENT (object);
+
+  if (self->connection == NULL)
+    return;
+
+  message = dbus_message_new_signal (GM_DBUS_OBJECT_PATH,
+				     GM_DBUS_INTERFACE, "StateChanged");
+
+  if (dbus_message_append_args (message,
+				DBUS_TYPE_STRING, state_to_string (new_state),
+				DBUS_TYPE_INVALID)) {
+    (void)dbus_connection_send (self->connection, message, NULL);
+    dbus_connection_flush (self->connection);
+  }
+  dbus_message_unref (message);
+}
+
+
+/* implementation of the DBUS helpers */
+
+
+static DBusHandlerResult
+filter_func (DBusConnection *connection,
+	     DBusMessage *message,
+	     void *user_data)
+{
+  DBusComponent *self = NULL;
+
+  g_return_val_if_fail (user_data != NULL,
+			DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+
+  self = DBUS_COMPONENT (user_data);
+
+  g_return_val_if_fail (self->connection == connection,
+			DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+
+  if (dbus_message_is_signal (message,
+                              DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL,
+                              "Disconnected"))
+    {
+      dbus_connection_unref (self->connection);
+      self->connection = NULL;
+      self->is_registered = FALSE;
+      self->owns_the_service = FALSE;
+      g_timeout_add (3000, connect_component, (gpointer)self);
+
+      return DBUS_HANDLER_RESULT_HANDLED;
+    }
+
+  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+static DBusHandlerResult
+path_message_func (DBusConnection *connection,
+                   DBusMessage *message,
+                   void *user_data)
+{
+  DBusComponent *self = NULL;
+  DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+  self = DBUS_COMPONENT (user_data);
+  if (dbus_message_is_method_call (message,
+                                   GM_DBUS_SERVICE,
+                                   "ConnectTo")) {
+    handle_connect_to_message (connection, message);
+    result = DBUS_HANDLER_RESULT_HANDLED;
+  }
+  else if (dbus_message_is_method_call (message,
+					GM_DBUS_SERVICE,
+					"GetState")) {
+    handle_get_state_message (connection, message);
+    result = DBUS_HANDLER_RESULT_HANDLED;
+  }
+  else if (dbus_message_is_method_call (message,
+					GM_DBUS_SERVICE,
+					"Disconnect")) {
+    handle_disconnect_message (connection, message);
+    result = DBUS_HANDLER_RESULT_HANDLED;
+  }
+  else if (dbus_message_is_method_call (message,
+					GM_DBUS_SERVICE,
+					"GetCallsList")) {
+    handle_get_calls_list_message (connection, message);
+    result = DBUS_HANDLER_RESULT_HANDLED;
+  }
+  else if (dbus_message_is_method_call (message,
+					GM_DBUS_SERVICE,
+					"GetCallInfo")) {
+    handle_get_call_info_message (connection, message);
+    result = DBUS_HANDLER_RESULT_HANDLED;
+  }
+  
+  return result;
+}
+
+
+static void
+handle_connect_to_message (DBusConnection *connection,
+			   DBusMessage *message)
+{
+  gchar *address = NULL;
+
+  if (dbus_message_get_args (message, NULL,
+			     DBUS_TYPE_STRING, &address,
+			     DBUS_TYPE_INVALID)) {
+    GnomeMeeting::Process ()->Connect (address);
+  }
+}
+
+
+static void
+handle_get_state_message (DBusConnection *connection,
+			  DBusMessage *message)
+{
+  DBusMessage *reply = NULL;
+  const gchar *state = NULL;
+  GMH323EndPoint *ep = NULL;
+
+  ep = GnomeMeeting::Process ()->Endpoint ();
+  
+  reply = dbus_message_new_method_return (message);
+  state = state_to_string (ep->GetCallingState ());
+  if (dbus_message_append_args (reply,
+				DBUS_TYPE_STRING, state,
+				DBUS_TYPE_INVALID)) {
+    (void)dbus_connection_send (connection, reply, NULL);
+    dbus_connection_flush (connection);
+  }
+  dbus_message_unref (reply);
+}
+
+
+static void
+handle_disconnect_message (DBusConnection *connection,
+			   DBusMessage *message)
+{
+  gchar *call_token = NULL;
+  if (dbus_message_get_args (message, NULL,
+			     DBUS_TYPE_STRING, &call_token,
+			     DBUS_TYPE_INVALID)) {  
+    /* FIXME: should use call_token, when gnomemeeting will support it! */
+    GnomeMeeting::Process ()->Disconnect ();
+  }
+}
+
+
+static void
+handle_get_calls_list_message (DBusConnection *connection,
+			       DBusMessage *message)
+{
+  DBusMessage *reply = NULL;
+  const char *call_token = NULL;
+  GMH323EndPoint *ep = NULL;
+
+  ep = GnomeMeeting::Process ()->Endpoint ();
+  
+  reply = dbus_message_new_method_return (message);
+  call_token = (const char *)ep->GetCurrentCallToken ();
+  if (dbus_message_append_args (reply,
+				DBUS_TYPE_STRING, call_token,
+				DBUS_TYPE_INVALID)) {
+    (void)dbus_connection_send (connection, reply, NULL);
+    dbus_connection_flush (connection);
+  }
+  dbus_message_unref (reply);
+}
+
+
+static void
+handle_get_call_info_message (DBusConnection *connection,
+			      DBusMessage *message)
+{
+  DBusMessage *reply = NULL;
+  const char *call_token = NULL;
+  GMH323EndPoint *ep = NULL;
+  H323Connection *h323connection = NULL;
+  gchar *name = NULL;
+  gchar *url = NULL;
+  gchar *app = NULL;
+
+  ep = GnomeMeeting::Process ()->Endpoint ();
+
+  if (dbus_message_get_args (message, NULL,
+			     DBUS_TYPE_STRING, &call_token,
+			     DBUS_TYPE_INVALID)) {  
+    h323connection = ep->FindConnectionWithLock((PString)call_token);
+    if (h323connection) {
+      ep->GetRemoteConnectionInfo (*h323connection, name, app, url);
+      h323connection->Unlock ();
+    }
+    reply = dbus_message_new_method_return (message);
+    if (dbus_message_append_args (reply,
+				  DBUS_TYPE_STRING, name,
+				  DBUS_TYPE_STRING, url,
+				  DBUS_TYPE_STRING, app,
+				  DBUS_TYPE_INVALID)) {
+      (void)dbus_connection_send (connection, reply, NULL);
+      dbus_connection_flush (connection);
+    }
+    dbus_message_unref (reply);
+  }
+}
+
+
+/* implementation of the externally-visible api */
+
+
+GObject*
+dbus_component_new(GMH323EndPoint *endpoint)
+{
+  DBusComponent *result = NULL;
+
+  result = DBUS_COMPONENT (g_object_new (DBUS_COMPONENT_TYPE, NULL));
+
+  result->endpoint = endpoint;
+  result->endpoint->AddObserver (G_OBJECT (result));
+
+  (void)connect_component ((gpointer)result);
+
+  return G_OBJECT (result);
+}
+
+
+gboolean
+dbus_component_is_first_instance (GObject *object)
+{
+  g_return_val_if_fail (IS_DBUS_COMPONENT (object), FALSE);
+
+  return DBUS_COMPONENT (object)->is_registered;
+}
+
+
+void
+dbus_component_call_address (GObject *object, 
+			     gchar *address)
+{
+  DBusMessage *message = NULL;
+  DBusComponent *self = NULL;
+
+  g_return_if_fail (IS_DBUS_COMPONENT (object));
+ 
+  self = DBUS_COMPONENT (object);
+ 
+  if (self->connection == NULL)
+    return;
+
+  message = dbus_message_new_method_call (GM_DBUS_SERVICE,
+					  GM_DBUS_OBJECT_PATH,
+					  GM_DBUS_INTERFACE, "Call");
+
+  dbus_message_set_no_reply (message, TRUE);
+  
+  if (dbus_message_append_args (message,
+				DBUS_TYPE_STRING, address,
+				DBUS_TYPE_INVALID)) {
+    
+    (void)dbus_connection_send (self->connection,
+				message, NULL);
+    dbus_connection_flush (self->connection);
+  }
+  dbus_message_unref (message);
+}
diff -urN gnomemeeting/src/dbus_component.h gnomemeeting.dbus/src/dbus_component.h
--- gnomemeeting/src/dbus_component.h	1970-01-01 01:00:00.000000000 +0100
+++ gnomemeeting.dbus/src/dbus_component.h	2004-08-31 16:32:43.000000000 +0200
@@ -0,0 +1,95 @@
+#ifndef _DBUS_COMPONENT_H_
+#define _DBUS_COMPONENT_H_
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "endpoint.h"
+
+
+/* Here is the description of DBUS messages understood by this component:
+ *
+ * Method calls:
+ * =============
+ *
+ * "ConnectTo"
+ * in    : string (url)
+ * out   : nil
+ * action: makes gnomemeeting call the given url
+ *
+ * "GetState"
+ * in    : nil
+ * out   : string (describes gnomemeeting's state: Standby, Calling, etc)
+ * action: none
+ *
+ * "Disconnect"
+ * in    : string (call token)
+ * out   : nil
+ * action: gnomemeeting disconnects the given call
+ *
+ * "GetCallsList"
+ * in    : nil
+ * out   : list of strings (call tokens, iterate to get them)
+ * action: none
+ *
+ * "GetCallInfo"
+ * in    : string (call token)
+ * out   : string (name), string (url) and string (application)
+ * action: none
+ *
+ * Signals:
+ * ========
+ *
+ * "StateChanged"
+ * data: string (state)
+ * goal: gnomemeeting's state changed
+ *
+ * "AddCall"
+ * data: string (call token)
+ * goal: gnomemeeting manages a new call
+ *
+ * "DeleteCall"
+ * data: string (call token)
+ * goal: gnomemeeting closed a call (ie: the call token isn't valid anymore!)
+ *
+ */
+
+#define GM_DBUS_OBJECT_PATH "/org/gnomemeeting/Endpoint"
+#define GM_DBUS_INTERFACE "org.gnomemeeting.CallService"
+#define GM_DBUS_SERVICE "org.gnomemeeting.CallService"
+
+
+G_BEGIN_DECLS
+
+
+/* DESCRIPTION  : /
+ * BEHAVIOR     : Returns a valid DBUS component casted as a GObject
+ * PRE          : a valid endpoint
+ */
+GObject *dbus_component_new(GMH323EndPoint *endpoint);
+
+
+/* the two following functions, currently unused, could be used by Damien
+ * to make so that if gnomemeeting is called with "-c url", then if there's
+ * already another instance, pass it the call and exit.
+ */
+
+/* DESCRIPTION  : /
+ * BEHAVIOR     : Returns TRUE if there's no other gnomemeeting registered
+ *                on DBUS.
+ * PRE          : A non-NULL DBUS component casted as a GObject
+ */
+gboolean dbus_component_is_first_instance (GObject *object);
+
+
+/* DESCRIPTION  : /
+ * BEHAVIOR     : Makes the gnomemeeting registered on DBUS call the given
+ *                url.
+ * PRE          : A non-NULL DBUS component casted as a GObject, and an URL
+ */
+void dbus_component_call_address (GObject *object, gchar *url);
+
+
+G_END_DECLS
+
+#endif
diff -urN gnomemeeting/src/gnomemeeting.cpp gnomemeeting.dbus/src/gnomemeeting.cpp
--- gnomemeeting/src/gnomemeeting.cpp	2004-08-30 23:11:32.000000000 +0200
+++ gnomemeeting.dbus/src/gnomemeeting.cpp	2004-08-31 16:38:52.000000000 +0200
@@ -52,6 +52,9 @@
 #include "log_window.h"
 #include "main_window.h"
 #include "misc.h"
+#ifdef HAS_DBUS
+#include "dbus_component.h"
+#endif
 
 #include "history-combo.h"
 #include "dialog.h"
@@ -145,6 +148,8 @@
     gtk_widget_destroy (gm);
   if (druid_window)
     gtk_widget_destroy (druid_window);
+  if (dbus_component)
+    g_object_unref (dbus_component);
 }
 
 
@@ -450,6 +455,12 @@
 #ifndef WIN32
   tray = gm_tray_new ();
 #endif
+#ifdef HAS_DBUS
+  dbus_component = dbus_component_new (endpoint);
+#else
+  dbus_component = NULL;
+#endif
+
   gm_main_window_new (gw);
 
 
diff -urN gnomemeeting/src/gnomemeeting.h gnomemeeting.dbus/src/gnomemeeting.h
--- gnomemeeting/src/gnomemeeting.h	2004-08-26 22:40:39.000000000 +0200
+++ gnomemeeting.dbus/src/gnomemeeting.h	2004-08-31 16:38:45.000000000 +0200
@@ -159,7 +159,7 @@
    */
   GtkWidget *GetTray ();
   
-  
+
   /* Needed for PProcess */
   void Main();
 
@@ -266,6 +266,9 @@
   GtkWidget *prefs_window;
   GtkWidget *tray;
 
+  /* other things */
+  GObject *dbus_component;
+
   static GnomeMeeting *GM;
 };
 
diff -urN gnomemeeting/src/Makefile.am gnomemeeting.dbus/src/Makefile.am
--- gnomemeeting/src/Makefile.am	2004-08-30 23:11:31.000000000 +0200
+++ gnomemeeting.dbus/src/Makefile.am	2004-08-31 16:33:51.000000000 +0200
@@ -59,6 +59,10 @@
 gnomemeeting_SOURCES += bonobo_component.cpp bonobo_component.h
 endif
 
+if HAS_DBUS 
+gnomemeeting_SOURCES += dbus_component.cpp dbus_component.h
+endif
+
 gnomemeeting_LDADD = \
 	@GNOMEMEETING_LIBS@ \
 	$(top_builddir)/lib/libgnomemeeting.la
#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#define DBUS_API_SUBJECT_TO_CHANGE
#include <dbus/dbus.h>
#include <dbus/dbus-glib-lowlevel.h>


#define OBJECT "/org/gnomemeeting/Endpoint"
#define INTERFACE "org.gnomemeeting.CallService"
#define SERVICE "org.gnomemeeting.CallService"


struct needed_data {
  DBusConnection *connection;
  GtkWidget *window;
  GtkListStore *call_store;
  GtkTreeView *call_view;
};


enum {
  CALL_TOKEN_COLUMN,
  NAME_COLUMN,
  URL_COLUMN,
  APP_COLUMN,
  NUMBER_OF_COLUMNS
};


/* various helpers (declaration) */

static GtkWidget *call_popup_new (const struct needed_data *my_data);

static gboolean set_iter_on_right_call_token (GtkTreeModel *model,
					      GtkTreeIter *iter,
					      gchar *call_token);


static void add_call (struct needed_data *my_data,
		      gchar *call_token);


static void delete_call (struct needed_data *my_data,
			 gchar *call_token);


/* gtk callbacks (declaration) */


static gint call_clicked_cb (GtkWidget *widget,
			     GdkEventButton *button,
			     gpointer user_data);


static void connect_cb (GtkWidget *,
			gpointer);


static void disconnect_cb (GtkWidget *,
			   gpointer);

static void launch_cb (GtkWidget *,
		       gpointer);


/* DBUS helpers  (declaration) */


static DBusHandlerResult filter_func (DBusConnection *connection,
				      DBusMessage *message,
				      void *user_data);


/* various helpers (implementation) */


static GtkWidget *
call_popup_new (const struct needed_data *my_data)
{
  GtkWidget *popup = NULL;
  GtkWidget *item = NULL;
  
  popup = gtk_menu_new ();

  item = gtk_menu_item_new_with_label ("Disconnect");
  g_signal_connect (G_OBJECT (item), "activate",
		    GTK_SIGNAL_FUNC (disconnect_cb), (gpointer)my_data);
  gtk_menu_append (popup, item);

  return popup;
}

static gboolean
set_iter_on_right_call_token (GtkTreeModel *model,
			      GtkTreeIter *iter,
			      gchar *call_token)
{
  gchar *iter_call_token = NULL;

  g_return_val_if_fail (model != NULL 
			&& iter != NULL 
			&& call_token != NULL, FALSE);

  if (gtk_tree_model_get_iter_first (model, iter)) {
    do {
      gtk_tree_model_get (model, iter,
			  CALL_TOKEN_COLUMN, &iter_call_token,
			  -1);
      if (strcmp (call_token, iter_call_token) == 0)
	return TRUE;
    } while (gtk_tree_model_iter_next (model, iter));
  }

  return FALSE;
}


static void
add_call (struct needed_data *my_data,
	  gchar *call_token)
{
  gchar *name = NULL;
  gchar *url = NULL;
  gchar *app = NULL;
  GtkTreeIter iter;
  DBusMessage *message = NULL;
  DBusMessage *reply = NULL;
  DBusPendingCall *call = NULL;
  DBusError error;

  g_return_if_fail (my_data != NULL && call_token != NULL);

  g_print ("add call %s\n", call_token);

  dbus_error_init (&error);

  /* first, get the data */
  message = dbus_message_new_method_call (SERVICE, OBJECT,
                                          INTERFACE, "GetCallInfo");
  (void)dbus_message_append_args (message,
                                  DBUS_TYPE_STRING, call_token,
                                  DBUS_TYPE_INVALID);
  dbus_message_set_no_reply (message, FALSE);
  (void)dbus_connection_send_with_reply (my_data->connection, message,
                                         &call, -1);
  dbus_connection_flush (my_data->connection);
  dbus_pending_call_block (call);
  reply = dbus_pending_call_get_reply (call);
  if (!dbus_message_get_args (reply, NULL,
			      DBUS_TYPE_STRING, &name,
			      DBUS_TYPE_STRING, &url,
			      DBUS_TYPE_STRING, &app,
			      DBUS_TYPE_INVALID)) {
    g_print ("Couldn't get name, url & app: %s\n", name);
    dbus_error_free (&error);
    return;
  }
 
  /* then push it in the store */
  gtk_list_store_append (my_data->call_store, &iter);
  
  gtk_list_store_set (my_data->call_store, &iter,
		      CALL_TOKEN_COLUMN, call_token,
		      NAME_COLUMN, name,
		      URL_COLUMN, url,
		      APP_COLUMN, app,
		      -1); 
}


static void
delete_call (struct needed_data *my_data,
	     gchar *call_token)
{
  GtkTreeIter iter;

  g_print ("delete_call %s\n", call_token);

  g_return_if_fail (my_data != NULL && call_token != NULL);

  if (set_iter_on_right_call_token (GTK_TREE_MODEL (my_data->call_store),
				    &iter, call_token)) {
    (void)gtk_list_store_remove (my_data->call_store, &iter);
  }
}


static void
update_status (struct needed_data *my_data)
{
  DBusMessage *message = NULL;
  DBusMessage *reply = NULL;
  DBusPendingCall *call = NULL;
  gchar *title = NULL;

  g_print ("update_status\n");

  if (dbus_bus_service_exists (my_data->connection, SERVICE, NULL)) {

    message = dbus_message_new_method_call (SERVICE, OBJECT,
                                            INTERFACE, "GetState");
    dbus_message_set_no_reply (message, FALSE);
    (void)dbus_connection_send_with_reply (my_data->connection, message,
                                           &call, -1);
    dbus_connection_flush (my_data->connection);

    dbus_pending_call_block (call);
    reply = dbus_pending_call_get_reply (call);
    if (dbus_message_get_args (reply, NULL,
                               DBUS_TYPE_STRING, &title,
                               DBUS_TYPE_INVALID)) {
      gtk_window_set_title (GTK_WINDOW (my_data->window), title);
      g_free (title);
    }
    dbus_message_unref (message);
    dbus_message_unref (reply);
    dbus_pending_call_unref (call);
  }
  else
    gtk_window_set_title (GTK_WINDOW (my_data->window), "Not running");

}


/* gtk callbacks (implementation) */


static gint
call_clicked_cb (GtkWidget *widget,
		 GdkEventButton *button,
		 gpointer user_data)
{
  struct needed_data *my_data = (struct needed_data *)user_data;
  GtkWidget *popup = NULL;

  if (button->type == GDK_BUTTON_PRESS && button->button == 3) {
    popup = call_popup_new (my_data);
    gtk_menu_popup (GTK_MENU (popup), NULL, NULL, NULL, NULL,
		    button->button, button->time);

    g_signal_connect (G_OBJECT (popup), "hide",
		      GTK_SIGNAL_FUNC (g_object_unref), (gpointer)popup);
    g_object_ref (G_OBJECT (popup));      /* gtk memory management */
    gtk_object_sink (GTK_OBJECT (popup)); /* is amazing            */
    gtk_widget_show_all (popup);
  }

  return TRUE;
}


static void
connect_cb (GtkWidget *widget,
	    gpointer user_data)
{
  struct needed_data *my_data = (struct needed_data *)user_data;
  DBusMessage *message = NULL;

  message = dbus_message_new_method_call (SERVICE, OBJECT,
					  INTERFACE, "ConnectTo");
  (void)dbus_message_append_args (message,
				  DBUS_TYPE_STRING,
				  gtk_entry_get_text (GTK_ENTRY (widget)),
				  DBUS_TYPE_INVALID);
  dbus_message_set_no_reply (message, TRUE);
  (void)dbus_connection_send (my_data->connection, message, NULL);
  dbus_connection_flush (my_data->connection);
  dbus_message_unref (message);
}


static void
disconnect_cb (GtkWidget *widget,
	       gpointer user_data)
{
  struct needed_data *my_data = (struct needed_data *)user_data;
  GtkTreeSelection *selection = NULL;
  GtkTreeIter iter;
  gchar *call_token = NULL;
  DBusMessage *message = NULL;

  selection = gtk_tree_view_get_selection (my_data->call_view);
  
  if (gtk_tree_selection_get_selected (selection, NULL, &iter)) {
    gtk_tree_model_get (GTK_TREE_MODEL (my_data->call_store), &iter,
			CALL_TOKEN_COLUMN, &call_token,
			-1);
    message = dbus_message_new_method_call (SERVICE, OBJECT,
					    INTERFACE, "Disconnect");
    (void)dbus_message_append_args (message,
				    DBUS_TYPE_STRING, call_token,
				    DBUS_TYPE_INVALID);
    dbus_message_set_no_reply (message, TRUE);
    (void)dbus_connection_send (my_data->connection, message, NULL);
    dbus_connection_flush (my_data->connection);
    dbus_message_unref (message);
  }
}


static void
launch_cb (GtkWidget *widget,
	   gpointer user_data)
{
  struct needed_data *my_data = (struct needed_data *)user_data;

  (void)dbus_bus_activate_service (my_data->connection, SERVICE, 0, NULL, NULL);
}


/* DBUS helpers (implementation) */

static DBusHandlerResult
filter_func(DBusConnection *connection,
            DBusMessage *message,
            void *user_data)
{
  struct needed_data *my_data = (struct needed_data *)user_data;
  char *str = NULL;

  /* we just lost contact with DBUS */
  if (dbus_message_is_signal (message,
                              DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL,
                              "Disconnected"))
    {
      g_print ("Disconnected\n");
      exit (-1); /* just die */
      return DBUS_HANDLER_RESULT_HANDLED;
    }
  /* DBUS says someone got/lost a service */
  else if (dbus_message_is_signal (message,
                                   DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
                                   "ServiceCreated")
           || dbus_message_is_signal (message,
                                      DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
                                      "ServiceDeleted")) {
    dbus_message_get_args (message,
                           NULL,
                           DBUS_TYPE_STRING, &str,
                           DBUS_TYPE_INVALID);
    if (strcmp (str, SERVICE) == 0) { /* check it's the right service */
      update_status (my_data);
      return DBUS_HANDLER_RESULT_HANDLED;
    }
  }
  /* the rest of the messages are from gnomemeeting itself */
  else if (dbus_message_is_signal (message,
                                   INTERFACE,
                                   "StateChanged")) {
    update_status (my_data);
  }
  else if (dbus_message_is_signal (message,
				   INTERFACE,
				   "AddCall")) {
    dbus_message_get_args (message,
			   NULL,
			   DBUS_TYPE_STRING, &str,
			   DBUS_TYPE_INVALID);
    add_call (my_data, str);
  }
  else if (dbus_message_is_signal (message,
				   INTERFACE,
				   "DeleteCall")) {
    dbus_message_get_args (message,
			   NULL,
			   DBUS_TYPE_STRING, &str,
			   DBUS_TYPE_INVALID);
    delete_call (my_data, str);
  }

  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}



/* well... */


int main (int argc,
	  char *argv [])
{
  GtkWidget *window = NULL;
  GtkListStore *call_store = NULL;
  GtkWidget *call_view = NULL;
  GtkCellRenderer *renderer = NULL;
  GtkTreeViewColumn *column = NULL;
  GtkWidget *vbox = NULL;
  GtkWidget *hbox = NULL;
  GtkWidget *entry = NULL;
  GtkWidget *entry_label = NULL;
  GtkWidget *launch_button = NULL;
  struct needed_data *my_data = NULL;

  gtk_set_locale ();

  gtk_init (&argc, &argv);

  my_data = g_new0 (struct needed_data, 1);

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  my_data->window = window;
  g_signal_connect (G_OBJECT (window), "delete-event",
                    G_CALLBACK (gtk_false), NULL);
  g_signal_connect (G_OBJECT (window), "destroy",
                    G_CALLBACK (gtk_main_quit), NULL);

  vbox = gtk_vbox_new (FALSE, 2);
  gtk_container_add (GTK_CONTAINER (window), vbox);

  call_store = gtk_list_store_new (NUMBER_OF_COLUMNS,
				   G_TYPE_STRING, /* call token */
				   G_TYPE_STRING, /* name */
				   G_TYPE_STRING, /* url */
				   G_TYPE_STRING); /* app */
  my_data->call_store = call_store;


  call_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (call_store));
  my_data->call_view = GTK_TREE_VIEW (call_view);
  gtk_box_pack_start (GTK_BOX (vbox), call_view, FALSE, FALSE, 0);
  g_signal_connect (G_OBJECT (call_view), "event-after",
		    G_CALLBACK (call_clicked_cb), my_data);

  renderer = gtk_cell_renderer_text_new ();
  column = gtk_tree_view_column_new_with_attributes ("Name",
						     renderer,
						     "text", NAME_COLUMN,
						     NULL);
  gtk_tree_view_append_column (GTK_TREE_VIEW (call_view), column);

  renderer = gtk_cell_renderer_text_new ();
  column = gtk_tree_view_column_new_with_attributes ("URL",
						     renderer,
						     "text", URL_COLUMN,
						     NULL);
  gtk_tree_view_append_column (GTK_TREE_VIEW (call_view), column);

  renderer = gtk_cell_renderer_text_new ();
  column = gtk_tree_view_column_new_with_attributes ("Application",
						     renderer,
						     "text", APP_COLUMN,
						     NULL);
  gtk_tree_view_append_column (GTK_TREE_VIEW (call_view), column);


  /* test line */
  GtkTreeIter iter;

  gtk_list_store_append (call_store, &iter);
  
  gtk_list_store_set (call_store, &iter,
		      CALL_TOKEN_COLUMN, "test",
		      NAME_COLUMN, "Remote user",
		      URL_COLUMN, "In a galaxy far far away",
		      APP_COLUMN, "Gnomemeeting II",
		      -1);
  /* end test */

  hbox = gtk_hbox_new (FALSE, 6);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);

  entry_label = gtk_label_new ("URL: ");
  gtk_box_pack_start (GTK_BOX (hbox), entry_label, FALSE, FALSE, 0);
  entry = gtk_entry_new ();
  gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
  g_signal_connect (G_OBJECT (entry), "activate",
		    GTK_SIGNAL_FUNC (connect_cb), (gpointer)my_data);

  launch_button = gtk_button_new_with_label ("Launch GM");
  g_signal_connect (G_OBJECT (launch_button), "clicked",
		    GTK_SIGNAL_FUNC (launch_cb), (gpointer)my_data);
  gtk_box_pack_start (GTK_BOX (hbox), launch_button, FALSE, FALSE, 0);
		    

  gtk_widget_show_all (window);

  my_data->connection = dbus_bus_get (DBUS_BUS_SESSION, NULL);

  /* tell DBUS where our ears are */
  if (!dbus_connection_add_filter (my_data->connection,
                                   filter_func,
                                   my_data, NULL)) {
    g_print ("Couldn't add filter\n");
    exit (-1);
  }

  /* DBUS will tell us when gnomemeeting comes and goes */
  dbus_bus_add_match (my_data->connection,
                      "type='signal',"
                      "sender='" DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS "',"
                      "interface='" DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS "'",
                      NULL);

  /* we want to know what gnomemeeting signals */
  dbus_bus_add_match (my_data->connection,
                      "type='signal',"
                      "interface='" INTERFACE "'",
                      NULL);

  update_status (my_data);

  dbus_connection_setup_with_g_main (my_data->connection, NULL);

  gtk_main ();

  return 0;
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]