[GnomeMeeting-devel-list] DBUS component



Hi,

there should be three things attached to this mail:
* a patch to re-enable events in gnomemeeting ;
* a patch to add a dbus component to gnomemeeting ;
* the sources of a little remote using dbus.

The re-enabling patch isn't 100% clean for two reasons:
1) there's a mutex that wasn't there last time, and that I'm not sure
won't be an issue
2) the disabling wasn't 100% clean either ;-)

The dbus component patch, as far as I remember, should be quite clean
and not give any problem.

The remote should mostly work, but will most certainly crash if any
problem arises (ie: no error detection and handling in there -- don't do
that for anything serious).

Now, how to use those:
* patch gnomemeeting (both) ;
* compile gnomemeeting ;
* install gnomemeeting (this will make sure gnomemeeting can be launched
through dbus -- you can skip that step if you don't want that) ;
* launch a dbus daemon (dbus-daemon-1 --session --print-address) and get
its address ;
* launch the remote with DBUS_SESSION_BUS_ADDRESS set to what the daemon
said ;
* launch gnomemeeting (either from the remote if you installed it, or by
hand, setting the DBUS_SESSION_BUS_ADDRESS).
* now you should be able to use gnomemeeting as usual, and the remote
will show if you're calling, in a call, etc...
* or make/stop a call using the remote.

If anything goes wrong, please notify me,

Snark
diff -urN gnomemeeting/configure.in gnomemeeting.patched/configure.in
--- gnomemeeting/configure.in	2004-10-01 18:12:04 +0200
+++ gnomemeeting.patched/configure.in	2004-10-03 11:30:17 +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.patched/gnomemeeting.service.in
--- gnomemeeting/gnomemeeting.service.in	1970-01-01 01:00:00 +0100
+++ gnomemeeting.patched/gnomemeeting.service.in	2004-10-03 11:30:17 +0200
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=org.gnomemeeting.CallService
+Exec= prefix@/bin/gnomemeeting
+
diff -urN gnomemeeting/Makefile.am gnomemeeting.patched/Makefile.am
--- gnomemeeting/Makefile.am	2004-08-14 16:00:31 +0200
+++ gnomemeeting.patched/Makefile.am	2004-10-03 11:30:17 +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.patched/src/dbus_component.cpp
--- gnomemeeting/src/dbus_component.cpp	1970-01-01 01:00:00 +0100
+++ gnomemeeting.patched/src/dbus_component.cpp	2004-10-03 11:30:17 +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.patched/src/dbus_component.h
--- gnomemeeting/src/dbus_component.h	1970-01-01 01:00:00 +0100
+++ gnomemeeting.patched/src/dbus_component.h	2004-10-03 11:30:17 +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.patched/src/gnomemeeting.cpp
--- gnomemeeting/src/gnomemeeting.cpp	2004-09-24 08:47:19 +0200
+++ gnomemeeting.patched/src/gnomemeeting.cpp	2004-10-03 11:30:17 +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 "dialog.h"
 #include "e-splash.h"
@@ -137,6 +140,8 @@
     gtk_widget_destroy (main_window);
   if (druid_window)
     gtk_widget_destroy (druid_window);
+  if (dbus_component)
+    g_object_unref (dbus_component);
 }
 
 
@@ -417,6 +422,11 @@
 #ifndef WIN32
   tray = gm_tray_new ();
 #endif
+#ifdef HAS_DBUS
+  dbus_component = dbus_component_new (endpoint);
+#else
+  dbus_component = NULL;
+#endif
   main_window = gm_main_window_new ();
 
 
diff -urN gnomemeeting/src/gnomemeeting.h gnomemeeting.patched/src/gnomemeeting.h
--- gnomemeeting/src/gnomemeeting.h	2004-09-10 21:20:22 +0200
+++ gnomemeeting.patched/src/gnomemeeting.h	2004-10-03 11:30:17 +0200
@@ -274,6 +274,9 @@
   GtkWidget *pc2phone_window;
   GtkWidget *tray;
 
+  /* other things */
+  GObject *dbus_component;
+
   static GnomeMeeting *GM;
 };
 
diff -urN gnomemeeting/src/Makefile.am gnomemeeting.patched/src/Makefile.am
--- gnomemeeting/src/Makefile.am	2004-10-01 18:12:04 +0200
+++ gnomemeeting.patched/src/Makefile.am	2004-10-03 11:30:18 +0200
@@ -61,6 +61,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
diff -urN gnomemeeting/lib/Makefile.am gnomemeeting.events/lib/Makefile.am
--- gnomemeeting/lib/Makefile.am	2004-10-16 20:04:37 +0200
+++ gnomemeeting.events/lib/Makefile.am	2004-10-21 09:50:58 +0200
@@ -29,7 +29,9 @@
 	eggtrayicon.h					\
 	eggtrayicon.c					\
 	stats_drawing_area.h				\
-	stats_drawing_area.c				
+	stats_drawing_area.c				\
+	gm_events.h					\
+	gm_events.c
 
 if DISABLE_GNOME
 libgnomemeeting_la_SOURCES += gm_conf-glib.c
diff -urN gnomemeeting/src/endpoint.cpp gnomemeeting.events/src/endpoint.cpp
--- gnomemeeting/src/endpoint.cpp	2004-10-21 07:30:19 +0200
+++ gnomemeeting.events/src/endpoint.cpp	2004-10-21 09:51:56 +0200
@@ -57,6 +57,7 @@
 
 #include "dialog.h"
 #include "gm_conf.h"
+#include "gm_events.h"
 
 #include <h261codec.h>
 
@@ -108,6 +109,8 @@
 
   missed_calls = 0;
 
+  dispatcher = gm_events_dispatcher_new ();
+
   last_audio_octets_received = 0;
   last_video_octets_received = 0;
   last_audio_octets_transmitted = 0;
@@ -140,6 +143,9 @@
   if (ils_client)
     delete (ils_client);
 
+  if (dispatcher)
+    g_object_unref (dispatcher);
+
   /* Create a new one to unregister */
   if (ils_registered) {
     
@@ -322,6 +328,9 @@
   PWaitAndSignal m(cs_access_mutex);
   
   calling_state = i;
+  /* FIXME: holding a mutex!? */
+  if (dispatcher)
+    g_signal_emit_by_name (dispatcher, "endpoint-state-changed", i);
 }
 
 
@@ -1087,6 +1096,8 @@
   gm_tray_update_calling_state (tray, GMH323EndPoint::Connected);
   gm_tray_update (tray, GMH323EndPoint::Connected, icm, forward_on_busy);
   gnomemeeting_threads_leave ();
+  if (dispatcher)
+    g_signal_emit_by_name (dispatcher, "call-begin", (const gchar *)token);
 
   
   /* Update ILS if needed */
@@ -1351,6 +1362,9 @@
   gnomemeeting_text_chat_call_stop_notification (chat_window);
   gm_main_window_flash_message (main_window, msg_reason);
   gnomemeeting_threads_leave ();
+  if (dispatcher)
+    g_signal_emit_by_name (dispatcher, "call-end",
+			   (const gchar *)clearedCallToken);
 
   g_free (utf8_app);
   g_free (utf8_name);
@@ -2720,6 +2734,13 @@
   return missed_calls;
 }
 
+void
+GMH323EndPoint::AddObserver (GObject *observer)
+{
+  g_return_if_fail (observer != NULL);
+
+  gm_events_dispatcher_add_observer (dispatcher, observer);
+}
 
 PString
 GMH323EndPoint::GetLastCallAddress ()
diff -urN gnomemeeting/src/endpoint.h gnomemeeting.events/src/endpoint.h
--- gnomemeeting/src/endpoint.h	2004-10-21 07:30:19 +0200
+++ gnomemeeting.events/src/endpoint.h	2004-10-21 09:50:58 +0200
@@ -738,6 +738,7 @@
   H323ListenerTCP *listener;  
 
   CallingState calling_state; 
+  GObject *dispatcher;
 
   PTimer GatekeeperTimer;
   PTimer ILSTimer;
diff -urN gnomemeeting/src/main_window.cpp gnomemeeting.events/src/main_window.cpp
--- gnomemeeting/src/main_window.cpp	2004-10-21 07:30:19 +0200
+++ gnomemeeting.events/src/main_window.cpp	2004-10-21 09:50:58 +0200
@@ -49,6 +49,7 @@
 #include "lid.h"
 #include "sound_handling.h"
 #include "urlhandler.h"
+#include "gm_events.h"
 
 #include <dialog.h>
 #include <gmentrydialog.h>
@@ -4139,7 +4140,7 @@
   xmlInitParser ();
 
   gm_conf_init (argc, argv);
-  
+  gm_events_init ();
   
   /* Upgrade the preferences */
   gnomemeeting_conf_upgrade ();
#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]