diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2012-02-20 16:35:23 +0000 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2012-02-20 16:35:23 +0000 |
commit | c99370d3e1532073f526378155bb5183a4f50a3f (patch) | |
tree | 66e5655beff01e7883f5342f5073cbab3ee2fba6 /examples/cm | |
parent | 89ebc5c794da4b2312ea299764f58de2bf037343 (diff) | |
parent | 50fb23a47944f2a47427010db697afe5e4a003df (diff) | |
download | telepathy-glib-c99370d3e1532073f526378155bb5183a4f50a3f.tar.gz |
Merge remote-tracking branch 'origin/call1' into call1
Conflicts:
docs/reference/telepathy-glib-docs.sgml
docs/reference/telepathy-glib-sections.txt
examples/future/call-cm/call-channel.c
examples/future/call-cm/call-stream.c
spec/Call_Content.xml
spec/Call_Content_Interface_Audio_Control.xml
spec/Call_Content_Interface_DTMF.xml
spec/Call_Content_Interface_Media.xml
spec/Call_Content_Interface_Video_Control.xml
spec/Call_Content_Media_Description.xml
spec/Call_Content_Media_Description_Interface_RTCP_Extended_Reports.xml
spec/Call_Content_Media_Description_Interface_RTCP_Feedback.xml
spec/Call_Content_Media_Description_Interface_RTP_Header_Extensions.xml
spec/Call_Interface_Mute.xml
spec/Call_Stream.xml
spec/Call_Stream_Endpoint.xml
spec/Call_Stream_Interface_Media.xml
spec/Channel_Interface_DTMF.xml
spec/Channel_Type_Call.xml
spec/all.xml
telepathy-glib/channel.xml
telepathy-glib/extra-gtkdoc.h
tests/dbus/call-example.c
Diffstat (limited to 'examples/cm')
27 files changed, 2210 insertions, 2566 deletions
diff --git a/examples/cm/Makefile.am b/examples/cm/Makefile.am index 49e67a034..bb2b6f143 100644 --- a/examples/cm/Makefile.am +++ b/examples/cm/Makefile.am @@ -1,5 +1,5 @@ SUBDIRS = \ - callable \ + call \ channelspecific \ contactlist \ echo-message-parts \ diff --git a/examples/cm/callable/Makefile.am b/examples/cm/call/Makefile.am index 0a04041ec..3b7ad1fac 100644 --- a/examples/cm/callable/Makefile.am +++ b/examples/cm/call/Makefile.am @@ -1,7 +1,7 @@ # Example connection manager with audio/video calls. -EXAMPLES = telepathy-example-cm-callable -noinst_LTLIBRARIES = libexample-cm-callable.la +EXAMPLES = telepathy-example-cm-call +noinst_LTLIBRARIES = libexample-cm-call.la if INSTALL_EXAMPLES libexec_PROGRAMS = $(EXAMPLES) @@ -9,40 +9,42 @@ else noinst_PROGRAMS = $(EXAMPLES) endif -libexample_cm_callable_la_SOURCES = \ +libexample_cm_call_la_SOURCES = \ + cm.c \ + cm.h \ conn.c \ conn.h \ - connection-manager.c \ - connection-manager.h \ - media-channel.c \ - media-channel.h \ - media-manager.c \ - media-manager.h \ - media-stream.c \ - media-stream.h \ + call-channel.c \ + call-channel.h \ + call-manager.c \ + call-manager.h \ + call-content.c \ + call-content.h \ + call-stream.c \ + call-stream.h \ protocol.c \ protocol.h -libexample_cm_callable_la_LIBADD = $(LDADD) +libexample_cm_call_la_LIBADD = $(LDADD) -telepathy_example_cm_callable_SOURCES = \ +telepathy_example_cm_call_SOURCES = \ main.c -telepathy_example_cm_callable_LDADD = \ +telepathy_example_cm_call_LDADD = \ $(noinst_LTLIBRARIES) servicedir = ${datadir}/dbus-1/services if INSTALL_EXAMPLES -service_DATA = _gen/org.freedesktop.Telepathy.ConnectionManager.example_callable.service +service_DATA = _gen/org.freedesktop.Telepathy.ConnectionManager.example_call.service $(service_DATA): %: Makefile $(mkdir_p) _gen { echo "[D-BUS Service]" && \ - echo "Name=org.freedesktop.Telepathy.ConnectionManager.example_callable" && \ - echo "Exec=${libexecdir}/telepathy-example-cm-callable"; } > $@ + echo "Name=org.freedesktop.Telepathy.ConnectionManager.example_call" && \ + echo "Exec=${libexecdir}/telepathy-example-cm-call"; } > $@ managerdir = ${datadir}/telepathy/managers -dist_manager_DATA = example_callable.manager +dist_manager_DATA = example_call.manager endif clean-local: @@ -64,8 +66,9 @@ AM_CPPFLAGS = \ $(GLIB_CFLAGS) \ $(DBUS_CFLAGS) \ $(NULL) - -AM_CFLAGS = $(ERROR_CFLAGS) AM_LDFLAGS = \ $(ERROR_LDFLAGS) \ $(NULL) + +AM_CFLAGS = $(ERROR_CFLAGS) + diff --git a/examples/cm/call/call-channel.c b/examples/cm/call/call-channel.c new file mode 100644 index 000000000..677c4951a --- /dev/null +++ b/examples/cm/call/call-channel.c @@ -0,0 +1,841 @@ +/* + * call-channel.c - an example 1-1 audio/video call + * + * For simplicity, this channel emulates a device with its own + * audio/video user interface, like a video-equipped form of the phones + * manipulated by telepathy-snom or gnome-phone-manager. + * + * As a result, this channel has the HardwareStreaming flag, its contents + * and streams do not have the Media interface, and clients should not attempt + * to do their own streaming using telepathy-farsight, telepathy-stream-engine + * or maemo-stream-engine. + * + * In practice, nearly all connection managers do not have HardwareStreaming, + * and do have the Media interface on their contents/streams. Usage for those + * CMs is the same, except that whichever client is the primary handler + * for the channel should also hand the channel over to telepathy-farsight or + * telepathy-stream-engine to implement the actual streaming. + * + * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright © 2007-2009 Nokia Corporation + * + * 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 + */ + +#include "call-channel.h" + +#include <string.h> + +#include <gobject/gvaluecollector.h> + +#include <telepathy-glib/base-connection.h> +#include <telepathy-glib/channel-iface.h> +#include <telepathy-glib/svc-channel.h> +#include <telepathy-glib/svc-call.h> +#include <telepathy-glib/telepathy-glib.h> + +#include "call-content.h" +#include "call-stream.h" + +static void hold_iface_init (gpointer iface, gpointer data); + +G_DEFINE_TYPE_WITH_CODE (ExampleCallChannel, + example_call_channel, + TP_TYPE_BASE_MEDIA_CALL_CHANNEL, + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_HOLD, + hold_iface_init)) + +enum +{ + PROP_SIMULATION_DELAY = 1, + N_PROPS +}; + +struct _ExampleCallChannelPrivate +{ + guint simulation_delay; + TpBaseConnection *conn; + TpHandle handle; + gboolean locally_requested; + + guint hold_state; + guint hold_state_reason; + + guint next_stream_id; + gboolean closed; +}; + +static const char * example_call_channel_interfaces[] = { + TP_IFACE_CHANNEL_INTERFACE_HOLD, + NULL +}; + +/* In practice you need one for audio, plus one per video (e.g. a + * presentation might have separate video contents for the slides + * and a camera pointed at the presenter), so having more than three + * would be highly unusual */ +#define MAX_CONTENTS_PER_CALL 100 + +G_GNUC_NULL_TERMINATED static void +example_call_channel_set_state (ExampleCallChannel *self, + TpCallState state, + TpCallFlags flags, + TpHandle actor, + TpCallStateChangeReason reason, + const gchar *error, + ...) +{ + /* FIXME: TpBaseCallChannel is not that flexible */ + tp_base_call_channel_set_state ((TpBaseCallChannel *) self, + state, actor, reason, error, ""); +} + +static void +example_call_channel_init (ExampleCallChannel *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + EXAMPLE_TYPE_CALL_CHANNEL, + ExampleCallChannelPrivate); + + self->priv->next_stream_id = 1; + + self->priv->hold_state = TP_LOCAL_HOLD_STATE_UNHELD; + self->priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_NONE; +} + +static ExampleCallContent *example_call_channel_add_content ( + ExampleCallChannel *self, TpMediaStreamType media_type, + gboolean locally_requested, gboolean initial, + const gchar *requested_name, GError **error); + +static void example_call_channel_initiate_outgoing (ExampleCallChannel *self); + +static void +constructed (GObject *object) +{ + void (*chain_up) (GObject *) = + ((GObjectClass *) example_call_channel_parent_class)->constructed; + ExampleCallChannel *self = EXAMPLE_CALL_CHANNEL (object); + TpBaseChannel *base = (TpBaseChannel *) self; + TpBaseCallChannel *call = (TpBaseCallChannel *) self; + + if (chain_up != NULL) + chain_up (object); + + self->priv->handle = tp_base_channel_get_target_handle (base); + self->priv->locally_requested = tp_base_channel_is_requested (base); + self->priv->conn = tp_base_channel_get_connection (base); + + tp_base_call_channel_update_member_flags (call, self->priv->handle, 0, + 0, TP_CALL_STATE_CHANGE_REASON_UNKNOWN, "", ""); + + if (self->priv->locally_requested) + { + /* Nobody is locally pending. The remote peer will turn up in + * remote-pending state when we actually contact them, which is done + * in example_call_channel_initiate_outgoing. */ + example_call_channel_set_state (self, + TP_CALL_STATE_PENDING_INITIATOR, 0, 0, + TP_CALL_STATE_CHANGE_REASON_USER_REQUESTED, "", + NULL); + } + else + { + /* This is an incoming call, so the self-handle is locally + * pending, to indicate that we need to answer. */ + example_call_channel_set_state (self, + TP_CALL_STATE_INITIALISED, 0, self->priv->handle, + TP_CALL_STATE_CHANGE_REASON_USER_REQUESTED, "", + NULL); + } + + /* FIXME: should respect initial names */ + if (tp_base_call_channel_has_initial_audio (call, NULL)) + { + g_message ("Channel initially has an audio stream"); + example_call_channel_add_content (self, TP_MEDIA_STREAM_TYPE_AUDIO, + self->priv->locally_requested, TRUE, NULL, NULL); + } + + if (tp_base_call_channel_has_initial_video (call, NULL)) + { + g_message ("Channel initially has a video stream"); + example_call_channel_add_content (self, TP_MEDIA_STREAM_TYPE_VIDEO, + self->priv->locally_requested, TRUE, NULL, NULL); + } + + tp_base_channel_register (base); +} + +static void +get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + ExampleCallChannel *self = EXAMPLE_CALL_CHANNEL (object); + + switch (property_id) + { + case PROP_SIMULATION_DELAY: + g_value_set_uint (value, self->priv->simulation_delay); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + ExampleCallChannel *self = EXAMPLE_CALL_CHANNEL (object); + + switch (property_id) + { + case PROP_SIMULATION_DELAY: + self->priv->simulation_delay = g_value_get_uint (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +example_call_channel_terminate (ExampleCallChannel *self, + TpHandle actor, + TpChannelGroupChangeReason reason, + TpCallStateChangeReason call_reason, + const gchar *error_name) +{ + TpBaseCallChannel *base = (TpBaseCallChannel *) self; + TpCallState call_state = tp_base_call_channel_get_state (base); + + if (call_state != TP_CALL_STATE_ENDED) + { + GList *contents; + + example_call_channel_set_state (self, + TP_CALL_STATE_ENDED, 0, actor, + call_reason, error_name, + NULL); + + /* FIXME: fd.o #24936 #c20: it's unclear in the spec whether we should + * remove peers on call termination or not. For now this example does. */ + tp_base_call_channel_remove_member (base, self->priv->handle, + actor, call_reason, error_name, NULL); + + if (actor == tp_base_connection_get_self_handle (self->priv->conn)) + { + const gchar *send_reason; + + /* In a real protocol these would be some sort of real protocol + * construct, like an XMPP error stanza or a SIP error code */ + switch (reason) + { + case TP_CHANNEL_GROUP_CHANGE_REASON_BUSY: + send_reason = "<user-is-busy/>"; + break; + + case TP_CHANNEL_GROUP_CHANGE_REASON_NO_ANSWER: + send_reason = "<no-answer/>"; + break; + + default: + send_reason = "<call-terminated/>"; + } + + g_message ("SIGNALLING: send: Terminating call: %s", send_reason); + } + + /* terminate all streams: to avoid modifying the hash table (in the + * streams-removed handler) while iterating over it, we have to copy the + * keys and iterate over those */ + contents = tp_base_call_channel_get_contents (base); + contents = g_list_copy (contents); + for (; contents != NULL; contents = g_list_delete_link (contents, contents)) + { + example_call_content_remove_stream (contents->data); + tp_base_call_channel_remove_content (base, contents->data, + 0, call_reason, error_name, ""); + } + } +} + +static void +dispose (GObject *object) +{ + ExampleCallChannel *self = EXAMPLE_CALL_CHANNEL (object); + + /* the manager is meant to hold a ref to us until we've closed */ + g_assert (self->priv->closed); + + ((GObjectClass *) example_call_channel_parent_class)->dispose (object); +} + +static void +close_channel (TpBaseChannel *base) +{ + ExampleCallChannel *self = EXAMPLE_CALL_CHANNEL (base); + + example_call_channel_terminate (self, + tp_base_connection_get_self_handle (self->priv->conn), + TP_CHANNEL_GROUP_CHANGE_REASON_NONE, + TP_CALL_STATE_CHANGE_REASON_USER_REQUESTED, ""); + + self->priv->closed = TRUE; + + tp_base_channel_destroyed (base); +} + +static void call_accept (TpBaseCallChannel *self); +static TpBaseCallContent * call_add_content (TpBaseCallChannel *self, + const gchar *name, + TpMediaStreamType media, + TpMediaStreamDirection initial_direction, + GError **error); +static void call_hangup (TpBaseCallChannel *self, + guint reason, + const gchar *detailed_reason, + const gchar *message); + +static void +example_call_channel_class_init (ExampleCallChannelClass *klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (klass); + TpBaseCallChannelClass *call_class = (TpBaseCallChannelClass *) klass; + GParamSpec *param_spec; + + g_type_class_add_private (klass, + sizeof (ExampleCallChannelPrivate)); + + call_class->accept = call_accept; + call_class->add_content = call_add_content; + call_class->hangup = call_hangup; + + base_class->target_handle_type = TP_HANDLE_TYPE_CONTACT; + base_class->interfaces = example_call_channel_interfaces; + base_class->close = close_channel; + + object_class->constructed = constructed; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->dispose = dispose; + + param_spec = g_param_spec_uint ("simulation-delay", "Simulation delay", + "Delay between simulated network events", + 0, G_MAXUINT32, 1000, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_SIMULATION_DELAY, + param_spec); +} + +static gboolean +simulate_contact_ended_cb (gpointer p) +{ + ExampleCallChannel *self = p; + TpBaseCallChannel *base = (TpBaseCallChannel *) self; + TpCallState call_state = tp_base_call_channel_get_state (base); + + /* if the call has been cancelled while we were waiting for the + * contact to do so, do nothing! */ + if (call_state == TP_CALL_STATE_ENDED) + return FALSE; + + g_message ("SIGNALLING: receive: call terminated: <call-terminated/>"); + + example_call_channel_terminate (self, self->priv->handle, + TP_CHANNEL_GROUP_CHANGE_REASON_NONE, + TP_CALL_STATE_CHANGE_REASON_USER_REQUESTED, ""); + + return FALSE; +} + +static gboolean +simulate_contact_answered_cb (gpointer p) +{ + ExampleCallChannel *self = p; + TpBaseCallChannel *base = (TpBaseCallChannel *) self; + TpCallState call_state = tp_base_call_channel_get_state (base); + GList *contents; + TpHandleRepoIface *contact_repo; + const gchar *peer; + + /* if the call has been cancelled while we were waiting for the + * contact to answer, do nothing! */ + if (call_state == TP_CALL_STATE_ENDED) + return FALSE; + + /* otherwise, we're waiting for a response from the contact, which now + * arrives */ + g_assert_cmpuint (call_state, ==, TP_CALL_STATE_INITIALISED); + + g_message ("SIGNALLING: receive: contact answered our call"); + + tp_base_call_channel_remote_accept (base); + + contents = tp_base_call_channel_get_contents (base); + for (; contents != NULL; contents = contents->next) + { + ExampleCallStream *stream = example_call_content_get_stream (contents->data); + + if (stream == NULL) + continue; + + /* remote contact accepts our proposed stream direction */ + example_call_stream_simulate_contact_agreed_to_send (stream); + } + + contact_repo = tp_base_connection_get_handles + (self->priv->conn, TP_HANDLE_TYPE_CONTACT); + peer = tp_handle_inspect (contact_repo, self->priv->handle); + + /* If the contact's ID contains the magic string "(terminate)", simulate + * them hanging up after a moment. */ + if (strstr (peer, "(terminate)") != NULL) + { + g_timeout_add_full (G_PRIORITY_DEFAULT, + self->priv->simulation_delay, + simulate_contact_ended_cb, g_object_ref (self), + g_object_unref); + } + + return FALSE; +} + +static gboolean +simulate_contact_busy_cb (gpointer p) +{ + ExampleCallChannel *self = p; + TpBaseCallChannel *base = (TpBaseCallChannel *) self; + TpCallState call_state = tp_base_call_channel_get_state (base); + + /* if the call has been cancelled while we were waiting for the + * contact to answer, do nothing */ + if (call_state == TP_CALL_STATE_ENDED) + return FALSE; + + /* otherwise, we're waiting for a response from the contact, which now + * arrives */ + g_assert_cmpuint (call_state, ==, TP_CALL_STATE_INITIALISED); + + g_message ("SIGNALLING: receive: call terminated: <user-is-busy/>"); + + example_call_channel_terminate (self, self->priv->handle, + TP_CHANNEL_GROUP_CHANGE_REASON_BUSY, + TP_CALL_STATE_CHANGE_REASON_USER_REQUESTED, + TP_ERROR_STR_BUSY); + + return FALSE; +} + +static ExampleCallContent * +example_call_channel_add_content (ExampleCallChannel *self, + TpMediaStreamType media_type, + gboolean locally_requested, + gboolean initial, + const gchar *requested_name, + GError **error) +{ + TpBaseCallChannel *base = (TpBaseCallChannel *) self; + GList *contents; + const gchar *type_str; + TpHandle creator = self->priv->handle; + TpCallContentDisposition disposition = + TP_CALL_CONTENT_DISPOSITION_NONE; + guint id = self->priv->next_stream_id++; + ExampleCallContent *content; + ExampleCallStream *stream; + gchar *name; + gchar *path; + guint i; + + /* an arbitrary limit much less than 2**32 means we don't use ridiculous + * amounts of memory, and also means @i can't wrap around when we use it to + * uniquify content names. */ + contents = tp_base_call_channel_get_contents (base); + if (g_list_length (contents) > MAX_CONTENTS_PER_CALL) + { + g_set_error (error, TP_ERRORS, TP_ERROR_PERMISSION_DENIED, + "What are you doing with all those contents anyway?!"); + return NULL; + } + + type_str = (media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? "audio" : "video"); + if (tp_str_empty (requested_name)) + { + requested_name = type_str; + } + + for (i = 0; ; i++) + { + GList *l; + + if (i == 0) + name = g_strdup (requested_name); + else + name = g_strdup_printf ("%s (%u)", requested_name, i); + + for (l = contents; l != NULL; l = l->next) + { + if (!tp_strdiff (tp_base_call_content_get_name (l->data), name)) + break; + } + + if (l == NULL) + { + /* this name hasn't been used - good enough */ + break; + } + + g_free (name); + name = NULL; + } + + if (initial) + disposition = TP_CALL_CONTENT_DISPOSITION_INITIAL; + + if (locally_requested) + { + g_message ("SIGNALLING: send: new %s stream %s", type_str, name); + creator = self->priv->conn->self_handle; + } + + path = g_strdup_printf ("%s/Content%u", + tp_base_channel_get_object_path ((TpBaseChannel *) self), + id); + content = g_object_new (EXAMPLE_TYPE_CALL_CONTENT, + "connection", self->priv->conn, + "creator", creator, + "media-type", media_type, + "name", name, + "disposition", disposition, + "object-path", path, + NULL); + + tp_base_call_channel_add_content (base, (TpBaseCallContent *) content); + g_free (path); + + path = g_strdup_printf ("%s/Stream%u", + tp_base_channel_get_object_path ((TpBaseChannel *) self), + id); + stream = g_object_new (EXAMPLE_TYPE_CALL_STREAM, + "connection", self->priv->conn, + "handle", self->priv->handle, + "locally-requested", locally_requested, + "object-path", path, + NULL); + + example_call_content_add_stream (content, stream); + g_free (path); + + g_object_unref (content); + g_object_unref (stream); + + return content; +} + +static gboolean +simulate_contact_ringing_cb (gpointer p) +{ + ExampleCallChannel *self = p; + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles + (self->priv->conn, TP_HANDLE_TYPE_CONTACT); + const gchar *peer; + + tp_base_call_channel_update_member_flags ((TpBaseCallChannel *) self, + self->priv->handle, TP_CALL_MEMBER_FLAG_RINGING, + 0, TP_CALL_STATE_CHANGE_REASON_UNKNOWN, "", ""); + + /* In this example there is no real contact, so just simulate them + * answering after a short time - unless the contact's name + * contains "(no answer)" or "(busy)" */ + + peer = tp_handle_inspect (contact_repo, self->priv->handle); + + if (strstr (peer, "(busy)") != NULL) + { + g_timeout_add_full (G_PRIORITY_DEFAULT, + self->priv->simulation_delay, + simulate_contact_busy_cb, g_object_ref (self), + g_object_unref); + } + else if (strstr (peer, "(no answer)") != NULL) + { + /* do nothing - the call just rings forever */ + } + else + { + g_timeout_add_full (G_PRIORITY_DEFAULT, + self->priv->simulation_delay, + simulate_contact_answered_cb, g_object_ref (self), + g_object_unref); + } + + return FALSE; +} + +static void +example_call_channel_initiate_outgoing (ExampleCallChannel *self) +{ + g_message ("SIGNALLING: send: new streamed media call"); + + example_call_channel_set_state (self, + TP_CALL_STATE_INITIALISED, 0, + tp_base_connection_get_self_handle (self->priv->conn), + TP_CALL_STATE_CHANGE_REASON_USER_REQUESTED, "", + NULL); + + /* After a moment, we're sent an informational message saying it's ringing */ + g_timeout_add_full (G_PRIORITY_DEFAULT, + self->priv->simulation_delay, + simulate_contact_ringing_cb, g_object_ref (self), + g_object_unref); +} + +static void +accept_incoming_call (ExampleCallChannel *self) +{ + TpBaseCallChannel *base = (TpBaseCallChannel *) self; + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles + (self->priv->conn, TP_HANDLE_TYPE_CONTACT); + GList *contents; + + g_message ("SIGNALLING: send: Accepting incoming call from %s", + tp_handle_inspect (contact_repo, self->priv->handle)); + + example_call_channel_set_state (self, + TP_CALL_STATE_ACCEPTED, 0, + tp_base_connection_get_self_handle (self->priv->conn), + TP_CALL_STATE_CHANGE_REASON_USER_REQUESTED, "", + NULL); + + contents = tp_base_call_channel_get_contents (base); + for (; contents != NULL; contents = contents->next) + { + ExampleCallStream *stream = example_call_content_get_stream (contents->data); + guint disposition = tp_base_call_content_get_disposition (contents->data); + + if (stream == NULL || disposition != TP_CALL_CONTENT_DISPOSITION_INITIAL) + continue; + + /* we accept the proposed stream direction */ + example_call_stream_accept_proposed_direction (stream); + } +} + +static void +call_accept (TpBaseCallChannel *base) +{ + ExampleCallChannel *self = EXAMPLE_CALL_CHANNEL (base); + + if (self->priv->locally_requested) + { + /* Take the contents we've already added, and make them happen */ + example_call_channel_initiate_outgoing (self); + } + else + { + accept_incoming_call (self); + } +} + +static void +call_hangup (TpBaseCallChannel *base, + guint reason, + const gchar *detailed_reason, + const gchar *message G_GNUC_UNUSED) +{ + ExampleCallChannel *self = EXAMPLE_CALL_CHANNEL (base); + + example_call_channel_terminate (self, + tp_base_connection_get_self_handle (self->priv->conn), + TP_CHANNEL_GROUP_CHANGE_REASON_NONE, reason, detailed_reason); +} + +static TpBaseCallContent * +call_add_content (TpBaseCallChannel *base, + const gchar *content_name, + guint content_type, + TpMediaStreamDirection initial_direction, + GError **error) +{ + ExampleCallChannel *self = EXAMPLE_CALL_CHANNEL (base); + + return (TpBaseCallContent *) example_call_channel_add_content (self, + content_type, TRUE, FALSE, content_name, error); +} + +static gboolean +simulate_hold (gpointer p) +{ + ExampleCallChannel *self = p; + TpBaseCallChannel *base = (TpBaseCallChannel *) self; + TpCallState call_state = tp_base_call_channel_get_state (base); + TpCallFlags call_flags = 0; /* FIXME */ + + self->priv->hold_state = TP_LOCAL_HOLD_STATE_HELD; + g_message ("SIGNALLING: hold state changed to held"); + tp_svc_channel_interface_hold_emit_hold_state_changed (self, + self->priv->hold_state, self->priv->hold_state_reason); + + example_call_channel_set_state (self, call_state, + call_flags | TP_CALL_FLAG_LOCALLY_HELD, + tp_base_connection_get_self_handle (self->priv->conn), + TP_CALL_STATE_CHANGE_REASON_USER_REQUESTED, "", NULL); + + return FALSE; +} + +static gboolean +simulate_unhold (gpointer p) +{ + ExampleCallChannel *self = p; + TpBaseCallChannel *base = (TpBaseCallChannel *) self; + TpCallState call_state = tp_base_call_channel_get_state (base); + TpCallFlags call_flags = 0; /* FIXME */ + + self->priv->hold_state = TP_LOCAL_HOLD_STATE_UNHELD; + g_message ("SIGNALLING: hold state changed to unheld"); + tp_svc_channel_interface_hold_emit_hold_state_changed (self, + self->priv->hold_state, self->priv->hold_state_reason); + + example_call_channel_set_state (self, call_state, + call_flags & ~TP_CALL_FLAG_LOCALLY_HELD, + tp_base_connection_get_self_handle (self->priv->conn), + TP_CALL_STATE_CHANGE_REASON_USER_REQUESTED, "", NULL); + + return FALSE; +} + +static gboolean +simulate_inability_to_unhold (gpointer p) +{ + ExampleCallChannel *self = p; + + self->priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_HOLD; + g_message ("SIGNALLING: unable to unhold - hold state changed to " + "pending hold"); + tp_svc_channel_interface_hold_emit_hold_state_changed (self, + self->priv->hold_state, self->priv->hold_state_reason); + /* hold again */ + g_timeout_add_full (G_PRIORITY_DEFAULT, + self->priv->simulation_delay, + simulate_hold, g_object_ref (self), + g_object_unref); + return FALSE; +} + +static void +hold_get_hold_state (TpSvcChannelInterfaceHold *iface, + DBusGMethodInvocation *context) +{ + ExampleCallChannel *self = EXAMPLE_CALL_CHANNEL (iface); + + tp_svc_channel_interface_hold_return_from_get_hold_state (context, + self->priv->hold_state, self->priv->hold_state_reason); +} + +static void +hold_request_hold (TpSvcChannelInterfaceHold *iface, + gboolean hold, + DBusGMethodInvocation *context) +{ + ExampleCallChannel *self = EXAMPLE_CALL_CHANNEL (iface); + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles + (self->priv->conn, TP_HANDLE_TYPE_CONTACT); + GError *error = NULL; + const gchar *peer; + GSourceFunc callback; + + if ((hold && self->priv->hold_state == TP_LOCAL_HOLD_STATE_HELD) || + (!hold && self->priv->hold_state == TP_LOCAL_HOLD_STATE_UNHELD)) + { + tp_svc_channel_interface_hold_return_from_request_hold (context); + return; + } + + peer = tp_handle_inspect (contact_repo, self->priv->handle); + + if (!hold && strstr (peer, "(no unhold)") != NULL) + { + g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, + "unable to unhold"); + goto error; + } + + self->priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_REQUESTED; + + if (hold) + { + self->priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_HOLD; + callback = simulate_hold; + } + else + { + self->priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_UNHOLD; + + peer = tp_handle_inspect (contact_repo, self->priv->handle); + + if (strstr (peer, "(inability to unhold)") != NULL) + { + callback = simulate_inability_to_unhold; + } + else + { + callback = simulate_unhold; + } + } + + g_message ("SIGNALLING: hold state changed to pending %s", + (hold ? "hold" : "unhold")); + tp_svc_channel_interface_hold_emit_hold_state_changed (iface, + self->priv->hold_state, self->priv->hold_state_reason); + /* No need to change the call flags - we never change the actual hold state + * here, only the pending hold state */ + + g_timeout_add_full (G_PRIORITY_DEFAULT, + self->priv->simulation_delay, + callback, g_object_ref (self), + g_object_unref); + + tp_svc_channel_interface_hold_return_from_request_hold (context); + return; + +error: + dbus_g_method_return_error (context, error); + g_error_free (error); +} + + +void +hold_iface_init (gpointer iface, + gpointer data) +{ + TpSvcChannelInterfaceHoldClass *klass = iface; + +#define IMPLEMENT(x) \ + tp_svc_channel_interface_hold_implement_##x (klass, hold_##x) + IMPLEMENT (get_hold_state); + IMPLEMENT (request_hold); +#undef IMPLEMENT +} diff --git a/examples/cm/call/call-channel.h b/examples/cm/call/call-channel.h new file mode 100644 index 000000000..58c1db34c --- /dev/null +++ b/examples/cm/call/call-channel.h @@ -0,0 +1,67 @@ +/* + * call-channel.h - header for an example channel + * + * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright © 2007-2009 Nokia Corporation + * + * 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 + */ + +#ifndef EXAMPLE_CALL_CHANNEL_H +#define EXAMPLE_CALL_CHANNEL_H + +#include <glib-object.h> +#include <telepathy-glib/telepathy-glib.h> + +G_BEGIN_DECLS + +typedef struct _ExampleCallChannel ExampleCallChannel; +typedef struct _ExampleCallChannelPrivate + ExampleCallChannelPrivate; + +typedef struct _ExampleCallChannelClass + ExampleCallChannelClass; + +GType example_call_channel_get_type (void); + +#define EXAMPLE_TYPE_CALL_CHANNEL \ + (example_call_channel_get_type ()) +#define EXAMPLE_CALL_CHANNEL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXAMPLE_TYPE_CALL_CHANNEL, \ + ExampleCallChannel)) +#define EXAMPLE_CALL_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), EXAMPLE_TYPE_CALL_CHANNEL, \ + ExampleCallChannelClass)) +#define EXAMPLE_IS_CALL_CHANNEL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXAMPLE_TYPE_CALL_CHANNEL)) +#define EXAMPLE_IS_CALL_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), EXAMPLE_TYPE_CALL_CHANNEL)) +#define EXAMPLE_CALL_CHANNEL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_CALL_CHANNEL, \ + ExampleCallChannelClass)) + +struct _ExampleCallChannelClass { + TpBaseMediaCallChannelClass parent_class; +}; + +struct _ExampleCallChannel { + TpBaseMediaCallChannel parent; + + ExampleCallChannelPrivate *priv; +}; + +G_END_DECLS + +#endif diff --git a/examples/cm/call/call-content.c b/examples/cm/call/call-content.c new file mode 100644 index 000000000..2f81835bb --- /dev/null +++ b/examples/cm/call/call-content.c @@ -0,0 +1,103 @@ +/* + * call-content.c - a content in a call. + * + * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright © 2007-2009 Nokia Corporation + * + * 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 + */ + +#include "call-content.h" + +#include <telepathy-glib/base-connection.h> +#include <telepathy-glib/telepathy-glib.h> +#include <telepathy-glib/svc-call.h> + +G_DEFINE_TYPE (ExampleCallContent, + example_call_content, + TP_TYPE_BASE_MEDIA_CALL_CONTENT) + +struct _ExampleCallContentPrivate +{ + ExampleCallStream *stream; +}; + +static void +example_call_content_init (ExampleCallContent *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + EXAMPLE_TYPE_CALL_CONTENT, + ExampleCallContentPrivate); +} + +static void +dispose (GObject *object) +{ + ExampleCallContent *self = EXAMPLE_CALL_CONTENT (object); + + g_clear_object (&self->priv->stream); + + ((GObjectClass *) example_call_content_parent_class)->dispose (object); +} + +static void +example_call_content_class_init (ExampleCallContentClass *klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + g_type_class_add_private (klass, + sizeof (ExampleCallContentPrivate)); + + object_class->dispose = dispose; +} + +ExampleCallStream * +example_call_content_get_stream (ExampleCallContent *self) +{ + g_return_val_if_fail (EXAMPLE_IS_CALL_CONTENT (self), NULL); + + return self->priv->stream; +} + +void +example_call_content_add_stream (ExampleCallContent *self, + ExampleCallStream *stream) +{ + g_return_if_fail (EXAMPLE_IS_CALL_CONTENT (self)); + g_return_if_fail (EXAMPLE_IS_CALL_STREAM (stream)); + g_return_if_fail (self->priv->stream == NULL); + + self->priv->stream = g_object_ref (stream); + + tp_base_call_content_add_stream ((TpBaseCallContent *) self, + (TpBaseCallStream *) stream); +} + +void +example_call_content_remove_stream (ExampleCallContent *self) +{ + TpBaseCallStream *stream; + + g_return_if_fail (EXAMPLE_IS_CALL_CONTENT (self)); + g_return_if_fail (self->priv->stream != NULL); + + stream = (TpBaseCallStream *) self->priv->stream; + self->priv->stream = NULL; + + tp_base_call_content_remove_stream ((TpBaseCallContent *) self, stream, + 0, TP_CALL_STATE_CHANGE_REASON_UNKNOWN, "", ""); + + g_object_unref (stream); +} diff --git a/examples/cm/call/call-content.h b/examples/cm/call/call-content.h new file mode 100644 index 000000000..06a5a5415 --- /dev/null +++ b/examples/cm/call/call-content.h @@ -0,0 +1,82 @@ +/* + * call-content.h - header for an example content + * + * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright © 2007-2009 Nokia Corporation + * + * 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 + */ + +#ifndef EXAMPLE_CALL_CONTENT_H +#define EXAMPLE_CALL_CONTENT_H + +#include <glib-object.h> + +#include <telepathy-glib/telepathy-glib.h> + +#include "call-stream.h" + +G_BEGIN_DECLS + +typedef struct _ExampleCallContent ExampleCallContent; +typedef struct _ExampleCallContentPrivate + ExampleCallContentPrivate; + +typedef struct _ExampleCallContentClass + ExampleCallContentClass; +typedef struct _ExampleCallContentClassPrivate + ExampleCallContentClassPrivate; + +GType example_call_content_get_type (void); + +#define EXAMPLE_TYPE_CALL_CONTENT \ + (example_call_content_get_type ()) +#define EXAMPLE_CALL_CONTENT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXAMPLE_TYPE_CALL_CONTENT, \ + ExampleCallContent)) +#define EXAMPLE_CALL_CONTENT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), EXAMPLE_TYPE_CALL_CONTENT, \ + ExampleCallContentClass)) +#define EXAMPLE_IS_CALL_CONTENT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXAMPLE_TYPE_CALL_CONTENT)) +#define EXAMPLE_IS_CALL_CONTENT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), EXAMPLE_TYPE_CALL_CONTENT)) +#define EXAMPLE_CALL_CONTENT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_CALL_CONTENT, \ + ExampleCallContentClass)) + +struct _ExampleCallContentClass { + TpBaseMediaCallContentClass parent_class; + + ExampleCallContentClassPrivate *priv; +}; + +struct _ExampleCallContent { + TpBaseMediaCallContent parent; + + ExampleCallContentPrivate *priv; +}; + +/* In this example, each content can only have one stream. */ +ExampleCallStream *example_call_content_get_stream (ExampleCallContent *self); + +void example_call_content_add_stream (ExampleCallContent *self, + ExampleCallStream *stream); + +void example_call_content_remove_stream (ExampleCallContent *self); + +G_END_DECLS + +#endif diff --git a/examples/cm/callable/media-manager.c b/examples/cm/call/call-manager.c index b24a65774..8f1ac61be 100644 --- a/examples/cm/callable/media-manager.c +++ b/examples/cm/call/call-manager.c @@ -1,5 +1,6 @@ /* - * media-manager.c - an example channel manager for StreamedMedia calls. + * call-manager.c - an example channel manager for Call channels. + * * This channel manager emulates a protocol like XMPP Jingle, where you can * make several simultaneous calls to the same or different contacts. * @@ -23,7 +24,7 @@ #include "config.h" -#include "media-manager.h" +#include "call-manager.h" #include <dbus/dbus-glib.h> @@ -33,12 +34,12 @@ #include <telepathy-glib/errors.h> #include <telepathy-glib/interfaces.h> -#include "media-channel.h" +#include "call-channel.h" static void channel_manager_iface_init (gpointer, gpointer); -G_DEFINE_TYPE_WITH_CODE (ExampleCallableMediaManager, - example_callable_media_manager, +G_DEFINE_TYPE_WITH_CODE (ExampleCallManager, + example_call_manager, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, channel_manager_iface_init)) @@ -52,17 +53,17 @@ enum N_PROPS }; -struct _ExampleCallableMediaManagerPrivate +struct _ExampleCallManagerPrivate { TpBaseConnection *conn; guint simulation_delay; - /* Map from reffed ExampleCallableMediaChannel to the same pointer; used as a + /* Map from reffed ExampleCallChannel to the same pointer; used as a * set. */ GHashTable *channels; - /* Next channel will be ("MediaChannel%u", next_channel_index) */ + /* Next channel will be ("CallChannel%u", next_channel_index) */ guint next_channel_index; gulong status_changed_id; @@ -70,11 +71,11 @@ struct _ExampleCallableMediaManagerPrivate }; static void -example_callable_media_manager_init (ExampleCallableMediaManager *self) +example_call_manager_init (ExampleCallManager *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - EXAMPLE_TYPE_CALLABLE_MEDIA_MANAGER, - ExampleCallableMediaManagerPrivate); + EXAMPLE_TYPE_CALL_MANAGER, + ExampleCallManagerPrivate); self->priv->conn = NULL; self->priv->channels = g_hash_table_new_full (NULL, NULL, g_object_unref, @@ -84,14 +85,21 @@ example_callable_media_manager_init (ExampleCallableMediaManager *self) } static void -example_callable_media_manager_close_all (ExampleCallableMediaManager *self) +example_call_manager_close_all (ExampleCallManager *self) { if (self->priv->channels != NULL) { GHashTable *tmp = self->priv->channels; + GHashTableIter iter; + gpointer v; self->priv->channels = NULL; + g_hash_table_iter_init (&iter, tmp); + + while (g_hash_table_iter_next (&iter, NULL, &v)) + tp_base_channel_close (v); + g_hash_table_unref (tmp); } @@ -113,22 +121,22 @@ example_callable_media_manager_close_all (ExampleCallableMediaManager *self) static void dispose (GObject *object) { - ExampleCallableMediaManager *self = EXAMPLE_CALLABLE_MEDIA_MANAGER (object); + ExampleCallManager *self = EXAMPLE_CALL_MANAGER (object); - example_callable_media_manager_close_all (self); + example_call_manager_close_all (self); g_assert (self->priv->channels == NULL); - ((GObjectClass *) example_callable_media_manager_parent_class)->dispose ( + ((GObjectClass *) example_call_manager_parent_class)->dispose ( object); } static void get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) + guint property_id, + GValue *value, + GParamSpec *pspec) { - ExampleCallableMediaManager *self = EXAMPLE_CALLABLE_MEDIA_MANAGER (object); + ExampleCallManager *self = EXAMPLE_CALL_MANAGER (object); switch (property_id) { @@ -147,11 +155,11 @@ get_property (GObject *object, static void set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) + guint property_id, + const GValue *value, + GParamSpec *pspec) { - ExampleCallableMediaManager *self = EXAMPLE_CALLABLE_MEDIA_MANAGER (object); + ExampleCallManager *self = EXAMPLE_CALL_MANAGER (object); switch (property_id) { @@ -175,13 +183,13 @@ static void status_changed_cb (TpBaseConnection *conn, guint status, guint reason, - ExampleCallableMediaManager *self) + ExampleCallManager *self) { switch (status) { case TP_CONNECTION_STATUS_DISCONNECTED: { - example_callable_media_manager_close_all (self); + example_call_manager_close_all (self); } break; @@ -190,14 +198,14 @@ status_changed_cb (TpBaseConnection *conn, } } -static ExampleCallableMediaChannel *new_channel ( - ExampleCallableMediaManager *self, TpHandle handle, TpHandle initiator, - gpointer request_token, gboolean initial_audio, gboolean initial_video); +static ExampleCallChannel *new_channel (ExampleCallManager *self, + TpHandle handle, TpHandle initiator, gpointer request_token, + gboolean initial_audio, gboolean initial_video); static gboolean simulate_incoming_call_cb (gpointer p) { - ExampleCallableMediaManager *self = p; + ExampleCallManager *self = p; TpHandleRepoIface *contact_repo; TpHandle caller; @@ -221,8 +229,8 @@ simulate_incoming_call_cb (gpointer p) * a contact */ static void available_cb (GObject *conn G_GNUC_UNUSED, - const gchar *message, - ExampleCallableMediaManager *self) + const gchar *message, + ExampleCallManager *self) { g_timeout_add_full (G_PRIORITY_DEFAULT, self->priv->simulation_delay, simulate_incoming_call_cb, g_object_ref (self), g_object_unref); @@ -231,9 +239,9 @@ available_cb (GObject *conn G_GNUC_UNUSED, static void constructed (GObject *object) { - ExampleCallableMediaManager *self = EXAMPLE_CALLABLE_MEDIA_MANAGER (object); + ExampleCallManager *self = EXAMPLE_CALL_MANAGER (object); void (*chain_up) (GObject *) = - ((GObjectClass *) example_callable_media_manager_parent_class)->constructed; + ((GObjectClass *) example_call_manager_parent_class)->constructed; if (chain_up != NULL) { @@ -248,8 +256,7 @@ constructed (GObject *object) } static void -example_callable_media_manager_class_init ( - ExampleCallableMediaManagerClass *klass) +example_call_manager_class_init (ExampleCallManagerClass *klass) { GParamSpec *param_spec; GObjectClass *object_class = (GObjectClass *) klass; @@ -273,16 +280,15 @@ example_callable_media_manager_class_init ( param_spec); g_type_class_add_private (klass, - sizeof (ExampleCallableMediaManagerPrivate)); + sizeof (ExampleCallManagerPrivate)); } static void -example_callable_media_manager_foreach_channel ( - TpChannelManager *iface, +example_call_manager_foreach_channel (TpChannelManager *iface, TpExportableChannelFunc callback, gpointer user_data) { - ExampleCallableMediaManager *self = EXAMPLE_CALLABLE_MEDIA_MANAGER (iface); + ExampleCallManager *self = EXAMPLE_CALL_MANAGER (iface); GHashTableIter iter; gpointer chan; @@ -293,8 +299,8 @@ example_callable_media_manager_foreach_channel ( } static void -channel_closed_cb (ExampleCallableMediaChannel *chan, - ExampleCallableMediaManager *self) +channel_closed_cb (ExampleCallChannel *chan, + ExampleCallManager *self) { tp_channel_manager_emit_channel_closed_for_object (self, TP_EXPORTABLE_CHANNEL (chan)); @@ -303,24 +309,24 @@ channel_closed_cb (ExampleCallableMediaChannel *chan, g_hash_table_remove (self->priv->channels, chan); } -static ExampleCallableMediaChannel * -new_channel (ExampleCallableMediaManager *self, - TpHandle handle, - TpHandle initiator, - gpointer request_token, - gboolean initial_audio, - gboolean initial_video) +static ExampleCallChannel * +new_channel (ExampleCallManager *self, + TpHandle handle, + TpHandle initiator, + gpointer request_token, + gboolean initial_audio, + gboolean initial_video) { - ExampleCallableMediaChannel *chan; + ExampleCallChannel *chan; gchar *object_path; GSList *requests = NULL; /* FIXME: This could potentially wrap around, but only after 4 billion * calls, which is probably plenty. */ - object_path = g_strdup_printf ("%s/MediaChannel%u", + object_path = g_strdup_printf ("%s/CallChannel%u", self->priv->conn->object_path, self->priv->next_channel_index++); - chan = g_object_new (EXAMPLE_TYPE_CALLABLE_MEDIA_CHANNEL, + chan = g_object_new (EXAMPLE_TYPE_CALL_CHANNEL, "connection", self->priv->conn, "object-path", object_path, "handle", handle, @@ -329,6 +335,7 @@ new_channel (ExampleCallableMediaManager *self, "simulation-delay", self->priv->simulation_delay, "initial-audio", initial_audio, "initial-video", initial_video, + "mutable-contents", TRUE, NULL); g_free (object_path); @@ -347,46 +354,70 @@ new_channel (ExampleCallableMediaManager *self, return chan; } -static const gchar * const fixed_properties[] = { +static const gchar * const audio_fixed_properties[] = { TP_PROP_CHANNEL_CHANNEL_TYPE, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, + TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, + NULL +}; + +static const gchar * const video_fixed_properties[] = { + TP_PROP_CHANNEL_CHANNEL_TYPE, + TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, + TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, + NULL +}; + +static const gchar * const audio_allowed_properties[] = { + TP_PROP_CHANNEL_TARGET_HANDLE, + TP_PROP_CHANNEL_TARGET_ID, + TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, NULL }; -static const gchar * const allowed_properties[] = { +static const gchar * const video_allowed_properties[] = { TP_PROP_CHANNEL_TARGET_HANDLE, TP_PROP_CHANNEL_TARGET_ID, + TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, NULL }; static void -example_callable_media_manager_type_foreach_channel_class (GType type, +example_call_manager_type_foreach_channel_class (GType type, TpChannelManagerTypeChannelClassFunc func, gpointer user_data) { GHashTable *table = tp_asv_new ( TP_PROP_CHANNEL_CHANNEL_TYPE, - G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, + G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_CALL, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT, + TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, G_TYPE_BOOLEAN, TRUE, NULL); - func (type, table, allowed_properties, user_data); + func (type, table, audio_allowed_properties, user_data); + + g_hash_table_remove (table, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO); + tp_asv_set_boolean (table, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, + TRUE); + + func (type, table, video_allowed_properties, user_data); g_hash_table_unref (table); } static gboolean -example_callable_media_manager_request (ExampleCallableMediaManager *self, - gpointer request_token, - GHashTable *request_properties, - gboolean require_new) +example_call_manager_request (ExampleCallManager *self, + gpointer request_token, + GHashTable *request_properties, + gboolean require_new) { TpHandle handle; GError *error = NULL; + gboolean initial_audio, initial_video; if (tp_strdiff (tp_asv_get_string (request_properties, TP_PROP_CHANNEL_CHANNEL_TYPE), - TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA)) + TP_IFACE_CHANNEL_TYPE_CALL)) { return FALSE; } @@ -401,8 +432,22 @@ example_callable_media_manager_request (ExampleCallableMediaManager *self, TP_PROP_CHANNEL_TARGET_HANDLE, NULL); g_assert (handle != 0); + initial_audio = tp_asv_get_boolean (request_properties, + TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, NULL); + initial_video = tp_asv_get_boolean (request_properties, + TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, NULL); + + if (!initial_audio && !initial_video) + { + g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, + "Call channels must initially have either audio or video content"); + goto error; + } + + /* the set of (fixed | allowed) properties is the same for audio and video, + * so we only need to check with one set */ if (tp_channel_manager_asv_has_unknown_properties (request_properties, - fixed_properties, allowed_properties, &error)) + audio_fixed_properties, audio_allowed_properties, &error)) { goto error; } @@ -445,8 +490,8 @@ example_callable_media_manager_request (ExampleCallableMediaManager *self, } } - new_channel (self, handle, self->priv->conn->self_handle, - request_token, FALSE, FALSE); + new_channel (self, handle, self->priv->conn->self_handle, request_token, + initial_audio, initial_video); return TRUE; error: @@ -457,41 +502,37 @@ error: } static gboolean -example_callable_media_manager_create_channel (TpChannelManager *manager, - gpointer request_token, - GHashTable *request_properties) +example_call_manager_create_channel (TpChannelManager *manager, + gpointer request_token, + GHashTable *request_properties) { - return example_callable_media_manager_request ( - EXAMPLE_CALLABLE_MEDIA_MANAGER (manager), + return example_call_manager_request ( + EXAMPLE_CALL_MANAGER (manager), request_token, request_properties, TRUE); } static gboolean -example_callable_media_manager_ensure_channel (TpChannelManager *manager, - gpointer request_token, - GHashTable *request_properties) +example_call_manager_ensure_channel (TpChannelManager *manager, + gpointer request_token, + GHashTable *request_properties) { - return example_callable_media_manager_request ( - EXAMPLE_CALLABLE_MEDIA_MANAGER (manager), + return example_call_manager_request ( + EXAMPLE_CALL_MANAGER (manager), request_token, request_properties, FALSE); } static void channel_manager_iface_init (gpointer g_iface, - gpointer iface_data G_GNUC_UNUSED) + gpointer iface_data G_GNUC_UNUSED) { TpChannelManagerIface *iface = g_iface; - iface->foreach_channel = example_callable_media_manager_foreach_channel; + iface->foreach_channel = example_call_manager_foreach_channel; iface->type_foreach_channel_class = - example_callable_media_manager_type_foreach_channel_class; - iface->create_channel = example_callable_media_manager_create_channel; - iface->ensure_channel = example_callable_media_manager_ensure_channel; - /* In this channel manager, RequestChannel is not supported (it's new - * code so there's no reason to be backwards compatible). The requirements - * for RequestChannel are somewhat complicated for backwards compatibility - * reasons: see telepathy-gabble or - * http://telepathy.freedesktop.org/wiki/Requesting%20StreamedMedia%20channels - * for the gory details. */ + example_call_manager_type_foreach_channel_class; + iface->create_channel = example_call_manager_create_channel; + iface->ensure_channel = example_call_manager_ensure_channel; + /* In this channel manager, RequestChannel is not supported; Call is not + * designed to work with the old RequestChannel API. */ iface->request_channel = NULL; } diff --git a/examples/cm/call/call-manager.h b/examples/cm/call/call-manager.h new file mode 100644 index 000000000..96f5aee39 --- /dev/null +++ b/examples/cm/call/call-manager.h @@ -0,0 +1,71 @@ +/* + * media-manager.h - header for an example channel manager + * + * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright © 2007-2009 Nokia Corporation + * + * 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 + */ + +#ifndef EXAMPLE_CALL_MANAGER_H +#define EXAMPLE_CALL_MANAGER_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +typedef struct _ExampleCallManager ExampleCallManager; +typedef struct _ExampleCallManagerPrivate + ExampleCallManagerPrivate; + +typedef struct _ExampleCallManagerClass + ExampleCallManagerClass; +typedef struct _ExampleCallManagerClassPrivate + ExampleCallManagerClassPrivate; + +struct _ExampleCallManagerClass { + GObjectClass parent_class; + + ExampleCallManagerClassPrivate *priv; +}; + +struct _ExampleCallManager { + GObject parent; + + ExampleCallManagerPrivate *priv; +}; + +GType example_call_manager_get_type (void); + +/* TYPE MACROS */ +#define EXAMPLE_TYPE_CALL_MANAGER \ + (example_call_manager_get_type ()) +#define EXAMPLE_CALL_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), EXAMPLE_TYPE_CALL_MANAGER, \ + ExampleCallManager)) +#define EXAMPLE_CALL_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), EXAMPLE_TYPE_CALL_MANAGER, \ + ExampleCallManagerClass)) +#define EXAMPLE_IS_CALL_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), EXAMPLE_TYPE_CALL_MANAGER)) +#define EXAMPLE_IS_CALL_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), EXAMPLE_TYPE_CALL_MANAGER)) +#define EXAMPLE_CALL_MANAGER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_CALL_MANAGER, \ + ExampleCallManagerClass)) + +G_END_DECLS + +#endif diff --git a/examples/cm/call/call-stream.c b/examples/cm/call/call-stream.c new file mode 100644 index 000000000..c8fe4f28d --- /dev/null +++ b/examples/cm/call/call-stream.c @@ -0,0 +1,481 @@ +/* + * call-stream.c - a stream in a call. + * + * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright © 2007-2009 Nokia Corporation + * + * 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 + */ + +#include "call-stream.h" + +#include <telepathy-glib/base-connection.h> +#include <telepathy-glib/gtypes.h> +#include <telepathy-glib/svc-call.h> + +G_DEFINE_TYPE (ExampleCallStream, + example_call_stream, + TP_TYPE_BASE_MEDIA_CALL_STREAM) + +enum +{ + PROP_SIMULATION_DELAY = 1, + PROP_LOCALLY_REQUESTED, + PROP_HANDLE, + N_PROPS +}; + +struct _ExampleCallStreamPrivate +{ + guint simulation_delay; + gboolean locally_requested; + TpHandle handle; + guint agreed_delay_id; +}; + +static void +example_call_stream_init (ExampleCallStream *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + EXAMPLE_TYPE_CALL_STREAM, + ExampleCallStreamPrivate); +} + +static void example_call_stream_receive_direction_request ( + ExampleCallStream *self, gboolean local_send, gboolean remote_send); +static void example_call_stream_change_direction (ExampleCallStream *self, + gboolean want_to_send, gboolean want_to_receive); + +static void +constructed (GObject *object) +{ + ExampleCallStream *self = EXAMPLE_CALL_STREAM (object); + void (*chain_up) (GObject *) = + ((GObjectClass *) example_call_stream_parent_class)->constructed; + static guint count = 0; + TpBaseConnection *conn; + TpDBusDaemon *dbus; + gchar *object_path; + TpCallStreamEndpoint *endpoint; + + if (chain_up != NULL) + chain_up (object); + + conn = tp_base_call_stream_get_connection ((TpBaseCallStream *) self); + dbus = tp_base_connection_get_dbus_daemon (conn); + object_path = g_strdup_printf ("%s/Endpoint%d", + tp_base_call_stream_get_object_path ((TpBaseCallStream *) self), + count++); + endpoint = tp_call_stream_endpoint_new (dbus, object_path, + TP_STREAM_TRANSPORT_TYPE_RAW_UDP, FALSE); + + tp_base_media_call_stream_add_endpoint ((TpBaseMediaCallStream *) self, + endpoint); + + if (self->priv->locally_requested) + { + example_call_stream_change_direction (self, TRUE, TRUE); + } + else + { + example_call_stream_receive_direction_request (self, TRUE, TRUE); + } + + g_object_unref (endpoint); + g_free (object_path); +} + +static void +get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + ExampleCallStream *self = EXAMPLE_CALL_STREAM (object); + + switch (property_id) + { + case PROP_SIMULATION_DELAY: + g_value_set_uint (value, self->priv->simulation_delay); + break; + + case PROP_LOCALLY_REQUESTED: + g_value_set_boolean (value, self->priv->locally_requested); + break; + + case PROP_HANDLE: + g_value_set_uint (value, self->priv->handle); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + ExampleCallStream *self = EXAMPLE_CALL_STREAM (object); + + switch (property_id) + { + case PROP_SIMULATION_DELAY: + self->priv->simulation_delay = g_value_get_uint (value); + break; + + case PROP_LOCALLY_REQUESTED: + self->priv->locally_requested = g_value_get_boolean (value); + break; + + case PROP_HANDLE: + self->priv->handle = g_value_get_uint (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean stream_request_receiving (TpBaseCallStream *base, + TpHandle contact, + gboolean receive, + GError **error); +static gboolean stream_set_sending (TpBaseCallStream *base, + gboolean sending, + GError **error); + +static void +finalize (GObject *object) +{ + ExampleCallStream *self = EXAMPLE_CALL_STREAM (object); + + if (self->priv->agreed_delay_id != 0) + g_source_remove (self->priv->agreed_delay_id); + + G_OBJECT_CLASS (example_call_stream_parent_class)->finalize (object); +} + +static void +example_call_stream_class_init (ExampleCallStreamClass *klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + TpBaseCallStreamClass *stream_class = (TpBaseCallStreamClass *) klass; + GParamSpec *param_spec; + + g_type_class_add_private (klass, sizeof (ExampleCallStreamPrivate)); + + object_class->constructed = constructed; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + + stream_class->request_receiving = stream_request_receiving; + stream_class->set_sending = stream_set_sending; + + param_spec = g_param_spec_uint ("simulation-delay", "Simulation delay", + "Delay between simulated network events", + 0, G_MAXUINT32, 1000, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_SIMULATION_DELAY, + param_spec); + + param_spec = g_param_spec_boolean ("locally-requested", "Locally requested?", + "True if this channel was requested by the local user", + FALSE, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_LOCALLY_REQUESTED, + param_spec); + + param_spec = g_param_spec_uint ("handle", "Peer's TpHandle", + "The handle with which this stream communicates or 0 if not applicable", + 0, G_MAXUINT32, 0, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_HANDLE, param_spec); +} + +void +example_call_stream_accept_proposed_direction (ExampleCallStream *self) +{ + TpBaseCallStream *base = (TpBaseCallStream *) self; + TpSendingState state = tp_base_call_stream_get_local_sending_state (base); + + if (state != TP_SENDING_STATE_PENDING_SEND) + return; + + tp_base_call_stream_update_local_sending_state (base, + TP_SENDING_STATE_SENDING, 0, TP_CALL_STATE_CHANGE_REASON_UNKNOWN, + "", ""); +} + +void +example_call_stream_simulate_contact_agreed_to_send (ExampleCallStream *self) +{ + TpBaseCallStream *base = (TpBaseCallStream *) self; + TpSendingState state = tp_base_call_stream_get_remote_sending_state (base, + self->priv->handle); + + if (state != TP_SENDING_STATE_PENDING_SEND) + + g_message ("%s: SIGNALLING: Sending to server: OK, I'll send you media", + tp_base_call_stream_get_object_path (base)); + + tp_base_call_stream_update_remote_sending_state ((TpBaseCallStream *) self, + self->priv->handle, TP_SENDING_STATE_SENDING, 0, + TP_CALL_STATE_CHANGE_REASON_UNKNOWN, "", ""); +} + +static gboolean +simulate_contact_agreed_to_send_cb (gpointer p) +{ + example_call_stream_simulate_contact_agreed_to_send (p); + return FALSE; +} + +static void +example_call_stream_change_direction (ExampleCallStream *self, + gboolean want_to_send, gboolean want_to_receive) +{ + TpBaseCallStream *base = (TpBaseCallStream *) self; + TpSendingState local_sending_state = + tp_base_call_stream_get_local_sending_state (base); + TpSendingState remote_sending_state = + tp_base_call_stream_get_remote_sending_state (base, self->priv->handle); + + if (want_to_send) + { + if (local_sending_state != TP_SENDING_STATE_SENDING) + { + if (local_sending_state == TP_SENDING_STATE_PENDING_SEND) + { + g_message ("%s: SIGNALLING: send: I will now send you media", + tp_base_call_stream_get_object_path (base)); + } + + g_message ("%s: MEDIA: sending media to peer", + tp_base_call_stream_get_object_path (base)); + + tp_base_call_stream_update_local_sending_state (base, + TP_SENDING_STATE_SENDING, 0, TP_CALL_STATE_CHANGE_REASON_UNKNOWN, + "", ""); + } + } + else + { + if (local_sending_state == TP_SENDING_STATE_SENDING) + { + g_message ("%s: SIGNALLING: send: I will no longer send you media", + tp_base_call_stream_get_object_path (base)); + g_message ("%s: MEDIA: no longer sending media to peer", + tp_base_call_stream_get_object_path (base)); + + tp_base_call_stream_update_local_sending_state (base, + TP_SENDING_STATE_NONE, 0, TP_CALL_STATE_CHANGE_REASON_UNKNOWN, + "", ""); + } + else if (local_sending_state == TP_SENDING_STATE_PENDING_SEND) + { + g_message ("%s: SIGNALLING: send: refusing to send you media", + tp_base_call_stream_get_object_path (base)); + + tp_base_call_stream_update_local_sending_state (base, + TP_SENDING_STATE_NONE, 0, TP_CALL_STATE_CHANGE_REASON_UNKNOWN, + "", ""); + } + } + + if (want_to_receive) + { + if (remote_sending_state == TP_SENDING_STATE_NONE) + { + g_message ("%s: SIGNALLING: send: send me media, please?", + tp_base_call_stream_get_object_path (base)); + + tp_base_call_stream_update_remote_sending_state ( + (TpBaseCallStream *) self, + self->priv->handle, TP_SENDING_STATE_PENDING_SEND, 0, + TP_CALL_STATE_CHANGE_REASON_UNKNOWN, "", ""); + + if (self->priv->agreed_delay_id == 0) + { + self->priv->agreed_delay_id = g_timeout_add ( + self->priv->simulation_delay, + simulate_contact_agreed_to_send_cb, self); + } + } + } + else + { + if (remote_sending_state != TP_SENDING_STATE_NONE) + { + g_message ("%s: SIGNALLING: send: Please stop sending me media", + tp_base_call_stream_get_object_path (base)); + g_message ("%s: MEDIA: suppressing output of stream", + tp_base_call_stream_get_object_path (base)); + + tp_base_call_stream_update_remote_sending_state ( + (TpBaseCallStream *) self, + self->priv->handle, TP_SENDING_STATE_NONE, 0, + TP_CALL_STATE_CHANGE_REASON_UNKNOWN, "", ""); + } + } +} + +/* The remote user wants to change the direction of this stream according + * to @local_send and @remote_send. Shall we let him? */ +static void +example_call_stream_receive_direction_request (ExampleCallStream *self, + gboolean local_send, + gboolean remote_send) +{ + TpBaseCallStream *base = (TpBaseCallStream *) self; + TpSendingState local_sending_state = + tp_base_call_stream_get_local_sending_state (base); + TpSendingState remote_sending_state = + tp_base_call_stream_get_remote_sending_state (base, self->priv->handle); + + /* In some protocols, streams cannot be neither sending nor receiving, so + * if a stream is set to TP_MEDIA_STREAM_DIRECTION_NONE, this is equivalent + * to removing it. (This is true in XMPP, for instance.) + * + * However, for this example we'll emulate a protocol where streams can be + * directionless. + */ + + if (local_send) + { + g_message ("%s: SIGNALLING: send: Please start sending me media", + tp_base_call_stream_get_object_path (base)); + + if (local_sending_state == TP_SENDING_STATE_NONE) + { + /* ask the user for permission */ + tp_base_call_stream_update_local_sending_state (base, + TP_SENDING_STATE_PENDING_SEND, 0, + TP_CALL_STATE_CHANGE_REASON_UNKNOWN, + "", ""); + } + else + { + /* nothing to do, we're already sending (or asking the user for + * permission to do so) on that stream */ + } + } + else + { + g_message ("%s: SIGNALLING: receive: Please stop sending me media", + tp_base_call_stream_get_object_path (base)); + g_message ("%s: SIGNALLING: reply: OK!", + tp_base_call_stream_get_object_path (base)); + + if (local_sending_state == TP_SENDING_STATE_SENDING) + { + g_message ("%s: MEDIA: no longer sending media to peer", + tp_base_call_stream_get_object_path (base)); + + tp_base_call_stream_update_local_sending_state (base, + TP_SENDING_STATE_NONE, 0, + TP_CALL_STATE_CHANGE_REASON_UNKNOWN, + "", ""); + } + else if (local_sending_state == TP_SENDING_STATE_PENDING_SEND) + { + tp_base_call_stream_update_local_sending_state (base, + TP_SENDING_STATE_NONE, 0, + TP_CALL_STATE_CHANGE_REASON_UNKNOWN, + "", ""); + } + else + { + /* nothing to do, we're not sending on that stream anyway */ + } + } + + if (remote_send) + { + g_message ("%s: SIGNALLING: receive: I will now send you media", + tp_base_call_stream_get_object_path (base)); + + if (remote_sending_state != TP_SENDING_STATE_SENDING) + { + tp_base_call_stream_update_remote_sending_state ( + (TpBaseCallStream *) self, + self->priv->handle, TP_SENDING_STATE_SENDING, 0, + TP_CALL_STATE_CHANGE_REASON_UNKNOWN, "", ""); + } + } + else + { + if (remote_sending_state == TP_SENDING_STATE_PENDING_SEND) + { + g_message ("%s: SIGNALLING: receive: No, I refuse to send you media", + tp_base_call_stream_get_object_path (base)); + + tp_base_call_stream_update_remote_sending_state ( + (TpBaseCallStream *) self, + self->priv->handle, TP_SENDING_STATE_NONE, 0, + TP_CALL_STATE_CHANGE_REASON_UNKNOWN, "", ""); + } + else if (remote_sending_state == TP_SENDING_STATE_SENDING) + { + g_message ("%s: SIGNALLING: receive: I will no longer send media", + tp_base_call_stream_get_object_path (base)); + + tp_base_call_stream_update_remote_sending_state ( + (TpBaseCallStream *) self, + self->priv->handle, TP_SENDING_STATE_NONE, 0, + TP_CALL_STATE_CHANGE_REASON_UNKNOWN, "", ""); + } + } +} + +static gboolean +stream_set_sending (TpBaseCallStream *base, + gboolean sending, + GError **error) +{ + ExampleCallStream *self = EXAMPLE_CALL_STREAM (base); + TpSendingState remote_sending_state = + tp_base_call_stream_get_remote_sending_state (base, self->priv->handle); + + example_call_stream_change_direction (self, sending, + (remote_sending_state == TP_SENDING_STATE_SENDING)); + + return TRUE; +} + +static gboolean +stream_request_receiving (TpBaseCallStream *base, + TpHandle contact, + gboolean receive, + GError **error) +{ + ExampleCallStream *self = EXAMPLE_CALL_STREAM (base); + TpSendingState local_sending_state = + tp_base_call_stream_get_local_sending_state (base); + + /* This is the only member */ + g_assert (contact == self->priv->handle); + + example_call_stream_change_direction (self, + (local_sending_state == TP_SENDING_STATE_SENDING), + receive); + + return TRUE; +} diff --git a/examples/cm/call/call-stream.h b/examples/cm/call/call-stream.h new file mode 100644 index 000000000..0b8b08dc5 --- /dev/null +++ b/examples/cm/call/call-stream.h @@ -0,0 +1,80 @@ +/* + * call-stream.h - header for an example stream + * + * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright © 2007-2009 Nokia Corporation + * + * 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 + */ + +#ifndef EXAMPLE_CALL_STREAM_H +#define EXAMPLE_CALL_STREAM_H + +#include <glib-object.h> + +#include <telepathy-glib/telepathy-glib.h> + +G_BEGIN_DECLS + +typedef struct _ExampleCallStream ExampleCallStream; +typedef struct _ExampleCallStreamPrivate + ExampleCallStreamPrivate; + +typedef struct _ExampleCallStreamClass + ExampleCallStreamClass; +typedef struct _ExampleCallStreamClassPrivate + ExampleCallStreamClassPrivate; + +GType example_call_stream_get_type (void); + +#define EXAMPLE_TYPE_CALL_STREAM \ + (example_call_stream_get_type ()) +#define EXAMPLE_CALL_STREAM(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXAMPLE_TYPE_CALL_STREAM, \ + ExampleCallStream)) +#define EXAMPLE_CALL_STREAM_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), EXAMPLE_TYPE_CALL_STREAM, \ + ExampleCallStreamClass)) +#define EXAMPLE_IS_CALL_STREAM(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXAMPLE_TYPE_CALL_STREAM)) +#define EXAMPLE_IS_CALL_STREAM_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), EXAMPLE_TYPE_CALL_STREAM)) +#define EXAMPLE_CALL_STREAM_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_CALL_STREAM, \ + ExampleCallStreamClass)) + +struct _ExampleCallStreamClass { + TpBaseMediaCallStreamClass parent_class; + + ExampleCallStreamClassPrivate *priv; +}; + +struct _ExampleCallStream { + TpBaseMediaCallStream parent; + + ExampleCallStreamPrivate *priv; +}; + +void example_call_stream_accept_proposed_direction (ExampleCallStream *self); +void example_call_stream_connect (ExampleCallStream *self); + +/* This controls receiving emulated network events, so it wouldn't exist in + * a real connection manager */ +void example_call_stream_simulate_contact_agreed_to_send ( + ExampleCallStream *self); + +G_END_DECLS + +#endif diff --git a/examples/cm/callable/connection-manager.c b/examples/cm/call/cm.c index 3ec4d788c..5e42d97d3 100644 --- a/examples/cm/callable/connection-manager.c +++ b/examples/cm/call/cm.c @@ -21,7 +21,7 @@ #include "config.h" -#include "connection-manager.h" +#include "cm.h" #include <dbus/dbus-glib.h> @@ -31,38 +31,37 @@ #include "conn.h" #include "protocol.h" -G_DEFINE_TYPE (ExampleCallableConnectionManager, - example_callable_connection_manager, +G_DEFINE_TYPE (ExampleCallConnectionManager, + example_call_connection_manager, TP_TYPE_BASE_CONNECTION_MANAGER) -struct _ExampleCallableConnectionManagerPrivate +struct _ExampleCallConnectionManagerPrivate { int dummy; }; static void -example_callable_connection_manager_init ( - ExampleCallableConnectionManager *self) +example_call_connection_manager_init (ExampleCallConnectionManager *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - EXAMPLE_TYPE_CALLABLE_CONNECTION_MANAGER, - ExampleCallableConnectionManagerPrivate); + EXAMPLE_TYPE_CALL_CONNECTION_MANAGER, + ExampleCallConnectionManagerPrivate); } static void -example_callable_connection_manager_constructed (GObject *object) +example_call_connection_manager_constructed (GObject *object) { - ExampleCallableConnectionManager *self = - EXAMPLE_CALLABLE_CONNECTION_MANAGER (object); + ExampleCallConnectionManager *self = + EXAMPLE_CALL_CONNECTION_MANAGER (object); TpBaseConnectionManager *base = (TpBaseConnectionManager *) self; void (*constructed) (GObject *) = - ((GObjectClass *) example_callable_connection_manager_parent_class)->constructed; + ((GObjectClass *) example_call_connection_manager_parent_class)->constructed; TpBaseProtocol *protocol; if (constructed != NULL) constructed (object); - protocol = g_object_new (EXAMPLE_TYPE_CALLABLE_PROTOCOL, + protocol = g_object_new (EXAMPLE_TYPE_CALL_PROTOCOL, "name", "example", NULL); tp_base_connection_manager_add_protocol (base, protocol); @@ -70,16 +69,16 @@ example_callable_connection_manager_constructed (GObject *object) } static void -example_callable_connection_manager_class_init ( - ExampleCallableConnectionManagerClass *klass) +example_call_connection_manager_class_init ( + ExampleCallConnectionManagerClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; TpBaseConnectionManagerClass *base_class = (TpBaseConnectionManagerClass *) klass; g_type_class_add_private (klass, - sizeof (ExampleCallableConnectionManagerPrivate)); + sizeof (ExampleCallConnectionManagerPrivate)); - object_class->constructed = example_callable_connection_manager_constructed; - base_class->cm_dbus_name = "example_callable"; + object_class->constructed = example_call_connection_manager_constructed; + base_class->cm_dbus_name = "example_call"; } diff --git a/examples/cm/call/cm.h b/examples/cm/call/cm.h new file mode 100644 index 000000000..275abb229 --- /dev/null +++ b/examples/cm/call/cm.h @@ -0,0 +1,73 @@ +/* + * manager.h - header for an example connection manager + * + * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright © 2007-2009 Nokia Corporation + * + * 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 + */ + +#ifndef EXAMPLE_CALL_CM_H +#define EXAMPLE_CALL_CM_H + +#include <glib-object.h> +#include <telepathy-glib/base-connection-manager.h> + +G_BEGIN_DECLS + +typedef struct _ExampleCallConnectionManager + ExampleCallConnectionManager; +typedef struct _ExampleCallConnectionManagerPrivate + ExampleCallConnectionManagerPrivate; + +typedef struct _ExampleCallConnectionManagerClass + ExampleCallConnectionManagerClass; +typedef struct _ExampleCallConnectionManagerClassPrivate + ExampleCallConnectionManagerClassPrivate; + +struct _ExampleCallConnectionManagerClass { + TpBaseConnectionManagerClass parent_class; + + ExampleCallConnectionManagerClassPrivate *priv; +}; + +struct _ExampleCallConnectionManager { + TpBaseConnectionManager parent; + + ExampleCallConnectionManagerPrivate *priv; +}; + +GType example_call_connection_manager_get_type (void); + +/* TYPE MACROS */ +#define EXAMPLE_TYPE_CALL_CONNECTION_MANAGER \ + (example_call_connection_manager_get_type ()) +#define EXAMPLE_CALL_CONNECTION_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), EXAMPLE_TYPE_CALL_CONNECTION_MANAGER, \ + ExampleCallConnectionManager)) +#define EXAMPLE_CALL_CONNECTION_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), EXAMPLE_TYPE_CALL_CONNECTION_MANAGER, \ + ExampleCallConnectionManagerClass)) +#define EXAMPLE_IS_CALL_CONNECTION_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), EXAMPLE_TYPE_CALL_CONNECTION_MANAGER)) +#define EXAMPLE_IS_CALL_CONNECTION_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), EXAMPLE_TYPE_CALL_CONNECTION_MANAGER)) +#define EXAMPLE_CALL_CONNECTION_MANAGER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_CALL_CONNECTION_MANAGER, \ + ExampleCallConnectionManagerClass)) + +G_END_DECLS + +#endif diff --git a/examples/cm/callable/conn.c b/examples/cm/call/conn.c index f29d80925..2b7503c16 100644 --- a/examples/cm/callable/conn.c +++ b/examples/cm/call/conn.c @@ -29,11 +29,11 @@ #include <telepathy-glib/handle-repo-dynamic.h> #include <telepathy-glib/handle-repo-static.h> -#include "media-manager.h" +#include "call-manager.h" #include "protocol.h" -G_DEFINE_TYPE_WITH_CODE (ExampleCallableConnection, - example_callable_connection, +G_DEFINE_TYPE_WITH_CODE (ExampleCallConnection, + example_call_connection, TP_TYPE_BASE_CONNECTION, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACTS, tp_contacts_mixin_iface_init); @@ -57,7 +57,7 @@ enum static guint signals[N_SIGNALS] = { 0 }; -struct _ExampleCallableConnectionPrivate +struct _ExampleCallConnectionPrivate { gchar *account; guint simulation_delay; @@ -66,11 +66,11 @@ struct _ExampleCallableConnectionPrivate }; static void -example_callable_connection_init (ExampleCallableConnection *self) +example_call_connection_init (ExampleCallConnection *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - EXAMPLE_TYPE_CALLABLE_CONNECTION, - ExampleCallableConnectionPrivate); + EXAMPLE_TYPE_CALL_CONNECTION, + ExampleCallConnectionPrivate); self->priv->away = FALSE; self->priv->presence_message = g_strdup (""); } @@ -81,7 +81,7 @@ get_property (GObject *object, GValue *value, GParamSpec *spec) { - ExampleCallableConnection *self = EXAMPLE_CALLABLE_CONNECTION (object); + ExampleCallConnection *self = EXAMPLE_CALL_CONNECTION (object); switch (property_id) { @@ -100,11 +100,11 @@ get_property (GObject *object, static void set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *spec) + guint property_id, + const GValue *value, + GParamSpec *spec) { - ExampleCallableConnection *self = EXAMPLE_CALLABLE_CONNECTION (object); + ExampleCallConnection *self = EXAMPLE_CALL_CONNECTION (object); switch (property_id) { @@ -125,32 +125,32 @@ set_property (GObject *object, static void finalize (GObject *object) { - ExampleCallableConnection *self = EXAMPLE_CALLABLE_CONNECTION (object); + ExampleCallConnection *self = EXAMPLE_CALL_CONNECTION (object); tp_contacts_mixin_finalize (object); g_free (self->priv->account); g_free (self->priv->presence_message); - G_OBJECT_CLASS (example_callable_connection_parent_class)->finalize (object); + G_OBJECT_CLASS (example_call_connection_parent_class)->finalize (object); } static gchar * get_unique_connection_name (TpBaseConnection *conn) { - ExampleCallableConnection *self = EXAMPLE_CALLABLE_CONNECTION (conn); + ExampleCallConnection *self = EXAMPLE_CALL_CONNECTION (conn); return g_strdup_printf ("%s@%p", self->priv->account, self); } -gchar * -example_callable_normalize_contact (TpHandleRepoIface *repo, - const gchar *id, - gpointer context, - GError **error) +static gchar * +example_call_normalize_contact (TpHandleRepoIface *repo, + const gchar *id, + gpointer context, + GError **error) { gchar *normal = NULL; - if (example_callable_protocol_check_contact_id (id, &normal, error)) + if (example_call_protocol_check_contact_id (id, &normal, error)) return normal; else return NULL; @@ -158,20 +158,20 @@ example_callable_normalize_contact (TpHandleRepoIface *repo, static void create_handle_repos (TpBaseConnection *conn, - TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES]) + TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES]) { repos[TP_HANDLE_TYPE_CONTACT] = tp_dynamic_handle_repo_new - (TP_HANDLE_TYPE_CONTACT, example_callable_normalize_contact, NULL); + (TP_HANDLE_TYPE_CONTACT, example_call_normalize_contact, NULL); } static GPtrArray * create_channel_managers (TpBaseConnection *conn) { - ExampleCallableConnection *self = EXAMPLE_CALLABLE_CONNECTION (conn); + ExampleCallConnection *self = EXAMPLE_CALL_CONNECTION (conn); GPtrArray *ret = g_ptr_array_sized_new (1); g_ptr_array_add (ret, - g_object_new (EXAMPLE_TYPE_CALLABLE_MEDIA_MANAGER, + g_object_new (EXAMPLE_TYPE_CALL_MANAGER, "connection", conn, "simulation-delay", self->priv->simulation_delay, NULL)); @@ -181,9 +181,9 @@ create_channel_managers (TpBaseConnection *conn) static gboolean start_connecting (TpBaseConnection *conn, - GError **error) + GError **error) { - ExampleCallableConnection *self = EXAMPLE_CALLABLE_CONNECTION (conn); + ExampleCallConnection *self = EXAMPLE_CALL_CONNECTION (conn); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); @@ -217,23 +217,23 @@ constructed (GObject *object) { TpBaseConnection *base = TP_BASE_CONNECTION (object); void (*chain_up) (GObject *) = - G_OBJECT_CLASS (example_callable_connection_parent_class)->constructed; + G_OBJECT_CLASS (example_call_connection_parent_class)->constructed; if (chain_up != NULL) chain_up (object); tp_contacts_mixin_init (object, - G_STRUCT_OFFSET (ExampleCallableConnection, contacts_mixin)); + G_STRUCT_OFFSET (ExampleCallConnection, contacts_mixin)); tp_base_connection_register_with_contacts_mixin (base); tp_presence_mixin_init (object, - G_STRUCT_OFFSET (ExampleCallableConnection, presence_mixin)); + G_STRUCT_OFFSET (ExampleCallConnection, presence_mixin)); tp_presence_mixin_simple_presence_register_with_contacts_mixin (object); } static gboolean status_available (GObject *object, - guint index_) + guint index_) { TpBaseConnection *base = TP_BASE_CONNECTION (object); @@ -245,11 +245,11 @@ status_available (GObject *object, static GHashTable * get_contact_statuses (GObject *object, - const GArray *contacts, - GError **error) + const GArray *contacts, + GError **error) { - ExampleCallableConnection *self = - EXAMPLE_CALLABLE_CONNECTION (object); + ExampleCallConnection *self = + EXAMPLE_CALL_CONNECTION (object); TpBaseConnection *base = TP_BASE_CONNECTION (object); guint i; GHashTable *result = g_hash_table_new_full (g_direct_hash, g_direct_equal, @@ -258,7 +258,7 @@ get_contact_statuses (GObject *object, for (i = 0; i < contacts->len; i++) { TpHandle contact = g_array_index (contacts, guint, i); - ExampleCallablePresence presence; + ExampleCallPresence presence; GHashTable *parameters; parameters = g_hash_table_new_full (g_str_hash, @@ -268,8 +268,8 @@ get_contact_statuses (GObject *object, * everyone else's status is assumed to be "available" */ if (contact == base->self_handle) { - presence = (self->priv->away ? EXAMPLE_CALLABLE_PRESENCE_AWAY - : EXAMPLE_CALLABLE_PRESENCE_AVAILABLE); + presence = (self->priv->away ? EXAMPLE_CALL_PRESENCE_AWAY + : EXAMPLE_CALL_PRESENCE_AVAILABLE); if (self->priv->presence_message[0] != '\0') g_hash_table_insert (parameters, "message", @@ -277,7 +277,7 @@ get_contact_statuses (GObject *object, } else { - presence = EXAMPLE_CALLABLE_PRESENCE_AVAILABLE; + presence = EXAMPLE_CALL_PRESENCE_AVAILABLE; } g_hash_table_insert (result, GUINT_TO_POINTER (contact), @@ -290,11 +290,11 @@ get_contact_statuses (GObject *object, static gboolean set_own_status (GObject *object, - const TpPresenceStatus *status, - GError **error) + const TpPresenceStatus *status, + GError **error) { - ExampleCallableConnection *self = - EXAMPLE_CALLABLE_CONNECTION (object); + ExampleCallConnection *self = + EXAMPLE_CALL_CONNECTION (object); TpBaseConnection *base = TP_BASE_CONNECTION (object); GHashTable *presences; const gchar *message = ""; @@ -312,7 +312,7 @@ set_own_status (GObject *object, } } - if (status->index == EXAMPLE_CALLABLE_PRESENCE_AWAY) + if (status->index == EXAMPLE_CALL_PRESENCE_AWAY) { if (self->priv->away && !tp_strdiff (message, self->priv->presence_message)) @@ -352,7 +352,7 @@ static const TpPresenceStatusOptionalArgumentSpec can_have_message[] = { { NULL } }; -/* Must be kept in sync with ExampleCallablePresence enum in header */ +/* Must be kept in sync with ExampleCallPresence enum in header */ static const TpPresenceStatusSpec presence_statuses[] = { { "offline", TP_CONNECTION_PRESENCE_TYPE_OFFLINE, FALSE, NULL }, { "unknown", TP_CONNECTION_PRESENCE_TYPE_UNKNOWN, FALSE, NULL }, @@ -371,7 +371,7 @@ static const gchar *interfaces_always_present[] = { NULL }; const gchar * const * -example_callable_connection_get_possible_interfaces (void) +example_call_connection_get_possible_interfaces (void) { /* in this example CM we don't have any extra interfaces that are sometimes, * but not always, present */ @@ -379,8 +379,8 @@ example_callable_connection_get_possible_interfaces (void) } static void -example_callable_connection_class_init ( - ExampleCallableConnectionClass *klass) +example_call_connection_class_init ( + ExampleCallConnectionClass *klass) { TpBaseConnectionClass *base_class = (TpBaseConnectionClass *) klass; GObjectClass *object_class = (GObjectClass *) klass; @@ -391,7 +391,7 @@ example_callable_connection_class_init ( object_class->constructed = constructed; object_class->finalize = finalize; g_type_class_add_private (klass, - sizeof (ExampleCallableConnectionPrivate)); + sizeof (ExampleCallConnectionPrivate)); base_class->create_handle_repos = create_handle_repos; base_class->get_unique_connection_name = get_unique_connection_name; @@ -412,7 +412,7 @@ example_callable_connection_class_init ( g_object_class_install_property (object_class, PROP_SIMULATION_DELAY, param_spec); - /* Used in the media manager, to simulate an incoming call when we become + /* Used in the call manager, to simulate an incoming call when we become * available */ signals[SIGNAL_AVAILABLE] = g_signal_new ("available", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, @@ -420,9 +420,9 @@ example_callable_connection_class_init ( G_TYPE_NONE, 1, G_TYPE_STRING); tp_contacts_mixin_class_init (object_class, - G_STRUCT_OFFSET (ExampleCallableConnectionClass, contacts_mixin)); + G_STRUCT_OFFSET (ExampleCallConnectionClass, contacts_mixin)); tp_presence_mixin_class_init (object_class, - G_STRUCT_OFFSET (ExampleCallableConnectionClass, presence_mixin), + G_STRUCT_OFFSET (ExampleCallConnectionClass, presence_mixin), status_available, get_contact_statuses, set_own_status, presence_statuses); tp_presence_mixin_simple_presence_init_dbus_properties (object_class); diff --git a/examples/cm/call/conn.h b/examples/cm/call/conn.h new file mode 100644 index 000000000..1ad22ae3a --- /dev/null +++ b/examples/cm/call/conn.h @@ -0,0 +1,77 @@ +/* + * conn.h - header for an example connection + * + * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright © 2007-2009 Nokia Corporation + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. + */ + +#ifndef EXAMPLE_CALL_CONN_H +#define EXAMPLE_CALL_CONN_H + +#include <glib-object.h> +#include <telepathy-glib/base-connection.h> +#include <telepathy-glib/contacts-mixin.h> +#include <telepathy-glib/presence-mixin.h> + +G_BEGIN_DECLS + +typedef struct _ExampleCallConnection ExampleCallConnection; +typedef struct _ExampleCallConnectionPrivate + ExampleCallConnectionPrivate; + +typedef struct _ExampleCallConnectionClass ExampleCallConnectionClass; +typedef struct _ExampleCallConnectionClassPrivate + ExampleCallConnectionClassPrivate; + +struct _ExampleCallConnectionClass { + TpBaseConnectionClass parent_class; + TpPresenceMixinClass presence_mixin; + TpContactsMixinClass contacts_mixin; + + ExampleCallConnectionClassPrivate *priv; +}; + +struct _ExampleCallConnection { + TpBaseConnection parent; + TpPresenceMixin presence_mixin; + TpContactsMixin contacts_mixin; + + ExampleCallConnectionPrivate *priv; +}; + +GType example_call_connection_get_type (void); + +#define EXAMPLE_TYPE_CALL_CONNECTION \ + (example_call_connection_get_type ()) +#define EXAMPLE_CALL_CONNECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), EXAMPLE_TYPE_CALL_CONNECTION, \ + ExampleCallConnection)) +#define EXAMPLE_CALL_CONNECTION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), EXAMPLE_TYPE_CALL_CONNECTION, \ + ExampleCallConnectionClass)) +#define EXAMPLE_IS_CALL_CONNECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), EXAMPLE_TYPE_CALL_CONNECTION)) +#define EXAMPLE_IS_CALL_CONNECTION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), EXAMPLE_TYPE_CALL_CONNECTION)) +#define EXAMPLE_CALL_CONNECTION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_CALL_CONNECTION, \ + ExampleCallConnectionClass)) + +/* Must be kept in sync with the array presence_statuses in conn.c */ +typedef enum { + EXAMPLE_CALL_PRESENCE_OFFLINE = 0, + EXAMPLE_CALL_PRESENCE_UNKNOWN, + EXAMPLE_CALL_PRESENCE_ERROR, + EXAMPLE_CALL_PRESENCE_AWAY, + EXAMPLE_CALL_PRESENCE_AVAILABLE +} ExampleCallPresence; + +const gchar * const *example_call_connection_get_possible_interfaces (void); + +G_END_DECLS + +#endif diff --git a/examples/cm/call/example_call.manager b/examples/cm/call/example_call.manager new file mode 100644 index 000000000..b83bd93d5 --- /dev/null +++ b/examples/cm/call/example_call.manager @@ -0,0 +1,25 @@ +[ConnectionManager] +Interfaces= + +[Protocol example] +Interfaces= +ConnectionInterfaces=org.freedesktop.Telepathy.Connection.Interface.Requests;org.freedesktop.Telepathy.Connection.Interface.Contacts;org.freedesktop.Telepathy.Connection.Interface.Presence;org.freedesktop.Telepathy.Connection.Interface.SimplePresence; +param-account=s required register +param-simulation-delay=u +default-simulation-delay=1000 +RequestableChannelClasses=audio;video; +VCardField=x-telepathy-example +EnglishName=Example with Call channels +Icon=face-smile + +[audio] +org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.Call.DRAFT +org.freedesktop.Telepathy.Channel.TargetHandleType u=1 +org.freedesktop.Telepathy.Channel.Type.Call.DRAFT.InitialAudio b=1 +allowed=org.freedesktop.Telepathy.Channel.TargetHandle;org.freedesktop.Telepathy.Channel.TargetID;org.freedesktop.Telepathy.Channel.Type.Call.DRAFT.InitialVideo; + +[video] +org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.Call.DRAFT +org.freedesktop.Telepathy.Channel.TargetHandleType u=1 +org.freedesktop.Telepathy.Channel.Type.Call.DRAFT.InitialVideo b=1 +allowed=org.freedesktop.Telepathy.Channel.TargetHandle;org.freedesktop.Telepathy.Channel.TargetID;org.freedesktop.Telepathy.Channel.Type.Call.DRAFT.InitialAudio; diff --git a/examples/cm/callable/main.c b/examples/cm/call/main.c index ea0b1715b..a25936e1b 100644 --- a/examples/cm/callable/main.c +++ b/examples/cm/call/main.c @@ -21,16 +21,18 @@ #include "config.h" +#include "config.h" + #include <telepathy-glib/debug.h> #include <telepathy-glib/run.h> -#include "connection-manager.h" +#include "cm.h" static TpBaseConnectionManager * construct_cm (void) { return (TpBaseConnectionManager *) g_object_new ( - EXAMPLE_TYPE_CALLABLE_CONNECTION_MANAGER, + EXAMPLE_TYPE_CALL_CONNECTION_MANAGER, NULL); } @@ -49,6 +51,10 @@ main (int argc, tp_debug_set_persistent (TRUE); #endif - return tp_run_connection_manager ("telepathy-example-cm-callable", + /* strictly speaking, this is only necessary for client code, but it's + * harmless here */ + g_type_init (); + + return tp_run_connection_manager ("telepathy-example-cm-call", VERSION, construct_cm, argc, argv); } diff --git a/examples/cm/callable/protocol.c b/examples/cm/call/protocol.c index cf752e3d7..26540ddcc 100644 --- a/examples/cm/callable/protocol.c +++ b/examples/cm/call/protocol.c @@ -14,21 +14,21 @@ #include <telepathy-glib/telepathy-glib.h> +#include "call-manager.h" #include "conn.h" -#include "media-manager.h" -G_DEFINE_TYPE (ExampleCallableProtocol, - example_callable_protocol, +G_DEFINE_TYPE (ExampleCallProtocol, + example_call_protocol, TP_TYPE_BASE_PROTOCOL) static void -example_callable_protocol_init ( - ExampleCallableProtocol *self) +example_call_protocol_init ( + ExampleCallProtocol *self) { } gboolean -example_callable_protocol_check_contact_id (const gchar *id, +example_call_protocol_check_contact_id (const gchar *id, gchar **normal, GError **error) { @@ -54,10 +54,10 @@ account_param_filter (const TpCMParamSpec *paramspec, { const gchar *id = g_value_get_string (value); - return example_callable_protocol_check_contact_id (id, NULL, error); + return example_call_protocol_check_contact_id (id, NULL, error); } -static const TpCMParamSpec example_callable_example_params[] = { +static const TpCMParamSpec example_call_example_params[] = { { "account", "s", G_TYPE_STRING, TP_CONN_MGR_PARAM_FLAG_REQUIRED | TP_CONN_MGR_PARAM_FLAG_REGISTER, NULL, /* no default */ @@ -78,7 +78,7 @@ static const TpCMParamSpec example_callable_example_params[] = { static const TpCMParamSpec * get_parameters (TpBaseProtocol *self) { - return example_callable_example_params; + return example_call_example_params; } static TpBaseConnection * @@ -86,7 +86,7 @@ new_connection (TpBaseProtocol *protocol, GHashTable *asv, GError **error) { - ExampleCallableConnection *conn; + ExampleCallConnection *conn; const gchar *account; guint sim_delay; @@ -96,8 +96,8 @@ new_connection (TpBaseProtocol *protocol, sim_delay = tp_asv_get_uint32 (asv, "simulation-delay", NULL); - conn = EXAMPLE_CALLABLE_CONNECTION ( - g_object_new (EXAMPLE_TYPE_CALLABLE_CONNECTION, + conn = EXAMPLE_CALL_CONNECTION ( + g_object_new (EXAMPLE_TYPE_CALL_CONNECTION, "account", account, "protocol", tp_base_protocol_get_name (protocol), "simulation-delay", sim_delay, @@ -113,7 +113,7 @@ normalize_contact (TpBaseProtocol *self G_GNUC_UNUSED, { gchar *normal; - if (example_callable_protocol_check_contact_id (contact, &normal, error)) + if (example_call_protocol_check_contact_id (contact, &normal, error)) return normal; else return NULL; @@ -151,12 +151,12 @@ get_connection_details (TpBaseProtocol *self G_GNUC_UNUSED, if (connection_interfaces != NULL) { *connection_interfaces = g_strdupv ( - (GStrv) example_callable_connection_get_possible_interfaces ()); + (GStrv) example_call_connection_get_possible_interfaces ()); } if (channel_managers != NULL) { - GType types[] = { EXAMPLE_TYPE_CALLABLE_MEDIA_MANAGER, G_TYPE_INVALID }; + GType types[] = { EXAMPLE_TYPE_CALL_MANAGER, G_TYPE_INVALID }; *channel_managers = g_memdup (types, sizeof (types)); } @@ -165,15 +165,15 @@ get_connection_details (TpBaseProtocol *self G_GNUC_UNUSED, *icon_name = g_strdup ("face-smile"); if (english_name != NULL) - *english_name = g_strdup ("Example with StreamedMedia calls"); + *english_name = g_strdup ("Example with Call channels"); if (vcard_field != NULL) *vcard_field = g_strdup ("x-telepathy-example"); } static void -example_callable_protocol_class_init ( - ExampleCallableProtocolClass *klass) +example_call_protocol_class_init ( + ExampleCallProtocolClass *klass) { TpBaseProtocolClass *base_class = (TpBaseProtocolClass *) klass; diff --git a/examples/cm/call/protocol.h b/examples/cm/call/protocol.h new file mode 100644 index 000000000..68d2acd7c --- /dev/null +++ b/examples/cm/call/protocol.h @@ -0,0 +1,64 @@ +/* + * protocol.h - header for an example Protocol + * Copyright © 2007-2010 Collabora Ltd. + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. + */ + +#ifndef EXAMPLE_CALL_PROTOCOL_H +#define EXAMPLE_CALL_PROTOCOL_H + +#include <glib-object.h> +#include <telepathy-glib/base-protocol.h> + +G_BEGIN_DECLS + +typedef struct _ExampleCallProtocol ExampleCallProtocol; +typedef struct _ExampleCallProtocolPrivate ExampleCallProtocolPrivate; +typedef struct _ExampleCallProtocolClass ExampleCallProtocolClass; +typedef struct _ExampleCallProtocolClassPrivate ExampleCallProtocolClassPrivate; + +struct _ExampleCallProtocolClass { + TpBaseProtocolClass parent_class; + + ExampleCallProtocolClassPrivate *priv; +}; + +struct _ExampleCallProtocol { + TpBaseProtocol parent; + + ExampleCallProtocolPrivate *priv; +}; + +GType example_call_protocol_get_type (void); + +#define EXAMPLE_TYPE_CALL_PROTOCOL \ + (example_call_protocol_get_type ()) +#define EXAMPLE_CALL_PROTOCOL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + EXAMPLE_TYPE_CALL_PROTOCOL, \ + ExampleCallProtocol)) +#define EXAMPLE_CALL_PROTOCOL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + EXAMPLE_TYPE_CALL_PROTOCOL, \ + ExampleCallProtocolClass)) +#define EXAMPLE_IS_CALL_PROTOCOL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + EXAMPLE_TYPE_CALL_PROTOCOL)) +#define EXAMPLE_IS_CALL_PROTOCOL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + EXAMPLE_TYPE_CALL_PROTOCOL)) +#define EXAMPLE_CALL_PROTOCOL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + EXAMPLE_TYPE_CALL_PROTOCOL, \ + ExampleCallProtocolClass)) + +gboolean example_call_protocol_check_contact_id (const gchar *id, + gchar **normal, + GError **error); + +G_END_DECLS + +#endif diff --git a/examples/cm/callable/conn.h b/examples/cm/callable/conn.h deleted file mode 100644 index eee9d988c..000000000 --- a/examples/cm/callable/conn.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * conn.h - header for an example connection - * - * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/> - * Copyright © 2007-2009 Nokia Corporation - * - * Copying and distribution of this file, with or without modification, - * are permitted in any medium without royalty provided the copyright - * notice and this notice are preserved. - */ - -#ifndef __EXAMPLE_CALLABLE_CONN_H__ -#define __EXAMPLE_CALLABLE_CONN_H__ - -#include <glib-object.h> -#include <telepathy-glib/base-connection.h> -#include <telepathy-glib/contacts-mixin.h> -#include <telepathy-glib/presence-mixin.h> - -G_BEGIN_DECLS - -typedef struct _ExampleCallableConnection ExampleCallableConnection; -typedef struct _ExampleCallableConnectionPrivate - ExampleCallableConnectionPrivate; - -typedef struct _ExampleCallableConnectionClass ExampleCallableConnectionClass; -typedef struct _ExampleCallableConnectionClassPrivate - ExampleCallableConnectionClassPrivate; - -struct _ExampleCallableConnectionClass { - TpBaseConnectionClass parent_class; - TpPresenceMixinClass presence_mixin; - TpContactsMixinClass contacts_mixin; - - ExampleCallableConnectionClassPrivate *priv; -}; - -struct _ExampleCallableConnection { - TpBaseConnection parent; - TpPresenceMixin presence_mixin; - TpContactsMixin contacts_mixin; - - ExampleCallableConnectionPrivate *priv; -}; - -GType example_callable_connection_get_type (void); - -#define EXAMPLE_TYPE_CALLABLE_CONNECTION \ - (example_callable_connection_get_type ()) -#define EXAMPLE_CALLABLE_CONNECTION(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), EXAMPLE_TYPE_CALLABLE_CONNECTION, \ - ExampleCallableConnection)) -#define EXAMPLE_CALLABLE_CONNECTION_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), EXAMPLE_TYPE_CALLABLE_CONNECTION, \ - ExampleCallableConnectionClass)) -#define EXAMPLE_IS_CALLABLE_CONNECTION(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), EXAMPLE_TYPE_CALLABLE_CONNECTION)) -#define EXAMPLE_IS_CALLABLE_CONNECTION_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), EXAMPLE_TYPE_CALLABLE_CONNECTION)) -#define EXAMPLE_CALLABLE_CONNECTION_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_CALLABLE_CONNECTION, \ - ExampleCallableConnectionClass)) - -gchar *example_callable_normalize_contact (TpHandleRepoIface *repo, - const gchar *id, gpointer context, GError **error); - -/* Must be kept in sync with the array presence_statuses in conn.c */ -typedef enum { - EXAMPLE_CALLABLE_PRESENCE_OFFLINE = 0, - EXAMPLE_CALLABLE_PRESENCE_UNKNOWN, - EXAMPLE_CALLABLE_PRESENCE_ERROR, - EXAMPLE_CALLABLE_PRESENCE_AWAY, - EXAMPLE_CALLABLE_PRESENCE_AVAILABLE -} ExampleCallablePresence; - -const gchar * const * example_callable_connection_get_possible_interfaces ( - void); - -G_END_DECLS - -#endif diff --git a/examples/cm/callable/connection-manager.h b/examples/cm/callable/connection-manager.h deleted file mode 100644 index 5d854cb1c..000000000 --- a/examples/cm/callable/connection-manager.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * manager.h - header for an example connection manager - * - * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/> - * Copyright © 2007-2009 Nokia Corporation - * - * 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 - */ - -#ifndef __EXAMPLE_CALLABLE_CONNECTION_MANAGER_H__ -#define __EXAMPLE_CALLABLE_CONNECTION_MANAGER_H__ - -#include <glib-object.h> -#include <telepathy-glib/base-connection-manager.h> - -G_BEGIN_DECLS - -typedef struct _ExampleCallableConnectionManager - ExampleCallableConnectionManager; -typedef struct _ExampleCallableConnectionManagerPrivate - ExampleCallableConnectionManagerPrivate; - -typedef struct _ExampleCallableConnectionManagerClass - ExampleCallableConnectionManagerClass; -typedef struct _ExampleCallableConnectionManagerClassPrivate - ExampleCallableConnectionManagerClassPrivate; - -struct _ExampleCallableConnectionManagerClass { - TpBaseConnectionManagerClass parent_class; - - ExampleCallableConnectionManagerClassPrivate *priv; -}; - -struct _ExampleCallableConnectionManager { - TpBaseConnectionManager parent; - - ExampleCallableConnectionManagerPrivate *priv; -}; - -GType example_callable_connection_manager_get_type (void); - -/* TYPE MACROS */ -#define EXAMPLE_TYPE_CALLABLE_CONNECTION_MANAGER \ - (example_callable_connection_manager_get_type ()) -#define EXAMPLE_CALLABLE_CONNECTION_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), EXAMPLE_TYPE_CALLABLE_CONNECTION_MANAGER, \ - ExampleCallableConnectionManager)) -#define EXAMPLE_CALLABLE_CONNECTION_MANAGER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), EXAMPLE_TYPE_CALLABLE_CONNECTION_MANAGER, \ - ExampleCallableConnectionManagerClass)) -#define EXAMPLE_IS_CALLABLE_CONNECTION_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), EXAMPLE_TYPE_CALLABLE_CONNECTION_MANAGER)) -#define EXAMPLE_IS_CALLABLE_CONNECTION_MANAGER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), EXAMPLE_TYPE_CALLABLE_CONNECTION_MANAGER)) -#define EXAMPLE_CALLABLE_CONNECTION_MANAGER_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_CALLABLE_CONNECTION_MANAGER, \ - ExampleCallableConnectionManagerClass)) - -G_END_DECLS - -#endif diff --git a/examples/cm/callable/example_callable.manager b/examples/cm/callable/example_callable.manager deleted file mode 100644 index 0caf732a7..000000000 --- a/examples/cm/callable/example_callable.manager +++ /dev/null @@ -1,19 +0,0 @@ -[ConnectionManager] -Interfaces= - -[Protocol example] -Interfaces= -ConnectionInterfaces=org.freedesktop.Telepathy.Connection.Interface.Requests;org.freedesktop.Telepathy.Connection.Interface.Contacts;org.freedesktop.Telepathy.Connection.Interface.Presence;org.freedesktop.Telepathy.Connection.Interface.SimplePresence; -param-account=s required register -param-simulation-delay=u -default-simulation-delay=1000 -RequestableChannelClasses=streamedmedia; -VCardField=x-telepathy-example -EnglishName=Example with StreamedMedia calls -Icon=face-smile - -[streamedmedia] -org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.StreamedMedia -org.freedesktop.Telepathy.Channel.TargetHandleType u=1 -allowed=org.freedesktop.Telepathy.Channel.TargetHandle;org.freedesktop.Telepathy.Channel.TargetID; - diff --git a/examples/cm/callable/media-channel.c b/examples/cm/callable/media-channel.c deleted file mode 100644 index a9920668b..000000000 --- a/examples/cm/callable/media-channel.c +++ /dev/null @@ -1,1244 +0,0 @@ -/* - * media-channel.c - an example 1-1 streamed media call. - * - * For simplicity, this channel emulates a device with its own - * audio/video user interface, like a video-equipped form of the phones - * manipulated by telepathy-snom or gnome-phone-manager. - * - * As a result, this channel does not have the MediaSignalling interface, and - * clients should not attempt to do their own streaming using - * telepathy-farsight, telepathy-stream-engine or maemo-stream-engine. - * - * In practice, nearly all connection managers also have the MediaSignalling - * interface on their streamed media channels. Usage for those CMs is the - * same, except that whichever client is the primary handler for the channel - * should also hand the channel over to telepathy-farsight or - * telepathy-stream-engine to implement the actual streaming. - * - * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/> - * Copyright © 2007-2009 Nokia Corporation - * - * 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 - */ - -#include "config.h" - -#include "media-channel.h" - -#include "media-stream.h" - -#include <string.h> - -#include <telepathy-glib/base-connection.h> -#include <telepathy-glib/channel-iface.h> -#include <telepathy-glib/dbus.h> -#include <telepathy-glib/gtypes.h> -#include <telepathy-glib/interfaces.h> -#include <telepathy-glib/svc-channel.h> -#include <telepathy-glib/svc-generic.h> - -static void media_iface_init (gpointer iface, gpointer data); -static void hold_iface_init (gpointer iface, gpointer data); -static void dtmf_iface_init (gpointer iface, gpointer data); - -G_DEFINE_TYPE_WITH_CODE (ExampleCallableMediaChannel, - example_callable_media_channel, - TP_TYPE_BASE_CHANNEL, - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_STREAMED_MEDIA, - media_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP, - tp_group_mixin_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_HOLD, - hold_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_DTMF, - dtmf_iface_init);) - -enum -{ - PROP_SIMULATION_DELAY = 1, - PROP_INITIAL_AUDIO, - PROP_INITIAL_VIDEO, - N_PROPS -}; - -enum -{ - SIGNAL_CALL_TERMINATED, - N_SIGNALS -}; - -typedef enum { - PROGRESS_NONE, - PROGRESS_CALLING, - PROGRESS_ACTIVE, - PROGRESS_ENDED -} ExampleCallableCallProgress; - -static guint signals[N_SIGNALS] = { 0 }; - -struct _ExampleCallableMediaChannelPrivate -{ - ExampleCallableCallProgress progress; - - guint simulation_delay; - - guint next_stream_id; - - GHashTable *streams; - - guint hold_state; - guint hold_state_reason; - - gboolean initial_audio; - gboolean initial_video; - gboolean disposed; -}; - -static const char * example_callable_media_channel_interfaces[] = { - TP_IFACE_CHANNEL_INTERFACE_GROUP, - TP_IFACE_CHANNEL_INTERFACE_HOLD, - TP_IFACE_CHANNEL_INTERFACE_DTMF, - NULL -}; - -static void -example_callable_media_channel_init (ExampleCallableMediaChannel *self) -{ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - EXAMPLE_TYPE_CALLABLE_MEDIA_CHANNEL, - ExampleCallableMediaChannelPrivate); - - self->priv->next_stream_id = 1; - self->priv->streams = g_hash_table_new_full (g_direct_hash, g_direct_equal, - NULL, g_object_unref); - - self->priv->hold_state = TP_LOCAL_HOLD_STATE_UNHELD; - self->priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_NONE; -} - -static ExampleCallableMediaStream *example_callable_media_channel_add_stream ( - ExampleCallableMediaChannel *self, TpMediaStreamType media_type, - gboolean locally_requested); - -static void -constructed (GObject *object) -{ - void (*chain_up) (GObject *) = - ((GObjectClass *) example_callable_media_channel_parent_class)->constructed; - ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (object); - TpBaseChannel *base_chan = TP_BASE_CHANNEL (self); - TpBaseConnection *connection = tp_base_channel_get_connection (base_chan); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles - (connection, TP_HANDLE_TYPE_CONTACT); - TpIntset *members; - TpIntset *local_pending; - gboolean requested; - - if (chain_up != NULL) - chain_up (object); - - tp_base_channel_register (base_chan); - - tp_group_mixin_init (object, - G_STRUCT_OFFSET (ExampleCallableMediaChannel, group), - contact_repo, - connection->self_handle); - - /* Initially, the channel contains the initiator as a member; they are also - * the actor for the change that adds any initial members. */ - - members = tp_intset_new_containing (tp_base_channel_get_initiator (base_chan)); - requested = tp_base_channel_is_requested (base_chan); - - if (requested) - { - /* Nobody is locally pending. The remote peer will turn up in - * remote-pending state when we actually contact them, which is done - * in RequestStreams */ - self->priv->progress = PROGRESS_NONE; - local_pending = NULL; - } - else - { - /* This is an incoming call, so the self-handle is locally - * pending, to indicate that we need to answer. */ - self->priv->progress = PROGRESS_CALLING; - local_pending = tp_intset_new_containing (connection->self_handle); - } - - tp_group_mixin_change_members (object, "", - members /* added */, - NULL /* nobody removed */, - local_pending, /* added to local-pending */ - NULL /* nobody added to remote-pending */, - tp_base_channel_get_initiator (base_chan) /* actor */, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE); - tp_intset_destroy (members); - - if (local_pending != NULL) - tp_intset_destroy (local_pending); - - /* We don't need to allow adding or removing members to this Group in ways - * that need flags set, so the only flag we set is to say we support the - * Properties interface to the Group. - * - * It doesn't make sense to add anyone to the Group, since we already know - * who we're going to call (or were called by). The only call to AddMembers - * we need to support is to move ourselves from local-pending to member in - * the incoming call case, and that's always allowed anyway. - * - * (Connection managers that support the various backwards-compatible - * ways to make an outgoing StreamedMedia channel have to support adding the - * peer to remote-pending, but that has no actual effect other than to - * obscure what's going on; in this one, there's no need to support that - * usage.) - * - * Similarly, it doesn't make sense to remove anyone from this Group apart - * from ourselves (to hang up), and removing the SelfHandle is always - * allowed anyway. - */ - tp_group_mixin_change_flags (object, TP_CHANNEL_GROUP_FLAG_PROPERTIES, 0); - - /* Future versions of telepathy-spec will allow a channel request to - * say "initially include an audio stream" and/or "initially include a video - * stream", which would be represented like this; we don't support this - * usage yet, though, so ExampleCallableMediaManager will never invoke - * our constructor in this way. */ - g_assert (!(requested && self->priv->initial_audio)); - g_assert (!(requested && self->priv->initial_video)); - - if (!requested) - { - /* the caller has almost certainly asked us for some streams - there's - * not much point in having a call otherwise */ - - if (self->priv->initial_audio) - { - g_message ("Channel initially has an audio stream"); - example_callable_media_channel_add_stream (self, - TP_MEDIA_STREAM_TYPE_AUDIO, FALSE); - } - - if (self->priv->initial_video) - { - g_message ("Channel initially has a video stream"); - example_callable_media_channel_add_stream (self, - TP_MEDIA_STREAM_TYPE_VIDEO, FALSE); - } - } -} - -static void -get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (object); - - switch (property_id) - { - case PROP_SIMULATION_DELAY: - g_value_set_uint (value, self->priv->simulation_delay); - break; - - case PROP_INITIAL_AUDIO: - g_value_set_boolean (value, self->priv->initial_audio); - break; - - case PROP_INITIAL_VIDEO: - g_value_set_boolean (value, self->priv->initial_video); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (object); - - switch (property_id) - { - case PROP_SIMULATION_DELAY: - self->priv->simulation_delay = g_value_get_uint (value); - break; - - case PROP_INITIAL_AUDIO: - self->priv->initial_audio = g_value_get_boolean (value); - break; - - case PROP_INITIAL_VIDEO: - self->priv->initial_video = g_value_get_boolean (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -example_callable_media_channel_close (ExampleCallableMediaChannel *self, - TpHandle actor, - TpChannelGroupChangeReason reason) -{ - if (self->priv->progress != PROGRESS_ENDED) - { - TpIntset *everyone; - - self->priv->progress = PROGRESS_ENDED; - - if (actor == self->group.self_handle) - { - const gchar *send_reason; - - /* In a real protocol these would be some sort of real protocol - * construct, like an XMPP error stanza or a SIP error code */ - switch (reason) - { - case TP_CHANNEL_GROUP_CHANGE_REASON_BUSY: - send_reason = "<user-is-busy/>"; - break; - - case TP_CHANNEL_GROUP_CHANGE_REASON_NO_ANSWER: - send_reason = "<no-answer/>"; - break; - - default: - send_reason = "<call-terminated/>"; - } - - g_message ("SIGNALLING: send: Terminating call: %s", send_reason); - } - - everyone = tp_intset_new_containing (tp_base_channel_get_target_handle - (TP_BASE_CHANNEL (self))); - tp_intset_add (everyone, self->group.self_handle); - tp_group_mixin_change_members ((GObject *) self, "", - NULL /* nobody added */, - everyone /* removed */, - NULL /* nobody locally pending */, - NULL /* nobody remotely pending */, - actor, - reason); - tp_intset_destroy (everyone); - - g_signal_emit (self, signals[SIGNAL_CALL_TERMINATED], 0); - tp_base_channel_destroyed (TP_BASE_CHANNEL (self)); - } -} - -static void -dispose (GObject *object) -{ - ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (object); - - if (self->priv->disposed) - return; - - self->priv->disposed = TRUE; - - g_hash_table_unref (self->priv->streams); - self->priv->streams = NULL; - - example_callable_media_channel_close (self, self->group.self_handle, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE); - - ((GObjectClass *) example_callable_media_channel_parent_class)->dispose (object); -} - -static void -finalize (GObject *object) -{ - tp_group_mixin_finalize (object); - - ((GObjectClass *) example_callable_media_channel_parent_class)->finalize (object); -} - -static gboolean -add_member (GObject *object, - TpHandle member, - const gchar *message, - GError **error) -{ - ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (object); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles - (tp_base_channel_get_connection (TP_BASE_CHANNEL (self)), - TP_HANDLE_TYPE_CONTACT); - - /* In connection managers that supported the RequestChannel method for - * streamed media channels, it would be necessary to support adding the - * called contact to the members of an outgoing call. However, in this - * legacy-free example, we don't support that usage, so the only use for - * AddMembers is to accept an incoming call. - */ - - if (member == self->group.self_handle && - tp_handle_set_is_member (self->group.local_pending, member)) - { - /* We're in local-pending, move to members to accept. */ - TpIntset *set = tp_intset_new_containing (member); - GHashTableIter iter; - gpointer v; - - g_assert (self->priv->progress == PROGRESS_CALLING); - - g_message ("SIGNALLING: send: Accepting incoming call from %s", - tp_handle_inspect (contact_repo, tp_base_channel_get_target_handle - (TP_BASE_CHANNEL (self)))); - - self->priv->progress = PROGRESS_ACTIVE; - - tp_group_mixin_change_members (object, "", - set /* added */, - NULL /* nobody removed */, - NULL /* nobody added to local pending */, - NULL /* nobody added to remote pending */, - member /* actor */, TP_CHANNEL_GROUP_CHANGE_REASON_NONE); - - tp_intset_destroy (set); - - g_hash_table_iter_init (&iter, self->priv->streams); - - while (g_hash_table_iter_next (&iter, NULL, &v)) - { - /* we accept the proposed stream direction... */ - example_callable_media_stream_accept_proposed_direction (v); - /* ... and the stream tries to connect */ - example_callable_media_stream_connect (v); - } - - return TRUE; - } - - /* Otherwise it's a meaningless request, so reject it. */ - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "Cannot add handle %u to channel", member); - return FALSE; -} - -static gboolean -remove_member_with_reason (GObject *object, - TpHandle member, - const gchar *message, - guint reason, - GError **error) -{ - ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (object); - - /* The TpGroupMixin won't call this unless removing the member is allowed - * by the group flags, which in this case means it must be our own handle - * (because the other user never appears in local-pending). - */ - - g_assert (member == self->group.self_handle); - - example_callable_media_channel_close (self, self->group.self_handle, reason); - return TRUE; -} - -static void -channel_close (TpBaseChannel *channel) -{ - ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (channel); - - example_callable_media_channel_close (self, self->group.self_handle, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE); -} - -static void -example_callable_media_channel_class_init (ExampleCallableMediaChannelClass *klass) -{ - GObjectClass *object_class = (GObjectClass *) klass; - GParamSpec *param_spec; - TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (klass); - - g_type_class_add_private (klass, - sizeof (ExampleCallableMediaChannelPrivate)); - - object_class->constructed = constructed; - object_class->set_property = set_property; - object_class->get_property = get_property; - object_class->dispose = dispose; - object_class->finalize = finalize; - - base_class->channel_type = TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA; - base_class->target_handle_type = TP_HANDLE_TYPE_CONTACT; - base_class->interfaces = example_callable_media_channel_interfaces; - - base_class->close = channel_close; - - param_spec = g_param_spec_uint ("simulation-delay", "Simulation delay", - "Delay between simulated network events", - 0, G_MAXUINT32, 1000, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_SIMULATION_DELAY, - param_spec); - - param_spec = g_param_spec_boolean ("initial-audio", "Initial audio?", - "True if this channel had an audio stream when first announced", - FALSE, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_INITIAL_AUDIO, - param_spec); - - param_spec = g_param_spec_boolean ("initial-video", "Initial video?", - "True if this channel had a video stream when first announced", - FALSE, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_INITIAL_VIDEO, - param_spec); - - signals[SIGNAL_CALL_TERMINATED] = g_signal_new ("call-terminated", - G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - tp_group_mixin_class_init (object_class, - G_STRUCT_OFFSET (ExampleCallableMediaChannelClass, group_class), - add_member, - NULL); - tp_group_mixin_class_allow_self_removal (object_class); - tp_group_mixin_class_set_remove_with_reason_func (object_class, - remove_member_with_reason); - tp_group_mixin_init_dbus_properties (object_class); -} - -static void -media_list_streams (TpSvcChannelTypeStreamedMedia *iface, - DBusGMethodInvocation *context) -{ - ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (iface); - GPtrArray *array = g_ptr_array_sized_new (g_hash_table_size ( - self->priv->streams)); - GHashTableIter iter; - gpointer v; - - g_hash_table_iter_init (&iter, self->priv->streams); - - while (g_hash_table_iter_next (&iter, NULL, &v)) - { - ExampleCallableMediaStream *stream = v; - GValueArray *va; - - g_object_get (stream, - "stream-info", &va, - NULL); - - g_ptr_array_add (array, va); - } - - tp_svc_channel_type_streamed_media_return_from_list_streams (context, - array); - g_ptr_array_foreach (array, (GFunc) g_value_array_free, NULL); - g_ptr_array_unref (array); -} - -static void -media_remove_streams (TpSvcChannelTypeStreamedMedia *iface, - const GArray *stream_ids, - DBusGMethodInvocation *context) -{ - ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (iface); - guint i; - - for (i = 0; i < stream_ids->len; i++) - { - guint id = g_array_index (stream_ids, guint, i); - - if (g_hash_table_lookup (self->priv->streams, - GUINT_TO_POINTER (id)) == NULL) - { - GError *error = g_error_new (TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "No stream with ID %u in this channel", id); - - dbus_g_method_return_error (context, error); - g_error_free (error); - return; - } - } - - for (i = 0; i < stream_ids->len; i++) - { - guint id = g_array_index (stream_ids, guint, i); - - example_callable_media_stream_close ( - g_hash_table_lookup (self->priv->streams, GUINT_TO_POINTER (id))); - } - - tp_svc_channel_type_streamed_media_return_from_remove_streams (context); -} - -static void -media_request_stream_direction (TpSvcChannelTypeStreamedMedia *iface, - guint stream_id, - guint stream_direction, - DBusGMethodInvocation *context) -{ - ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (iface); - ExampleCallableMediaStream *stream = g_hash_table_lookup ( - self->priv->streams, GUINT_TO_POINTER (stream_id)); - GError *error = NULL; - - if (stream == NULL) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "No stream with ID %u in this channel", stream_id); - goto error; - } - - if (stream_direction > TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "Stream direction %u is not valid", stream_direction); - goto error; - } - - /* In some protocols, streams cannot be neither sending nor receiving, so - * if a stream is set to TP_MEDIA_STREAM_DIRECTION_NONE, this is equivalent - * to removing it with RemoveStreams. (This is true in XMPP, for instance.) - * - * If this was the case, there would be code like this here: - * - * if (stream_direction == TP_MEDIA_STREAM_DIRECTION_NONE) - * { - * example_callable_media_stream_close (stream); - * tp_svc_channel_type_streamed_media_return_from_request_stream_direction ( - * context); - * return; - * } - * - * However, for this example we'll emulate a protocol where streams can be - * directionless. - */ - - if (!example_callable_media_stream_change_direction (stream, - stream_direction, &error)) - goto error; - - tp_svc_channel_type_streamed_media_return_from_request_stream_direction ( - context); - return; - -error: - dbus_g_method_return_error (context, error); - g_error_free (error); -} - -static void -stream_removed_cb (ExampleCallableMediaStream *stream, - ExampleCallableMediaChannel *self) -{ - guint id; - - g_object_get (stream, - "id", &id, - NULL); - - g_signal_handlers_disconnect_matched (stream, G_SIGNAL_MATCH_DATA, - 0, 0, NULL, NULL, self); - g_hash_table_remove (self->priv->streams, GUINT_TO_POINTER (id)); - tp_svc_channel_type_streamed_media_emit_stream_removed (self, id); - - if (g_hash_table_size (self->priv->streams) == 0) - { - /* no streams left, so the call terminates */ - example_callable_media_channel_close (self, 0, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE); - } -} - -static void -stream_direction_changed_cb (ExampleCallableMediaStream *stream, - ExampleCallableMediaChannel *self) -{ - guint id, direction, pending; - - g_object_get (stream, - "id", &id, - "direction", &direction, - "pending-send", &pending, - NULL); - - tp_svc_channel_type_streamed_media_emit_stream_direction_changed (self, id, - direction, pending); -} - -static void -stream_state_changed_cb (ExampleCallableMediaStream *stream, - GParamSpec *spec G_GNUC_UNUSED, - ExampleCallableMediaChannel *self) -{ - guint id, state; - - g_object_get (stream, - "id", &id, - "state", &state, - NULL); - - tp_svc_channel_type_streamed_media_emit_stream_state_changed (self, id, - state); -} - -static gboolean -simulate_contact_ended_cb (gpointer p) -{ - ExampleCallableMediaChannel *self = p; - - /* if the call has been cancelled while we were waiting for the - * contact to do so, do nothing! */ - if (self->priv->progress == PROGRESS_ENDED) - return FALSE; - - g_message ("SIGNALLING: receive: call terminated: <call-terminated/>"); - - example_callable_media_channel_close (self, - tp_base_channel_get_target_handle (TP_BASE_CHANNEL (self)), - TP_CHANNEL_GROUP_CHANGE_REASON_NONE); - - return FALSE; -} - -static gboolean -simulate_contact_answered_cb (gpointer p) -{ - ExampleCallableMediaChannel *self = p; - TpIntset *peer_set; - GHashTableIter iter; - gpointer v; - TpHandleRepoIface *contact_repo; - const gchar *peer; - TpHandle target; - - /* if the call has been cancelled while we were waiting for the - * contact to answer, do nothing */ - if (self->priv->progress == PROGRESS_ENDED) - return FALSE; - - /* otherwise, we're waiting for a response from the contact, which now - * arrives */ - g_assert (self->priv->progress == PROGRESS_CALLING); - - g_message ("SIGNALLING: receive: contact answered our call"); - - self->priv->progress = PROGRESS_ACTIVE; - - target = tp_base_channel_get_target_handle (TP_BASE_CHANNEL (self)); - peer_set = tp_intset_new_containing (target); - tp_group_mixin_change_members ((GObject *) self, "", - peer_set /* added */, - NULL /* nobody removed */, - NULL /* nobody added to local-pending */, - NULL /* nobody added to remote-pending */, - target /* actor */, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE); - tp_intset_destroy (peer_set); - - g_hash_table_iter_init (&iter, self->priv->streams); - - while (g_hash_table_iter_next (&iter, NULL, &v)) - { - /* remote contact accepts our proposed stream direction... */ - example_callable_media_stream_simulate_contact_agreed_to_send (v); - /* ... and the stream tries to connect */ - example_callable_media_stream_connect (v); - } - - contact_repo = tp_base_connection_get_handles - (tp_base_channel_get_connection (TP_BASE_CHANNEL (self)), - TP_HANDLE_TYPE_CONTACT); - peer = tp_handle_inspect (contact_repo, target); - - /* If the contact's ID contains the magic string "(terminate)", simulate - * them hanging up after a moment. */ - if (strstr (peer, "(terminate)") != NULL) - { - g_timeout_add_full (G_PRIORITY_DEFAULT, - self->priv->simulation_delay, - simulate_contact_ended_cb, g_object_ref (self), - g_object_unref); - } - - return FALSE; -} - -static gboolean -simulate_contact_busy_cb (gpointer p) -{ - ExampleCallableMediaChannel *self = p; - - /* if the call has been cancelled while we were waiting for the - * contact to answer, do nothing */ - if (self->priv->progress == PROGRESS_ENDED) - return FALSE; - - /* otherwise, we're waiting for a response from the contact, which now - * arrives */ - g_assert (self->priv->progress == PROGRESS_CALLING); - - g_message ("SIGNALLING: receive: call terminated: <user-is-busy/>"); - - example_callable_media_channel_close (self, - tp_base_channel_get_target_handle (TP_BASE_CHANNEL (self)), - TP_CHANNEL_GROUP_CHANGE_REASON_BUSY); - - return FALSE; -} - -static ExampleCallableMediaStream * -example_callable_media_channel_add_stream (ExampleCallableMediaChannel *self, - TpMediaStreamType media_type, - gboolean locally_requested) -{ - ExampleCallableMediaStream *stream; - guint id = self->priv->next_stream_id++; - guint state, direction, pending_send; - TpHandle target; - - if (locally_requested) - { - g_message ("SIGNALLING: send: new %s stream", - media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? "audio" : "video"); - } - - target = tp_base_channel_get_target_handle (TP_BASE_CHANNEL (self)); - stream = g_object_new (EXAMPLE_TYPE_CALLABLE_MEDIA_STREAM, - "channel", self, - "id", id, - "handle", target, - "type", media_type, - "locally-requested", locally_requested, - NULL); - - g_hash_table_insert (self->priv->streams, GUINT_TO_POINTER (id), stream); - - tp_svc_channel_type_streamed_media_emit_stream_added (self, id, - target, media_type); - - g_object_get (stream, - "state", &state, - "direction", &direction, - "pending-send", &pending_send, - NULL); - - /* this is the "implicit" initial state mandated by telepathy-spec */ - if (state != TP_MEDIA_STREAM_STATE_DISCONNECTED) - { - tp_svc_channel_type_streamed_media_emit_stream_state_changed (self, id, - state); - } - - /* this is the "implicit" initial direction mandated by telepathy-spec */ - if (direction != TP_MEDIA_STREAM_DIRECTION_RECEIVE || - pending_send != TP_MEDIA_STREAM_PENDING_LOCAL_SEND) - { - tp_svc_channel_type_streamed_media_emit_stream_direction_changed (self, - id, direction, pending_send); - } - - g_signal_connect (stream, "removed", G_CALLBACK (stream_removed_cb), - self); - g_signal_connect (stream, "notify::state", - G_CALLBACK (stream_state_changed_cb), self); - g_signal_connect (stream, "direction-changed", - G_CALLBACK (stream_direction_changed_cb), self); - - if (self->priv->progress == PROGRESS_ACTIVE) - { - example_callable_media_stream_connect (stream); - } - - return stream; -} - -static void -media_request_streams (TpSvcChannelTypeStreamedMedia *iface, - guint contact_handle, - const GArray *media_types, - DBusGMethodInvocation *context) -{ - ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (iface); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles - (tp_base_channel_get_connection (TP_BASE_CHANNEL (self)), - TP_HANDLE_TYPE_CONTACT); - GPtrArray *array; - guint i; - GError *error = NULL; - TpHandle target; - - if (!tp_handle_is_valid (contact_repo, contact_handle, &error)) - goto error; - - target = tp_base_channel_get_target_handle (TP_BASE_CHANNEL (self)); - if (contact_handle != target) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "This channel is for handle #%u, we can't make a stream to #%u", - target, contact_handle); - goto error; - } - - if (self->priv->progress == PROGRESS_ENDED) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "Call has terminated"); - goto error; - } - - for (i = 0; i < media_types->len; i++) - { - guint media_type = g_array_index (media_types, guint, i); - - switch (media_type) - { - case TP_MEDIA_STREAM_TYPE_AUDIO: - case TP_MEDIA_STREAM_TYPE_VIDEO: - break; - default: - g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "%u is not a valid Media_Stream_Type", media_type); - goto error; - } - } - - array = g_ptr_array_sized_new (media_types->len); - - for (i = 0; i < media_types->len; i++) - { - guint media_type = g_array_index (media_types, guint, i); - ExampleCallableMediaStream *stream; - GValueArray *info; - - if (self->priv->progress < PROGRESS_CALLING) - { - TpIntset *peer_set = tp_intset_new_containing (target); - const gchar *peer; - - g_message ("SIGNALLING: send: new streamed media call"); - self->priv->progress = PROGRESS_CALLING; - - tp_group_mixin_change_members ((GObject *) self, "", - NULL /* nobody added */, - NULL /* nobody removed */, - NULL /* nobody added to local-pending */, - peer_set /* added to remote-pending */, - self->group.self_handle /* actor */, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE); - - tp_intset_destroy (peer_set); - - /* In this example there is no real contact, so just simulate them - * answering after a short time - unless the contact's name - * contains "(no answer)" or "(busy)" */ - - peer = tp_handle_inspect (contact_repo, target); - - if (strstr (peer, "(busy)") != NULL) - { - g_timeout_add_full (G_PRIORITY_DEFAULT, - self->priv->simulation_delay, - simulate_contact_busy_cb, g_object_ref (self), - g_object_unref); - } - else if (strstr (peer, "(no answer)") != NULL) - { - /* do nothing - the call just rings forever */ - } - else - { - g_timeout_add_full (G_PRIORITY_DEFAULT, - self->priv->simulation_delay, - simulate_contact_answered_cb, g_object_ref (self), - g_object_unref); - } - } - - stream = example_callable_media_channel_add_stream (self, media_type, - TRUE); - - g_object_get (stream, - "stream-info", &info, - NULL); - - g_ptr_array_add (array, info); - } - - tp_svc_channel_type_streamed_media_return_from_request_streams (context, - array); - g_boxed_free (TP_ARRAY_TYPE_MEDIA_STREAM_INFO_LIST, array); - - return; - -error: - dbus_g_method_return_error (context, error); - g_error_free (error); -} - -static void -media_iface_init (gpointer iface, - gpointer data) -{ - TpSvcChannelTypeStreamedMediaClass *klass = iface; - -#define IMPLEMENT(x) \ - tp_svc_channel_type_streamed_media_implement_##x (klass, media_##x) - IMPLEMENT (list_streams); - IMPLEMENT (remove_streams); - IMPLEMENT (request_stream_direction); - IMPLEMENT (request_streams); -#undef IMPLEMENT -} - -static gboolean -simulate_hold (gpointer p) -{ - ExampleCallableMediaChannel *self = p; - - self->priv->hold_state = TP_LOCAL_HOLD_STATE_HELD; - g_message ("SIGNALLING: hold state changed to held"); - tp_svc_channel_interface_hold_emit_hold_state_changed (self, - self->priv->hold_state, self->priv->hold_state_reason); - return FALSE; -} - -static gboolean -simulate_unhold (gpointer p) -{ - ExampleCallableMediaChannel *self = p; - - self->priv->hold_state = TP_LOCAL_HOLD_STATE_UNHELD; - g_message ("SIGNALLING: hold state changed to unheld"); - tp_svc_channel_interface_hold_emit_hold_state_changed (self, - self->priv->hold_state, self->priv->hold_state_reason); - return FALSE; -} - -static gboolean -simulate_inability_to_unhold (gpointer p) -{ - ExampleCallableMediaChannel *self = p; - - self->priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_HOLD; - g_message ("SIGNALLING: unable to unhold - hold state changed to " - "pending hold"); - tp_svc_channel_interface_hold_emit_hold_state_changed (self, - self->priv->hold_state, self->priv->hold_state_reason); - /* hold again */ - g_timeout_add_full (G_PRIORITY_DEFAULT, - self->priv->simulation_delay, - simulate_hold, g_object_ref (self), - g_object_unref); - return FALSE; -} - -static void -hold_get_hold_state (TpSvcChannelInterfaceHold *iface, - DBusGMethodInvocation *context) -{ - ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (iface); - - tp_svc_channel_interface_hold_return_from_get_hold_state (context, - self->priv->hold_state, self->priv->hold_state_reason); -} - -static void -hold_request_hold (TpSvcChannelInterfaceHold *iface, - gboolean hold, - DBusGMethodInvocation *context) -{ - ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (iface); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles - (tp_base_channel_get_connection (TP_BASE_CHANNEL (self)), - TP_HANDLE_TYPE_CONTACT); - GError *error = NULL; - const gchar *peer; - GSourceFunc callback; - TpHandle target; - - if ((hold && self->priv->hold_state == TP_LOCAL_HOLD_STATE_HELD) || - (!hold && self->priv->hold_state == TP_LOCAL_HOLD_STATE_UNHELD)) - { - tp_svc_channel_interface_hold_return_from_request_hold (context); - return; - } - - target = tp_base_channel_get_target_handle (TP_BASE_CHANNEL (self)); - peer = tp_handle_inspect (contact_repo, target); - - if (!hold && strstr (peer, "(no unhold)") != NULL) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "unable to unhold"); - goto error; - } - - self->priv->hold_state_reason = TP_LOCAL_HOLD_STATE_REASON_REQUESTED; - - if (hold) - { - self->priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_HOLD; - callback = simulate_hold; - } - else - { - self->priv->hold_state = TP_LOCAL_HOLD_STATE_PENDING_UNHOLD; - - peer = tp_handle_inspect (contact_repo, target); - - if (strstr (peer, "(inability to unhold)") != NULL) - { - callback = simulate_inability_to_unhold; - } - else - { - callback = simulate_unhold; - } - } - - g_message ("SIGNALLING: hold state changed to pending %s", - (hold ? "hold" : "unhold")); - tp_svc_channel_interface_hold_emit_hold_state_changed (iface, - self->priv->hold_state, self->priv->hold_state_reason); - - g_timeout_add_full (G_PRIORITY_DEFAULT, - self->priv->simulation_delay, - callback, g_object_ref (self), - g_object_unref); - - tp_svc_channel_interface_hold_return_from_request_hold (context); - return; - -error: - dbus_g_method_return_error (context, error); - g_error_free (error); -} - - -void -hold_iface_init (gpointer iface, - gpointer data) -{ - TpSvcChannelInterfaceHoldClass *klass = iface; - -#define IMPLEMENT(x) \ - tp_svc_channel_interface_hold_implement_##x (klass, hold_##x) - IMPLEMENT (get_hold_state); - IMPLEMENT (request_hold); -#undef IMPLEMENT -} - -static void -dtmf_start_tone (TpSvcChannelInterfaceDTMF *iface, - guint stream_id, - guchar event, - DBusGMethodInvocation *context) -{ - ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (iface); - ExampleCallableMediaStream *stream = g_hash_table_lookup (self->priv->streams, - GUINT_TO_POINTER (stream_id)); - GError *error = NULL; - guint media_type; - - if (stream == NULL) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "No stream with ID %u in this channel", stream_id); - goto error; - } - - g_object_get (G_OBJECT (stream), "type", &media_type, NULL); - if (media_type != TP_MEDIA_STREAM_TYPE_AUDIO) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "DTMF is only supported by audio streams"); - goto error; - } - - tp_svc_channel_interface_dtmf_return_from_start_tone (context); - - return; - -error: - dbus_g_method_return_error (context, error); - g_error_free (error); -} - -static void -dtmf_stop_tone (TpSvcChannelInterfaceDTMF *iface, - guint stream_id, - DBusGMethodInvocation *context) -{ - ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (iface); - ExampleCallableMediaStream *stream = g_hash_table_lookup (self->priv->streams, - GUINT_TO_POINTER (stream_id)); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles - (tp_base_channel_get_connection (TP_BASE_CHANNEL (self)), - TP_HANDLE_TYPE_CONTACT); - GError *error = NULL; - const gchar *peer; - guint media_type; - - if (stream == NULL) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "No stream with ID %u in this channel", stream_id); - goto error; - } - - g_object_get (G_OBJECT (stream), "type", &media_type, NULL); - if (media_type != TP_MEDIA_STREAM_TYPE_AUDIO) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "DTMF is only supported by audio streams"); - goto error; - } - - peer = tp_handle_inspect (contact_repo, - tp_base_channel_get_target_handle (TP_BASE_CHANNEL (self))); - if (strstr (peer, "(no continuous tone)") != NULL) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "Continuous tones are not supported by this stream"); - goto error; - } - - tp_svc_channel_interface_dtmf_return_from_stop_tone (context); - - return; - -error: - dbus_g_method_return_error (context, error); - g_error_free (error); -} - -static void -dtmf_iface_init (gpointer iface, - gpointer data) -{ - TpSvcChannelInterfaceDTMFClass *klass = iface; - -#define IMPLEMENT(x) \ - tp_svc_channel_interface_dtmf_implement_##x (klass, dtmf_##x) - IMPLEMENT (start_tone); - IMPLEMENT (stop_tone); -#undef IMPLEMENT -} diff --git a/examples/cm/callable/media-channel.h b/examples/cm/callable/media-channel.h deleted file mode 100644 index e45f6cd19..000000000 --- a/examples/cm/callable/media-channel.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * media-channel.h - header for an example channel - * - * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/> - * Copyright © 2007-2009 Nokia Corporation - * - * 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 - */ - -#ifndef __EXAMPLE_CALLABLE_MEDIA_CHANNEL_H__ -#define __EXAMPLE_CALLABLE_MEDIA_CHANNEL_H__ - -#include <telepathy-glib/base-channel.h> -#include <telepathy-glib/group-mixin.h> - -G_BEGIN_DECLS - -typedef struct _ExampleCallableMediaChannel ExampleCallableMediaChannel; -typedef struct _ExampleCallableMediaChannelPrivate - ExampleCallableMediaChannelPrivate; - -typedef struct _ExampleCallableMediaChannelClass - ExampleCallableMediaChannelClass; -typedef struct _ExampleCallableMediaChannelClassPrivate - ExampleCallableMediaChannelClassPrivate; - -GType example_callable_media_channel_get_type (void); - -#define EXAMPLE_TYPE_CALLABLE_MEDIA_CHANNEL \ - (example_callable_media_channel_get_type ()) -#define EXAMPLE_CALLABLE_MEDIA_CHANNEL(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXAMPLE_TYPE_CALLABLE_MEDIA_CHANNEL, \ - ExampleCallableMediaChannel)) -#define EXAMPLE_CALLABLE_MEDIA_CHANNEL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), EXAMPLE_TYPE_CALLABLE_MEDIA_CHANNEL, \ - ExampleCallableMediaChannelClass)) -#define EXAMPLE_IS_CALLABLE_MEDIA_CHANNEL(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXAMPLE_TYPE_CALLABLE_MEDIA_CHANNEL)) -#define EXAMPLE_IS_CALLABLE_MEDIA_CHANNEL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), EXAMPLE_TYPE_CALLABLE_MEDIA_CHANNEL)) -#define EXAMPLE_CALLABLE_MEDIA_CHANNEL_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_CALLABLE_MEDIA_CHANNEL, \ - ExampleCallableMediaChannelClass)) - -struct _ExampleCallableMediaChannelClass { - TpBaseChannelClass parent_class; - TpGroupMixinClass group_class; - - ExampleCallableMediaChannelClassPrivate *priv; -}; - -struct _ExampleCallableMediaChannel { - TpBaseChannel parent; - TpGroupMixin group; - - ExampleCallableMediaChannelPrivate *priv; -}; - -G_END_DECLS - -#endif diff --git a/examples/cm/callable/media-manager.h b/examples/cm/callable/media-manager.h deleted file mode 100644 index 5be239e6e..000000000 --- a/examples/cm/callable/media-manager.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * media-manager.h - header for an example channel manager - * - * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/> - * Copyright © 2007-2009 Nokia Corporation - * - * 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 - */ - -#ifndef __EXAMPLE_CALLABLE_MEDIA_MANAGER_H__ -#define __EXAMPLE_CALLABLE_MEDIA_MANAGER_H__ - -#include <glib-object.h> - -G_BEGIN_DECLS - -typedef struct _ExampleCallableMediaManager ExampleCallableMediaManager; -typedef struct _ExampleCallableMediaManagerPrivate - ExampleCallableMediaManagerPrivate; - -typedef struct _ExampleCallableMediaManagerClass - ExampleCallableMediaManagerClass; -typedef struct _ExampleCallableMediaManagerClassPrivate - ExampleCallableMediaManagerClassPrivate; - -struct _ExampleCallableMediaManagerClass { - GObjectClass parent_class; - - ExampleCallableMediaManagerClassPrivate *priv; -}; - -struct _ExampleCallableMediaManager { - GObject parent; - - ExampleCallableMediaManagerPrivate *priv; -}; - -GType example_callable_media_manager_get_type (void); - -/* TYPE MACROS */ -#define EXAMPLE_TYPE_CALLABLE_MEDIA_MANAGER \ - (example_callable_media_manager_get_type ()) -#define EXAMPLE_CALLABLE_MEDIA_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), EXAMPLE_TYPE_CALLABLE_MEDIA_MANAGER, \ - ExampleCallableMediaManager)) -#define EXAMPLE_CALLABLE_MEDIA_MANAGER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), EXAMPLE_TYPE_CALLABLE_MEDIA_MANAGER, \ - ExampleCallableMediaManagerClass)) -#define EXAMPLE_IS_CALLABLE_MEDIA_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), EXAMPLE_TYPE_CALLABLE_MEDIA_MANAGER)) -#define EXAMPLE_IS_CALLABLE_MEDIA_MANAGER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), EXAMPLE_TYPE_CALLABLE_MEDIA_MANAGER)) -#define EXAMPLE_CALLABLE_MEDIA_MANAGER_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_CALLABLE_MEDIA_MANAGER, \ - ExampleCallableMediaManagerClass)) - -G_END_DECLS - -#endif diff --git a/examples/cm/callable/media-stream.c b/examples/cm/callable/media-stream.c deleted file mode 100644 index 22da0bc87..000000000 --- a/examples/cm/callable/media-stream.c +++ /dev/null @@ -1,652 +0,0 @@ -/* - * media-stream.c - a stream in a streamed media call. - * - * In connection managers with MediaSignalling, this object would be a D-Bus - * object in its own right. In this CM, MediaSignalling is not used, and this - * object just represents internal state of the MediaChannel. - * - * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/> - * Copyright © 2007-2009 Nokia Corporation - * - * 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 - */ - -#include "config.h" - -#include "media-stream.h" - -#include <telepathy-glib/base-connection.h> -#include <telepathy-glib/gtypes.h> - -#include "media-channel.h" - -G_DEFINE_TYPE (ExampleCallableMediaStream, - example_callable_media_stream, - G_TYPE_OBJECT) - -enum -{ - PROP_CHANNEL = 1, - PROP_ID, - PROP_HANDLE, - PROP_TYPE, - PROP_STATE, - PROP_PENDING_SEND, - PROP_DIRECTION, - PROP_STREAM_INFO, - PROP_SIMULATION_DELAY, - PROP_LOCALLY_REQUESTED, - N_PROPS -}; - -enum -{ - SIGNAL_REMOVED, - SIGNAL_DIRECTION_CHANGED, - N_SIGNALS -}; - -static guint signals[N_SIGNALS] = { 0 }; - -struct _ExampleCallableMediaStreamPrivate -{ - TpBaseConnection *conn; - ExampleCallableMediaChannel *channel; - guint id; - TpHandle handle; - TpMediaStreamType type; - TpMediaStreamState state; - TpMediaStreamDirection direction; - TpMediaStreamPendingSend pending_send; - - guint simulation_delay; - - gulong call_terminated_id; - - guint connected_event_id; - - gboolean locally_requested; - gboolean removed; -}; - -static void -example_callable_media_stream_init (ExampleCallableMediaStream *self) -{ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - EXAMPLE_TYPE_CALLABLE_MEDIA_STREAM, - ExampleCallableMediaStreamPrivate); - - /* start off directionless */ - self->priv->direction = TP_MEDIA_STREAM_DIRECTION_NONE; - self->priv->pending_send = 0; - self->priv->state = TP_MEDIA_STREAM_STATE_DISCONNECTED; -} - -static void -call_terminated_cb (ExampleCallableMediaChannel *channel, - ExampleCallableMediaStream *self) -{ - g_signal_handler_disconnect (channel, self->priv->call_terminated_id); - self->priv->call_terminated_id = 0; - example_callable_media_stream_close (self); -} - -static void -constructed (GObject *object) -{ - ExampleCallableMediaStream *self = EXAMPLE_CALLABLE_MEDIA_STREAM (object); - void (*chain_up) (GObject *) = - ((GObjectClass *) example_callable_media_stream_parent_class)->constructed; - - if (chain_up != NULL) - chain_up (object); - - g_object_get (self->priv->channel, - "connection", &self->priv->conn, - NULL); - self->priv->call_terminated_id = g_signal_connect (self->priv->channel, - "call-terminated", G_CALLBACK (call_terminated_cb), self); - - if (self->priv->handle != 0) - { - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - self->priv->conn, TP_HANDLE_TYPE_CONTACT); - - tp_handle_ref (contact_repo, self->priv->handle); - } -} - -static void -get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - ExampleCallableMediaStream *self = EXAMPLE_CALLABLE_MEDIA_STREAM (object); - - switch (property_id) - { - case PROP_ID: - g_value_set_uint (value, self->priv->id); - break; - - case PROP_HANDLE: - g_value_set_uint (value, self->priv->handle); - break; - - case PROP_TYPE: - g_value_set_uint (value, self->priv->type); - break; - - case PROP_STATE: - g_value_set_uint (value, self->priv->state); - break; - - case PROP_PENDING_SEND: - g_value_set_uint (value, self->priv->pending_send); - break; - - case PROP_DIRECTION: - g_value_set_uint (value, self->priv->direction); - break; - - case PROP_CHANNEL: - g_value_set_object (value, self->priv->channel); - break; - - case PROP_STREAM_INFO: - { - GValueArray *va = g_value_array_new (6); - guint i; - - for (i = 0; i < 6; i++) - { - g_value_array_append (va, NULL); - g_value_init (va->values + i, G_TYPE_UINT); - } - - g_value_set_uint (va->values + 0, self->priv->id); - g_value_set_uint (va->values + 1, self->priv->handle); - g_value_set_uint (va->values + 2, self->priv->type); - g_value_set_uint (va->values + 3, self->priv->state); - g_value_set_uint (va->values + 4, self->priv->direction); - g_value_set_uint (va->values + 5, self->priv->pending_send); - - g_value_take_boxed (value, va); - } - break; - - case PROP_SIMULATION_DELAY: - g_value_set_uint (value, self->priv->simulation_delay); - break; - - case PROP_LOCALLY_REQUESTED: - g_value_set_boolean (value, self->priv->locally_requested); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - ExampleCallableMediaStream *self = EXAMPLE_CALLABLE_MEDIA_STREAM (object); - - switch (property_id) - { - case PROP_ID: - self->priv->id = g_value_get_uint (value); - break; - - case PROP_HANDLE: - self->priv->handle = g_value_get_uint (value); - break; - - case PROP_TYPE: - self->priv->type = g_value_get_uint (value); - break; - - case PROP_CHANNEL: - g_assert (self->priv->channel == NULL); - self->priv->channel = g_value_dup_object (value); - break; - - case PROP_SIMULATION_DELAY: - self->priv->simulation_delay = g_value_get_uint (value); - break; - - case PROP_LOCALLY_REQUESTED: - self->priv->locally_requested = g_value_get_boolean (value); - - if (self->priv->locally_requested) - { - example_callable_media_stream_change_direction (self, - TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, NULL); - } - else - { - example_callable_media_stream_receive_direction_request (self, - TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL); - } - - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -dispose (GObject *object) -{ - ExampleCallableMediaStream *self = EXAMPLE_CALLABLE_MEDIA_STREAM (object); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - self->priv->conn, TP_HANDLE_TYPE_CONTACT); - - example_callable_media_stream_close (self); - - if (self->priv->handle != 0) - { - tp_handle_unref (contact_repo, self->priv->handle); - self->priv->handle = 0; - } - - if (self->priv->channel != NULL) - { - if (self->priv->call_terminated_id != 0) - { - g_signal_handler_disconnect (self->priv->channel, - self->priv->call_terminated_id); - self->priv->call_terminated_id = 0; - } - - g_object_unref (self->priv->channel); - self->priv->channel = NULL; - } - - if (self->priv->conn != NULL) - { - g_object_unref (self->priv->conn); - self->priv->conn = NULL; - } - - ((GObjectClass *) example_callable_media_stream_parent_class)->dispose (object); -} - -static void -example_callable_media_stream_class_init (ExampleCallableMediaStreamClass *klass) -{ - GObjectClass *object_class = (GObjectClass *) klass; - GParamSpec *param_spec; - - g_type_class_add_private (klass, - sizeof (ExampleCallableMediaStreamPrivate)); - - object_class->constructed = constructed; - object_class->set_property = set_property; - object_class->get_property = get_property; - object_class->dispose = dispose; - - param_spec = g_param_spec_object ("channel", "ExampleCallableMediaChannel", - "Media channel that owns this stream", - EXAMPLE_TYPE_CALLABLE_MEDIA_CHANNEL, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_CHANNEL, param_spec); - - param_spec = g_param_spec_uint ("id", "Stream ID", - "ID of this stream", - 0, G_MAXUINT32, 0, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_ID, param_spec); - - param_spec = g_param_spec_uint ("handle", "Peer's TpHandle", - "The handle with which this stream communicates or 0 if not applicable", - 0, G_MAXUINT32, 0, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_HANDLE, param_spec); - - param_spec = g_param_spec_uint ("type", "TpMediaStreamType", - "Media stream type", - 0, NUM_TP_MEDIA_STREAM_TYPES - 1, 0, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_TYPE, param_spec); - - param_spec = g_param_spec_uint ("state", "TpMediaStreamState", - "Media stream connection state", - 0, NUM_TP_MEDIA_STREAM_STATES - 1, 0, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_STATE, param_spec); - - param_spec = g_param_spec_uint ("direction", "TpMediaStreamDirection", - "Media stream direction", - 0, NUM_TP_MEDIA_STREAM_DIRECTIONS - 1, 0, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_DIRECTION, param_spec); - - param_spec = g_param_spec_uint ("pending-send", "TpMediaStreamPendingSend", - "Requested media stream directions pending approval", - 0, - TP_MEDIA_STREAM_PENDING_LOCAL_SEND | TP_MEDIA_STREAM_PENDING_REMOTE_SEND, - 0, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_PENDING_SEND, param_spec); - - param_spec = g_param_spec_boxed ("stream-info", "Stream info", - "6-entry GValueArray as returned by ListStreams and RequestStreams", - TP_STRUCT_TYPE_MEDIA_STREAM_INFO, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_STREAM_INFO, param_spec); - - param_spec = g_param_spec_uint ("simulation-delay", "Simulation delay", - "Delay between simulated network events", - 0, G_MAXUINT32, 1000, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_SIMULATION_DELAY, - param_spec); - - param_spec = g_param_spec_boolean ("locally-requested", "Locally requested?", - "True if this channel was requested by the local user", - FALSE, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_LOCALLY_REQUESTED, - param_spec); - - signals[SIGNAL_REMOVED] = g_signal_new ("removed", - G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - signals[SIGNAL_DIRECTION_CHANGED] = g_signal_new ("direction-changed", - G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); -} - -void -example_callable_media_stream_close (ExampleCallableMediaStream *self) -{ - if (!self->priv->removed) - { - self->priv->removed = TRUE; - - g_message ("Sending to server: Closing stream %u", - self->priv->id); - - if (self->priv->connected_event_id != 0) - { - g_source_remove (self->priv->connected_event_id); - } - - /* this has to come last, because the MediaChannel may unref us in - * response to the removed signal */ - g_signal_emit (self, signals[SIGNAL_REMOVED], 0); - } -} - -void -example_callable_media_stream_accept_proposed_direction ( - ExampleCallableMediaStream *self) -{ - if (self->priv->removed || - !(self->priv->pending_send & TP_MEDIA_STREAM_PENDING_LOCAL_SEND)) - return; - - g_message ("SIGNALLING: send: OK, I'll send you media on stream %u", - self->priv->id); - - self->priv->direction |= TP_MEDIA_STREAM_DIRECTION_SEND; - self->priv->pending_send &= ~TP_MEDIA_STREAM_PENDING_LOCAL_SEND; - - g_signal_emit (self, signals[SIGNAL_DIRECTION_CHANGED], 0); -} - -void -example_callable_media_stream_simulate_contact_agreed_to_send ( - ExampleCallableMediaStream *self) -{ - if (self->priv->removed || - !(self->priv->pending_send & TP_MEDIA_STREAM_PENDING_REMOTE_SEND)) - return; - - g_message ("SIGNALLING: receive: OK, I'll send you media on stream %u", - self->priv->id); - - self->priv->direction |= TP_MEDIA_STREAM_DIRECTION_RECEIVE; - self->priv->pending_send &= ~TP_MEDIA_STREAM_PENDING_REMOTE_SEND; - - g_signal_emit (self, signals[SIGNAL_DIRECTION_CHANGED], 0); -} - -static gboolean -simulate_contact_agreed_to_send_cb (gpointer p) -{ - example_callable_media_stream_simulate_contact_agreed_to_send (p); - return FALSE; -} - -gboolean -example_callable_media_stream_change_direction ( - ExampleCallableMediaStream *self, - TpMediaStreamDirection direction, - GError **error) -{ - gboolean sending = - ((self->priv->direction & TP_MEDIA_STREAM_DIRECTION_SEND) != 0); - gboolean receiving = - ((self->priv->direction & TP_MEDIA_STREAM_DIRECTION_RECEIVE) != 0); - gboolean want_to_send = - ((direction & TP_MEDIA_STREAM_DIRECTION_SEND) != 0); - gboolean want_to_receive = - ((direction & TP_MEDIA_STREAM_DIRECTION_RECEIVE) != 0); - gboolean pending_remote_send = - ((self->priv->pending_send & TP_MEDIA_STREAM_PENDING_REMOTE_SEND) != 0); - gboolean pending_local_send = - ((self->priv->pending_send & TP_MEDIA_STREAM_PENDING_LOCAL_SEND) != 0); - gboolean changed = FALSE; - - if (want_to_send) - { - if (!sending) - { - if (pending_local_send) - { - g_message ("SIGNALLING: send: I will now send you media on " - "stream %u", self->priv->id); - } - - g_message ("MEDIA: Sending media to peer for stream %u", - self->priv->id); - changed = TRUE; - self->priv->direction |= TP_MEDIA_STREAM_DIRECTION_SEND; - } - } - else - { - if (sending) - { - g_message ("SIGNALLING: send: I will no longer send you media on " - "stream %u", self->priv->id); - g_message ("MEDIA: No longer sending media to peer for stream %u", - self->priv->id); - changed = TRUE; - self->priv->direction &= ~TP_MEDIA_STREAM_DIRECTION_SEND; - } - else if (pending_local_send) - { - g_message ("SIGNALLING: send: No, I refuse to send you media on " - "stream %u", self->priv->id); - changed = TRUE; - self->priv->pending_send &= ~TP_MEDIA_STREAM_PENDING_LOCAL_SEND; - } - } - - if (want_to_receive) - { - if (!receiving && !pending_remote_send) - { - g_message ("SIGNALLING: send: Please start sending me stream %u", - self->priv->id); - changed = TRUE; - self->priv->pending_send |= TP_MEDIA_STREAM_PENDING_REMOTE_SEND; - g_timeout_add_full (G_PRIORITY_DEFAULT, self->priv->simulation_delay, - simulate_contact_agreed_to_send_cb, g_object_ref (self), - g_object_unref); - } - } - else - { - if (receiving) - { - g_message ("SIGNALLING: send: Please stop sending me stream %u", - self->priv->id); - g_message ("MEDIA: Suppressing output of stream %u", - self->priv->id); - changed = TRUE; - self->priv->direction &= ~TP_MEDIA_STREAM_DIRECTION_RECEIVE; - } - } - - if (changed) - g_signal_emit (self, signals[SIGNAL_DIRECTION_CHANGED], 0); - - return TRUE; -} - -static gboolean -simulate_stream_connected_cb (gpointer p) -{ - ExampleCallableMediaStream *self = EXAMPLE_CALLABLE_MEDIA_STREAM (p); - - g_message ("MEDIA: stream connected"); - self->priv->state = TP_MEDIA_STREAM_STATE_CONNECTED; - g_object_notify ((GObject *) self, "state"); - - return FALSE; -} - -void -example_callable_media_stream_connect (ExampleCallableMediaStream *self) -{ - /* if already trying to connect, do nothing */ - if (self->priv->connected_event_id != 0) - return; - - /* simulate it taking a short time to connect */ - self->priv->connected_event_id = g_timeout_add (self->priv->simulation_delay, - simulate_stream_connected_cb, self); -} - -void -example_callable_media_stream_receive_direction_request ( - ExampleCallableMediaStream *self, - TpMediaStreamDirection direction) -{ - /* The remote user wants to change the direction of this stream to - * @direction. Shall we let him? */ - gboolean sending = - ((self->priv->direction & TP_MEDIA_STREAM_DIRECTION_SEND) != 0); - gboolean receiving = - ((self->priv->direction & TP_MEDIA_STREAM_DIRECTION_RECEIVE) != 0); - gboolean send_requested = - ((direction & TP_MEDIA_STREAM_DIRECTION_RECEIVE) != 0); - gboolean receive_requested = - ((direction & TP_MEDIA_STREAM_DIRECTION_RECEIVE) != 0); - gboolean pending_remote_send = - ((self->priv->pending_send & TP_MEDIA_STREAM_PENDING_REMOTE_SEND) != 0); - gboolean pending_local_send = - ((self->priv->pending_send & TP_MEDIA_STREAM_PENDING_LOCAL_SEND) != 0); - gboolean changed = FALSE; - - if (send_requested) - { - g_message ("SIGNALLING: receive: Please start sending me stream %u", - self->priv->id); - - if (!sending) - { - /* ask the user for permission */ - self->priv->pending_send |= TP_MEDIA_STREAM_PENDING_LOCAL_SEND; - changed = TRUE; - } - else - { - /* nothing to do, we're already sending on that stream */ - } - } - else - { - g_message ("SIGNALLING: receive: Please stop sending me stream %u", - self->priv->id); - g_message ("SIGNALLING: send: OK, not sending stream %u", - self->priv->id); - - if (sending) - { - g_message ("MEDIA: No longer sending media to peer for stream %u", - self->priv->id); - self->priv->direction &= ~TP_MEDIA_STREAM_DIRECTION_SEND; - changed = TRUE; - } - else if (pending_local_send) - { - self->priv->pending_send &= ~TP_MEDIA_STREAM_PENDING_LOCAL_SEND; - changed = TRUE; - } - else - { - /* nothing to do, we're not sending on that stream anyway */ - } - } - - if (receive_requested) - { - g_message ("SIGNALLING: receive: I will now send you media on stream %u", - self->priv->id); - - if (!receiving) - { - self->priv->pending_send &= ~TP_MEDIA_STREAM_PENDING_REMOTE_SEND; - self->priv->direction |= TP_MEDIA_STREAM_DIRECTION_RECEIVE; - changed = TRUE; - } - } - else - { - if (pending_remote_send) - { - g_message ("SIGNALLING: receive: No, I refuse to send you media on " - "stream %u", self->priv->id); - self->priv->pending_send &= ~TP_MEDIA_STREAM_PENDING_REMOTE_SEND; - changed = TRUE; - } - else if (receiving) - { - g_message ("SIGNALLING: receive: I will no longer send you media on " - "stream %u", self->priv->id); - self->priv->direction &= ~TP_MEDIA_STREAM_DIRECTION_RECEIVE; - changed = TRUE; - } - } - - if (changed) - g_signal_emit (self, signals[SIGNAL_DIRECTION_CHANGED], 0); -} diff --git a/examples/cm/callable/media-stream.h b/examples/cm/callable/media-stream.h deleted file mode 100644 index e7ec04804..000000000 --- a/examples/cm/callable/media-stream.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * media-stream.h - header for an example stream - * - * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/> - * Copyright © 2007-2009 Nokia Corporation - * - * 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 - */ - -#ifndef __EXAMPLE_CALLABLE_MEDIA_STREAM_H__ -#define __EXAMPLE_CALLABLE_MEDIA_STREAM_H__ - -#include <glib-object.h> - -#include <telepathy-glib/enums.h> - -G_BEGIN_DECLS - -typedef struct _ExampleCallableMediaStream ExampleCallableMediaStream; -typedef struct _ExampleCallableMediaStreamPrivate - ExampleCallableMediaStreamPrivate; - -typedef struct _ExampleCallableMediaStreamClass - ExampleCallableMediaStreamClass; -typedef struct _ExampleCallableMediaStreamClassPrivate - ExampleCallableMediaStreamClassPrivate; - -GType example_callable_media_stream_get_type (void); - -#define EXAMPLE_TYPE_CALLABLE_MEDIA_STREAM \ - (example_callable_media_stream_get_type ()) -#define EXAMPLE_CALLABLE_MEDIA_STREAM(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXAMPLE_TYPE_CALLABLE_MEDIA_STREAM, \ - ExampleCallableMediaStream)) -#define EXAMPLE_CALLABLE_MEDIA_STREAM_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), EXAMPLE_TYPE_CALLABLE_MEDIA_STREAM, \ - ExampleCallableMediaStreamClass)) -#define EXAMPLE_IS_CALLABLE_MEDIA_STREAM(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXAMPLE_TYPE_CALLABLE_MEDIA_STREAM)) -#define EXAMPLE_IS_CALLABLE_MEDIA_STREAM_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), EXAMPLE_TYPE_CALLABLE_MEDIA_STREAM)) -#define EXAMPLE_CALLABLE_MEDIA_STREAM_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), EXAMPLE_TYPE_CALLABLE_MEDIA_STREAM, \ - ExampleCallableMediaStreamClass)) - -struct _ExampleCallableMediaStreamClass { - GObjectClass parent_class; - - ExampleCallableMediaStreamClassPrivate *priv; -}; - -struct _ExampleCallableMediaStream { - GObject parent; - - ExampleCallableMediaStreamPrivate *priv; -}; - -void example_callable_media_stream_close (ExampleCallableMediaStream *self); -gboolean example_callable_media_stream_change_direction ( - ExampleCallableMediaStream *self, TpMediaStreamDirection direction, - GError **error); -void example_callable_media_stream_accept_proposed_direction ( - ExampleCallableMediaStream *self); -void example_callable_media_stream_connect (ExampleCallableMediaStream *self); - -/* This controls receiving emulated network events, so it wouldn't exist in - * a real connection manager */ -void example_callable_media_stream_simulate_contact_agreed_to_send ( - ExampleCallableMediaStream *self); - -void example_callable_media_stream_receive_direction_request ( - ExampleCallableMediaStream *self, TpMediaStreamDirection direction); - -G_END_DECLS - -#endif diff --git a/examples/cm/callable/protocol.h b/examples/cm/callable/protocol.h deleted file mode 100644 index 0a5e0a795..000000000 --- a/examples/cm/callable/protocol.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * protocol.h - header for an example Protocol - * Copyright © 2007-2010 Collabora Ltd. - * - * Copying and distribution of this file, with or without modification, - * are permitted in any medium without royalty provided the copyright - * notice and this notice are preserved. - */ - -#ifndef EXAMPLE_CALLABLE_PROTOCOL_H -#define EXAMPLE_CALLABLE_PROTOCOL_H - -#include <glib-object.h> -#include <telepathy-glib/base-protocol.h> - -G_BEGIN_DECLS - -typedef struct _ExampleCallableProtocol - ExampleCallableProtocol; -typedef struct _ExampleCallableProtocolPrivate - ExampleCallableProtocolPrivate; -typedef struct _ExampleCallableProtocolClass - ExampleCallableProtocolClass; -typedef struct _ExampleCallableProtocolClassPrivate - ExampleCallableProtocolClassPrivate; - -struct _ExampleCallableProtocolClass { - TpBaseProtocolClass parent_class; - - ExampleCallableProtocolClassPrivate *priv; -}; - -struct _ExampleCallableProtocol { - TpBaseProtocol parent; - - ExampleCallableProtocolPrivate *priv; -}; - -GType example_callable_protocol_get_type (void); - -#define EXAMPLE_TYPE_CALLABLE_PROTOCOL \ - (example_callable_protocol_get_type ()) -#define EXAMPLE_CALLABLE_PROTOCOL(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - EXAMPLE_TYPE_CALLABLE_PROTOCOL, \ - ExampleCallableProtocol)) -#define EXAMPLE_CALLABLE_PROTOCOL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - EXAMPLE_TYPE_CALLABLE_PROTOCOL, \ - ExampleCallableProtocolClass)) -#define EXAMPLE_IS_CALLABLE_PROTOCOL(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - EXAMPLE_TYPE_CALLABLE_PROTOCOL)) -#define EXAMPLE_IS_CALLABLE_PROTOCOL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - EXAMPLE_TYPE_CALLABLE_PROTOCOL)) -#define EXAMPLE_CALLABLE_PROTOCOL_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - EXAMPLE_TYPE_CALLABLE_PROTOCOL, \ - ExampleCallableProtocolClass)) - -gboolean example_callable_protocol_check_contact_id (const gchar *id, - gchar **normal, - GError **error); - -G_END_DECLS - -#endif |