aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXavier Claessens <xclaesse@gmail.com>2007-09-29 20:08:45 +0800
committerXavier Claessens <xclaesse@src.gnome.org>2007-09-29 20:08:45 +0800
commite3e5702a683c468ccc1742515b4033f32c7d1083 (patch)
tree9036ec7c855079e59ec6b67128ee37134bb79abf
parentf5490a416f103d552344f7df0238a39c1e76b537 (diff)
downloadgsoc2013-empathy-e3e5702a683c468ccc1742515b4033f32c7d1083.tar
gsoc2013-empathy-e3e5702a683c468ccc1742515b4033f32c7d1083.tar.gz
gsoc2013-empathy-e3e5702a683c468ccc1742515b4033f32c7d1083.tar.bz2
gsoc2013-empathy-e3e5702a683c468ccc1742515b4033f32c7d1083.tar.lz
gsoc2013-empathy-e3e5702a683c468ccc1742515b4033f32c7d1083.tar.xz
gsoc2013-empathy-e3e5702a683c468ccc1742515b4033f32c7d1083.tar.zst
gsoc2013-empathy-e3e5702a683c468ccc1742515b4033f32c7d1083.zip
Initial Voice+Video support Fixes bug #468204 (Elliot Fairweather, Xavier
2007-09-29 Xavier Claessens <xclaesse@gmail.com> * libempathy-gtk/empathy-call-window.c: * libempathy-gtk/empathy-call-window.h: * libempathy-gtk/empathy-contact-list-view.c: * libempathy-gtk/empathy-call-window.glade: * libempathy-gtk/Makefile.am: * libempathy-gtk/empathy-chat-window.c: * src/empathy-call-chandler.c: * src/empathy-call.chandler: * src/org.gnome.Empathy.Call.service.in: * src/Makefile.am: * libempathy/empathy-utils.c: * libempathy/empathy-utils.h: * libempathy/empathy-tp-call.c: * libempathy/empathy-tp-call.h: * libempathy/Makefile.am: * libempathy/tp-stream-engine.xml: * configure.ac: * doc/libempathy-gtk/libempathy-gtk-docs.sgml: * doc/libempathy/libempathy.types: * doc/libempathy/libempathy-docs.sgml: * doc/libempathy/Makefile.am: * doc/libempathy/tmpl/empathy-utils.sgml: Initial Voice+Video support Fixes bug #468204 (Elliot Fairweather, Xavier Claessens). svn path=/trunk/; revision=339
-rw-r--r--ChangeLog33
-rw-r--r--configure.ac17
-rw-r--r--doc/libempathy-gtk/libempathy-gtk-docs.sgml1
-rw-r--r--doc/libempathy/Makefile.am6
-rw-r--r--doc/libempathy/libempathy-docs.sgml1
-rw-r--r--doc/libempathy/libempathy.types2
-rw-r--r--doc/libempathy/tmpl/empathy-utils.sgml8
-rw-r--r--libempathy-gtk/Makefile.am3
-rw-r--r--libempathy-gtk/empathy-call-window.c198
-rw-r--r--libempathy-gtk/empathy-call-window.glade335
-rw-r--r--libempathy-gtk/empathy-call-window.h36
-rw-r--r--libempathy-gtk/empathy-chat-window.c4
-rw-r--r--libempathy-gtk/empathy-contact-list-view.c55
-rw-r--r--libempathy/Makefile.am9
-rw-r--r--libempathy/empathy-tp-call.c655
-rw-r--r--libempathy/empathy-tp-call.h89
-rw-r--r--libempathy/empathy-utils.c62
-rw-r--r--libempathy/empathy-utils.h1
-rw-r--r--libempathy/tp-stream-engine.xml54
-rw-r--r--src/Makefile.am17
-rw-r--r--src/empathy-call-chandler.c79
-rw-r--r--src/empathy-call.chandler5
-rw-r--r--src/org.gnome.Empathy.Call.service.in3
23 files changed, 1606 insertions, 67 deletions
diff --git a/ChangeLog b/ChangeLog
index 9a716210d..108cb1f40 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,10 +1,28 @@
-2007-09-28 Xavier Claessens <xclaesse@gmail.com>
+2007-09-29 Xavier Claessens <xclaesse@gmail.com>
+ * libempathy-gtk/empathy-call-window.c:
+ * libempathy-gtk/empathy-call-window.h:
* libempathy-gtk/empathy-contact-list-view.c:
+ * libempathy-gtk/empathy-call-window.glade:
+ * libempathy-gtk/Makefile.am:
* libempathy-gtk/empathy-chat-window.c:
+ * src/empathy-call-chandler.c:
+ * src/empathy-call.chandler:
+ * src/org.gnome.Empathy.Call.service.in:
* src/Makefile.am:
- * libempathy/empathy-tp-group.c:
- * libempathy/Makefile.am: Ported from VOIP branch.
+ * libempathy/empathy-utils.c:
+ * libempathy/empathy-utils.h:
+ * libempathy/empathy-tp-call.c:
+ * libempathy/empathy-tp-call.h:
+ * libempathy/Makefile.am:
+ * libempathy/tp-stream-engine.xml:
+ * configure.ac:
+ * doc/libempathy-gtk/libempathy-gtk-docs.sgml:
+ * doc/libempathy/libempathy.types:
+ * doc/libempathy/libempathy-docs.sgml:
+ * doc/libempathy/Makefile.am:
+ * doc/libempathy/tmpl/empathy-utils.sgml: Initial Voice+Video support
+ Fixes bug #468204 (Elliot Fairweather, Xavier Claessens).
2007-09-28 Xavier Claessens <xclaesse@gmail.com>
@@ -33,6 +51,10 @@
2007-09-28 Xavier Claessens <xclaesse@gmail.com>
+ * src/Makefile.am: Correctly modify service.in files to expand bindir.
+
+2007-09-28 Xavier Claessens <xclaesse@gmail.com>
+
* libempathy/empathy-tp-group.c: Look in the local_pendings list if a
contact is already local pending.
* tests/contact-manager.c: Fix white space typo.
@@ -1674,4 +1696,9 @@
2007-03-16 Xavier Claessens <xclaesse@gmail.com>
* Initial version
+2007-09-28 Xavier Claessens <xclaesse@gmail.com>
+
+ * libempathy/empathy-tp-group.c: Look in the local_pendings list if a
+ contact is already local pending.
+ * tests/contact-manager.c: Fix white space typo.
diff --git a/configure.ac b/configure.ac
index f3fe46945..9fd3a1c36 100644
--- a/configure.ac
+++ b/configure.ac
@@ -251,6 +251,20 @@ fi
AM_CONDITIONAL(HAVE_PYTHON, test "x$have_python" = "xyes")
dnl -----------------------------------------------------------
+dnl VoIP support
+dnl -----------------------------------------------------------
+AC_ARG_ENABLE(voip,
+ AS_HELP_STRING([--enable-voip=@<:@no/yes@:>@],
+ [Add support for Voice and Video call]), ,
+ enable_voip=no)
+
+if test "x$enable_voip" = "xyes"; then
+ AC_DEFINE(HAVE_VOIP, 1, [Define if we have voip])
+fi
+
+AM_CONDITIONAL(HAVE_VOIP, test "x$enable_voip" = "xyes")
+
+dnl -----------------------------------------------------------
AC_OUTPUT([
Makefile
@@ -273,7 +287,8 @@ AC_OUTPUT([
megaphone/data/Makefile
nothere/Makefile
nothere/src/Makefile
- nothere/data/Makefile doc/Makefile
+ nothere/data/Makefile
+ doc/Makefile
doc/libempathy/Makefile
doc/libempathy-gtk/Makefile
python/Makefile
diff --git a/doc/libempathy-gtk/libempathy-gtk-docs.sgml b/doc/libempathy-gtk/libempathy-gtk-docs.sgml
index 05f9bd2bc..f66ffa1f4 100644
--- a/doc/libempathy-gtk/libempathy-gtk-docs.sgml
+++ b/doc/libempathy-gtk/libempathy-gtk-docs.sgml
@@ -43,5 +43,6 @@
<xi:include href="xml/empathy-contact-groups.xml"/>
<xi:include href="xml/empathy-log-window.xml"/>
<xi:include href="xml/empathy-spell-dialog.xml"/>
+ <xi:include href="xml/empathy-call-window.xml"/>
</chapter>
</book>
diff --git a/doc/libempathy/Makefile.am b/doc/libempathy/Makefile.am
index 0988c364f..206e2218a 100644
--- a/doc/libempathy/Makefile.am
+++ b/doc/libempathy/Makefile.am
@@ -43,7 +43,11 @@ CFILE_GLOB=$(top_srcdir)/libempathy/*.c
# Header files to ignore when scanning.
# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h
-IGNORE_HFILES=empathy-filter-glue.h empathy-debug.h empathy-marshal.h
+IGNORE_HFILES= \
+ empathy-filter-glue.h \
+ empathy-debug.h \
+ empathy-marshal.h \
+ tp-stream-engine-gen.h
# Images to copy into HTML directory.
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
diff --git a/doc/libempathy/libempathy-docs.sgml b/doc/libempathy/libempathy-docs.sgml
index 6c1bd79d1..942c5f21d 100644
--- a/doc/libempathy/libempathy-docs.sgml
+++ b/doc/libempathy/libempathy-docs.sgml
@@ -19,6 +19,7 @@
<xi:include href="xml/empathy-tp-contact-list.xml"/>
<xi:include href="xml/empathy-tp-chat.xml"/>
<xi:include href="xml/empathy-tp-chatroom.xml"/>
+ <xi:include href="xml/empathy-tp-call.xml"/>
<xi:include href="xml/empathy-contact-factory.xml"/>
<xi:include href="xml/empathy-chatroom.xml"/>
<xi:include href="xml/empathy-chatroom-manager.xml"/>
diff --git a/doc/libempathy/libempathy.types b/doc/libempathy/libempathy.types
index 3f8d7bf01..dbebb0bc0 100644
--- a/doc/libempathy/libempathy.types
+++ b/doc/libempathy/libempathy.types
@@ -17,6 +17,7 @@
#include <libempathy/empathy-tp-chatroom.h>
#include <libempathy/empathy-enum-types.h>
#include <libempathy/empathy-contact-factory.h>
+#include <libempathy/empathy-tp-call.h>
empathy_avatar_get_type
empathy_chatroom_manager_get_type
@@ -38,4 +39,5 @@ empathy_tp_chatroom_get_type
empathy_reg_ex_type_get_type
empathy_message_type_get_type
empathy_contact_factory_get_type
+empathy_tp_call_get_type
diff --git a/doc/libempathy/tmpl/empathy-utils.sgml b/doc/libempathy/tmpl/empathy-utils.sgml
index fb8edba30..f25e0f6a3 100644
--- a/doc/libempathy/tmpl/empathy-utils.sgml
+++ b/doc/libempathy/tmpl/empathy-utils.sgml
@@ -188,3 +188,11 @@ empathy-utils
@Returns:
+<!-- ##### FUNCTION empathy_call_contact ##### -->
+<para>
+
+</para>
+
+@contact:
+
+
diff --git a/libempathy-gtk/Makefile.am b/libempathy-gtk/Makefile.am
index a825f4427..fa13e27cc 100644
--- a/libempathy-gtk/Makefile.am
+++ b/libempathy-gtk/Makefile.am
@@ -47,6 +47,7 @@ libempathy_gtk_la_SOURCES = \
empathy-new-chatroom-dialog.c \
empathy-chatrooms-window.c \
empathy-log-window.c \
+ empathy-call-window.c \
empathy-ui-utils.c
# do not distribute generated files
@@ -96,6 +97,7 @@ libempathy_gtk_headers = \
empathy-new-chatroom-dialog.h \
empathy-chatrooms-window.h \
empathy-log-window.h \
+ empathy-call-window.h \
empathy-ui-utils.h
libempathy_gtk_includedir = $(includedir)/libempathy-gtk/
@@ -120,6 +122,7 @@ glade_DATA = \
empathy-chatrooms-window.glade \
empathy-spell-dialog.glade \
empathy-log-window.glade \
+ empathy-call-window.glade \
empathy-chat.glade
empathy-gtk-enum-types.h: stamp-empathy-gtk-enum-types.h
diff --git a/libempathy-gtk/empathy-call-window.c b/libempathy-gtk/empathy-call-window.c
new file mode 100644
index 000000000..baa169755
--- /dev/null
+++ b/libempathy-gtk/empathy-call-window.c
@@ -0,0 +1,198 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Elliot Fairweather
+ * Copyright (C) 2007 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors: Elliot Fairweather <elliot.fairweather@collabora.co.uk>
+ * Xavier Claessens <xclaesse@gmail.com>
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <libempathy/empathy-debug.h>
+
+#include "empathy-call-window.h"
+#include "empathy-ui-utils.h"
+
+#define DEBUG_DOMAIN "CallWindow"
+
+typedef struct {
+ GtkWidget *window;
+ GtkWidget *input_volume_scale;
+ GtkWidget *output_volume_scale;
+ GtkWidget *input_mute_togglebutton;
+ GtkWidget *output_mute_togglebutton;
+ GtkWidget *preview_video_frame;
+ GtkWidget *output_video_frame;
+ GtkWidget *preview_video_socket;
+ GtkWidget *output_video_socket;
+ GtkWidget *send_video_checkbutton;
+
+ EmpathyTpCall *call;
+} EmpathyCallWindow;
+
+static void
+call_window_output_volume_changed_cb (GtkWidget *scale,
+ EmpathyCallWindow *window)
+{
+ guint volume;
+
+ volume = (guint) gtk_range_get_value (GTK_RANGE (scale));
+ empathy_tp_call_set_output_volume (window->call, volume);
+}
+
+
+static void
+call_window_output_mute_toggled_cb (GtkWidget *button,
+ EmpathyCallWindow *window)
+{
+ gboolean is_muted;
+
+ is_muted = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+ empathy_tp_call_mute_output (window->call, is_muted);
+}
+
+
+static void
+call_window_input_mute_toggled_cb (GtkWidget *button,
+ EmpathyCallWindow *window)
+{
+ gboolean is_muted;
+
+ is_muted = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+ empathy_tp_call_mute_input (window->call, is_muted);
+}
+
+
+static void
+call_window_send_video_toggled_cb (GtkWidget *button,
+ EmpathyCallWindow *window)
+{
+ gboolean is_sending;
+
+ is_sending = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+ empathy_tp_call_send_video (window->call, is_sending);
+}
+
+static void
+call_window_capabilities_notify_cb (EmpathyContact *contact,
+ GParamSpec *param,
+ EmpathyCallWindow *window)
+{
+ EmpathyCapabilities capabilities;
+
+ capabilities = empathy_contact_get_capabilities (contact);
+ empathy_tp_call_request_streams (window->call,
+ capabilities & EMPATHY_CAPABILITIES_AUDIO,
+ capabilities & EMPATHY_CAPABILITIES_VIDEO);
+}
+
+static void
+call_window_status_notify_cb (EmpathyTpCall *call,
+ GParamSpec *param,
+ EmpathyCallWindow *window)
+{
+ guint status;
+
+ status = empathy_tp_call_get_status (call);
+ empathy_debug (DEBUG_DOMAIN, "Status changed to %d",
+ status);
+
+ if (status == EMPATHY_TP_CALL_STATUS_RINGING) {
+ if (empathy_tp_call_is_incoming (window->call)) {
+ empathy_tp_call_accept (window->call);
+ } else {
+ EmpathyContact *contact;
+
+ contact = empathy_tp_call_get_contact (call);
+ g_signal_connect (contact, "notify::capabilities",
+ G_CALLBACK (call_window_capabilities_notify_cb),
+ window);
+ call_window_capabilities_notify_cb (contact, NULL, window);
+ }
+ }
+
+ if (status == EMPATHY_TP_CALL_STATUS_RUNNING) {
+ empathy_tp_call_set_output_window (window->call,
+ gtk_socket_get_id (GTK_SOCKET (window->output_video_socket)));
+ }
+}
+
+static void
+call_window_destroy_cb (GtkWidget *widget,
+ EmpathyCallWindow *window)
+{
+ g_object_unref (window->call);
+ g_slice_free (EmpathyCallWindow, window);
+}
+
+void
+empathy_call_window_show (EmpathyTpCall *call)
+{
+ EmpathyCallWindow *window;
+ GladeXML *glade;
+
+ window = g_slice_new0 (EmpathyCallWindow);
+
+ glade = empathy_glade_get_file ("empathy-call-window.glade",
+ "window",
+ NULL,
+ "window", &window->window,
+ "input_volume_scale", &window->input_volume_scale,
+ "output_volume_scale", &window->output_volume_scale,
+ "input_mute_togglebutton", &window->input_mute_togglebutton,
+ "output_mute_togglebutton", &window->output_mute_togglebutton,
+ "preview_video_frame", &window->preview_video_frame,
+ "output_video_frame", &window->output_video_frame,
+ "send_video_checkbutton", &window->send_video_checkbutton,
+ NULL);
+
+ empathy_glade_connect (glade,
+ window,
+ "window", "destroy", call_window_destroy_cb,
+ "input_mute_togglebutton", "toggled", call_window_input_mute_toggled_cb,
+ "output_mute_togglebutton", "toggled", call_window_output_mute_toggled_cb,
+ "output_volume_scale", "value-changed", call_window_output_volume_changed_cb,
+ "send_video_checkbutton", "toggled", call_window_send_video_toggled_cb,
+ NULL);
+ g_object_unref (glade);
+
+ /* Set output window socket */
+ window->output_video_socket = gtk_socket_new ();
+ gtk_widget_show (window->output_video_socket);
+ gtk_container_add (GTK_CONTAINER (window->output_video_frame),
+ window->output_video_socket);
+
+ /* Set preview window socket */
+ window->preview_video_socket = gtk_socket_new ();
+ gtk_widget_show (window->preview_video_socket);
+ gtk_container_add (GTK_CONTAINER (window->preview_video_frame),
+ window->preview_video_socket);
+
+ /* Setup TpCall */
+ window->call = g_object_ref (call);
+ empathy_tp_call_add_preview_window (window->call,
+ gtk_socket_get_id (GTK_SOCKET (window->preview_video_socket)));
+ g_signal_connect (window->call, "notify::status",
+ G_CALLBACK (call_window_status_notify_cb),
+ window);
+
+ gtk_widget_show (window->window);
+}
+
diff --git a/libempathy-gtk/empathy-call-window.glade b/libempathy-gtk/empathy-call-window.glade
new file mode 100644
index 000000000..bc18952b6
--- /dev/null
+++ b/libempathy-gtk/empathy-call-window.glade
@@ -0,0 +1,335 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="window">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="title" translatable="yes">Call</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">10</property>
+
+ <child>
+ <widget class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Input</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVScale" id="input_volume_scale">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="draw_value">False</property>
+ <property name="value_pos">GTK_POS_TOP</property>
+ <property name="digits">1</property>
+ <property name="update_policy">GTK_UPDATE_CONTINUOUS</property>
+ <property name="inverted">True</property>
+ <property name="adjustment">100 0 100 1 0 0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkToggleButton" id="input_mute_togglebutton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Mute</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox5">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Output</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVScale" id="output_volume_scale">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="draw_value">False</property>
+ <property name="value_pos">GTK_POS_TOP</property>
+ <property name="digits">1</property>
+ <property name="update_policy">GTK_UPDATE_CONTINUOUS</property>
+ <property name="inverted">True</property>
+ <property name="adjustment">100 0 100 1 0 0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkToggleButton" id="output_mute_togglebutton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Mute</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Volume</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkAspectFrame" id="output_video_frame">
+ <property name="width_request">352</property>
+ <property name="height_request">288</property>
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="ratio">1.22000002861</property>
+ <property name="obey_child">False</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkAspectFrame" id="preview_video_frame">
+ <property name="width_request">176</property>
+ <property name="height_request">144</property>
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="ratio">1.22000002861</property>
+ <property name="obey_child">False</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="send_video_checkbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Send Video</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">True</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">10</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
diff --git a/libempathy-gtk/empathy-call-window.h b/libempathy-gtk/empathy-call-window.h
new file mode 100644
index 000000000..ea310d622
--- /dev/null
+++ b/libempathy-gtk/empathy-call-window.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Elliot Fairweather
+ * Copyright (C) 2007 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors: Elliot Fairweather <elliot.fairweather@collabora.co.uk>
+ * Xavier Claessens <xclaesse@gmail.com>
+ */
+
+#ifndef __EMPATHY_CALL_WINDOW_H__
+#define __EMPATHY_CALL_WINDOW_H__
+
+#include <libempathy/empathy-tp-call.h>
+
+G_BEGIN_DECLS
+
+void empathy_call_window_show (EmpathyTpCall *call);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_CALL_WINDOW_H__ */
+
diff --git a/libempathy-gtk/empathy-chat-window.c b/libempathy-gtk/empathy-chat-window.c
index 29fe5a521..41402f252 100644
--- a/libempathy-gtk/empathy-chat-window.c
+++ b/libempathy-gtk/empathy-chat-window.c
@@ -883,9 +883,7 @@ chat_window_call_activate_cb (GtkWidget *menuitem,
chat = EMPATHY_PRIVATE_CHAT (priv->current_chat);
contact = empathy_private_chat_get_contact (chat);
- /* FIXME: See contact_list_view_voip_activated() to know how to
- * call a contact. We need a function to call a contact and use
- * it here and in EmpathyContactListView. */
+ empathy_call_contact (contact);
}
}
diff --git a/libempathy-gtk/empathy-contact-list-view.c b/libempathy-gtk/empathy-contact-list-view.c
index c959eec01..1fcbf4440 100644
--- a/libempathy-gtk/empathy-contact-list-view.c
+++ b/libempathy-gtk/empathy-contact-list-view.c
@@ -1438,59 +1438,6 @@ static void
contact_list_view_voip_activated (EmpathyContactListView *view,
EmpathyContact *contact)
{
- MissionControl *mc;
- McAccount *account;
- TpConn *tp_conn;
- gchar *object_path;
- const gchar *bus_name;
- TpChan *new_chan;
- EmpathyTpGroup *group;
- GError *error;
-
- /* StreamedMedia channels must have handle=0 and handle_type=none.
- * To call a contact we have to add him in the group interface of the
- * channel. MissionControl will detect the channel creation and
- * dispatch it to the VoIP chandler automatically. */
-
- mc = empathy_mission_control_new ();
- account = empathy_contact_get_account (contact);
- tp_conn = mission_control_get_connection (mc, account, NULL);
- /* FIXME: Should be async */
- if (!tp_conn_request_channel (DBUS_G_PROXY (tp_conn),
- TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
- TP_HANDLE_TYPE_NONE,
- 0,
- FALSE,
- &object_path,
- &error)) {
- empathy_debug (DEBUG_DOMAIN,
- "Couldn't request channel: %s",
- error ? error->message : "No error given");
- g_clear_error (&error);
- g_object_unref (mc);
- g_object_unref (tp_conn);
- return;
- }
-
- bus_name = dbus_g_proxy_get_bus_name (DBUS_G_PROXY (tp_conn));
- new_chan = tp_chan_new (tp_get_bus (),
- bus_name,
- object_path,
- TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
- TP_HANDLE_TYPE_NONE,
- 0);
-
- /* FIXME: group is leaked, we can't unref it directly because
- * _add_member is async so we have to wait for it to return before
- * finalizing the group. I think EmpathyTpGroup should ref itself
- * when it does async calls to avoid finalizing when there is calls
- * in fligth like that we could unref it here. */
- group = empathy_tp_group_new (account, new_chan);
- empathy_tp_group_add_member (group, contact, "");
-
- g_object_unref (mc);
- g_object_unref (tp_conn);
- g_object_unref (new_chan);
- g_free (object_path);
+ empathy_call_contact (contact);
}
diff --git a/libempathy/Makefile.am b/libempathy/Makefile.am
index 9dc4021e2..0480d0bff 100644
--- a/libempathy/Makefile.am
+++ b/libempathy/Makefile.am
@@ -34,6 +34,7 @@ libempathy_la_SOURCES = \
empathy-tp-chat.c \
empathy-tp-chatroom.c \
empathy-tp-roomlist.c \
+ empathy-tp-call.c \
empathy-chandler.c \
empathy-filter.c \
empathy-idle.c \
@@ -68,10 +69,12 @@ libempathy_headers = \
empathy-tp-chat.h \
empathy-tp-chatroom.h \
empathy-tp-roomlist.h \
+ empathy-tp-call.h \
empathy-chandler.h \
empathy-filter.h \
empathy-idle.h \
- empathy-log-manager.h
+ empathy-log-manager.h \
+ tp-stream-engine-gen.h
libempathy_includedir = $(includedir)/libempathy/
libempathy_include_HEADERS = \
@@ -90,6 +93,10 @@ empathy-chandler-glue.h: empathy-chandler.xml Makefile
empathy-filter-glue.h: empathy-filter.xml Makefile
$(LIBTOOL) --mode=execute $(DBUS_BINDING_TOOL) --prefix=empathy_filter --mode=glib-server --output=$@ $<
+tp-stream-engine-gen.h: tp-stream-engine.xml Makefile.am
+ $(LIBTOOL) --mode=execute $(DBUS_BINDING_TOOL) --prefix=tp_stream_engine --mode=glib-client --output=$@ $<
+
+
empathy-enum-types.h: stamp-empathy-enum-types.h
@true
stamp-empathy-enum-types.h: $(libempathy_headers) Makefile
diff --git a/libempathy/empathy-tp-call.c b/libempathy/empathy-tp-call.c
new file mode 100644
index 000000000..e0347ba9a
--- /dev/null
+++ b/libempathy/empathy-tp-call.c
@@ -0,0 +1,655 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Elliot Fairweather
+ * Copyright (C) 2007 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors: Elliot Fairweather <elliot.fairweather@collabora.co.uk>
+ * Xavier Claessens <xclaesse@gmail.com>
+ */
+
+#include "config.h"
+
+#include <libtelepathy/tp-chan-type-streamed-media-gen.h>
+#include <libtelepathy/tp-helpers.h>
+#include <libtelepathy/tp-conn.h>
+
+#include <libmissioncontrol/mission-control.h>
+
+#include "empathy-tp-call.h"
+#include "empathy-tp-group.h"
+#include "empathy-utils.h"
+#include "empathy-debug.h"
+#include "empathy-enum-types.h"
+#include "tp-stream-engine-gen.h"
+
+#define DEBUG_DOMAIN "TpCall"
+
+#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_TP_CALL, EmpathyTpCallPriv))
+
+#define STREAM_ENGINE_BUS_NAME "org.freedesktop.Telepathy.StreamEngine"
+#define STREAM_ENGINE_OBJECT_PATH "/org/freedesktop/Telepathy/StreamEngine"
+#define STREAM_ENGINE_INTERFACE "org.freedesktop.Telepathy.StreamEngine"
+#define CHANNEL_HANDLER_INTERFACE "org.freedesktop.Telepathy.ChannelHandler"
+
+typedef struct _EmpathyTpCallPriv EmpathyTpCallPriv;
+
+struct _EmpathyTpCallPriv {
+ TpChan *tp_chan;
+ DBusGProxy *streamed_iface;
+ DBusGProxy *se_ch_proxy;
+ DBusGProxy *se_proxy;
+ McAccount *account;
+ EmpathyTpGroup *group;
+ EmpathyContact *contact;
+ EmpathyTpCallStatus status;
+ gboolean is_incoming;
+ guint audio_stream;
+ guint video_stream;
+};
+
+static void empathy_tp_call_class_init (EmpathyTpCallClass *klass);
+static void empathy_tp_call_init (EmpathyTpCall *call);
+
+enum {
+ PROP_0,
+ PROP_ACCOUNT,
+ PROP_TP_CHAN,
+ PROP_STATUS
+};
+
+enum {
+ DESTROY,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (EmpathyTpCall, empathy_tp_call, G_TYPE_OBJECT)
+
+static void
+tp_call_set_status (EmpathyTpCall *call,
+ EmpathyTpCallStatus status)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+
+ priv->status = status;
+ g_object_notify (G_OBJECT (call), "status");
+}
+
+static void
+tp_call_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (object);
+
+ switch (prop_id) {
+ case PROP_ACCOUNT:
+ priv->account = g_object_ref (g_value_get_object (value));
+ break;
+ case PROP_TP_CHAN:
+ priv->tp_chan = g_object_ref (g_value_get_object (value));
+ break;
+ case PROP_STATUS:
+ tp_call_set_status (EMPATHY_TP_CALL (object),
+ g_value_get_enum (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+tp_call_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (object);
+
+ switch (prop_id) {
+ case PROP_ACCOUNT:
+ g_value_set_object (value, priv->account);
+ break;
+ case PROP_TP_CHAN:
+ g_value_set_object (value, priv->tp_chan);
+ break;
+ case PROP_STATUS:
+ g_value_set_enum (value, priv->status);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+tp_call_destroy_cb (TpChan *call_chan,
+ EmpathyTpCall *call)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+
+ empathy_debug (DEBUG_DOMAIN, "Channel Closed or CM crashed");
+
+ g_object_unref (priv->tp_chan);
+ priv->tp_chan = NULL;
+ priv->streamed_iface = NULL;
+
+ g_signal_emit (call, signals[DESTROY], 0);
+}
+
+static void
+tp_call_closed_cb (TpChan *call_chan,
+ EmpathyTpCall *call)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+
+ /* The channel is closed, do just like if the proxy was destroyed */
+ g_signal_handlers_disconnect_by_func (priv->tp_chan,
+ tp_call_destroy_cb,
+ call);
+ tp_call_destroy_cb (call_chan, call);
+}
+
+static void
+tp_call_stream_added_cb (DBusGProxy *streamed_iface,
+ guint stream_id,
+ guint contact_handle,
+ guint stream_type,
+ EmpathyTpCall *call)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+
+ empathy_debug (DEBUG_DOMAIN, "Stream added: id=%d, stream_type=%d",
+ stream_id, stream_type);
+
+ switch (stream_type) {
+ case TP_MEDIA_STREAM_TYPE_AUDIO:
+ priv->audio_stream = stream_id;
+ break;
+ case TP_MEDIA_STREAM_TYPE_VIDEO:
+ priv->video_stream = stream_id;
+ break;
+ default:
+ empathy_debug (DEBUG_DOMAIN, "Unknown stream type: %d", stream_type);
+ }
+}
+
+
+static void
+tp_call_stream_removed_cb (DBusGProxy *streamed_iface,
+ guint stream_id,
+ EmpathyTpCall *call)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+
+ empathy_debug (DEBUG_DOMAIN, "Stream removed: %d", stream_id);
+
+ if (stream_id == priv->audio_stream) {
+ priv->audio_stream = 0;
+ }
+ else if (stream_id == priv->video_stream) {
+ priv->video_stream = 0;
+ }
+}
+
+static void
+tp_call_list_streams_cb (DBusGProxy *proxy,
+ GPtrArray *streams,
+ GError *error,
+ gpointer user_data)
+{
+ guint i;
+
+ if (error) {
+ empathy_debug (DEBUG_DOMAIN, "Failed to list streams: %s",
+ error->message);
+ return;
+ }
+
+ for (i = 0; i < streams->len; i++) {
+ GValueArray *values;
+ guint stream_id;
+ guint contact_handle;
+ guint stream_type;
+
+ values = g_ptr_array_index (streams, i);
+ stream_id = g_value_get_uint (g_value_array_get_nth (values, 0));
+ contact_handle = g_value_get_uint (g_value_array_get_nth (values, 1));
+ stream_type = g_value_get_uint (g_value_array_get_nth (values, 2));
+
+ tp_call_stream_added_cb (proxy,
+ stream_id,
+ contact_handle,
+ stream_type,
+ user_data);
+ }
+}
+
+static void
+tp_call_member_added_cb (EmpathyTpGroup *group,
+ EmpathyContact *contact,
+ EmpathyContact *actor,
+ guint reason,
+ const gchar *message,
+ EmpathyTpCall *call)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+
+ empathy_debug (DEBUG_DOMAIN, "Members added %s (%d)",
+ empathy_contact_get_id (contact),
+ empathy_contact_get_handle (contact));
+
+ if (!priv->contact) {
+ if (!empathy_contact_is_user (contact)) {
+ priv->is_incoming = TRUE;
+ priv->contact = g_object_ref (contact);
+ tp_call_set_status (call, EMPATHY_TP_CALL_STATUS_RINGING);
+ }
+ return;
+ }
+
+ /* We already have the other contact, that means we now have 2 members,
+ * so we can start the call */
+ tp_call_set_status (call, EMPATHY_TP_CALL_STATUS_RUNNING);
+}
+
+static void
+tp_call_remote_pending_cb (EmpathyTpGroup *group,
+ EmpathyContact *contact,
+ EmpathyContact *actor,
+ guint reason,
+ const gchar *message,
+ EmpathyTpCall *call)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+
+ empathy_debug (DEBUG_DOMAIN, "Remote pending: %s (%d)",
+ empathy_contact_get_id (contact),
+ empathy_contact_get_handle (contact));
+
+ if (!priv->contact) {
+ priv->is_incoming = FALSE;
+ priv->contact = g_object_ref (contact);
+ tp_call_set_status (call, EMPATHY_TP_CALL_STATUS_RINGING);
+ }
+}
+
+static void
+tp_call_async_cb (DBusGProxy *proxy,
+ GError *error,
+ gpointer user_data)
+{
+ if (error) {
+ empathy_debug (DEBUG_DOMAIN, "Failed to %s: %s",
+ user_data,
+ error->message);
+ }
+}
+
+static GObject *
+tp_call_constructor (GType type,
+ guint n_props,
+ GObjectConstructParam *props)
+{
+ GObject *call;
+ EmpathyTpCallPriv *priv;
+ TpConn *tp_conn;
+ MissionControl *mc;
+
+ call = G_OBJECT_CLASS (empathy_tp_call_parent_class)->constructor (type, n_props, props);
+ priv = GET_PRIV (call);
+
+ priv->group = empathy_tp_group_new (priv->account, priv->tp_chan);
+ priv->streamed_iface = tp_chan_get_interface (priv->tp_chan,
+ TELEPATHY_CHAN_IFACE_STREAMED_QUARK);
+
+ /* Connect signals */
+ dbus_g_proxy_connect_signal (priv->streamed_iface, "StreamAdded",
+ G_CALLBACK (tp_call_stream_added_cb),
+ call, NULL);
+ dbus_g_proxy_connect_signal (priv->streamed_iface, "StreamRemoved",
+ G_CALLBACK (tp_call_stream_removed_cb),
+ call, NULL);
+ dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->tp_chan), "Closed",
+ G_CALLBACK (tp_call_closed_cb),
+ call, NULL);
+ g_signal_connect (priv->tp_chan, "destroy",
+ G_CALLBACK (tp_call_destroy_cb),
+ call);
+ g_signal_connect (priv->group, "member-added",
+ G_CALLBACK (tp_call_member_added_cb),
+ call);
+ g_signal_connect (priv->group, "remote-pending",
+ G_CALLBACK (tp_call_remote_pending_cb),
+ call);
+
+ /* Start stream engine */
+ mc = empathy_mission_control_new ();
+ tp_conn = mission_control_get_connection (mc, priv->account, NULL);
+ priv->se_ch_proxy = dbus_g_proxy_new_for_name (tp_get_bus (),
+ STREAM_ENGINE_BUS_NAME,
+ STREAM_ENGINE_OBJECT_PATH,
+ CHANNEL_HANDLER_INTERFACE);
+ priv->se_proxy = dbus_g_proxy_new_for_name (tp_get_bus (),
+ STREAM_ENGINE_BUS_NAME,
+ STREAM_ENGINE_OBJECT_PATH,
+ STREAM_ENGINE_INTERFACE);
+ org_freedesktop_Telepathy_ChannelHandler_handle_channel_async (priv->se_ch_proxy,
+ dbus_g_proxy_get_bus_name (DBUS_G_PROXY (tp_conn)),
+ dbus_g_proxy_get_path (DBUS_G_PROXY (tp_conn)),
+ priv->tp_chan->type,
+ dbus_g_proxy_get_path (DBUS_G_PROXY (priv->tp_chan)),
+ priv->tp_chan->handle_type,
+ priv->tp_chan->handle,
+ tp_call_async_cb,
+ "handle channel");
+ g_object_unref (tp_conn);
+ g_object_unref (mc);
+
+ /* Get streams */
+ tp_chan_type_streamed_media_list_streams_async (priv->streamed_iface,
+ tp_call_list_streams_cb,
+ call);
+
+ return call;
+}
+
+static void
+tp_call_finalize (GObject *object)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (object);
+
+ empathy_debug (DEBUG_DOMAIN, "Finalizing: %p", object);
+
+ if (priv->tp_chan) {
+ GError *error;
+
+ g_signal_handlers_disconnect_by_func (priv->tp_chan,
+ tp_call_destroy_cb,
+ object);
+ empathy_debug (DEBUG_DOMAIN, "Closing channel...");
+ if (!tp_chan_close (DBUS_G_PROXY (priv->tp_chan), &error)) {
+ empathy_debug (DEBUG_DOMAIN,
+ "Error closing text channel: %s",
+ error ? error->message : "No error given");
+ g_clear_error (&error);
+ }
+ g_object_unref (priv->tp_chan);
+ }
+
+ g_object_unref (priv->group);
+ g_object_unref (priv->contact);
+ g_object_unref (priv->account);
+ g_object_unref (priv->se_ch_proxy);
+ g_object_unref (priv->se_proxy);
+
+ G_OBJECT_CLASS (empathy_tp_call_parent_class)->finalize (object);
+}
+
+static void
+empathy_tp_call_class_init (EmpathyTpCallClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructor = tp_call_constructor;
+ object_class->finalize = tp_call_finalize;
+ object_class->set_property = tp_call_set_property;
+ object_class->get_property = tp_call_get_property;
+
+ /* Construct-only properties */
+ g_object_class_install_property (object_class,
+ PROP_ACCOUNT,
+ g_param_spec_object ("account",
+ "channel Account",
+ "The account associated with the channel",
+ MC_TYPE_ACCOUNT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class,
+ PROP_TP_CHAN,
+ g_param_spec_object ("tp-chan",
+ "telepathy channel",
+ "The media channel for the call",
+ TELEPATHY_CHAN_TYPE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ /* Normal properties */
+ g_object_class_install_property (object_class,
+ PROP_STATUS,
+ g_param_spec_enum ("status",
+ "call status",
+ "The status of the call",
+ EMPATHY_TYPE_TP_CALL_STATUS,
+ EMPATHY_TP_CALL_STATUS_PREPARING,
+ G_PARAM_READABLE));
+
+ /* Signals */
+ signals[DESTROY] =
+ g_signal_new ("destroy",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+
+ g_type_class_add_private (klass, sizeof (EmpathyTpCallPriv));
+}
+
+static void
+empathy_tp_call_init (EmpathyTpCall *call)
+{
+}
+
+EmpathyTpCall *
+empathy_tp_call_new (McAccount *account, TpChan *channel)
+{
+ return g_object_new (EMPATHY_TYPE_TP_CALL,
+ "account", account,
+ "tp_chan", channel,
+ NULL);
+}
+
+gboolean
+empathy_tp_call_is_incoming (EmpathyTpCall *call)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+
+ return priv->is_incoming;
+}
+
+EmpathyTpCallStatus
+empathy_tp_call_get_status (EmpathyTpCall *call)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+
+ return priv->status;
+}
+
+EmpathyContact *
+empathy_tp_call_get_contact (EmpathyTpCall *call)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+
+ return priv->contact;
+}
+
+void
+empathy_tp_call_accept (EmpathyTpCall *call)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+ EmpathyContact *contact;
+
+ contact = empathy_tp_group_get_self_contact (priv->group);
+ empathy_tp_group_add_member (priv->group, contact, "");
+}
+
+void
+empathy_tp_call_invite (EmpathyTpCall *call,
+ EmpathyContact *contact)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+
+ empathy_tp_group_add_member (priv->group, contact, "you're welcome");
+}
+
+void
+empathy_tp_call_request_streams (EmpathyTpCall *call,
+ gboolean audio,
+ gboolean video)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+ GArray *stream_types;
+ guint handle;
+ guint type;
+
+ empathy_debug (DEBUG_DOMAIN, "Requesting streams for audio=%s video=%s",
+ audio ? "Yes" : "No",
+ video ? "Yes" : "No");
+
+ stream_types = g_array_new (FALSE, FALSE, sizeof (guint));
+ if (audio) {
+ type = TP_MEDIA_STREAM_TYPE_AUDIO;
+ g_array_append_val (stream_types, type);
+ }
+ if (video) {
+ type = TP_MEDIA_STREAM_TYPE_VIDEO;
+ g_array_append_val (stream_types, type);
+ }
+
+ handle = empathy_contact_get_handle (priv->contact);
+ tp_chan_type_streamed_media_request_streams_async (priv->streamed_iface,
+ handle,
+ stream_types,
+ tp_call_list_streams_cb,
+ call);
+
+ g_array_free (stream_types, TRUE);
+}
+
+void
+empathy_tp_call_send_video (EmpathyTpCall *call,
+ gboolean send)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+ guint new_direction;
+
+ if (!priv->video_stream) {
+ return;
+ }
+
+ if (send) {
+ new_direction = TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL;
+ } else {
+ new_direction = TP_MEDIA_STREAM_DIRECTION_RECEIVE;
+ }
+
+ tp_chan_type_streamed_media_request_stream_direction_async (priv->streamed_iface,
+ priv->video_stream,
+ new_direction,
+ tp_call_async_cb,
+ "request stream direction");
+}
+
+void
+empathy_tp_call_add_preview_window (EmpathyTpCall *call,
+ guint socket_id)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+
+ org_freedesktop_Telepathy_StreamEngine_add_preview_window_async (priv->se_proxy,
+ socket_id,
+ tp_call_async_cb,
+ "add preview window");
+}
+
+void
+empathy_tp_call_remove_preview_window (EmpathyTpCall *call,
+ guint socket_id)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+
+ org_freedesktop_Telepathy_StreamEngine_remove_preview_window_async (priv->se_proxy,
+ socket_id,
+ tp_call_async_cb,
+ "remove preview window");
+}
+
+void
+empathy_tp_call_set_output_window (EmpathyTpCall *call,
+ guint socket_id)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+
+ org_freedesktop_Telepathy_StreamEngine_set_output_window_async (priv->se_proxy,
+ dbus_g_proxy_get_path (DBUS_G_PROXY (priv->tp_chan)),
+ priv->video_stream,
+ socket_id,
+ tp_call_async_cb,
+ "set output window");
+}
+
+void
+empathy_tp_call_set_output_volume (EmpathyTpCall *call,
+ guint volume)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+
+ org_freedesktop_Telepathy_StreamEngine_set_output_volume_async (priv->se_proxy,
+ dbus_g_proxy_get_path (DBUS_G_PROXY (priv->tp_chan)),
+ priv->audio_stream,
+ volume,
+ tp_call_async_cb,
+ "set output volume");
+}
+
+
+void
+empathy_tp_call_mute_output (EmpathyTpCall *call,
+ gboolean is_muted)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+
+ org_freedesktop_Telepathy_StreamEngine_mute_output_async (priv->se_proxy,
+ dbus_g_proxy_get_path (DBUS_G_PROXY (priv->tp_chan)),
+ priv->audio_stream,
+ is_muted,
+ tp_call_async_cb,
+ "mute output");
+}
+
+
+void
+empathy_tp_call_mute_input (EmpathyTpCall *call,
+ gboolean is_muted)
+{
+ EmpathyTpCallPriv *priv = GET_PRIV (call);
+
+ org_freedesktop_Telepathy_StreamEngine_mute_input_async (priv->se_proxy,
+ dbus_g_proxy_get_path (DBUS_G_PROXY (priv->tp_chan)),
+ priv->audio_stream,
+ is_muted,
+ tp_call_async_cb,
+ "mute output");
+}
+
diff --git a/libempathy/empathy-tp-call.h b/libempathy/empathy-tp-call.h
new file mode 100644
index 000000000..13ab33fcb
--- /dev/null
+++ b/libempathy/empathy-tp-call.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Elliot Fairweather
+ * Copyright (C) 2007 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors: Elliot Fairweather <elliot.fairweather@collabora.co.uk>
+ * Xavier Claessens <xclaesse@gmail.com>
+ */
+
+#ifndef __EMPATHY_TP_CALL_H__
+#define __EMPATHY_TP_CALL_H__
+
+#include <libtelepathy/tp-chan.h>
+#include <libtelepathy/tp-constants.h>
+
+#include <libmissioncontrol/mc-account.h>
+
+#include "empathy-contact.h"
+
+G_BEGIN_DECLS
+
+#define EMPATHY_TYPE_TP_CALL (empathy_tp_call_get_type ())
+#define EMPATHY_TP_CALL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), EMPATHY_TYPE_TP_CALL, EmpathyTpCall))
+#define EMPATHY_TP_CALL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EMPATHY_TYPE_TP_CALL, EmpathyTpCallClass))
+#define EMPATHY_IS_TP_CALL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), EMPATHY_TYPE_TP_CALL))
+#define EMPATHY_IS_TP_CALL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EMPATHY_TYPE_TP_CALL))
+#define EMPATHY_TP_CALL_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object), EMPATHY_TYPE_TP_CALL, EmpathyTpCallClass))
+
+typedef struct _EmpathyTpCall EmpathyTpCall;
+typedef struct _EmpathyTpCallClass EmpathyTpCallClass;
+
+struct _EmpathyTpCall {
+ GObject parent;
+};
+
+struct _EmpathyTpCallClass {
+ GObjectClass parent_class;
+};
+
+typedef enum {
+ EMPATHY_TP_CALL_STATUS_PREPARING,
+ EMPATHY_TP_CALL_STATUS_RINGING,
+ EMPATHY_TP_CALL_STATUS_RUNNING
+} EmpathyTpCallStatus;
+
+GType empathy_tp_call_get_type (void) G_GNUC_CONST;
+EmpathyTpCall * empathy_tp_call_new (McAccount *account,
+ TpChan *tp_chan);
+gboolean empathy_tp_call_is_incoming (EmpathyTpCall *call);
+EmpathyTpCallStatus empathy_tp_call_get_status (EmpathyTpCall *call);
+EmpathyContact * empathy_tp_call_get_contact (EmpathyTpCall *call);
+void empathy_tp_call_accept (EmpathyTpCall *call);
+void empathy_tp_call_invite (EmpathyTpCall *call,
+ EmpathyContact *contact);
+void empathy_tp_call_request_streams (EmpathyTpCall *call,
+ gboolean audio,
+ gboolean video);
+void empathy_tp_call_send_video (EmpathyTpCall *call,
+ gboolean send);
+void empathy_tp_call_add_preview_window (EmpathyTpCall *call,
+ guint socket_id);
+void empathy_tp_call_remove_preview_window (EmpathyTpCall *call,
+ guint socket_id);
+void empathy_tp_call_set_output_window (EmpathyTpCall *call,
+ guint socket_id);
+void empathy_tp_call_set_output_volume (EmpathyTpCall *call,
+ guint volume);
+void empathy_tp_call_mute_output (EmpathyTpCall *call,
+ gboolean is_muted);
+void empathy_tp_call_mute_input (EmpathyTpCall *call,
+ gboolean is_muted);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_TP_CALL_H__ */
diff --git a/libempathy/empathy-utils.c b/libempathy/empathy-utils.c
index e0c6cfc9d..f33d83e48 100644
--- a/libempathy/empathy-utils.c
+++ b/libempathy/empathy-utils.c
@@ -38,6 +38,7 @@
#include "empathy-debug.h"
#include "empathy-utils.h"
#include "empathy-contact-manager.h"
+#include "empathy-tp-group.h"
#define DEBUG_DOMAIN "Utils"
@@ -462,3 +463,64 @@ empathy_inspect_handle (McAccount *account,
return name;
}
+void
+empathy_call_contact (EmpathyContact *contact)
+{
+#ifdef HAVE_VOIP
+ MissionControl *mc;
+ McAccount *account;
+ TpConn *tp_conn;
+ gchar *object_path;
+ const gchar *bus_name;
+ TpChan *new_chan;
+ EmpathyTpGroup *group;
+ GError *error;
+
+ /* StreamedMedia channels must have handle=0 and handle_type=none.
+ * To call a contact we have to add him in the group interface of the
+ * channel. MissionControl will detect the channel creation and
+ * dispatch it to the VoIP chandler automatically. */
+
+ mc = empathy_mission_control_new ();
+ account = empathy_contact_get_account (contact);
+ tp_conn = mission_control_get_connection (mc, account, NULL);
+ /* FIXME: Should be async */
+ if (!tp_conn_request_channel (DBUS_G_PROXY (tp_conn),
+ TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
+ TP_HANDLE_TYPE_NONE,
+ 0,
+ FALSE,
+ &object_path,
+ &error)) {
+ empathy_debug (DEBUG_DOMAIN,
+ "Couldn't request channel: %s",
+ error ? error->message : "No error given");
+ g_clear_error (&error);
+ g_object_unref (mc);
+ g_object_unref (tp_conn);
+ return;
+ }
+
+ bus_name = dbus_g_proxy_get_bus_name (DBUS_G_PROXY (tp_conn));
+ new_chan = tp_chan_new (tp_get_bus (),
+ bus_name,
+ object_path,
+ TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
+ TP_HANDLE_TYPE_NONE,
+ 0);
+
+ /* FIXME: group is leaked, we can't unref it directly because
+ * _add_member is async so we have to wait for it to return before
+ * finalizing the group. I think EmpathyTpGroup should ref itself
+ * when it does async calls to avoid finalizing when there is calls
+ * in fligth like that we could unref it here. */
+ group = empathy_tp_group_new (account, new_chan);
+ empathy_tp_group_add_member (group, contact, "");
+
+ g_object_unref (mc);
+ g_object_unref (tp_conn);
+ g_object_unref (new_chan);
+ g_free (object_path);
+#endif
+}
+
diff --git a/libempathy/empathy-utils.h b/libempathy/empathy-utils.h
index 88d533728..587adaedc 100644
--- a/libempathy/empathy-utils.h
+++ b/libempathy/empathy-utils.h
@@ -91,6 +91,7 @@ gchar * empathy_inspect_handle (McAccount *account,
guint handle_type);
gchar * empathy_inspect_channel (McAccount *account,
TpChan *tp_chan);
+void empathy_call_contact (EmpathyContact *contact);
G_END_DECLS
#endif /* __EMPATHY_UTILS_H__ */
diff --git a/libempathy/tp-stream-engine.xml b/libempathy/tp-stream-engine.xml
new file mode 100644
index 000000000..5a8783d0e
--- /dev/null
+++ b/libempathy/tp-stream-engine.xml
@@ -0,0 +1,54 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node name="/TpMediaEngine">
+ <interface name="org.freedesktop.Telepathy.ChannelHandler">
+ <method name="HandleChannel">
+ <arg direction="in" type="s" name="bus_name" />
+ <arg direction="in" type="o" name="connection" />
+ <arg direction="in" type="s" name="channel_type" />
+ <arg direction="in" type="o" name="channel" />
+ <arg direction="in" type="u" name="handle_type" />
+ <arg direction="in" type="u" name="handle" />
+ </method>
+ </interface>
+ <interface name="org.freedesktop.Telepathy.StreamEngine">
+ <method name="SetOutputVolume">
+ <arg direction="in" type="o" name="channel_path" />
+ <arg direction="in" type="u" name="stream_id" />
+ <arg direction="in" type="u" name="volume" />
+ </method>
+ <method name="MuteInput">
+ <arg direction="in" type="o" name="channel_path" />
+ <arg direction="in" type="u" name="stream_id" />
+ <arg direction="in" type="b" name="mute_state" />
+ </method>
+ <method name="MuteOutput">
+ <arg direction="in" type="o" name="channel_path" />
+ <arg direction="in" type="u" name="stream_id" />
+ <arg direction="in" type="b" name="mute_state" />
+ </method>
+ <method name="SetOutputWindow">
+ <arg direction="in" type="o" name="channel_path" />
+ <arg direction="in" type="u" name="stream_id" />
+ <arg direction="in" type="u" name="window" />
+ </method>
+ <method name="AddPreviewWindow">
+ <arg direction="in" type="u" name="window" />
+ </method>
+ <method name="RemovePreviewWindow">
+ <arg direction="in" type="u" name="window" />
+ </method>
+ <signal name="Receiving">
+ <arg type="o" name="channel_path" />
+ <arg type="u" name="stream_id" />
+ <arg type="b" name="state" />
+ </signal>
+ <signal name="StreamStateChanged">
+ <arg type="o" name="channel_path" />
+ <arg type="u" name="stream_id" />
+ <arg type="u" name="state" />
+ <arg type="u" name="direction" />
+ </signal>
+ <method name="Shutdown">
+ </method>
+ </interface>
+</node>
diff --git a/src/Makefile.am b/src/Makefile.am
index d81601ed0..ae336df09 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -15,15 +15,18 @@ LDADD = \
bin_PROGRAMS = \
empathy \
- empathy-accounts
+ empathy-accounts \
+ empathy-call-chandler
empathy_SOURCES = empathy.c
empathy_accounts_SOURCES = empathy-accounts.c
+empathy_call_chandler_SOURCES = empathy-call-chandler.c
-# Dbus service file
+# Dbus service files
servicedir = $(datadir)/dbus-1/services
service_DATA = \
- org.gnome.Empathy.Chat.service
+ org.gnome.Empathy.Chat.service \
+ org.gnome.Empathy.Call.service
%.service: %.service.in Makefile
@sed -e "s|\@bindir\@|$(bindir)|" $< > $@
@@ -31,11 +34,17 @@ chandlerdir = $(datadir)/telepathy/managers
chandler_DATA = \
empathy-chat.chandler
+if HAVE_VOIP
+chandler_DATA += empathy-call.chandler
+endif
+
BUILT_SOURCES = \
- org.gnome.Empathy.Chat.service
+ org.gnome.Empathy.Chat.service \
+ org.gnome.Empathy.Call.service
EXTRA_DIST = \
org.gnome.Empathy.Chat.service.in \
+ org.gnome.Empathy.Call.service.in \
$(autostart_DATA) \
$(chandler_DATA)
diff --git a/src/empathy-call-chandler.c b/src/empathy-call-chandler.c
new file mode 100644
index 000000000..bb88747f4
--- /dev/null
+++ b/src/empathy-call-chandler.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2007 Elliot Fairweather
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors: Elliot Fairweather <elliot.fairweather@collabora.co.uk>
+ */
+
+#include <stdlib.h>
+
+#include <gtk/gtk.h>
+
+#include <libmissioncontrol/mission-control.h>
+
+#include <libempathy/empathy-chandler.h>
+#include <libempathy/empathy-utils.h>
+#include <libempathy/empathy-tp-call.h>
+#include <libempathy/empathy-debug.h>
+
+#include <libempathy-gtk/empathy-call-window.h>
+
+#define DEBUG_DOMAIN "EmpathyCall"
+
+#define BUS_NAME "org.gnome.Empathy.Call"
+#define OBJECT_PATH "/org/freedesktop/Telepathy/ChannelHandler"
+
+static void
+call_chandler_new_channel_cb (EmpathyChandler *chandler,
+ TpConn *tp_conn,
+ TpChan *tp_chan,
+ MissionControl *mc)
+{
+ EmpathyTpCall *call;
+ McAccount *account;
+
+ account = mission_control_get_account_for_connection (mc, tp_conn, NULL);
+
+ call = empathy_tp_call_new (account, tp_chan);
+ empathy_call_window_show (call);
+ g_object_unref (account);
+ g_object_unref (call);
+}
+
+int
+main (int argc, char *argv[])
+{
+ EmpathyChandler *chandler;
+ MissionControl *mc;
+
+ gtk_init (&argc, &argv);
+
+ mc = empathy_mission_control_new ();
+ chandler = empathy_chandler_new (BUS_NAME, OBJECT_PATH);
+ g_signal_connect (chandler, "new-channel",
+ G_CALLBACK (call_chandler_new_channel_cb),
+ mc);
+
+ empathy_debug (DEBUG_DOMAIN, "Ready to handle new streamed media channels");
+
+ gtk_main ();
+
+ g_object_unref (chandler);
+ g_object_unref (mc);
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/src/empathy-call.chandler b/src/empathy-call.chandler
new file mode 100644
index 000000000..190494d3c
--- /dev/null
+++ b/src/empathy-call.chandler
@@ -0,0 +1,5 @@
+[ChannelHandler]
+BusName = org.gnome.Empathy.Call
+ObjectPath = /org/freedesktop/Telepathy/ChannelHandler
+ChannelType = org.freedesktop.Telepathy.Channel.Type.StreamedMedia
+TypeSpecificCapabilities = 15
diff --git a/src/org.gnome.Empathy.Call.service.in b/src/org.gnome.Empathy.Call.service.in
new file mode 100644
index 000000000..38d0e763a
--- /dev/null
+++ b/src/org.gnome.Empathy.Call.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gnome.Empathy.Call
+Exec=@bindir@/empathy-call-chandler