~ubuntu-branches/ubuntu/maverick/telepathy-glib/maverick

« back to all changes in this revision

Viewing changes to examples/future/call-cm/call-manager.c

  • Committer: Bazaar Package Importer
  • Author(s): Simon McVittie
  • Date: 2010-05-10 17:59:54 UTC
  • mfrom: (1.6.1 upstream) (27.1.3 sid)
  • Revision ID: james.westby@ubuntu.com-20100510175954-bxvqq3xx0sy4itmp
Tags: 0.11.5-1
New upstream version with new API/ABI

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * call-manager.c - an example channel manager for Call channels.
 
3
 *
 
4
 * This channel manager emulates a protocol like XMPP Jingle, where you can
 
5
 * make several simultaneous calls to the same or different contacts.
 
6
 *
 
7
 * Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/>
 
8
 * Copyright © 2007-2009 Nokia Corporation
 
9
 *
 
10
 * This library is free software; you can redistribute it and/or
 
11
 * modify it under the terms of the GNU Lesser General Public
 
12
 * License as published by the Free Software Foundation; either
 
13
 * version 2.1 of the License, or (at your option) any later version.
 
14
 *
 
15
 * This library is distributed in the hope that it will be useful,
 
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
18
 * Lesser General Public License for more details.
 
19
 *
 
20
 * You should have received a copy of the GNU Lesser General Public
 
21
 * License along with this library; if not, write to the Free Software
 
22
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
23
 */
 
24
 
 
25
#include "call-manager.h"
 
26
 
 
27
#include <dbus/dbus-glib.h>
 
28
 
 
29
#include <telepathy-glib/base-connection.h>
 
30
#include <telepathy-glib/channel-manager.h>
 
31
#include <telepathy-glib/dbus.h>
 
32
#include <telepathy-glib/errors.h>
 
33
#include <telepathy-glib/interfaces.h>
 
34
 
 
35
#include "extensions/extensions.h"
 
36
 
 
37
#include "call-channel.h"
 
38
 
 
39
static void channel_manager_iface_init (gpointer, gpointer);
 
40
 
 
41
G_DEFINE_TYPE_WITH_CODE (ExampleCallManager,
 
42
    example_call_manager,
 
43
    G_TYPE_OBJECT,
 
44
    G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER,
 
45
      channel_manager_iface_init))
 
46
 
 
47
/* type definition stuff */
 
48
 
 
49
enum
 
50
{
 
51
  PROP_CONNECTION = 1,
 
52
  PROP_SIMULATION_DELAY,
 
53
  N_PROPS
 
54
};
 
55
 
 
56
struct _ExampleCallManagerPrivate
 
57
{
 
58
  TpBaseConnection *conn;
 
59
  guint simulation_delay;
 
60
 
 
61
  /* Map from reffed ExampleCallChannel to the same pointer; used as a
 
62
   * set.
 
63
   */
 
64
  GHashTable *channels;
 
65
 
 
66
  /* Next channel will be ("CallChannel%u", next_channel_index) */
 
67
  guint next_channel_index;
 
68
 
 
69
  gulong status_changed_id;
 
70
  gulong available_id;
 
71
};
 
72
 
 
73
static void
 
74
example_call_manager_init (ExampleCallManager *self)
 
75
{
 
76
  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
 
77
      EXAMPLE_TYPE_CALL_MANAGER,
 
78
      ExampleCallManagerPrivate);
 
79
 
 
80
  self->priv->conn = NULL;
 
81
  self->priv->channels = g_hash_table_new_full (NULL, NULL, g_object_unref,
 
82
      NULL);
 
83
  self->priv->status_changed_id = 0;
 
84
  self->priv->available_id = 0;
 
85
}
 
86
 
 
87
static void
 
88
example_call_manager_close_all (ExampleCallManager *self)
 
89
{
 
90
  if (self->priv->channels != NULL)
 
91
    {
 
92
      GHashTable *tmp = self->priv->channels;
 
93
      GHashTableIter iter;
 
94
      gpointer v;
 
95
 
 
96
      self->priv->channels = NULL;
 
97
 
 
98
      g_hash_table_iter_init (&iter, tmp);
 
99
 
 
100
      while (g_hash_table_iter_next (&iter, NULL, &v))
 
101
        example_call_channel_disconnected (v);
 
102
 
 
103
      g_hash_table_unref (tmp);
 
104
    }
 
105
 
 
106
  if (self->priv->available_id != 0)
 
107
    {
 
108
      g_signal_handler_disconnect (self->priv->conn,
 
109
          self->priv->available_id);
 
110
      self->priv->available_id = 0;
 
111
    }
 
112
 
 
113
  if (self->priv->status_changed_id != 0)
 
114
    {
 
115
      g_signal_handler_disconnect (self->priv->conn,
 
116
          self->priv->status_changed_id);
 
117
      self->priv->status_changed_id = 0;
 
118
    }
 
119
}
 
120
 
 
121
static void
 
122
dispose (GObject *object)
 
123
{
 
124
  ExampleCallManager *self = EXAMPLE_CALL_MANAGER (object);
 
125
 
 
126
  example_call_manager_close_all (self);
 
127
  g_assert (self->priv->channels == NULL);
 
128
 
 
129
  ((GObjectClass *) example_call_manager_parent_class)->dispose (
 
130
    object);
 
131
}
 
132
 
 
133
static void
 
134
get_property (GObject *object,
 
135
    guint property_id,
 
136
    GValue *value,
 
137
    GParamSpec *pspec)
 
138
{
 
139
  ExampleCallManager *self = EXAMPLE_CALL_MANAGER (object);
 
140
 
 
141
  switch (property_id)
 
142
    {
 
143
    case PROP_CONNECTION:
 
144
      g_value_set_object (value, self->priv->conn);
 
145
      break;
 
146
 
 
147
    case PROP_SIMULATION_DELAY:
 
148
      g_value_set_uint (value, self->priv->simulation_delay);
 
149
      break;
 
150
 
 
151
    default:
 
152
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
153
    }
 
154
}
 
155
 
 
156
static void
 
157
set_property (GObject *object,
 
158
    guint property_id,
 
159
    const GValue *value,
 
160
    GParamSpec *pspec)
 
161
{
 
162
  ExampleCallManager *self = EXAMPLE_CALL_MANAGER (object);
 
163
 
 
164
  switch (property_id)
 
165
    {
 
166
    case PROP_CONNECTION:
 
167
      /* We don't ref the connection, because it owns a reference to the
 
168
       * channel manager, and it guarantees that the manager's lifetime is
 
169
       * less than its lifetime */
 
170
      self->priv->conn = g_value_get_object (value);
 
171
      break;
 
172
 
 
173
    case PROP_SIMULATION_DELAY:
 
174
      self->priv->simulation_delay = g_value_get_uint (value);
 
175
      break;
 
176
 
 
177
    default:
 
178
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
179
    }
 
180
}
 
181
 
 
182
static void
 
183
status_changed_cb (TpBaseConnection *conn,
 
184
                   guint status,
 
185
                   guint reason,
 
186
                   ExampleCallManager *self)
 
187
{
 
188
  switch (status)
 
189
    {
 
190
    case TP_CONNECTION_STATUS_DISCONNECTED:
 
191
        {
 
192
          example_call_manager_close_all (self);
 
193
        }
 
194
      break;
 
195
 
 
196
    default:
 
197
      break;
 
198
    }
 
199
}
 
200
 
 
201
static ExampleCallChannel *new_channel (ExampleCallManager *self,
 
202
    TpHandle handle, TpHandle initiator, gpointer request_token,
 
203
    gboolean initial_audio, gboolean initial_video);
 
204
 
 
205
static gboolean
 
206
simulate_incoming_call_cb (gpointer p)
 
207
{
 
208
  ExampleCallManager *self = p;
 
209
  TpHandleRepoIface *contact_repo;
 
210
  TpHandle caller;
 
211
 
 
212
  /* do nothing if we've been disconnected while waiting for the contact to
 
213
   * call us */
 
214
  if (self->priv->available_id == 0)
 
215
    return FALSE;
 
216
 
 
217
  /* We're called by someone whose ID on the IM service is "caller" */
 
218
  contact_repo = tp_base_connection_get_handles (self->priv->conn,
 
219
      TP_HANDLE_TYPE_CONTACT);
 
220
  caller = tp_handle_ensure (contact_repo, "caller", NULL, NULL);
 
221
 
 
222
  new_channel (self, caller, caller, NULL, TRUE, FALSE);
 
223
 
 
224
  return FALSE;
 
225
}
 
226
 
 
227
/* Whenever our presence changes from away to available, and whenever our
 
228
 * presence message changes while remaining available, simulate a call from
 
229
 * a contact */
 
230
static void
 
231
available_cb (GObject *conn G_GNUC_UNUSED,
 
232
    const gchar *message,
 
233
    ExampleCallManager *self)
 
234
{
 
235
  g_timeout_add_full (G_PRIORITY_DEFAULT, self->priv->simulation_delay,
 
236
      simulate_incoming_call_cb, g_object_ref (self), g_object_unref);
 
237
}
 
238
 
 
239
static void
 
240
constructed (GObject *object)
 
241
{
 
242
  ExampleCallManager *self = EXAMPLE_CALL_MANAGER (object);
 
243
  void (*chain_up) (GObject *) =
 
244
      ((GObjectClass *) example_call_manager_parent_class)->constructed;
 
245
 
 
246
  if (chain_up != NULL)
 
247
    {
 
248
      chain_up (object);
 
249
    }
 
250
 
 
251
  self->priv->status_changed_id = g_signal_connect (self->priv->conn,
 
252
      "status-changed", (GCallback) status_changed_cb, self);
 
253
 
 
254
  self->priv->available_id = g_signal_connect (self->priv->conn,
 
255
      "available", (GCallback) available_cb, self);
 
256
}
 
257
 
 
258
static void
 
259
example_call_manager_class_init (ExampleCallManagerClass *klass)
 
260
{
 
261
  GParamSpec *param_spec;
 
262
  GObjectClass *object_class = (GObjectClass *) klass;
 
263
 
 
264
  object_class->constructed = constructed;
 
265
  object_class->dispose = dispose;
 
266
  object_class->get_property = get_property;
 
267
  object_class->set_property = set_property;
 
268
 
 
269
  param_spec = g_param_spec_object ("connection", "Connection object",
 
270
      "The connection that owns this channel manager",
 
271
      TP_TYPE_BASE_CONNECTION,
 
272
      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
273
  g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
 
274
 
 
275
  param_spec = g_param_spec_uint ("simulation-delay", "Simulation delay",
 
276
      "Delay between simulated network events",
 
277
      0, G_MAXUINT32, 1000,
 
278
      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
279
  g_object_class_install_property (object_class, PROP_SIMULATION_DELAY,
 
280
      param_spec);
 
281
 
 
282
  g_type_class_add_private (klass,
 
283
      sizeof (ExampleCallManagerPrivate));
 
284
}
 
285
 
 
286
static void
 
287
example_call_manager_foreach_channel (TpChannelManager *iface,
 
288
    TpExportableChannelFunc callback,
 
289
    gpointer user_data)
 
290
{
 
291
  ExampleCallManager *self = EXAMPLE_CALL_MANAGER (iface);
 
292
  GHashTableIter iter;
 
293
  gpointer chan;
 
294
 
 
295
  g_hash_table_iter_init (&iter, self->priv->channels);
 
296
 
 
297
  while (g_hash_table_iter_next (&iter, &chan, NULL))
 
298
    callback (chan, user_data);
 
299
}
 
300
 
 
301
static void
 
302
channel_closed_cb (ExampleCallChannel *chan,
 
303
    ExampleCallManager *self)
 
304
{
 
305
  tp_channel_manager_emit_channel_closed_for_object (self,
 
306
      TP_EXPORTABLE_CHANNEL (chan));
 
307
 
 
308
  if (self->priv->channels != NULL)
 
309
    g_hash_table_remove (self->priv->channels, chan);
 
310
}
 
311
 
 
312
static ExampleCallChannel *
 
313
new_channel (ExampleCallManager *self,
 
314
    TpHandle handle,
 
315
    TpHandle initiator,
 
316
    gpointer request_token,
 
317
    gboolean initial_audio,
 
318
    gboolean initial_video)
 
319
{
 
320
  ExampleCallChannel *chan;
 
321
  gchar *object_path;
 
322
  GSList *requests = NULL;
 
323
 
 
324
  /* FIXME: This could potentially wrap around, but only after 4 billion
 
325
   * calls, which is probably plenty. */
 
326
  object_path = g_strdup_printf ("%s/CallChannel%u",
 
327
      self->priv->conn->object_path, self->priv->next_channel_index++);
 
328
 
 
329
  chan = g_object_new (EXAMPLE_TYPE_CALL_CHANNEL,
 
330
      "connection", self->priv->conn,
 
331
      "object-path", object_path,
 
332
      "handle", handle,
 
333
      "initiator-handle", initiator,
 
334
      "requested", (self->priv->conn->self_handle == initiator),
 
335
      "simulation-delay", self->priv->simulation_delay,
 
336
      "initial-audio", initial_audio,
 
337
      "initial-video", initial_video,
 
338
      NULL);
 
339
 
 
340
  g_free (object_path);
 
341
 
 
342
  g_signal_connect (chan, "closed", G_CALLBACK (channel_closed_cb), self);
 
343
 
 
344
  g_hash_table_insert (self->priv->channels, chan, chan);
 
345
 
 
346
  if (request_token != NULL)
 
347
    requests = g_slist_prepend (requests, request_token);
 
348
 
 
349
  tp_channel_manager_emit_new_channel (self, TP_EXPORTABLE_CHANNEL (chan),
 
350
      requests);
 
351
  g_slist_free (requests);
 
352
 
 
353
  return chan;
 
354
}
 
355
 
 
356
static const gchar * const audio_fixed_properties[] = {
 
357
    TP_PROP_CHANNEL_CHANNEL_TYPE,
 
358
    TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
 
359
    FUTURE_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO,
 
360
    NULL
 
361
};
 
362
 
 
363
static const gchar * const video_fixed_properties[] = {
 
364
    TP_PROP_CHANNEL_CHANNEL_TYPE,
 
365
    TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
 
366
    FUTURE_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO,
 
367
    NULL
 
368
};
 
369
 
 
370
static const gchar * const audio_allowed_properties[] = {
 
371
    TP_PROP_CHANNEL_TARGET_HANDLE,
 
372
    TP_PROP_CHANNEL_TARGET_ID,
 
373
    FUTURE_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO,
 
374
    NULL
 
375
};
 
376
 
 
377
static const gchar * const video_allowed_properties[] = {
 
378
    TP_PROP_CHANNEL_TARGET_HANDLE,
 
379
    TP_PROP_CHANNEL_TARGET_ID,
 
380
    FUTURE_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO,
 
381
    NULL
 
382
};
 
383
 
 
384
static void
 
385
example_call_manager_foreach_channel_class (
 
386
    TpChannelManager *manager,
 
387
    TpChannelManagerChannelClassFunc func,
 
388
    gpointer user_data)
 
389
{
 
390
  GHashTable *table = tp_asv_new (
 
391
      TP_PROP_CHANNEL_CHANNEL_TYPE,
 
392
          G_TYPE_STRING, FUTURE_IFACE_CHANNEL_TYPE_CALL,
 
393
      TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
 
394
    FUTURE_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, G_TYPE_BOOLEAN, TRUE,
 
395
      NULL);
 
396
 
 
397
  func (manager, table, audio_allowed_properties, user_data);
 
398
 
 
399
  g_hash_table_remove (table, FUTURE_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO);
 
400
  tp_asv_set_boolean (table, FUTURE_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO,
 
401
      TRUE);
 
402
 
 
403
  func (manager, table, video_allowed_properties, user_data);
 
404
 
 
405
  g_hash_table_destroy (table);
 
406
}
 
407
 
 
408
static gboolean
 
409
example_call_manager_request (ExampleCallManager *self,
 
410
    gpointer request_token,
 
411
    GHashTable *request_properties,
 
412
    gboolean require_new)
 
413
{
 
414
  TpHandle handle;
 
415
  GError *error = NULL;
 
416
  gboolean initial_audio, initial_video;
 
417
 
 
418
  if (tp_strdiff (tp_asv_get_string (request_properties,
 
419
          TP_PROP_CHANNEL_CHANNEL_TYPE),
 
420
      FUTURE_IFACE_CHANNEL_TYPE_CALL))
 
421
    {
 
422
      return FALSE;
 
423
    }
 
424
 
 
425
  if (tp_asv_get_uint32 (request_properties,
 
426
      TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL) != TP_HANDLE_TYPE_CONTACT)
 
427
    {
 
428
      return FALSE;
 
429
    }
 
430
 
 
431
  handle = tp_asv_get_uint32 (request_properties,
 
432
      TP_PROP_CHANNEL_TARGET_HANDLE, NULL);
 
433
  g_assert (handle != 0);
 
434
 
 
435
  initial_audio = tp_asv_get_boolean (request_properties,
 
436
      FUTURE_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, NULL);
 
437
  initial_video = tp_asv_get_boolean (request_properties,
 
438
        FUTURE_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, NULL);
 
439
 
 
440
  if (!initial_audio && !initial_video)
 
441
    {
 
442
      g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
 
443
          "Call channels must initially have either audio or video content");
 
444
      goto error;
 
445
    }
 
446
 
 
447
  /* the set of (fixed | allowed) properties is the same for audio and video,
 
448
   * so we only need to check with one set */
 
449
  if (tp_channel_manager_asv_has_unknown_properties (request_properties,
 
450
        audio_fixed_properties, audio_allowed_properties, &error))
 
451
    {
 
452
      goto error;
 
453
    }
 
454
 
 
455
  if (handle == self->priv->conn->self_handle)
 
456
    {
 
457
      /* In protocols with a concept of multiple "resources" signed in to
 
458
       * one account (XMPP, and possibly MSN) it is technically possible to
 
459
       * call yourself - e.g. if you're signed in on two PCs, you can call one
 
460
       * from the other. For simplicity, this example simulates a protocol
 
461
       * where this is not the case.
 
462
       */
 
463
      g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
 
464
          "In this protocol, you can't call yourself");
 
465
      goto error;
 
466
    }
 
467
 
 
468
  if (!require_new)
 
469
    {
 
470
      /* see if we're already calling that handle */
 
471
      GHashTableIter iter;
 
472
      gpointer chan;
 
473
 
 
474
      g_hash_table_iter_init (&iter, self->priv->channels);
 
475
 
 
476
      while (g_hash_table_iter_next (&iter, &chan, NULL))
 
477
        {
 
478
          guint its_handle;
 
479
 
 
480
          g_object_get (chan,
 
481
              "handle", &its_handle,
 
482
              NULL);
 
483
 
 
484
          if (its_handle == handle)
 
485
            {
 
486
              tp_channel_manager_emit_request_already_satisfied (self,
 
487
                  request_token, TP_EXPORTABLE_CHANNEL (chan));
 
488
              return TRUE;
 
489
            }
 
490
        }
 
491
    }
 
492
 
 
493
  new_channel (self, handle, self->priv->conn->self_handle, request_token,
 
494
      initial_audio, initial_video);
 
495
  return TRUE;
 
496
 
 
497
error:
 
498
  tp_channel_manager_emit_request_failed (self, request_token,
 
499
      error->domain, error->code, error->message);
 
500
  g_error_free (error);
 
501
  return TRUE;
 
502
}
 
503
 
 
504
static gboolean
 
505
example_call_manager_create_channel (TpChannelManager *manager,
 
506
    gpointer request_token,
 
507
    GHashTable *request_properties)
 
508
{
 
509
    return example_call_manager_request (
 
510
        EXAMPLE_CALL_MANAGER (manager),
 
511
        request_token, request_properties, TRUE);
 
512
}
 
513
 
 
514
static gboolean
 
515
example_call_manager_ensure_channel (TpChannelManager *manager,
 
516
    gpointer request_token,
 
517
    GHashTable *request_properties)
 
518
{
 
519
    return example_call_manager_request (
 
520
        EXAMPLE_CALL_MANAGER (manager),
 
521
        request_token, request_properties, FALSE);
 
522
}
 
523
 
 
524
static void
 
525
channel_manager_iface_init (gpointer g_iface,
 
526
    gpointer iface_data G_GNUC_UNUSED)
 
527
{
 
528
  TpChannelManagerIface *iface = g_iface;
 
529
 
 
530
  iface->foreach_channel = example_call_manager_foreach_channel;
 
531
  iface->foreach_channel_class = example_call_manager_foreach_channel_class;
 
532
  iface->create_channel = example_call_manager_create_channel;
 
533
  iface->ensure_channel = example_call_manager_ensure_channel;
 
534
  /* In this channel manager, RequestChannel is not supported; Call is not
 
535
   * designed to work with the old RequestChannel API. */
 
536
  iface->request_channel = NULL;
 
537
}