~ubuntu-branches/ubuntu/precise/empathy/precise-proposed-201205180810

« back to all changes in this revision

Viewing changes to src/empathy-call-window.c

  • Committer: Bazaar Package Importer
  • Author(s): Brian Curtis, Brian Curtis, Ken VanDine
  • Date: 2011-06-01 10:35:24 UTC
  • mfrom: (1.1.70 upstream) (6.3.44 experimental)
  • Revision ID: james.westby@ubuntu.com-20110601103524-wx3wgp71394730jt
Tags: 3.1.1-1ubuntu1
[ Brian Curtis ]
* Merge with Debian experimental, remaining Ubuntu changes:
* debian/control:
  - Drop geoclue/mapping build-depends (they are in Universe)
  - Add Vcz-Bzr link
  - Add Suggests on telepathy-idle
  - Bump telepathy-butterfly, telepathy-haze to recommends
  - Don't recommend the freedesktop sound theme we have an ubuntu one
  - Add build depend for libunity-dev
* debian/rules:
  - Use autoreconf.mk
  - Disable map and location
* debian/empathy.install:
  - Install message indicator configuration
* debian/indicators/empathy:
  - Message indicator configuration
* debian/patches/01_lpi.patch:
  - Add Launchpad integration
* debian/patches/10_use_notify_osd_icons.patch:
  - Use the notify-osd image for new messages
* debian/patches/34_start_raised_execpt_in_session.patch
  - If not started with the session, we should always raise
* debian/patches/36_chat_window_default_size.patch:
  - Make the default chat window size larger
* debian/patches/37_facebook_default.patch:
  - Make facebook the default chat account type
* debian/patches/38_lp_569289.patch
  - Set freenode as default IRC network for new IRC accounts 
* debian/patches/41_unity_launcher_progress.patch
  - Display file transfer progress in the unity launcher

[ Ken VanDine ]
* debian/control
  - build depend on libgcr-3-dev instead of libgcr-dev
  - dropped build depends for libindicate, we will use telepathy-indicator
  - Depend on dconf-gsettings-backend | gsettings-backend
  - Added a Recommends for telepathy-indicator
* +debian/empathy.gsettings-override
  - Added an override for notifications-focus
* debian/patches/series
  - commented out 23_idomessagedialog_for_voip_and_ft.patch, until ido has 
    been ported to gtk3

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * empathy-call-window.c - Source for EmpathyCallWindow
3
 
 * Copyright (C) 2008-2011 Collabora Ltd.
4
 
 * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
5
 
 *
6
 
 * This library is free software; you can redistribute it and/or
7
 
 * modify it under the terms of the GNU Lesser General Public
8
 
 * License as published by the Free Software Foundation; either
9
 
 * version 2.1 of the License, or (at your option) any later version.
10
 
 *
11
 
 * This library is distributed in the hope that it will be useful,
12
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 
 * Lesser General Public License for more details.
15
 
 *
16
 
 * You should have received a copy of the GNU Lesser General Public
17
 
 * License along with this library; if not, write to the Free Software
18
 
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19
 
 */
20
 
 
21
 
 
22
 
#include <stdio.h>
23
 
#include <stdlib.h>
24
 
 
25
 
#include <math.h>
26
 
 
27
 
#include <gdk/gdkkeysyms.h>
28
 
#include <gst/gst.h>
29
 
#include <gtk/gtk.h>
30
 
#include <glib/gi18n.h>
31
 
 
32
 
#include <telepathy-glib/util.h>
33
 
#include <telepathy-farstream/telepathy-farstream.h>
34
 
#include <telepathy-glib/util.h>
35
 
 
36
 
#include <gst/farsight/fs-element-added-notifier.h>
37
 
#include <gst/farsight/fs-utils.h>
38
 
 
39
 
#include <libempathy/empathy-tp-contact-factory.h>
40
 
#include <libempathy/empathy-utils.h>
41
 
#include <libempathy-gtk/empathy-avatar-image.h>
42
 
#include <libempathy-gtk/empathy-ui-utils.h>
43
 
#include <libempathy-gtk/empathy-sound.h>
44
 
#include <libempathy-gtk/empathy-geometry.h>
45
 
#include <libempathy-gtk/empathy-images.h>
46
 
 
47
 
#define DEBUG_FLAG EMPATHY_DEBUG_VOIP
48
 
#include <libempathy/empathy-debug.h>
49
 
 
50
 
#include "empathy-call-window.h"
51
 
#include "empathy-call-window-fullscreen.h"
52
 
#include "empathy-call-factory.h"
53
 
#include "empathy-video-widget.h"
54
 
#include "empathy-audio-src.h"
55
 
#include "empathy-audio-sink.h"
56
 
#include "empathy-video-src.h"
57
 
#include "empathy-sidebar.h"
58
 
 
59
 
#define BUTTON_ID "empathy-call-dtmf-button-id"
60
 
 
61
 
#define CONTENT_HBOX_BORDER_WIDTH 6
62
 
#define CONTENT_HBOX_SPACING 3
63
 
#define CONTENT_HBOX_CHILDREN_PACKING_PADDING 3
64
 
 
65
 
#define SELF_VIDEO_SECTION_WIDTH 160
66
 
#define SELF_VIDEO_SECTION_HEIGTH 120
67
 
 
68
 
/* The avatar's default width and height are set to the same value because we
69
 
   want a square icon. */
70
 
#define REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
71
 
#define REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT \
72
 
  EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
73
 
 
74
 
/* If an video input error occurs, the error message will start with "v4l" */
75
 
#define VIDEO_INPUT_ERROR_PREFIX "v4l"
76
 
 
77
 
/* The time interval in milliseconds between 2 outgoing rings */
78
 
#define MS_BETWEEN_RING 500
79
 
 
80
 
G_DEFINE_TYPE(EmpathyCallWindow, empathy_call_window, GTK_TYPE_WINDOW)
81
 
 
82
 
enum {
83
 
  PROP_CALL_HANDLER = 1,
84
 
};
85
 
 
86
 
typedef enum {
87
 
  CONNECTING,
88
 
  CONNECTED,
89
 
  DISCONNECTED,
90
 
  REDIALING
91
 
} CallState;
92
 
 
93
 
typedef enum {
94
 
  CAMERA_STATE_OFF = 0,
95
 
  CAMERA_STATE_PREVIEW,
96
 
  CAMERA_STATE_ON,
97
 
} CameraState;
98
 
 
99
 
struct _EmpathyCallWindowPriv
100
 
{
101
 
  gboolean dispose_has_run;
102
 
  EmpathyCallHandler *handler;
103
 
 
104
 
  EmpathyContact *contact;
105
 
 
106
 
  guint call_state;
107
 
  gboolean outgoing;
108
 
 
109
 
  GtkUIManager *ui_manager;
110
 
  GtkWidget *errors_vbox;
111
 
  /* widget displays the video received from the remote user. This widget is
112
 
   * alive only during call. */
113
 
  GtkWidget *video_output;
114
 
  GtkWidget *video_preview;
115
 
  GtkWidget *remote_user_avatar_widget;
116
 
  GtkWidget *self_user_avatar_widget;
117
 
  GtkWidget *sidebar;
118
 
  GtkWidget *sidebar_button;
119
 
  GtkWidget *statusbar;
120
 
  GtkWidget *volume_button;
121
 
  GtkWidget *redial_button;
122
 
  GtkWidget *mic_button;
123
 
  GtkWidget *toolbar;
124
 
  GtkWidget *pane;
125
 
  GtkAction *redial;
126
 
  GtkAction *menu_fullscreen;
127
 
  GtkAction *action_camera_on;
128
 
  GtkWidget *tool_button_camera_off;
129
 
  GtkWidget *tool_button_camera_preview;
130
 
  GtkWidget *tool_button_camera_on;
131
 
 
132
 
  /* The frames and boxes that contain self and remote avatar and video
133
 
     input/output. When we redial, we destroy and re-create the boxes */
134
 
  GtkWidget *remote_user_output_frame;
135
 
  GtkWidget *self_user_output_frame;
136
 
  GtkWidget *remote_user_output_hbox;
137
 
  GtkWidget *self_user_output_hbox;
138
 
 
139
 
  /* We keep a reference on the hbox which contains the main content so we can
140
 
     easilly repack everything when toggling fullscreen */
141
 
  GtkWidget *content_hbox;
142
 
 
143
 
  /* This vbox is contained in the content_hbox and it contains the
144
 
     self_user_output_frame and the sidebar button. When toggling fullscreen,
145
 
     it needs to be repacked. We keep a reference on it for easier access. */
146
 
  GtkWidget *vbox;
147
 
 
148
 
  gulong video_output_motion_handler_id;
149
 
  guint bus_message_source_id;
150
 
 
151
 
  gdouble volume;
152
 
  GtkWidget *volume_scale;
153
 
  GtkWidget *volume_progress_bar;
154
 
  GtkAdjustment *audio_input_adj;
155
 
 
156
 
  GtkWidget *dtmf_panel;
157
 
 
158
 
  /* Details vbox */
159
 
  GtkWidget *details_vbox;
160
 
  GtkWidget *vcodec_encoding_label;
161
 
  GtkWidget *acodec_encoding_label;
162
 
  GtkWidget *vcodec_decoding_label;
163
 
  GtkWidget *acodec_decoding_label;
164
 
 
165
 
  GtkWidget *audio_remote_candidate_label;
166
 
  GtkWidget *audio_local_candidate_label;
167
 
  GtkWidget *video_remote_candidate_label;
168
 
  GtkWidget *video_local_candidate_label;
169
 
  GtkWidget *video_remote_candidate_info_img;
170
 
  GtkWidget *video_local_candidate_info_img;
171
 
  GtkWidget *audio_remote_candidate_info_img;
172
 
  GtkWidget *audio_local_candidate_info_img;
173
 
 
174
 
  GstElement *video_input;
175
 
  GstElement *audio_input;
176
 
  GstElement *audio_output;
177
 
  GstElement *pipeline;
178
 
  GstElement *video_tee;
179
 
 
180
 
  GstElement *funnel;
181
 
 
182
 
  GList *notifiers;
183
 
 
184
 
  guint context_id;
185
 
 
186
 
  GTimer *timer;
187
 
  guint timer_id;
188
 
 
189
 
  GtkWidget *video_contrast;
190
 
  GtkWidget *video_brightness;
191
 
  GtkWidget *video_gamma;
192
 
 
193
 
  GMutex *lock;
194
 
  gboolean call_started;
195
 
  gboolean sending_video;
196
 
  CameraState camera_state;
197
 
 
198
 
  EmpathyCallWindowFullscreen *fullscreen;
199
 
  gboolean is_fullscreen;
200
 
 
201
 
  /* Those fields represent the state of the window before it actually was in
202
 
     fullscreen mode. */
203
 
  gboolean sidebar_was_visible_before_fs;
204
 
  gint original_width_before_fs;
205
 
  gint original_height_before_fs;
206
 
 
207
 
  /* TRUE if the call should be started when the pipeline is playing */
208
 
  gboolean start_call_when_playing;
209
 
  /* TRUE if we requested to set the pipeline in the playing state */
210
 
  gboolean pipeline_playing;
211
 
};
212
 
 
213
 
#define GET_PRIV(o) (EMPATHY_CALL_WINDOW (o)->priv)
214
 
 
215
 
static void empathy_call_window_realized_cb (GtkWidget *widget,
216
 
  EmpathyCallWindow *window);
217
 
 
218
 
static gboolean empathy_call_window_delete_cb (GtkWidget *widget,
219
 
  GdkEvent *event, EmpathyCallWindow *window);
220
 
 
221
 
static gboolean empathy_call_window_state_event_cb (GtkWidget *widget,
222
 
  GdkEventWindowState *event, EmpathyCallWindow *window);
223
 
 
224
 
static void empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
225
 
  EmpathyCallWindow *window);
226
 
 
227
 
static void empathy_call_window_set_send_video (EmpathyCallWindow *window,
228
 
  CameraState state);
229
 
 
230
 
static void empathy_call_window_mic_toggled_cb (
231
 
  GtkToggleToolButton *toggle, EmpathyCallWindow *window);
232
 
 
233
 
static void empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
234
 
  EmpathyCallWindow *window);
235
 
 
236
 
static void empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
237
 
  EmpathyCallWindow *window);
238
 
 
239
 
static void empathy_call_window_hangup_cb (gpointer object,
240
 
  EmpathyCallWindow *window);
241
 
 
242
 
static void empathy_call_window_fullscreen_cb (gpointer object,
243
 
  EmpathyCallWindow *window);
244
 
 
245
 
static void empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window);
246
 
 
247
 
static gboolean empathy_call_window_video_button_press_cb (
248
 
  GtkWidget *video_output, GdkEventButton *event, EmpathyCallWindow *window);
249
 
 
250
 
static gboolean empathy_call_window_key_press_cb (GtkWidget *video_output,
251
 
  GdkEventKey *event, EmpathyCallWindow *window);
252
 
 
253
 
static gboolean empathy_call_window_video_output_motion_notify (
254
 
  GtkWidget *widget, GdkEventMotion *event, EmpathyCallWindow *window);
255
 
 
256
 
static void empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
257
 
  guint button);
258
 
 
259
 
static void empathy_call_window_redial_cb (gpointer object,
260
 
  EmpathyCallWindow *window);
261
 
 
262
 
static void empathy_call_window_restart_call (EmpathyCallWindow *window);
263
 
 
264
 
static void empathy_call_window_status_message (EmpathyCallWindow *window,
265
 
  gchar *message);
266
 
 
267
 
static gboolean empathy_call_window_bus_message (GstBus *bus,
268
 
  GstMessage *message, gpointer user_data);
269
 
 
270
 
static void
271
 
empathy_call_window_volume_changed_cb (GtkScaleButton *button,
272
 
  gdouble value, EmpathyCallWindow *window);
273
 
 
274
 
static void block_camera_control_signals (EmpathyCallWindow *self);
275
 
static void unblock_camera_control_signals (EmpathyCallWindow *self);
276
 
 
277
 
static void
278
 
empathy_call_window_setup_toolbar (EmpathyCallWindow *self)
279
 
{
280
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
281
 
  GtkToolItem *tool_item;
282
 
  GtkWidget *camera_off_icon;
283
 
  GdkPixbuf *pixbuf, *modded_pixbuf;
284
 
 
285
 
  /* set the icon of the 'camera off' button by greying off the webcam icon */
286
 
  pixbuf = empathy_pixbuf_from_icon_name ("camera-web",
287
 
      GTK_ICON_SIZE_SMALL_TOOLBAR);
288
 
 
289
 
  modded_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
290
 
      gdk_pixbuf_get_width (pixbuf),
291
 
      gdk_pixbuf_get_height (pixbuf));
292
 
 
293
 
  gdk_pixbuf_saturate_and_pixelate (pixbuf, modded_pixbuf, 1.0, TRUE);
294
 
  g_object_unref (pixbuf);
295
 
 
296
 
  camera_off_icon = gtk_image_new_from_pixbuf (modded_pixbuf);
297
 
  g_object_unref (modded_pixbuf);
298
 
  gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (
299
 
        priv->tool_button_camera_off), camera_off_icon);
300
 
 
301
 
  /* Add an empty expanded GtkToolItem so the volume button is at the end of
302
 
   * the toolbar. */
303
 
  tool_item = gtk_tool_item_new ();
304
 
  gtk_tool_item_set_expand (tool_item, TRUE);
305
 
  gtk_widget_show (GTK_WIDGET (tool_item));
306
 
  gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
307
 
 
308
 
  priv->volume_button = gtk_volume_button_new ();
309
 
  /* FIXME listen to the audiosinks signals and update the button according to
310
 
   * that, for now starting out at 1.0 and assuming only the app changes the
311
 
   * volume will do */
312
 
  gtk_scale_button_set_value (GTK_SCALE_BUTTON (priv->volume_button), 1.0);
313
 
  g_signal_connect (G_OBJECT (priv->volume_button), "value-changed",
314
 
    G_CALLBACK (empathy_call_window_volume_changed_cb), self);
315
 
 
316
 
  tool_item = gtk_tool_item_new ();
317
 
  gtk_container_add (GTK_CONTAINER (tool_item), priv->volume_button);
318
 
  gtk_widget_show_all (GTK_WIDGET (tool_item));
319
 
  gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
320
 
}
321
 
 
322
 
static void
323
 
dtmf_button_pressed_cb (GtkButton *button, EmpathyCallWindow *window)
324
 
{
325
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
326
 
  TpyCallChannel *call;
327
 
  GQuark button_quark;
328
 
  TpDTMFEvent event;
329
 
 
330
 
  g_object_get (priv->handler, "call-channel", &call, NULL);
331
 
 
332
 
  button_quark = g_quark_from_static_string (BUTTON_ID);
333
 
  event = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (button),
334
 
    button_quark));
335
 
 
336
 
  tpy_call_channel_dtmf_start_tone (call, event);
337
 
 
338
 
  g_object_unref (call);
339
 
}
340
 
 
341
 
static void
342
 
dtmf_button_released_cb (GtkButton *button, EmpathyCallWindow *window)
343
 
{
344
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
345
 
  TpyCallChannel *call;
346
 
 
347
 
  g_object_get (priv->handler, "call-channel", &call, NULL);
348
 
 
349
 
  tpy_call_channel_dtmf_stop_tone (call);
350
 
 
351
 
  g_object_unref (call);
352
 
}
353
 
 
354
 
static GtkWidget *
355
 
empathy_call_window_create_dtmf (EmpathyCallWindow *self)
356
 
{
357
 
  GtkWidget *table;
358
 
  int i;
359
 
  GQuark button_quark;
360
 
  struct {
361
 
    const gchar *label;
362
 
    TpDTMFEvent event;
363
 
  } dtmfbuttons[] = { { "1", TP_DTMF_EVENT_DIGIT_1 },
364
 
                      { "2", TP_DTMF_EVENT_DIGIT_2 },
365
 
                      { "3", TP_DTMF_EVENT_DIGIT_3 },
366
 
                      { "4", TP_DTMF_EVENT_DIGIT_4 },
367
 
                      { "5", TP_DTMF_EVENT_DIGIT_5 },
368
 
                      { "6", TP_DTMF_EVENT_DIGIT_6 },
369
 
                      { "7", TP_DTMF_EVENT_DIGIT_7 },
370
 
                      { "8", TP_DTMF_EVENT_DIGIT_8 },
371
 
                      { "9", TP_DTMF_EVENT_DIGIT_9 },
372
 
                      { "#", TP_DTMF_EVENT_HASH },
373
 
                      { "0", TP_DTMF_EVENT_DIGIT_0 },
374
 
                      { "*", TP_DTMF_EVENT_ASTERISK },
375
 
                      { NULL, } };
376
 
 
377
 
  button_quark = g_quark_from_static_string (BUTTON_ID);
378
 
 
379
 
  table = gtk_table_new (4, 3, TRUE);
380
 
 
381
 
  for (i = 0; dtmfbuttons[i].label != NULL; i++)
382
 
    {
383
 
      GtkWidget *button = gtk_button_new_with_label (dtmfbuttons[i].label);
384
 
      gtk_table_attach (GTK_TABLE (table), button, i % 3, i % 3 + 1,
385
 
        i/3, i/3 + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
386
 
 
387
 
      g_object_set_qdata (G_OBJECT (button), button_quark,
388
 
        GUINT_TO_POINTER (dtmfbuttons[i].event));
389
 
 
390
 
      g_signal_connect (G_OBJECT (button), "pressed",
391
 
        G_CALLBACK (dtmf_button_pressed_cb), self);
392
 
      g_signal_connect (G_OBJECT (button), "released",
393
 
        G_CALLBACK (dtmf_button_released_cb), self);
394
 
    }
395
 
 
396
 
  return table;
397
 
}
398
 
 
399
 
static GtkWidget *
400
 
empathy_call_window_create_video_input_add_slider (EmpathyCallWindow *self,
401
 
  gchar *label_text, GtkWidget *bin)
402
 
{
403
 
   GtkWidget *vbox = gtk_vbox_new (FALSE, 2);
404
 
   GtkWidget *scale = gtk_vscale_new_with_range (0, 100, 10);
405
 
   GtkWidget *label = gtk_label_new (label_text);
406
 
 
407
 
   gtk_widget_set_sensitive (scale, FALSE);
408
 
 
409
 
   gtk_container_add (GTK_CONTAINER (bin), vbox);
410
 
 
411
 
   gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
412
 
   gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 0);
413
 
   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
414
 
 
415
 
   return scale;
416
 
}
417
 
 
418
 
static void
419
 
empathy_call_window_video_contrast_changed_cb (GtkAdjustment *adj,
420
 
  EmpathyCallWindow *self)
421
 
 
422
 
{
423
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
424
 
 
425
 
  empathy_video_src_set_channel (priv->video_input,
426
 
    EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST, gtk_adjustment_get_value (adj));
427
 
}
428
 
 
429
 
static void
430
 
empathy_call_window_video_brightness_changed_cb (GtkAdjustment *adj,
431
 
  EmpathyCallWindow *self)
432
 
 
433
 
{
434
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
435
 
 
436
 
  empathy_video_src_set_channel (priv->video_input,
437
 
    EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS, gtk_adjustment_get_value (adj));
438
 
}
439
 
 
440
 
static void
441
 
empathy_call_window_video_gamma_changed_cb (GtkAdjustment *adj,
442
 
  EmpathyCallWindow *self)
443
 
 
444
 
{
445
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
446
 
 
447
 
  empathy_video_src_set_channel (priv->video_input,
448
 
    EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA, gtk_adjustment_get_value (adj));
449
 
}
450
 
 
451
 
 
452
 
static GtkWidget *
453
 
empathy_call_window_create_video_input (EmpathyCallWindow *self)
454
 
{
455
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
456
 
  GtkWidget *hbox;
457
 
 
458
 
  hbox = gtk_hbox_new (TRUE, 3);
459
 
 
460
 
  priv->video_contrast = empathy_call_window_create_video_input_add_slider (
461
 
    self,  _("Contrast"), hbox);
462
 
 
463
 
  priv->video_brightness = empathy_call_window_create_video_input_add_slider (
464
 
    self,  _("Brightness"), hbox);
465
 
 
466
 
  priv->video_gamma = empathy_call_window_create_video_input_add_slider (
467
 
    self,  _("Gamma"), hbox);
468
 
 
469
 
  return hbox;
470
 
}
471
 
 
472
 
static void
473
 
empathy_call_window_setup_video_input (EmpathyCallWindow *self)
474
 
{
475
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
476
 
  guint supported;
477
 
  GtkAdjustment *adj;
478
 
 
479
 
  supported = empathy_video_src_get_supported_channels (priv->video_input);
480
 
 
481
 
  if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST)
482
 
    {
483
 
      adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_contrast));
484
 
 
485
 
      gtk_adjustment_set_value (adj,
486
 
        empathy_video_src_get_channel (priv->video_input,
487
 
          EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST));
488
 
 
489
 
      g_signal_connect (G_OBJECT (adj), "value-changed",
490
 
        G_CALLBACK (empathy_call_window_video_contrast_changed_cb), self);
491
 
 
492
 
      gtk_widget_set_sensitive (priv->video_contrast, TRUE);
493
 
    }
494
 
 
495
 
  if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS)
496
 
    {
497
 
      adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_brightness));
498
 
 
499
 
      gtk_adjustment_set_value (adj,
500
 
        empathy_video_src_get_channel (priv->video_input,
501
 
          EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS));
502
 
 
503
 
      g_signal_connect (G_OBJECT (adj), "value-changed",
504
 
        G_CALLBACK (empathy_call_window_video_brightness_changed_cb), self);
505
 
      gtk_widget_set_sensitive (priv->video_brightness, TRUE);
506
 
    }
507
 
 
508
 
  if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA)
509
 
    {
510
 
      adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_gamma));
511
 
 
512
 
      gtk_adjustment_set_value (adj,
513
 
        empathy_video_src_get_channel (priv->video_input,
514
 
          EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA));
515
 
 
516
 
      g_signal_connect (G_OBJECT (adj), "value-changed",
517
 
        G_CALLBACK (empathy_call_window_video_gamma_changed_cb), self);
518
 
      gtk_widget_set_sensitive (priv->video_gamma, TRUE);
519
 
    }
520
 
}
521
 
 
522
 
static void
523
 
empathy_call_window_mic_volume_changed_cb (GtkAdjustment *adj,
524
 
  EmpathyCallWindow *self)
525
 
{
526
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
527
 
  gdouble volume;
528
 
 
529
 
  volume = gtk_adjustment_get_value (adj)/100.0;
530
 
 
531
 
  /* Don't store the volume because of muting */
532
 
  if (volume > 0 || gtk_toggle_tool_button_get_active (
533
 
        GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
534
 
    priv->volume = volume;
535
 
 
536
 
  /* Ensure that the toggle button is active if the volume is > 0 and inactive
537
 
   * if it's smaller than 0 */
538
 
  if ((volume > 0) != gtk_toggle_tool_button_get_active (
539
 
        GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
540
 
    gtk_toggle_tool_button_set_active (
541
 
      GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), volume > 0);
542
 
 
543
 
  empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
544
 
    volume);
545
 
}
546
 
 
547
 
static void
548
 
empathy_call_window_audio_input_level_changed_cb (EmpathyGstAudioSrc *src,
549
 
  gdouble level, EmpathyCallWindow *window)
550
 
{
551
 
  gdouble value;
552
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
553
 
 
554
 
  value = CLAMP (pow (10, level / 20), 0.0, 1.0);
555
 
  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
556
 
      value);
557
 
}
558
 
 
559
 
static GtkWidget *
560
 
empathy_call_window_create_audio_input (EmpathyCallWindow *self)
561
 
{
562
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
563
 
  GtkWidget *hbox, *vbox, *label;
564
 
 
565
 
  hbox = gtk_hbox_new (TRUE, 3);
566
 
 
567
 
  vbox = gtk_vbox_new (FALSE, 3);
568
 
  gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 3);
569
 
 
570
 
  priv->volume_scale = gtk_vscale_new_with_range (0, 150, 100);
571
 
  gtk_range_set_inverted (GTK_RANGE (priv->volume_scale), TRUE);
572
 
  label = gtk_label_new (_("Volume"));
573
 
 
574
 
  priv->audio_input_adj = gtk_range_get_adjustment (
575
 
    GTK_RANGE (priv->volume_scale));
576
 
  priv->volume =  empathy_audio_src_get_volume (EMPATHY_GST_AUDIO_SRC
577
 
    (priv->audio_input));
578
 
  gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
579
 
 
580
 
  g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
581
 
    G_CALLBACK (empathy_call_window_mic_volume_changed_cb), self);
582
 
 
583
 
  gtk_box_pack_start (GTK_BOX (vbox), priv->volume_scale, TRUE, TRUE, 3);
584
 
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 3);
585
 
 
586
 
  priv->volume_progress_bar = gtk_progress_bar_new ();
587
 
 
588
 
  gtk_progress_bar_set_orientation (
589
 
    GTK_PROGRESS_BAR (priv->volume_progress_bar),
590
 
      GTK_PROGRESS_BOTTOM_TO_TOP);
591
 
 
592
 
  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
593
 
      0);
594
 
 
595
 
  gtk_box_pack_start (GTK_BOX (hbox), priv->volume_progress_bar, FALSE, FALSE,
596
 
      3);
597
 
 
598
 
  return hbox;
599
 
}
600
 
 
601
 
static void
602
 
create_video_output_widget (EmpathyCallWindow *self)
603
 
{
604
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
605
 
  GstBus *bus;
606
 
 
607
 
  g_assert (priv->video_output == NULL);
608
 
  g_assert (priv->pipeline != NULL);
609
 
 
610
 
  bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
611
 
  priv->video_output = empathy_video_widget_new (bus);
612
 
 
613
 
  gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
614
 
      priv->video_output, TRUE, TRUE, 0);
615
 
 
616
 
  gtk_widget_add_events (priv->video_output,
617
 
      GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
618
 
  g_signal_connect (G_OBJECT (priv->video_output), "button-press-event",
619
 
      G_CALLBACK (empathy_call_window_video_button_press_cb), self);
620
 
 
621
 
  g_object_unref (bus);
622
 
}
623
 
 
624
 
static void
625
 
create_video_input (EmpathyCallWindow *self)
626
 
{
627
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
628
 
 
629
 
  g_assert (priv->video_input == NULL);
630
 
  priv->video_input = empathy_video_src_new ();
631
 
  gst_object_ref (priv->video_input);
632
 
  gst_object_sink (priv->video_input);
633
 
}
634
 
 
635
 
static void
636
 
create_audio_input (EmpathyCallWindow *self)
637
 
{
638
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
639
 
 
640
 
  g_assert (priv->audio_input == NULL);
641
 
  priv->audio_input = empathy_audio_src_new ();
642
 
  gst_object_ref (priv->audio_input);
643
 
  gst_object_sink (priv->audio_input);
644
 
 
645
 
  tp_g_signal_connect_object (priv->audio_input, "peak-level-changed",
646
 
    G_CALLBACK (empathy_call_window_audio_input_level_changed_cb),
647
 
    self, 0);
648
 
}
649
 
 
650
 
static void
651
 
add_video_preview_to_pipeline (EmpathyCallWindow *self)
652
 
{
653
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
654
 
  GstElement *preview;
655
 
 
656
 
  g_assert (priv->video_preview != NULL);
657
 
  g_assert (priv->pipeline != NULL);
658
 
  g_assert (priv->video_input != NULL);
659
 
  g_assert (priv->video_tee != NULL);
660
 
 
661
 
  preview = empathy_video_widget_get_element (
662
 
      EMPATHY_VIDEO_WIDGET (priv->video_preview));
663
 
 
664
 
  if (!gst_bin_add (GST_BIN (priv->pipeline), priv->video_input))
665
 
    {
666
 
      g_warning ("Could not add video input to pipeline");
667
 
      return;
668
 
    }
669
 
 
670
 
  if (!gst_bin_add (GST_BIN (priv->pipeline), priv->video_tee))
671
 
    {
672
 
      g_warning ("Could not add video tee to pipeline");
673
 
      return;
674
 
    }
675
 
 
676
 
  if (!gst_bin_add (GST_BIN (priv->pipeline), preview))
677
 
    {
678
 
      g_warning ("Could not add video preview to pipeline");
679
 
      return;
680
 
    }
681
 
 
682
 
  if (!gst_element_link (priv->video_input, priv->video_tee))
683
 
    {
684
 
      g_warning ("Could not link video input to video tee");
685
 
      return;
686
 
    }
687
 
 
688
 
  if (!gst_element_link (priv->video_tee, preview))
689
 
    {
690
 
      g_warning ("Could not link video tee to video preview");
691
 
      return;
692
 
    }
693
 
}
694
 
 
695
 
static void
696
 
create_video_preview (EmpathyCallWindow *self)
697
 
{
698
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
699
 
  GstBus *bus;
700
 
 
701
 
  g_assert (priv->video_preview == NULL);
702
 
  g_assert (priv->video_tee == NULL);
703
 
 
704
 
  bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
705
 
 
706
 
  priv->video_preview = empathy_video_widget_new_with_size (bus,
707
 
      SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
708
 
  g_object_set (priv->video_preview, "sync", FALSE, "async", TRUE, NULL);
709
 
 
710
 
  gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
711
 
      priv->video_preview, TRUE, TRUE, 0);
712
 
 
713
 
  priv->video_tee = gst_element_factory_make ("tee", NULL);
714
 
  gst_object_ref (priv->video_tee);
715
 
  gst_object_sink (priv->video_tee);
716
 
 
717
 
  g_object_unref (bus);
718
 
}
719
 
 
720
 
static void
721
 
play_camera (EmpathyCallWindow *window,
722
 
    gboolean play)
723
 
{
724
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
725
 
  GstElement *preview;
726
 
  GstState state;
727
 
 
728
 
  if (priv->video_preview == NULL)
729
 
    {
730
 
      create_video_preview (window);
731
 
      add_video_preview_to_pipeline (window);
732
 
    }
733
 
 
734
 
  if (play)
735
 
    state = GST_STATE_PLAYING;
736
 
  else
737
 
    state = GST_STATE_NULL;
738
 
 
739
 
  preview = empathy_video_widget_get_element (
740
 
      EMPATHY_VIDEO_WIDGET (priv->video_preview));
741
 
 
742
 
  gst_element_set_state (preview, state);
743
 
  gst_element_set_state (priv->video_input, state);
744
 
  gst_element_set_state (priv->video_tee, state);
745
 
}
746
 
 
747
 
static void
748
 
display_video_preview (EmpathyCallWindow *self,
749
 
    gboolean display)
750
 
{
751
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
752
 
 
753
 
  if (display)
754
 
    {
755
 
      /* Display the preview and hide the self avatar */
756
 
      DEBUG ("Show video preview");
757
 
 
758
 
      play_camera (self, TRUE);
759
 
      gtk_widget_show (priv->video_preview);
760
 
      gtk_widget_hide (priv->self_user_avatar_widget);
761
 
    }
762
 
  else
763
 
    {
764
 
      /* Display the self avatar and hide the preview */
765
 
      DEBUG ("Show self avatar");
766
 
 
767
 
      if (priv->video_preview != NULL)
768
 
        {
769
 
          gtk_widget_hide (priv->video_preview);
770
 
          play_camera (self, FALSE);
771
 
        }
772
 
      gtk_widget_show (priv->self_user_avatar_widget);
773
 
    }
774
 
}
775
 
 
776
 
static void
777
 
empathy_call_window_set_state_connecting (EmpathyCallWindow *window)
778
 
{
779
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
780
 
 
781
 
  empathy_call_window_status_message (window, _("Connecting…"));
782
 
  priv->call_state = CONNECTING;
783
 
 
784
 
  if (priv->outgoing)
785
 
    empathy_sound_start_playing (GTK_WIDGET (window),
786
 
        EMPATHY_SOUND_PHONE_OUTGOING, MS_BETWEEN_RING);
787
 
}
788
 
 
789
 
static void
790
 
disable_camera (EmpathyCallWindow *self)
791
 
{
792
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
793
 
 
794
 
  if (priv->camera_state == CAMERA_STATE_OFF)
795
 
    return;
796
 
 
797
 
  DEBUG ("Disable camera");
798
 
 
799
 
  display_video_preview (self, FALSE);
800
 
 
801
 
  if (priv->camera_state == CAMERA_STATE_ON)
802
 
    empathy_call_window_set_send_video (self, CAMERA_STATE_OFF);
803
 
 
804
 
  block_camera_control_signals (self);
805
 
  gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
806
 
        priv->tool_button_camera_on), FALSE);
807
 
  gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
808
 
      priv->tool_button_camera_preview), FALSE);
809
 
 
810
 
  gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
811
 
      priv->tool_button_camera_off), TRUE);
812
 
  gtk_radio_action_set_current_value (GTK_RADIO_ACTION (priv->action_camera_on),
813
 
      CAMERA_STATE_OFF);
814
 
  unblock_camera_control_signals (self);
815
 
 
816
 
  priv->camera_state = CAMERA_STATE_OFF;
817
 
}
818
 
 
819
 
static void
820
 
tool_button_camera_off_toggled_cb (GtkToggleToolButton *toggle,
821
 
  EmpathyCallWindow *self)
822
 
{
823
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
824
 
 
825
 
  if (!gtk_toggle_tool_button_get_active (toggle))
826
 
    {
827
 
      if (priv->camera_state == CAMERA_STATE_OFF)
828
 
        {
829
 
          /* We can't change the state by disabling the button */
830
 
          block_camera_control_signals (self);
831
 
          gtk_toggle_tool_button_set_active (toggle, TRUE);
832
 
          unblock_camera_control_signals (self);
833
 
        }
834
 
 
835
 
      return;
836
 
    }
837
 
 
838
 
  disable_camera (self);
839
 
}
840
 
 
841
 
static void
842
 
enable_preview (EmpathyCallWindow *self)
843
 
{
844
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
845
 
 
846
 
  if (priv->camera_state == CAMERA_STATE_PREVIEW)
847
 
    return;
848
 
 
849
 
  DEBUG ("Enable preview");
850
 
 
851
 
  if (priv->camera_state == CAMERA_STATE_ON)
852
 
    {
853
 
      /* preview is already displayed so we just have to stop sending */
854
 
      empathy_call_window_set_send_video (self, CAMERA_STATE_PREVIEW);
855
 
    }
856
 
  else
857
 
    {
858
 
      display_video_preview (self, TRUE);
859
 
    }
860
 
 
861
 
  block_camera_control_signals (self);
862
 
  gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
863
 
      priv->tool_button_camera_off), FALSE);
864
 
  gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
865
 
        priv->tool_button_camera_on), FALSE);
866
 
 
867
 
  gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
868
 
        priv->tool_button_camera_preview), TRUE);
869
 
  gtk_radio_action_set_current_value (GTK_RADIO_ACTION (priv->action_camera_on),
870
 
      CAMERA_STATE_PREVIEW);
871
 
  unblock_camera_control_signals (self);
872
 
 
873
 
  priv->camera_state = CAMERA_STATE_PREVIEW;
874
 
}
875
 
 
876
 
static void
877
 
tool_button_camera_preview_toggled_cb (GtkToggleToolButton *toggle,
878
 
  EmpathyCallWindow *self)
879
 
{
880
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
881
 
 
882
 
  if (!gtk_toggle_tool_button_get_active (toggle))
883
 
    {
884
 
      if (priv->camera_state == CAMERA_STATE_PREVIEW)
885
 
        {
886
 
          /* We can't change the state by disabling the button */
887
 
          block_camera_control_signals (self);
888
 
          gtk_toggle_tool_button_set_active (toggle, TRUE);
889
 
          unblock_camera_control_signals (self);
890
 
        }
891
 
 
892
 
      return;
893
 
    }
894
 
 
895
 
  enable_preview (self);
896
 
}
897
 
 
898
 
static void
899
 
enable_camera (EmpathyCallWindow *self)
900
 
{
901
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
902
 
 
903
 
  if (priv->camera_state == CAMERA_STATE_ON)
904
 
    return;
905
 
 
906
 
  if (priv->video_input == NULL)
907
 
    {
908
 
      DEBUG ("Can't enable camera, no input");
909
 
      return;
910
 
    }
911
 
 
912
 
 
913
 
  DEBUG ("Enable camera");
914
 
 
915
 
  empathy_call_window_set_send_video (self, CAMERA_STATE_ON);
916
 
 
917
 
  block_camera_control_signals (self);
918
 
  gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
919
 
      priv->tool_button_camera_off), FALSE);
920
 
  gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
921
 
        priv->tool_button_camera_preview), FALSE);
922
 
 
923
 
  gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
924
 
      priv->tool_button_camera_on), TRUE);
925
 
  gtk_radio_action_set_current_value (GTK_RADIO_ACTION (priv->action_camera_on),
926
 
      CAMERA_STATE_ON);
927
 
  unblock_camera_control_signals (self);
928
 
 
929
 
  priv->camera_state = CAMERA_STATE_ON;
930
 
}
931
 
 
932
 
static void
933
 
tool_button_camera_on_toggled_cb (GtkToggleToolButton *toggle,
934
 
  EmpathyCallWindow *self)
935
 
{
936
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
937
 
 
938
 
  if (!gtk_toggle_tool_button_get_active (toggle))
939
 
    {
940
 
      if (priv->camera_state == CAMERA_STATE_ON)
941
 
        {
942
 
          /* We can't change the state by disabling the button */
943
 
          block_camera_control_signals (self);
944
 
          gtk_toggle_tool_button_set_active (toggle, TRUE);
945
 
          unblock_camera_control_signals (self);
946
 
        }
947
 
 
948
 
      return;
949
 
    }
950
 
 
951
 
  enable_camera (self);
952
 
}
953
 
 
954
 
static void
955
 
action_camera_change_cb (GtkRadioAction *action,
956
 
    GtkRadioAction *current,
957
 
    EmpathyCallWindow *self)
958
 
{
959
 
  CameraState state;
960
 
 
961
 
  state = gtk_radio_action_get_current_value (current);
962
 
 
963
 
  switch (state)
964
 
    {
965
 
      case CAMERA_STATE_OFF:
966
 
        disable_camera (self);
967
 
        break;
968
 
 
969
 
      case CAMERA_STATE_PREVIEW:
970
 
        enable_preview (self);
971
 
        break;
972
 
 
973
 
      case CAMERA_STATE_ON:
974
 
        enable_camera (self);
975
 
        break;
976
 
 
977
 
      default:
978
 
        g_assert_not_reached ();
979
 
    }
980
 
}
981
 
 
982
 
static void
983
 
create_pipeline (EmpathyCallWindow *self)
984
 
{
985
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
986
 
  GstBus *bus;
987
 
 
988
 
  g_assert (priv->pipeline == NULL);
989
 
 
990
 
  priv->pipeline = gst_pipeline_new (NULL);
991
 
  priv->pipeline_playing = FALSE;
992
 
 
993
 
  bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
994
 
  priv->bus_message_source_id = gst_bus_add_watch (bus,
995
 
      empathy_call_window_bus_message, self);
996
 
 
997
 
  g_object_unref (bus);
998
 
}
999
 
 
1000
 
 
1001
 
static void
1002
 
empathy_call_window_init (EmpathyCallWindow *self)
1003
 
{
1004
 
  EmpathyCallWindowPriv *priv;
1005
 
  GtkBuilder *gui;
1006
 
  GtkWidget *top_vbox;
1007
 
  GtkWidget *h;
1008
 
  GtkWidget *arrow;
1009
 
  GtkWidget *page;
1010
 
  gchar *filename;
1011
 
  GtkWidget *scroll;
1012
 
 
1013
 
  priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
1014
 
    EMPATHY_TYPE_CALL_WINDOW, EmpathyCallWindowPriv);
1015
 
 
1016
 
  filename = empathy_file_lookup ("empathy-call-window.ui", "src");
1017
 
  gui = empathy_builder_get_file (filename,
1018
 
    "call_window_vbox", &top_vbox,
1019
 
    "errors_vbox", &priv->errors_vbox,
1020
 
    "pane", &priv->pane,
1021
 
    "statusbar", &priv->statusbar,
1022
 
    "redial", &priv->redial_button,
1023
 
    "microphone", &priv->mic_button,
1024
 
    "toolbar", &priv->toolbar,
1025
 
    "menuredial", &priv->redial,
1026
 
    "ui_manager", &priv->ui_manager,
1027
 
    "menufullscreen", &priv->menu_fullscreen,
1028
 
    "camera_off", &priv->tool_button_camera_off,
1029
 
    "camera_preview", &priv->tool_button_camera_preview,
1030
 
    "camera_on", &priv->tool_button_camera_on,
1031
 
    "action_camera_on",  &priv->action_camera_on,
1032
 
    "details_vbox",  &priv->details_vbox,
1033
 
    "vcodec_encoding_label", &priv->vcodec_encoding_label,
1034
 
    "acodec_encoding_label", &priv->acodec_encoding_label,
1035
 
    "acodec_decoding_label", &priv->acodec_decoding_label,
1036
 
    "vcodec_decoding_label", &priv->vcodec_decoding_label,
1037
 
    "audio_remote_candidate_label", &priv->audio_remote_candidate_label,
1038
 
    "audio_local_candidate_label", &priv->audio_local_candidate_label,
1039
 
    "video_remote_candidate_label", &priv->video_remote_candidate_label,
1040
 
    "video_local_candidate_label", &priv->video_local_candidate_label,
1041
 
    "video_remote_candidate_info_img", &priv->video_remote_candidate_info_img,
1042
 
    "video_local_candidate_info_img", &priv->video_local_candidate_info_img,
1043
 
    "audio_remote_candidate_info_img", &priv->audio_remote_candidate_info_img,
1044
 
    "audio_local_candidate_info_img", &priv->audio_local_candidate_info_img,
1045
 
    NULL);
1046
 
  g_free (filename);
1047
 
 
1048
 
  empathy_builder_connect (gui, self,
1049
 
    "menuhangup", "activate", empathy_call_window_hangup_cb,
1050
 
    "hangup", "clicked", empathy_call_window_hangup_cb,
1051
 
    "menuredial", "activate", empathy_call_window_redial_cb,
1052
 
    "redial", "clicked", empathy_call_window_redial_cb,
1053
 
    "microphone", "toggled", empathy_call_window_mic_toggled_cb,
1054
 
    "menufullscreen", "activate", empathy_call_window_fullscreen_cb,
1055
 
    "camera_off", "toggled", tool_button_camera_off_toggled_cb,
1056
 
    "camera_preview", "toggled", tool_button_camera_preview_toggled_cb,
1057
 
    "camera_on", "toggled", tool_button_camera_on_toggled_cb,
1058
 
    "action_camera_on", "changed", action_camera_change_cb,
1059
 
    NULL);
1060
 
 
1061
 
  gtk_action_set_sensitive (priv->menu_fullscreen, FALSE);
1062
 
 
1063
 
  priv->lock = g_mutex_new ();
1064
 
 
1065
 
  gtk_container_add (GTK_CONTAINER (self), top_vbox);
1066
 
 
1067
 
  priv->content_hbox = gtk_hbox_new (FALSE, CONTENT_HBOX_SPACING);
1068
 
  gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
1069
 
                                  CONTENT_HBOX_BORDER_WIDTH);
1070
 
  gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
1071
 
 
1072
 
  /* remote user output frame */
1073
 
  priv->remote_user_output_frame = gtk_frame_new (NULL);
1074
 
  gtk_widget_set_size_request (priv->remote_user_output_frame,
1075
 
      EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT);
1076
 
  gtk_box_pack_start (GTK_BOX (priv->content_hbox),
1077
 
      priv->remote_user_output_frame, TRUE, TRUE,
1078
 
      CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1079
 
 
1080
 
  priv->remote_user_output_hbox = gtk_hbox_new (FALSE, 0);
1081
 
 
1082
 
  priv->remote_user_avatar_widget = gtk_image_new ();
1083
 
 
1084
 
  gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
1085
 
      priv->remote_user_avatar_widget, TRUE, TRUE, 0);
1086
 
 
1087
 
  gtk_container_add (GTK_CONTAINER (priv->remote_user_output_frame),
1088
 
      priv->remote_user_output_hbox);
1089
 
 
1090
 
  /* self user output frame */
1091
 
  priv->self_user_output_frame = gtk_frame_new (NULL);
1092
 
  gtk_widget_set_size_request (priv->self_user_output_frame,
1093
 
      SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
1094
 
 
1095
 
  priv->self_user_output_hbox = gtk_hbox_new (FALSE, 0);
1096
 
 
1097
 
  priv->self_user_avatar_widget = gtk_image_new ();
1098
 
  gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
1099
 
      priv->self_user_avatar_widget, TRUE, TRUE, 0);
1100
 
 
1101
 
  gtk_container_add (GTK_CONTAINER (priv->self_user_output_frame),
1102
 
      priv->self_user_output_hbox);
1103
 
 
1104
 
  create_pipeline (self);
1105
 
  create_video_output_widget (self);
1106
 
  create_audio_input (self);
1107
 
  create_video_input (self);
1108
 
 
1109
 
  /* The call will be started as soon the pipeline is playing */
1110
 
  priv->start_call_when_playing = TRUE;
1111
 
 
1112
 
  priv->vbox = gtk_vbox_new (FALSE, 3);
1113
 
  gtk_box_pack_start (GTK_BOX (priv->content_hbox), priv->vbox,
1114
 
      FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1115
 
  gtk_box_pack_start (GTK_BOX (priv->vbox), priv->self_user_output_frame,
1116
 
      FALSE, FALSE, 0);
1117
 
 
1118
 
  empathy_call_window_setup_toolbar (self);
1119
 
 
1120
 
  priv->sidebar_button = gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
1121
 
  arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
1122
 
  g_signal_connect (G_OBJECT (priv->sidebar_button), "toggled",
1123
 
    G_CALLBACK (empathy_call_window_sidebar_toggled_cb), self);
1124
 
 
1125
 
  gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
1126
 
 
1127
 
  h = gtk_hbox_new (FALSE, 3);
1128
 
  gtk_box_pack_end (GTK_BOX (priv->vbox), h, FALSE, FALSE, 3);
1129
 
  gtk_box_pack_end (GTK_BOX (h), priv->sidebar_button, FALSE, FALSE, 3);
1130
 
 
1131
 
  priv->sidebar = empathy_sidebar_new ();
1132
 
  g_signal_connect (G_OBJECT (priv->sidebar),
1133
 
    "hide", G_CALLBACK (empathy_call_window_sidebar_hidden_cb), self);
1134
 
  g_signal_connect (G_OBJECT (priv->sidebar),
1135
 
    "show", G_CALLBACK (empathy_call_window_sidebar_shown_cb), self);
1136
 
  gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
1137
 
 
1138
 
  page = empathy_call_window_create_audio_input (self);
1139
 
  empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar),
1140
 
      _("Audio input"), page);
1141
 
 
1142
 
  page = empathy_call_window_create_video_input (self);
1143
 
  empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar),
1144
 
      _("Video input"), page);
1145
 
 
1146
 
  priv->dtmf_panel = empathy_call_window_create_dtmf (self);
1147
 
  empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar),
1148
 
      _("Dialpad"), priv->dtmf_panel);
1149
 
 
1150
 
  gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
1151
 
 
1152
 
  /* Put the details vbox in a scroll window as it can require a lot of
1153
 
   * horizontal space. */
1154
 
  scroll = gtk_scrolled_window_new (NULL, NULL);
1155
 
  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scroll),
1156
 
      priv->details_vbox);
1157
 
 
1158
 
  empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Details"),
1159
 
    scroll);
1160
 
 
1161
 
  gtk_widget_show_all (top_vbox);
1162
 
 
1163
 
  gtk_widget_hide (priv->sidebar);
1164
 
 
1165
 
  priv->fullscreen = empathy_call_window_fullscreen_new (self);
1166
 
  empathy_call_window_fullscreen_set_video_widget (priv->fullscreen,
1167
 
      priv->video_output);
1168
 
  g_signal_connect (G_OBJECT (priv->fullscreen->leave_fullscreen_button),
1169
 
      "clicked", G_CALLBACK (empathy_call_window_fullscreen_cb), self);
1170
 
 
1171
 
  g_signal_connect (G_OBJECT (self), "realize",
1172
 
    G_CALLBACK (empathy_call_window_realized_cb), self);
1173
 
 
1174
 
  g_signal_connect (G_OBJECT (self), "delete-event",
1175
 
    G_CALLBACK (empathy_call_window_delete_cb), self);
1176
 
 
1177
 
  g_signal_connect (G_OBJECT (self), "window-state-event",
1178
 
    G_CALLBACK (empathy_call_window_state_event_cb), self);
1179
 
 
1180
 
  g_signal_connect (G_OBJECT (self), "key-press-event",
1181
 
      G_CALLBACK (empathy_call_window_key_press_cb), self);
1182
 
 
1183
 
  priv->timer = g_timer_new ();
1184
 
 
1185
 
  g_object_ref (priv->ui_manager);
1186
 
  g_object_unref (gui);
1187
 
 
1188
 
  empathy_geometry_bind (GTK_WINDOW (self), "call-window");
1189
 
}
1190
 
 
1191
 
/* Instead of specifying a width and a height, we specify only one size. That's
1192
 
   because we want a square avatar icon.  */
1193
 
static void
1194
 
init_contact_avatar_with_size (EmpathyContact *contact,
1195
 
    GtkWidget *image_widget,
1196
 
    gint size)
1197
 
{
1198
 
  GdkPixbuf *pixbuf_avatar = NULL;
1199
 
 
1200
 
  if (contact != NULL)
1201
 
    {
1202
 
      pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact,
1203
 
        size, size);
1204
 
    }
1205
 
 
1206
 
  if (pixbuf_avatar == NULL)
1207
 
    {
1208
 
      pixbuf_avatar = empathy_pixbuf_from_icon_name_sized (
1209
 
          EMPATHY_IMAGE_AVATAR_DEFAULT, size);
1210
 
    }
1211
 
 
1212
 
  gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget), pixbuf_avatar);
1213
 
 
1214
 
  if (pixbuf_avatar != NULL)
1215
 
    g_object_unref (pixbuf_avatar);
1216
 
}
1217
 
 
1218
 
static void
1219
 
set_window_title (EmpathyCallWindow *self)
1220
 
{
1221
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
1222
 
  gchar *tmp;
1223
 
 
1224
 
  if (priv->contact != NULL)
1225
 
    {
1226
 
      /* translators: Call is a noun and %s is the contact name. This string
1227
 
       * is used in the window title */
1228
 
      tmp = g_strdup_printf (_("Call with %s"),
1229
 
          empathy_contact_get_alias (priv->contact));
1230
 
      gtk_window_set_title (GTK_WINDOW (self), tmp);
1231
 
      g_free (tmp);
1232
 
    }
1233
 
  else
1234
 
    {
1235
 
      gtk_window_set_title (GTK_WINDOW (self), _("Call with %d participants"));
1236
 
    }
1237
 
}
1238
 
 
1239
 
static void
1240
 
contact_name_changed_cb (EmpathyContact *contact,
1241
 
    GParamSpec *pspec, EmpathyCallWindow *self)
1242
 
{
1243
 
  set_window_title (self);
1244
 
}
1245
 
 
1246
 
static void
1247
 
contact_avatar_changed_cb (EmpathyContact *contact,
1248
 
    GParamSpec *pspec, GtkWidget *avatar_widget)
1249
 
{
1250
 
  int size;
1251
 
  GtkAllocation allocation;
1252
 
 
1253
 
  gtk_widget_get_allocation (avatar_widget, &allocation);
1254
 
  size = allocation.height;
1255
 
 
1256
 
  if (size == 0)
1257
 
    {
1258
 
      /* the widget is not allocated yet, set a default size */
1259
 
      size = MIN (REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT,
1260
 
          REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH);
1261
 
    }
1262
 
 
1263
 
  init_contact_avatar_with_size (contact, avatar_widget, size);
1264
 
}
1265
 
 
1266
 
static void
1267
 
empathy_call_window_got_self_contact_cb (TpConnection *connection,
1268
 
    EmpathyContact *contact, const GError *error, gpointer user_data,
1269
 
    GObject *weak_object)
1270
 
{
1271
 
  EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1272
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
1273
 
 
1274
 
  init_contact_avatar_with_size (contact, priv->self_user_avatar_widget,
1275
 
      MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
1276
 
 
1277
 
  g_signal_connect (contact, "notify::avatar",
1278
 
      G_CALLBACK (contact_avatar_changed_cb), priv->self_user_avatar_widget);
1279
 
}
1280
 
 
1281
 
static void
1282
 
empathy_call_window_setup_avatars (EmpathyCallWindow *self,
1283
 
    EmpathyCallHandler *handler)
1284
 
{
1285
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
1286
 
  TpConnection *connection;
1287
 
 
1288
 
  g_signal_connect (priv->contact, "notify::name",
1289
 
      G_CALLBACK (contact_name_changed_cb), self);
1290
 
  g_signal_connect (priv->contact, "notify::avatar",
1291
 
    G_CALLBACK (contact_avatar_changed_cb),
1292
 
    priv->remote_user_avatar_widget);
1293
 
 
1294
 
  /* Retrieving the self avatar */
1295
 
  connection = empathy_contact_get_connection (priv->contact);
1296
 
  empathy_tp_contact_factory_get_from_handle (connection,
1297
 
      tp_connection_get_self_handle (connection),
1298
 
      empathy_call_window_got_self_contact_cb, self, NULL,
1299
 
      G_OBJECT (self));
1300
 
 
1301
 
  set_window_title (self);
1302
 
 
1303
 
  init_contact_avatar_with_size (priv->contact,
1304
 
      priv->remote_user_avatar_widget,
1305
 
      MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
1306
 
          REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
1307
 
 
1308
 
  /* The remote avatar is shown by default and will be hidden when we receive
1309
 
     video from the remote side. */
1310
 
  gtk_widget_hide (priv->video_output);
1311
 
  gtk_widget_show (priv->remote_user_avatar_widget);
1312
 
}
1313
 
 
1314
 
static void
1315
 
update_send_codec (EmpathyCallWindow *self,
1316
 
    gboolean audio)
1317
 
{
1318
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
1319
 
  FsCodec *codec;
1320
 
  GtkWidget *widget;
1321
 
  gchar *tmp;
1322
 
 
1323
 
  if (audio)
1324
 
    {
1325
 
      codec = empathy_call_handler_get_send_audio_codec (priv->handler);
1326
 
      widget = priv->acodec_encoding_label;
1327
 
    }
1328
 
  else
1329
 
    {
1330
 
      codec = empathy_call_handler_get_send_video_codec (priv->handler);
1331
 
      widget = priv->vcodec_encoding_label;
1332
 
    }
1333
 
 
1334
 
  if (codec == NULL)
1335
 
    return;
1336
 
 
1337
 
  tmp = g_strdup_printf ("%s/%u", codec->encoding_name, codec->clock_rate);
1338
 
  gtk_label_set_text (GTK_LABEL (widget), tmp);
1339
 
  g_free (tmp);
1340
 
}
1341
 
 
1342
 
static void
1343
 
send_audio_codec_notify_cb (GObject *object,
1344
 
    GParamSpec *pspec,
1345
 
    gpointer user_data)
1346
 
{
1347
 
  EmpathyCallWindow *self = user_data;
1348
 
 
1349
 
  update_send_codec (self, TRUE);
1350
 
}
1351
 
 
1352
 
static void
1353
 
send_video_codec_notify_cb (GObject *object,
1354
 
    GParamSpec *pspec,
1355
 
    gpointer user_data)
1356
 
{
1357
 
  EmpathyCallWindow *self = user_data;
1358
 
 
1359
 
  update_send_codec (self, FALSE);
1360
 
}
1361
 
 
1362
 
static void
1363
 
update_recv_codec (EmpathyCallWindow *self,
1364
 
    gboolean audio)
1365
 
{
1366
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
1367
 
  GList *codecs, *l;
1368
 
  GtkWidget *widget;
1369
 
  GString *str = NULL;
1370
 
 
1371
 
  if (audio)
1372
 
    {
1373
 
      codecs = empathy_call_handler_get_recv_audio_codecs (priv->handler);
1374
 
      widget = priv->acodec_decoding_label;
1375
 
    }
1376
 
  else
1377
 
    {
1378
 
      codecs = empathy_call_handler_get_recv_video_codecs (priv->handler);
1379
 
      widget = priv->vcodec_decoding_label;
1380
 
    }
1381
 
 
1382
 
  if (codecs == NULL)
1383
 
    return;
1384
 
 
1385
 
  for (l = codecs; l != NULL; l = g_list_next (l))
1386
 
    {
1387
 
      FsCodec *codec = l->data;
1388
 
 
1389
 
      if (str == NULL)
1390
 
        str = g_string_new (NULL);
1391
 
      else
1392
 
        g_string_append (str, ", ");
1393
 
 
1394
 
      g_string_append_printf (str, "%s/%u", codec->encoding_name,
1395
 
          codec->clock_rate);
1396
 
    }
1397
 
 
1398
 
  gtk_label_set_text (GTK_LABEL (widget), str->str);
1399
 
  g_string_free (str, TRUE);
1400
 
}
1401
 
 
1402
 
static void
1403
 
recv_audio_codecs_notify_cb (GObject *object,
1404
 
    GParamSpec *pspec,
1405
 
    gpointer user_data)
1406
 
{
1407
 
  EmpathyCallWindow *self = user_data;
1408
 
 
1409
 
  update_recv_codec (self, TRUE);
1410
 
}
1411
 
 
1412
 
static void
1413
 
recv_video_codecs_notify_cb (GObject *object,
1414
 
    GParamSpec *pspec,
1415
 
    gpointer user_data)
1416
 
{
1417
 
  EmpathyCallWindow *self = user_data;
1418
 
 
1419
 
  update_recv_codec (self, FALSE);
1420
 
}
1421
 
 
1422
 
static const gchar *
1423
 
candidate_type_to_str (FsCandidate *candidate)
1424
 
{
1425
 
  switch (candidate->type)
1426
 
    {
1427
 
      case FS_CANDIDATE_TYPE_HOST:
1428
 
        return "host";
1429
 
      case FS_CANDIDATE_TYPE_SRFLX:
1430
 
        return "server reflexive";
1431
 
      case FS_CANDIDATE_TYPE_PRFLX:
1432
 
        return "peer reflexive";
1433
 
      case FS_CANDIDATE_TYPE_RELAY:
1434
 
        return "relay";
1435
 
      case FS_CANDIDATE_TYPE_MULTICAST:
1436
 
        return "multicast";
1437
 
    }
1438
 
 
1439
 
  return NULL;
1440
 
}
1441
 
 
1442
 
static const gchar *
1443
 
candidate_type_to_desc (FsCandidate *candidate)
1444
 
{
1445
 
  switch (candidate->type)
1446
 
    {
1447
 
      case FS_CANDIDATE_TYPE_HOST:
1448
 
        return _("The IP address as seen by the machine");
1449
 
      case FS_CANDIDATE_TYPE_SRFLX:
1450
 
        return _("The IP address as seen by a server on the Internet");
1451
 
      case FS_CANDIDATE_TYPE_PRFLX:
1452
 
        return _("The IP address of the peer as seen by the other side");
1453
 
      case FS_CANDIDATE_TYPE_RELAY:
1454
 
        return _("The IP address of a relay server");
1455
 
      case FS_CANDIDATE_TYPE_MULTICAST:
1456
 
        return _("The IP address of the multicast group");
1457
 
    }
1458
 
 
1459
 
  return NULL;
1460
 
}
1461
 
 
1462
 
static void
1463
 
update_candidat_widget (EmpathyCallWindow *self,
1464
 
    GtkWidget *label,
1465
 
    GtkWidget *img,
1466
 
    FsCandidate *candidate)
1467
 
{
1468
 
  gchar *str;
1469
 
 
1470
 
  g_assert (candidate != NULL);
1471
 
  str = g_strdup_printf ("%s %u (%s)", candidate->ip,
1472
 
      candidate->port, candidate_type_to_str (candidate));
1473
 
 
1474
 
  gtk_label_set_text (GTK_LABEL (label), str);
1475
 
  gtk_widget_set_tooltip_text (img, candidate_type_to_desc (candidate));
1476
 
 
1477
 
  g_free (str);
1478
 
}
1479
 
 
1480
 
static void
1481
 
candidates_changed_cb (GObject *object,
1482
 
    FsMediaType type,
1483
 
    EmpathyCallWindow *self)
1484
 
{
1485
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
1486
 
  FsCandidate *candidate = NULL;
1487
 
 
1488
 
  if (type == FS_MEDIA_TYPE_VIDEO)
1489
 
    {
1490
 
      /* Update remote candidate */
1491
 
      candidate = empathy_call_handler_get_video_remote_candidate (
1492
 
          priv->handler);
1493
 
 
1494
 
      update_candidat_widget (self, priv->video_remote_candidate_label,
1495
 
          priv->video_remote_candidate_info_img, candidate);
1496
 
 
1497
 
      /* Update local candidate */
1498
 
      candidate = empathy_call_handler_get_video_local_candidate (
1499
 
          priv->handler);
1500
 
 
1501
 
      update_candidat_widget (self, priv->video_local_candidate_label,
1502
 
          priv->video_local_candidate_info_img, candidate);
1503
 
    }
1504
 
  else
1505
 
    {
1506
 
      /* Update remote candidate */
1507
 
      candidate = empathy_call_handler_get_audio_remote_candidate (
1508
 
          priv->handler);
1509
 
 
1510
 
      update_candidat_widget (self, priv->audio_remote_candidate_label,
1511
 
          priv->audio_remote_candidate_info_img, candidate);
1512
 
 
1513
 
      /* Update local candidate */
1514
 
      candidate = empathy_call_handler_get_audio_local_candidate (
1515
 
          priv->handler);
1516
 
 
1517
 
      update_candidat_widget (self, priv->audio_local_candidate_label,
1518
 
          priv->audio_local_candidate_info_img, candidate);
1519
 
    }
1520
 
}
1521
 
 
1522
 
static void
1523
 
empathy_call_window_constructed (GObject *object)
1524
 
{
1525
 
  EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1526
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
1527
 
  TpyCallChannel *call;
1528
 
 
1529
 
  g_assert (priv->handler != NULL);
1530
 
 
1531
 
  g_object_get (priv->handler, "call-channel", &call, NULL);
1532
 
  priv->outgoing = (call == NULL);
1533
 
  if (call != NULL)
1534
 
    g_object_unref (call);
1535
 
 
1536
 
  g_object_get (priv->handler, "target-contact", &priv->contact, NULL);
1537
 
  g_assert (priv->contact != NULL);
1538
 
 
1539
 
  empathy_call_window_setup_avatars (self, priv->handler);
1540
 
  empathy_call_window_set_state_connecting (self);
1541
 
 
1542
 
  if (!empathy_call_handler_has_initial_video (priv->handler))
1543
 
    {
1544
 
      gtk_toggle_tool_button_set_active (
1545
 
          GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_off), TRUE);
1546
 
    }
1547
 
  /* If call has InitialVideo, the preview will be started once the call has
1548
 
   * been started (start_call()). */
1549
 
 
1550
 
  update_send_codec (self, TRUE);
1551
 
  update_send_codec (self, FALSE);
1552
 
  update_recv_codec (self, TRUE);
1553
 
  update_recv_codec (self, FALSE);
1554
 
 
1555
 
  tp_g_signal_connect_object (priv->handler, "notify::send-audio-codec",
1556
 
      G_CALLBACK (send_audio_codec_notify_cb), self, 0);
1557
 
  tp_g_signal_connect_object (priv->handler, "notify::send-video-codec",
1558
 
      G_CALLBACK (send_video_codec_notify_cb), self, 0);
1559
 
  tp_g_signal_connect_object (priv->handler, "notify::recv-audio-codecs",
1560
 
      G_CALLBACK (recv_audio_codecs_notify_cb), self, 0);
1561
 
  tp_g_signal_connect_object (priv->handler, "notify::recv-video-codecs",
1562
 
      G_CALLBACK (recv_video_codecs_notify_cb), self, 0);
1563
 
 
1564
 
  tp_g_signal_connect_object (priv->handler, "candidates-changed",
1565
 
      G_CALLBACK (candidates_changed_cb), self, 0);
1566
 
}
1567
 
 
1568
 
static void empathy_call_window_dispose (GObject *object);
1569
 
static void empathy_call_window_finalize (GObject *object);
1570
 
 
1571
 
static void
1572
 
empathy_call_window_set_property (GObject *object,
1573
 
  guint property_id, const GValue *value, GParamSpec *pspec)
1574
 
{
1575
 
  EmpathyCallWindowPriv *priv = GET_PRIV (object);
1576
 
 
1577
 
  switch (property_id)
1578
 
    {
1579
 
      case PROP_CALL_HANDLER:
1580
 
        priv->handler = g_value_dup_object (value);
1581
 
        break;
1582
 
      default:
1583
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1584
 
    }
1585
 
}
1586
 
 
1587
 
static void
1588
 
empathy_call_window_get_property (GObject *object,
1589
 
  guint property_id, GValue *value, GParamSpec *pspec)
1590
 
{
1591
 
  EmpathyCallWindowPriv *priv = GET_PRIV (object);
1592
 
 
1593
 
  switch (property_id)
1594
 
    {
1595
 
      case PROP_CALL_HANDLER:
1596
 
        g_value_set_object (value, priv->handler);
1597
 
        break;
1598
 
      default:
1599
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1600
 
    }
1601
 
}
1602
 
 
1603
 
static void
1604
 
empathy_call_window_class_init (
1605
 
  EmpathyCallWindowClass *empathy_call_window_class)
1606
 
{
1607
 
  GObjectClass *object_class = G_OBJECT_CLASS (empathy_call_window_class);
1608
 
  GParamSpec *param_spec;
1609
 
 
1610
 
  g_type_class_add_private (empathy_call_window_class,
1611
 
    sizeof (EmpathyCallWindowPriv));
1612
 
 
1613
 
  object_class->constructed = empathy_call_window_constructed;
1614
 
  object_class->set_property = empathy_call_window_set_property;
1615
 
  object_class->get_property = empathy_call_window_get_property;
1616
 
 
1617
 
  object_class->dispose = empathy_call_window_dispose;
1618
 
  object_class->finalize = empathy_call_window_finalize;
1619
 
 
1620
 
  param_spec = g_param_spec_object ("handler",
1621
 
    "handler", "The call handler",
1622
 
    EMPATHY_TYPE_CALL_HANDLER,
1623
 
    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1624
 
  g_object_class_install_property (object_class,
1625
 
    PROP_CALL_HANDLER, param_spec);
1626
 
}
1627
 
 
1628
 
void
1629
 
empathy_call_window_dispose (GObject *object)
1630
 
{
1631
 
  EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1632
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
1633
 
 
1634
 
  if (priv->dispose_has_run)
1635
 
    return;
1636
 
 
1637
 
  priv->dispose_has_run = TRUE;
1638
 
 
1639
 
  if (priv->handler != NULL)
1640
 
    {
1641
 
      empathy_call_handler_stop_call (priv->handler);
1642
 
      tp_clear_object (&priv->handler);
1643
 
    }
1644
 
 
1645
 
  if (priv->bus_message_source_id != 0)
1646
 
    {
1647
 
      g_source_remove (priv->bus_message_source_id);
1648
 
      priv->bus_message_source_id = 0;
1649
 
    }
1650
 
 
1651
 
  tp_clear_object (&priv->pipeline);
1652
 
  tp_clear_object (&priv->video_input);
1653
 
  tp_clear_object (&priv->audio_input);
1654
 
  tp_clear_object (&priv->video_tee);
1655
 
  tp_clear_object (&priv->ui_manager);
1656
 
  tp_clear_object (&priv->fullscreen);
1657
 
 
1658
 
  g_list_free_full (priv->notifiers, g_object_unref);
1659
 
 
1660
 
  if (priv->timer_id != 0)
1661
 
    g_source_remove (priv->timer_id);
1662
 
  priv->timer_id = 0;
1663
 
 
1664
 
  if (priv->contact != NULL)
1665
 
    {
1666
 
      g_signal_handlers_disconnect_by_func (priv->contact,
1667
 
          contact_name_changed_cb, self);
1668
 
      priv->contact = NULL;
1669
 
    }
1670
 
 
1671
 
 
1672
 
  G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
1673
 
}
1674
 
 
1675
 
static void
1676
 
disconnect_video_output_motion_handler (EmpathyCallWindow *self)
1677
 
{
1678
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
1679
 
 
1680
 
  if (priv->video_output_motion_handler_id != 0)
1681
 
    {
1682
 
      g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1683
 
          priv->video_output_motion_handler_id);
1684
 
      priv->video_output_motion_handler_id = 0;
1685
 
    }
1686
 
}
1687
 
 
1688
 
void
1689
 
empathy_call_window_finalize (GObject *object)
1690
 
{
1691
 
  EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1692
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
1693
 
 
1694
 
  disconnect_video_output_motion_handler (self);
1695
 
 
1696
 
  /* free any data held directly by the object here */
1697
 
  g_mutex_free (priv->lock);
1698
 
 
1699
 
  g_timer_destroy (priv->timer);
1700
 
 
1701
 
  G_OBJECT_CLASS (empathy_call_window_parent_class)->finalize (object);
1702
 
}
1703
 
 
1704
 
 
1705
 
EmpathyCallWindow *
1706
 
empathy_call_window_new (EmpathyCallHandler *handler)
1707
 
{
1708
 
  return EMPATHY_CALL_WINDOW (
1709
 
    g_object_new (EMPATHY_TYPE_CALL_WINDOW, "handler", handler, NULL));
1710
 
}
1711
 
 
1712
 
static void
1713
 
empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
1714
 
  GstElement *conference, gpointer user_data)
1715
 
{
1716
 
  EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1717
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
1718
 
  FsElementAddedNotifier *notifier;
1719
 
  GKeyFile *keyfile;
1720
 
 
1721
 
  g_debug ("Conference added");
1722
 
 
1723
 
  /* Add notifier to set the various element properties as needed */
1724
 
  notifier = fs_element_added_notifier_new ();
1725
 
  keyfile = fs_utils_get_default_element_properties (conference);
1726
 
 
1727
 
  if (keyfile != NULL)
1728
 
    fs_element_added_notifier_set_properties_from_keyfile (notifier, keyfile);
1729
 
 
1730
 
  fs_element_added_notifier_add (notifier, GST_BIN (priv->pipeline));
1731
 
 
1732
 
  priv->notifiers = g_list_prepend (priv->notifiers, notifier);
1733
 
 
1734
 
  gst_bin_add (GST_BIN (priv->pipeline), conference);
1735
 
  gst_element_set_state (conference, GST_STATE_PLAYING);
1736
 
}
1737
 
 
1738
 
static void
1739
 
empathy_call_window_conference_removed_cb (EmpathyCallHandler *handler,
1740
 
  GstElement *conference, gpointer user_data)
1741
 
{
1742
 
  EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1743
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
1744
 
 
1745
 
  gst_bin_remove (GST_BIN (priv->pipeline), conference);
1746
 
  gst_element_set_state (conference, GST_STATE_NULL);
1747
 
}
1748
 
 
1749
 
static gboolean
1750
 
empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
1751
 
{
1752
 
  GstStateChangeReturn state_change_return;
1753
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
1754
 
 
1755
 
  if (priv->pipeline == NULL)
1756
 
    return TRUE;
1757
 
 
1758
 
  if (priv->bus_message_source_id != 0)
1759
 
    {
1760
 
      g_source_remove (priv->bus_message_source_id);
1761
 
      priv->bus_message_source_id = 0;
1762
 
    }
1763
 
 
1764
 
  state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1765
 
 
1766
 
  if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1767
 
        state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1768
 
    {
1769
 
      if (priv->pipeline != NULL)
1770
 
        g_object_unref (priv->pipeline);
1771
 
      priv->pipeline = NULL;
1772
 
 
1773
 
      g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
1774
 
          empathy_call_window_mic_volume_changed_cb, self);
1775
 
 
1776
 
      if (priv->video_tee != NULL)
1777
 
        g_object_unref (priv->video_tee);
1778
 
      priv->video_tee = NULL;
1779
 
 
1780
 
      if (priv->video_preview != NULL)
1781
 
        gtk_widget_destroy (priv->video_preview);
1782
 
      priv->video_preview = NULL;
1783
 
 
1784
 
      priv->funnel = NULL;
1785
 
 
1786
 
      create_pipeline (self);
1787
 
      /* Call will be started when user will hit the 'redial' button */
1788
 
      priv->start_call_when_playing = FALSE;
1789
 
      gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1790
 
 
1791
 
      return TRUE;
1792
 
    }
1793
 
  else
1794
 
    {
1795
 
      g_message ("Error: could not destroy pipeline. Closing call window");
1796
 
      gtk_widget_destroy (GTK_WIDGET (self));
1797
 
 
1798
 
      return FALSE;
1799
 
    }
1800
 
}
1801
 
 
1802
 
static void
1803
 
reset_details_pane (EmpathyCallWindow *self)
1804
 
{
1805
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
1806
 
 
1807
 
  gtk_label_set_text (GTK_LABEL (priv->vcodec_encoding_label), _("Unknown"));
1808
 
  gtk_label_set_text (GTK_LABEL (priv->acodec_encoding_label), _("Unknown"));
1809
 
  gtk_label_set_text (GTK_LABEL (priv->vcodec_decoding_label), _("Unknown"));
1810
 
  gtk_label_set_text (GTK_LABEL (priv->acodec_decoding_label), _("Unknown"));
1811
 
}
1812
 
 
1813
 
static gboolean
1814
 
empathy_call_window_disconnected (EmpathyCallWindow *self,
1815
 
    gboolean restart)
1816
 
{
1817
 
  gboolean could_disconnect = FALSE;
1818
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
1819
 
  gboolean could_reset_pipeline;
1820
 
 
1821
 
  /* Leave full screen mode if needed */
1822
 
  gtk_window_unfullscreen (GTK_WINDOW (self));
1823
 
 
1824
 
  gtk_action_set_sensitive (priv->menu_fullscreen, FALSE);
1825
 
 
1826
 
  could_reset_pipeline = empathy_call_window_reset_pipeline (self);
1827
 
 
1828
 
  if (priv->call_state == CONNECTING)
1829
 
      empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1830
 
 
1831
 
  if (priv->call_state != REDIALING)
1832
 
    priv->call_state = DISCONNECTED;
1833
 
 
1834
 
  if (could_reset_pipeline)
1835
 
    {
1836
 
      g_mutex_lock (priv->lock);
1837
 
 
1838
 
      g_timer_stop (priv->timer);
1839
 
 
1840
 
      if (priv->timer_id != 0)
1841
 
        g_source_remove (priv->timer_id);
1842
 
      priv->timer_id = 0;
1843
 
 
1844
 
      g_mutex_unlock (priv->lock);
1845
 
 
1846
 
      if (!restart)
1847
 
        /* We are about to destroy the window, no need to update it or create
1848
 
         * a video preview */
1849
 
        return TRUE;
1850
 
 
1851
 
      empathy_call_window_status_message (self, _("Disconnected"));
1852
 
 
1853
 
      gtk_action_set_sensitive (priv->redial, TRUE);
1854
 
      gtk_widget_set_sensitive (priv->redial_button, TRUE);
1855
 
 
1856
 
      /* Unsensitive the camera and mic button */
1857
 
      gtk_widget_set_sensitive (priv->tool_button_camera_on, FALSE);
1858
 
      gtk_action_set_sensitive (priv->action_camera_on, FALSE);
1859
 
      gtk_widget_set_sensitive (priv->mic_button, FALSE);
1860
 
 
1861
 
      /* Be sure that the mic button is enabled */
1862
 
      gtk_toggle_tool_button_set_active (
1863
 
          GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
1864
 
 
1865
 
      if (priv->camera_state == CAMERA_STATE_ON)
1866
 
        {
1867
 
          /* Enable the 'preview' button as we are not sending atm. */
1868
 
          gtk_toggle_tool_button_set_active (
1869
 
              GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_preview), TRUE);
1870
 
        }
1871
 
      else if (priv->camera_state == CAMERA_STATE_PREVIEW)
1872
 
        {
1873
 
          /* Restart the preview with the new pipeline. */
1874
 
          display_video_preview (self, TRUE);
1875
 
        }
1876
 
 
1877
 
      gtk_progress_bar_set_fraction (
1878
 
          GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
1879
 
 
1880
 
      /* destroy the video output; it will be recreated when we'll redial */
1881
 
      disconnect_video_output_motion_handler (self);
1882
 
      if (priv->video_output != NULL)
1883
 
        gtk_widget_destroy (priv->video_output);
1884
 
      priv->video_output = NULL;
1885
 
 
1886
 
      gtk_widget_show (priv->remote_user_avatar_widget);
1887
 
 
1888
 
      reset_details_pane (self);
1889
 
 
1890
 
      priv->sending_video = FALSE;
1891
 
      priv->call_started = FALSE;
1892
 
 
1893
 
      could_disconnect = TRUE;
1894
 
 
1895
 
      /* TODO: display the self avatar of the preview (depends if the "Always
1896
 
       * Show Video Preview" is enabled or not) */
1897
 
    }
1898
 
 
1899
 
  return could_disconnect;
1900
 
}
1901
 
 
1902
 
 
1903
 
static void
1904
 
empathy_call_window_channel_closed_cb (EmpathyCallHandler *handler,
1905
 
    gpointer user_data)
1906
 
{
1907
 
  EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1908
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
1909
 
 
1910
 
  if (empathy_call_window_disconnected (self, TRUE) &&
1911
 
      priv->call_state == REDIALING)
1912
 
      empathy_call_window_restart_call (self);
1913
 
}
1914
 
 
1915
 
static gboolean
1916
 
empathy_call_window_sink_removed_cb (EmpathyCallHandler *handler,
1917
 
    GstPad *sink,
1918
 
    FsMediaType media_type,
1919
 
    EmpathyCallWindow *self)
1920
 
{
1921
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
1922
 
 
1923
 
  g_print ("window: removing content\n");
1924
 
 
1925
 
  /*
1926
 
   * This assumes that there is only one video stream per channel...
1927
 
   */
1928
 
 
1929
 
  if (media_type == FS_MEDIA_TYPE_VIDEO)
1930
 
    {
1931
 
      if (priv->funnel != NULL)
1932
 
        {
1933
 
          GstElement *output;
1934
 
 
1935
 
          output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1936
 
              (priv->video_output));
1937
 
 
1938
 
          gst_element_set_state (output, GST_STATE_NULL);
1939
 
          gst_element_set_state (priv->funnel, GST_STATE_NULL);
1940
 
 
1941
 
          gst_bin_remove (GST_BIN (priv->pipeline), output);
1942
 
          gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
1943
 
          priv->funnel = NULL;
1944
 
          return TRUE;
1945
 
        }
1946
 
    }
1947
 
  else if (media_type == FS_MEDIA_TYPE_AUDIO)
1948
 
    {
1949
 
      if (priv->audio_output != NULL)
1950
 
        {
1951
 
          gst_element_set_state (priv->audio_output, GST_STATE_NULL);
1952
 
 
1953
 
          gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
1954
 
          priv->audio_output = NULL;
1955
 
          return TRUE;
1956
 
        }
1957
 
    }
1958
 
 
1959
 
  return FALSE;
1960
 
}
1961
 
 
1962
 
/* Called with global lock held */
1963
 
static GstPad *
1964
 
empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
1965
 
{
1966
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
1967
 
  GstPad *pad;
1968
 
  GstElement *output;
1969
 
 
1970
 
  if (priv->funnel == NULL)
1971
 
    {
1972
 
      output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1973
 
        (priv->video_output));
1974
 
 
1975
 
      priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
1976
 
 
1977
 
      if (!priv->funnel)
1978
 
        {
1979
 
          g_warning ("Could not create fsfunnel");
1980
 
          return NULL;
1981
 
        }
1982
 
 
1983
 
      if (!gst_bin_add (GST_BIN (priv->pipeline), priv->funnel))
1984
 
        {
1985
 
          gst_object_unref (priv->funnel);
1986
 
          priv->funnel = NULL;
1987
 
          g_warning ("Could  not add funnel to pipeline");
1988
 
          return NULL;
1989
 
        }
1990
 
 
1991
 
      if (!gst_bin_add (GST_BIN (priv->pipeline), output))
1992
 
        {
1993
 
          g_warning ("Could not add the video output widget to the pipeline");
1994
 
          goto error;
1995
 
        }
1996
 
 
1997
 
      if (!gst_element_link (priv->funnel, output))
1998
 
        {
1999
 
          g_warning ("Could not link output sink to funnel");
2000
 
          goto error_output_added;
2001
 
        }
2002
 
 
2003
 
      if (gst_element_set_state (output, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2004
 
        {
2005
 
          g_warning ("Could not start video sink");
2006
 
          goto error_output_added;
2007
 
        }
2008
 
 
2009
 
      if (gst_element_set_state (priv->funnel, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2010
 
        {
2011
 
          g_warning ("Could not start funnel");
2012
 
          goto error_output_added;
2013
 
        }
2014
 
    }
2015
 
 
2016
 
  pad = gst_element_get_request_pad (priv->funnel, "sink%d");
2017
 
 
2018
 
  if (!pad)
2019
 
    g_warning ("Could not get request pad from funnel");
2020
 
 
2021
 
  return pad;
2022
 
 
2023
 
 
2024
 
 error_output_added:
2025
 
 
2026
 
  gst_element_set_locked_state (priv->funnel, TRUE);
2027
 
  gst_element_set_locked_state (output, TRUE);
2028
 
 
2029
 
  gst_element_set_state (priv->funnel, GST_STATE_NULL);
2030
 
  gst_element_set_state (output, GST_STATE_NULL);
2031
 
 
2032
 
  gst_bin_remove (GST_BIN (priv->pipeline), output);
2033
 
  gst_element_set_locked_state (output, FALSE);
2034
 
 
2035
 
 error:
2036
 
 
2037
 
  gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
2038
 
  priv->funnel = NULL;
2039
 
 
2040
 
  return NULL;
2041
 
}
2042
 
 
2043
 
/* Called with global lock held */
2044
 
static GstPad *
2045
 
empathy_call_window_get_audio_sink_pad (EmpathyCallWindow *self)
2046
 
{
2047
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
2048
 
  GstPad *pad;
2049
 
  GstPadTemplate *template;
2050
 
 
2051
 
  if (priv->audio_output == NULL)
2052
 
    {
2053
 
      priv->audio_output = empathy_audio_sink_new ();
2054
 
 
2055
 
      if (!gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output))
2056
 
        {
2057
 
          g_warning ("Could not add audio sink to pipeline");
2058
 
          g_object_unref (priv->audio_output);
2059
 
          goto error_add_output;
2060
 
        }
2061
 
 
2062
 
      if (gst_element_set_state (priv->audio_output, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2063
 
        {
2064
 
          g_warning ("Could not start audio sink");
2065
 
          goto error;
2066
 
        }
2067
 
    }
2068
 
 
2069
 
  template = gst_element_class_get_pad_template (
2070
 
    GST_ELEMENT_GET_CLASS (priv->audio_output), "sink%d");
2071
 
 
2072
 
  pad = gst_element_request_pad (priv->audio_output,
2073
 
    template, NULL, NULL);
2074
 
 
2075
 
  if (pad == NULL)
2076
 
    {
2077
 
      g_warning ("Could not get sink pad from sink");
2078
 
      return NULL;
2079
 
    }
2080
 
 
2081
 
  return pad;
2082
 
 
2083
 
error:
2084
 
  gst_element_set_locked_state (priv->audio_output, TRUE);
2085
 
  gst_element_set_state (priv->audio_output, GST_STATE_NULL);
2086
 
  gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
2087
 
  priv->audio_output = NULL;
2088
 
 
2089
 
error_add_output:
2090
 
 
2091
 
  return NULL;
2092
 
}
2093
 
 
2094
 
static gboolean
2095
 
empathy_call_window_update_timer (gpointer user_data)
2096
 
{
2097
 
  EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
2098
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
2099
 
  gchar *str;
2100
 
  gdouble time_;
2101
 
 
2102
 
  time_ = g_timer_elapsed (priv->timer, NULL);
2103
 
 
2104
 
  /* Translators: number of minutes:seconds the caller has been connected */
2105
 
  str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time_ / 60,
2106
 
    (int) time_ % 60);
2107
 
  empathy_call_window_status_message (self, str);
2108
 
  g_free (str);
2109
 
 
2110
 
  return TRUE;
2111
 
}
2112
 
 
2113
 
#if 0
2114
 
static void
2115
 
display_error (EmpathyCallWindow *self,
2116
 
    TpyCallChannel *call,
2117
 
    const gchar *img,
2118
 
    const gchar *title,
2119
 
    const gchar *desc,
2120
 
    const gchar *details)
2121
 
{
2122
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
2123
 
  GtkWidget *info_bar;
2124
 
  GtkWidget *content_area;
2125
 
  GtkWidget *hbox;
2126
 
  GtkWidget *vbox;
2127
 
  GtkWidget *image;
2128
 
  GtkWidget *label;
2129
 
  gchar *txt;
2130
 
 
2131
 
  /* Create info bar */
2132
 
  info_bar = gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
2133
 
      NULL);
2134
 
 
2135
 
  gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
2136
 
 
2137
 
  content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
2138
 
 
2139
 
  /* hbox containing the image and the messages vbox */
2140
 
  hbox = gtk_hbox_new (FALSE, 3);
2141
 
  gtk_container_add (GTK_CONTAINER (content_area), hbox);
2142
 
 
2143
 
  /* Add image */
2144
 
  image = gtk_image_new_from_icon_name (img, GTK_ICON_SIZE_DIALOG);
2145
 
  gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
2146
 
 
2147
 
  /* vbox containing the main message and the details expander */
2148
 
  vbox = gtk_vbox_new (FALSE, 3);
2149
 
  gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
2150
 
 
2151
 
  /* Add text */
2152
 
  txt = g_strdup_printf ("<b>%s</b>\n%s", title, desc);
2153
 
 
2154
 
  label = gtk_label_new (NULL);
2155
 
  gtk_label_set_markup (GTK_LABEL (label), txt);
2156
 
  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
2157
 
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
2158
 
  g_free (txt);
2159
 
 
2160
 
  gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
2161
 
 
2162
 
  /* Add details */
2163
 
  if (details != NULL)
2164
 
    {
2165
 
      GtkWidget *expander;
2166
 
 
2167
 
      expander = gtk_expander_new (_("Technical Details"));
2168
 
 
2169
 
      txt = g_strdup_printf ("<i>%s</i>", details);
2170
 
 
2171
 
      label = gtk_label_new (NULL);
2172
 
      gtk_label_set_markup (GTK_LABEL (label), txt);
2173
 
      gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
2174
 
      gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
2175
 
      g_free (txt);
2176
 
 
2177
 
      gtk_container_add (GTK_CONTAINER (expander), label);
2178
 
      gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
2179
 
    }
2180
 
 
2181
 
  g_signal_connect (info_bar, "response",
2182
 
      G_CALLBACK (gtk_widget_destroy), NULL);
2183
 
 
2184
 
  gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar,
2185
 
      FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
2186
 
  gtk_widget_show_all (info_bar);
2187
 
}
2188
 
 
2189
 
static gchar *
2190
 
media_stream_error_to_txt (EmpathyCallWindow *self,
2191
 
    TpyCallChannel *call,
2192
 
    gboolean audio,
2193
 
    TpMediaStreamError error)
2194
 
{
2195
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
2196
 
  const gchar *cm = NULL;
2197
 
  gchar *url;
2198
 
  gchar *result;
2199
 
 
2200
 
  switch (error)
2201
 
    {
2202
 
      case TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED:
2203
 
        if (audio)
2204
 
          return g_strdup_printf (
2205
 
              _("%s's software does not understand any of the audio formats "
2206
 
                "supported by your computer"),
2207
 
            empathy_contact_get_alias (priv->contact));
2208
 
        else
2209
 
          return g_strdup_printf (
2210
 
              _("%s's software does not understand any of the video formats "
2211
 
                "supported by your computer"),
2212
 
            empathy_contact_get_alias (priv->contact));
2213
 
 
2214
 
      case TP_MEDIA_STREAM_ERROR_CONNECTION_FAILED:
2215
 
        return g_strdup_printf (
2216
 
            _("Can't establish a connection to %s. "
2217
 
              "One of you might be on a network that does not allow "
2218
 
              "direct connections."),
2219
 
          empathy_contact_get_alias (priv->contact));
2220
 
 
2221
 
      case TP_MEDIA_STREAM_ERROR_NETWORK_ERROR:
2222
 
          return g_strdup (_("There was a failure on the network"));
2223
 
 
2224
 
      case TP_MEDIA_STREAM_ERROR_NO_CODECS:
2225
 
        if (audio)
2226
 
          return g_strdup (_("The audio formats necessary for this call "
2227
 
                "are not installed on your computer"));
2228
 
        else
2229
 
          return g_strdup (_("The video formats necessary for this call "
2230
 
                "are not installed on your computer"));
2231
 
 
2232
 
      case TP_MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR:
2233
 
        tp_connection_parse_object_path (
2234
 
            tp_channel_borrow_connection (TP_CHANNEL (call)),
2235
 
            NULL, &cm);
2236
 
 
2237
 
        url = g_strdup_printf ("http://bugs.freedesktop.org/enter_bug.cgi?"
2238
 
            "product=Telepathy&amp;component=%s", cm);
2239
 
 
2240
 
        result = g_strdup_printf (
2241
 
            _("Something unexpected happened in a Telepathy component. "
2242
 
              "Please <a href=\"%s\">report this bug</a> and attach "
2243
 
              "logs gathered from the 'Debug' window in the Help menu."), url);
2244
 
 
2245
 
        g_free (url);
2246
 
        g_free (cm);
2247
 
        return result;
2248
 
 
2249
 
      case TP_MEDIA_STREAM_ERROR_MEDIA_ERROR:
2250
 
        return g_strdup (_("There was a failure in the call engine"));
2251
 
 
2252
 
      case TP_MEDIA_STREAM_ERROR_EOS:
2253
 
        return g_strdup (_("The end of the stream was reached"));
2254
 
 
2255
 
      case TP_MEDIA_STREAM_ERROR_UNKNOWN:
2256
 
      default:
2257
 
        return NULL;
2258
 
    }
2259
 
}
2260
 
 
2261
 
static void
2262
 
empathy_call_window_stream_error (EmpathyCallWindow *self,
2263
 
    TpyCallChannel *call,
2264
 
    gboolean audio,
2265
 
    guint code,
2266
 
    const gchar *msg,
2267
 
    const gchar *icon,
2268
 
    const gchar *title)
2269
 
{
2270
 
  gchar *desc;
2271
 
 
2272
 
  desc = media_stream_error_to_txt (self, call, audio, code);
2273
 
  if (desc == NULL)
2274
 
    {
2275
 
      /* No description, use the error message. That's not great as it's not
2276
 
       * localized but it's better than nothing. */
2277
 
      display_error (self, call, icon, title, msg, NULL);
2278
 
    }
2279
 
  else
2280
 
    {
2281
 
      display_error (self, call, icon, title, desc, msg);
2282
 
      g_free (desc);
2283
 
    }
2284
 
}
2285
 
 
2286
 
static void
2287
 
empathy_call_window_audio_stream_error (TpyCallChannel *call,
2288
 
    guint code,
2289
 
    const gchar *msg,
2290
 
    EmpathyCallWindow *self)
2291
 
{
2292
 
  empathy_call_window_stream_error (self, call, TRUE, code, msg,
2293
 
      "gnome-stock-mic", _("Can't establish audio stream"));
2294
 
}
2295
 
 
2296
 
static void
2297
 
empathy_call_window_video_stream_error (TpyCallChannel *call,
2298
 
    guint code,
2299
 
    const gchar *msg,
2300
 
    EmpathyCallWindow *self)
2301
 
{
2302
 
  empathy_call_window_stream_error (self, call, FALSE, code, msg,
2303
 
      "camera-web", _("Can't establish video stream"));
2304
 
}
2305
 
#endif
2306
 
 
2307
 
static void
2308
 
empathy_call_window_state_changed_cb (EmpathyCallHandler *handler,
2309
 
    TpyCallState state,
2310
 
    EmpathyCallWindow *self)
2311
 
{
2312
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
2313
 
  TpyCallChannel *call;
2314
 
  gboolean can_send_video;
2315
 
 
2316
 
  if (state != TPY_CALL_STATE_ACCEPTED)
2317
 
    return;
2318
 
 
2319
 
  if (priv->call_state == CONNECTED)
2320
 
    return;
2321
 
 
2322
 
  g_timer_start (priv->timer);
2323
 
  priv->call_state = CONNECTED;
2324
 
 
2325
 
  empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
2326
 
 
2327
 
  can_send_video = priv->video_input != NULL &&
2328
 
    empathy_contact_can_voip_video (priv->contact);
2329
 
 
2330
 
  g_object_get (priv->handler, "call-channel", &call, NULL);
2331
 
 
2332
 
  if (tpy_call_channel_has_dtmf (call))
2333
 
    gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
2334
 
 
2335
 
  if (priv->video_input == NULL)
2336
 
    empathy_call_window_set_send_video (self, CAMERA_STATE_OFF);
2337
 
 
2338
 
  gtk_widget_set_sensitive (priv->tool_button_camera_on, can_send_video);
2339
 
  gtk_action_set_sensitive (priv->action_camera_on, can_send_video);
2340
 
 
2341
 
  gtk_action_set_sensitive (priv->redial, FALSE);
2342
 
  gtk_widget_set_sensitive (priv->redial_button, FALSE);
2343
 
 
2344
 
  gtk_widget_set_sensitive (priv->mic_button, TRUE);
2345
 
 
2346
 
  gtk_widget_hide (priv->video_output);
2347
 
  gtk_widget_show (priv->remote_user_avatar_widget);
2348
 
 
2349
 
  g_object_unref (call);
2350
 
 
2351
 
  g_mutex_lock (priv->lock);
2352
 
 
2353
 
  priv->timer_id = g_timeout_add_seconds (1,
2354
 
    empathy_call_window_update_timer, self);
2355
 
 
2356
 
  g_mutex_unlock (priv->lock);
2357
 
 
2358
 
  empathy_call_window_update_timer (self);
2359
 
 
2360
 
  gtk_action_set_sensitive (priv->menu_fullscreen, TRUE);
2361
 
}
2362
 
 
2363
 
static gboolean
2364
 
emapthy_call_window_show_video_output_cb (gpointer user_data)
2365
 
{
2366
 
  EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
2367
 
 
2368
 
  gtk_widget_hide (self->priv->remote_user_avatar_widget);
2369
 
  gtk_widget_show (self->priv->video_output);
2370
 
 
2371
 
  return FALSE;
2372
 
}
2373
 
 
2374
 
/* Called from the streaming thread */
2375
 
static gboolean
2376
 
empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
2377
 
  GstPad *src, guint media_type, gpointer user_data)
2378
 
{
2379
 
  EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
2380
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
2381
 
  gboolean retval = FALSE;
2382
 
 
2383
 
  GstPad *pad;
2384
 
 
2385
 
  g_mutex_lock (priv->lock);
2386
 
 
2387
 
  switch (media_type)
2388
 
    {
2389
 
      case TP_MEDIA_STREAM_TYPE_AUDIO:
2390
 
        pad = empathy_call_window_get_audio_sink_pad (self);
2391
 
        break;
2392
 
      case TP_MEDIA_STREAM_TYPE_VIDEO:
2393
 
        g_idle_add (emapthy_call_window_show_video_output_cb, self);
2394
 
        pad = empathy_call_window_get_video_sink_pad (self);
2395
 
        break;
2396
 
      default:
2397
 
        g_assert_not_reached ();
2398
 
    }
2399
 
 
2400
 
  if (pad == NULL)
2401
 
    goto out;
2402
 
 
2403
 
  if (GST_PAD_LINK_FAILED (gst_pad_link (src, pad)))
2404
 
      g_warning ("Could not link %s sink pad",
2405
 
          media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? "audio" : "video");
2406
 
  else
2407
 
      retval = TRUE;
2408
 
 
2409
 
  gst_object_unref (pad);
2410
 
 
2411
 
 out:
2412
 
 
2413
 
  /* If no sink could be linked, try to add fakesink to prevent the whole call
2414
 
   * aborting */
2415
 
 
2416
 
  if (!retval)
2417
 
    {
2418
 
      GstElement *fakesink = gst_element_factory_make ("fakesink", NULL);
2419
 
 
2420
 
      if (gst_bin_add (GST_BIN (priv->pipeline), fakesink))
2421
 
        {
2422
 
          GstPad *sinkpad = gst_element_get_static_pad (fakesink, "sink");
2423
 
          if (gst_element_set_state (fakesink, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE ||
2424
 
              GST_PAD_LINK_FAILED (gst_pad_link (src, sinkpad)))
2425
 
            {
2426
 
              gst_element_set_locked_state (fakesink, TRUE);
2427
 
              gst_element_set_state (fakesink, GST_STATE_NULL);
2428
 
              gst_bin_remove (GST_BIN (priv->pipeline), fakesink);
2429
 
            }
2430
 
          else
2431
 
            {
2432
 
              g_debug ("Could not link real sink, linked fakesink instead");
2433
 
            }
2434
 
          gst_object_unref (sinkpad);
2435
 
        }
2436
 
      else
2437
 
        {
2438
 
          gst_object_unref (fakesink);
2439
 
        }
2440
 
    }
2441
 
 
2442
 
 
2443
 
  g_mutex_unlock (priv->lock);
2444
 
 
2445
 
  return TRUE;
2446
 
}
2447
 
 
2448
 
static gboolean
2449
 
empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
2450
 
  GstPad *sink, FsMediaType media_type, gpointer user_data)
2451
 
{
2452
 
  EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
2453
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
2454
 
  GstPad *pad;
2455
 
  gboolean retval = FALSE;
2456
 
 
2457
 
  switch (media_type)
2458
 
    {
2459
 
      case FS_MEDIA_TYPE_AUDIO:
2460
 
        if (!gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input))
2461
 
          {
2462
 
            g_warning ("Could not add audio source to pipeline");
2463
 
            break;
2464
 
          }
2465
 
 
2466
 
        pad = gst_element_get_static_pad (priv->audio_input, "src");
2467
 
        if (!pad)
2468
 
          {
2469
 
            gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
2470
 
            g_warning ("Could not get source pad from audio source");
2471
 
            break;
2472
 
          }
2473
 
 
2474
 
        if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sink)))
2475
 
          {
2476
 
            gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
2477
 
            g_warning ("Could not link audio source to farsight");
2478
 
            break;
2479
 
          }
2480
 
 
2481
 
        if (gst_element_set_state (priv->audio_input, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2482
 
          {
2483
 
            g_warning ("Could not start audio source");
2484
 
            gst_element_set_state (priv->audio_input, GST_STATE_NULL);
2485
 
            gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
2486
 
            break;
2487
 
          }
2488
 
 
2489
 
        retval = TRUE;
2490
 
        break;
2491
 
      case FS_MEDIA_TYPE_VIDEO:
2492
 
        if (priv->video_input != NULL)
2493
 
          {
2494
 
            if (priv->video_tee != NULL)
2495
 
              {
2496
 
                pad = gst_element_get_request_pad (priv->video_tee, "src%d");
2497
 
                if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sink)))
2498
 
                  {
2499
 
                    g_warning ("Could not link video source input pipeline");
2500
 
                    break;
2501
 
                  }
2502
 
                gst_object_unref (pad);
2503
 
              }
2504
 
 
2505
 
            retval = TRUE;
2506
 
          }
2507
 
        break;
2508
 
      default:
2509
 
        g_assert_not_reached ();
2510
 
    }
2511
 
 
2512
 
  return retval;
2513
 
}
2514
 
 
2515
 
static void
2516
 
empathy_call_window_remove_video_input (EmpathyCallWindow *self)
2517
 
{
2518
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
2519
 
  GstElement *preview;
2520
 
 
2521
 
  disable_camera (self);
2522
 
 
2523
 
  DEBUG ("remove video input");
2524
 
  preview = empathy_video_widget_get_element (
2525
 
    EMPATHY_VIDEO_WIDGET (priv->video_preview));
2526
 
 
2527
 
  gst_element_set_state (priv->video_input, GST_STATE_NULL);
2528
 
  gst_element_set_state (priv->video_tee, GST_STATE_NULL);
2529
 
  gst_element_set_state (preview, GST_STATE_NULL);
2530
 
 
2531
 
  gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
2532
 
    priv->video_tee, preview, NULL);
2533
 
 
2534
 
  g_object_unref (priv->video_input);
2535
 
  priv->video_input = NULL;
2536
 
  g_object_unref (priv->video_tee);
2537
 
  priv->video_tee = NULL;
2538
 
  gtk_widget_destroy (priv->video_preview);
2539
 
  priv->video_preview = NULL;
2540
 
 
2541
 
  gtk_widget_set_sensitive (priv->tool_button_camera_on, FALSE);
2542
 
  gtk_action_set_sensitive (priv->action_camera_on, FALSE);
2543
 
  gtk_widget_set_sensitive (priv->tool_button_camera_preview, FALSE);
2544
 
}
2545
 
 
2546
 
static void
2547
 
start_call (EmpathyCallWindow *self)
2548
 
{
2549
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
2550
 
 
2551
 
  priv->call_started = TRUE;
2552
 
  empathy_call_handler_start_call (priv->handler,
2553
 
      gtk_get_current_event_time ());
2554
 
 
2555
 
  if (empathy_call_handler_has_initial_video (priv->handler))
2556
 
    {
2557
 
      TpyCallChannel *call;
2558
 
      TpySendingState s;
2559
 
 
2560
 
      g_object_get (priv->handler, "call-channel", &call, NULL);
2561
 
      s = tpy_call_channel_get_video_state (call);
2562
 
 
2563
 
      if (s == TPY_SENDING_STATE_PENDING_SEND ||
2564
 
          s == TPY_SENDING_STATE_SENDING)
2565
 
        {
2566
 
          /* Enable 'send video' buttons and display the preview */
2567
 
          gtk_toggle_tool_button_set_active (
2568
 
            GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_on),
2569
 
            TRUE);
2570
 
        }
2571
 
      else
2572
 
        {
2573
 
          gtk_toggle_tool_button_set_active (
2574
 
            GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_off),
2575
 
            TRUE);
2576
 
 
2577
 
          if (priv->video_preview == NULL)
2578
 
            {
2579
 
              create_video_preview (self);
2580
 
              add_video_preview_to_pipeline (self);
2581
 
            }
2582
 
        }
2583
 
 
2584
 
      g_object_unref (call);
2585
 
    }
2586
 
}
2587
 
 
2588
 
static gboolean
2589
 
empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
2590
 
  gpointer user_data)
2591
 
{
2592
 
  EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
2593
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
2594
 
  GstState newstate;
2595
 
 
2596
 
  empathy_call_handler_bus_message (priv->handler, bus, message);
2597
 
 
2598
 
  switch (GST_MESSAGE_TYPE (message))
2599
 
    {
2600
 
      case GST_MESSAGE_STATE_CHANGED:
2601
 
        if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
2602
 
          {
2603
 
            gst_message_parse_state_changed (message, NULL, &newstate, NULL);
2604
 
            if (newstate == GST_STATE_PAUSED)
2605
 
                empathy_call_window_setup_video_input (self);
2606
 
          }
2607
 
        if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
2608
 
            !priv->call_started)
2609
 
          {
2610
 
            gst_message_parse_state_changed (message, NULL, &newstate, NULL);
2611
 
            if (newstate == GST_STATE_PAUSED)
2612
 
              {
2613
 
                gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2614
 
                priv->pipeline_playing = TRUE;
2615
 
 
2616
 
                if (priv->start_call_when_playing)
2617
 
                  start_call (self);
2618
 
              }
2619
 
          }
2620
 
        break;
2621
 
      case GST_MESSAGE_ERROR:
2622
 
        {
2623
 
          GError *error = NULL;
2624
 
          GstElement *gst_error;
2625
 
          gchar *debug;
2626
 
 
2627
 
          gst_message_parse_error (message, &error, &debug);
2628
 
          gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
2629
 
 
2630
 
          g_message ("Element error: %s -- %s\n", error->message, debug);
2631
 
 
2632
 
          if (g_str_has_prefix (gst_element_get_name (gst_error),
2633
 
                VIDEO_INPUT_ERROR_PREFIX))
2634
 
            {
2635
 
              /* Remove the video input and continue */
2636
 
              if (priv->video_input != NULL)
2637
 
                empathy_call_window_remove_video_input (self);
2638
 
              gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2639
 
            }
2640
 
          else
2641
 
            {
2642
 
              empathy_call_window_disconnected (self, TRUE);
2643
 
            }
2644
 
          g_error_free (error);
2645
 
          g_free (debug);
2646
 
        }
2647
 
      case GST_MESSAGE_UNKNOWN:
2648
 
      case GST_MESSAGE_EOS:
2649
 
      case GST_MESSAGE_WARNING:
2650
 
      case GST_MESSAGE_INFO:
2651
 
      case GST_MESSAGE_TAG:
2652
 
      case GST_MESSAGE_BUFFERING:
2653
 
      case GST_MESSAGE_STATE_DIRTY:
2654
 
      case GST_MESSAGE_STEP_DONE:
2655
 
      case GST_MESSAGE_CLOCK_PROVIDE:
2656
 
      case GST_MESSAGE_CLOCK_LOST:
2657
 
      case GST_MESSAGE_NEW_CLOCK:
2658
 
      case GST_MESSAGE_STRUCTURE_CHANGE:
2659
 
      case GST_MESSAGE_STREAM_STATUS:
2660
 
      case GST_MESSAGE_APPLICATION:
2661
 
      case GST_MESSAGE_ELEMENT:
2662
 
      case GST_MESSAGE_SEGMENT_START:
2663
 
      case GST_MESSAGE_SEGMENT_DONE:
2664
 
      case GST_MESSAGE_DURATION:
2665
 
      case GST_MESSAGE_ANY:
2666
 
      default:
2667
 
        break;
2668
 
    }
2669
 
 
2670
 
  return TRUE;
2671
 
}
2672
 
 
2673
 
static void
2674
 
call_handler_notify_call_cb (EmpathyCallHandler *handler,
2675
 
    GParamSpec *spec,
2676
 
    EmpathyCallWindow *self)
2677
 
{
2678
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
2679
 
  TpyCallChannel *call;
2680
 
 
2681
 
  g_object_get (priv->handler, "call-channel", &call, NULL);
2682
 
  if (call == NULL)
2683
 
    return;
2684
 
 
2685
 
/* FIXME
2686
 
  tp_g_signal_connect_object (call, "audio-stream-error",
2687
 
      G_CALLBACK (empathy_call_window_audio_stream_error), self, 0);
2688
 
  tp_g_signal_connect_object (call, "video-stream-error",
2689
 
      G_CALLBACK (empathy_call_window_video_stream_error), self, 0);
2690
 
*/
2691
 
  g_object_unref (call);
2692
 
}
2693
 
 
2694
 
static void
2695
 
empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
2696
 
{
2697
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
2698
 
  TpyCallChannel *call;
2699
 
 
2700
 
  g_signal_connect (priv->handler, "state-changed",
2701
 
    G_CALLBACK (empathy_call_window_state_changed_cb), window);
2702
 
  g_signal_connect (priv->handler, "conference-added",
2703
 
    G_CALLBACK (empathy_call_window_conference_added_cb), window);
2704
 
  g_signal_connect (priv->handler, "conference-removed",
2705
 
    G_CALLBACK (empathy_call_window_conference_removed_cb), window);
2706
 
  g_signal_connect (priv->handler, "closed",
2707
 
    G_CALLBACK (empathy_call_window_channel_closed_cb), window);
2708
 
  g_signal_connect (priv->handler, "src-pad-added",
2709
 
    G_CALLBACK (empathy_call_window_src_added_cb), window);
2710
 
  g_signal_connect (priv->handler, "sink-pad-added",
2711
 
    G_CALLBACK (empathy_call_window_sink_added_cb), window);
2712
 
  g_signal_connect (priv->handler, "sink-pad-removed",
2713
 
    G_CALLBACK (empathy_call_window_sink_removed_cb), window);
2714
 
 
2715
 
  g_object_get (priv->handler, "call-channel", &call, NULL);
2716
 
  if (call != NULL)
2717
 
    {
2718
 
/* FIXME: part of the improvements for DRAFT2
2719
 
      tp_g_signal_connect_object (call, "audio-stream-error",
2720
 
        G_CALLBACK (empathy_call_window_audio_stream_error), window,
2721
 
        0);
2722
 
      tp_g_signal_connect_object (call, "video-stream-error",
2723
 
        G_CALLBACK (empathy_call_window_video_stream_error), window,
2724
 
        0);
2725
 
*/
2726
 
      g_object_unref (call);
2727
 
    }
2728
 
  else
2729
 
    {
2730
 
      /* call-channel doesn't exist yet, we'll connect signals once it has been
2731
 
       * set */
2732
 
      g_signal_connect (priv->handler, "notify::call-channel",
2733
 
        G_CALLBACK (call_handler_notify_call_cb), window);
2734
 
    }
2735
 
 
2736
 
  gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
2737
 
}
2738
 
 
2739
 
static gboolean
2740
 
empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
2741
 
  EmpathyCallWindow *window)
2742
 
{
2743
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
2744
 
 
2745
 
  if (priv->pipeline != NULL)
2746
 
    {
2747
 
      if (priv->bus_message_source_id != 0)
2748
 
        {
2749
 
          g_source_remove (priv->bus_message_source_id);
2750
 
          priv->bus_message_source_id = 0;
2751
 
        }
2752
 
 
2753
 
      gst_element_set_state (priv->pipeline, GST_STATE_NULL);
2754
 
    }
2755
 
 
2756
 
  if (priv->call_state == CONNECTING)
2757
 
    empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
2758
 
 
2759
 
  return FALSE;
2760
 
}
2761
 
 
2762
 
static void
2763
 
show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
2764
 
{
2765
 
  GtkWidget *menu;
2766
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
2767
 
 
2768
 
  menu = gtk_ui_manager_get_widget (priv->ui_manager,
2769
 
            "/menubar1");
2770
 
 
2771
 
  if (set_fullscreen)
2772
 
    {
2773
 
      gtk_widget_hide (priv->sidebar);
2774
 
      gtk_widget_hide (menu);
2775
 
      gtk_widget_hide (priv->vbox);
2776
 
      gtk_widget_hide (priv->statusbar);
2777
 
      gtk_widget_hide (priv->toolbar);
2778
 
    }
2779
 
  else
2780
 
    {
2781
 
      if (priv->sidebar_was_visible_before_fs)
2782
 
        gtk_widget_show (priv->sidebar);
2783
 
 
2784
 
      gtk_widget_show (menu);
2785
 
      gtk_widget_show (priv->vbox);
2786
 
      gtk_widget_show (priv->statusbar);
2787
 
      gtk_widget_show (priv->toolbar);
2788
 
 
2789
 
      gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
2790
 
          priv->original_height_before_fs);
2791
 
    }
2792
 
}
2793
 
 
2794
 
static void
2795
 
show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
2796
 
{
2797
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
2798
 
 
2799
 
  gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
2800
 
      set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
2801
 
  gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
2802
 
      set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
2803
 
 
2804
 
  if (priv->video_output != NULL)
2805
 
    {
2806
 
      gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2807
 
          priv->video_output, TRUE, TRUE,
2808
 
          set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2809
 
          GTK_PACK_START);
2810
 
    }
2811
 
 
2812
 
  gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2813
 
      priv->vbox, TRUE, TRUE,
2814
 
      set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2815
 
      GTK_PACK_START);
2816
 
}
2817
 
 
2818
 
static gboolean
2819
 
empathy_call_window_state_event_cb (GtkWidget *widget,
2820
 
  GdkEventWindowState *event, EmpathyCallWindow *window)
2821
 
{
2822
 
  if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
2823
 
    {
2824
 
      EmpathyCallWindowPriv *priv = GET_PRIV (window);
2825
 
      gboolean set_fullscreen = event->new_window_state &
2826
 
        GDK_WINDOW_STATE_FULLSCREEN;
2827
 
 
2828
 
      if (set_fullscreen)
2829
 
        {
2830
 
          gboolean sidebar_was_visible;
2831
 
          GtkAllocation allocation;
2832
 
          gint original_width, original_height;
2833
 
 
2834
 
          gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2835
 
          original_width = allocation.width;
2836
 
          original_height = allocation.height;
2837
 
 
2838
 
          g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
2839
 
 
2840
 
          priv->sidebar_was_visible_before_fs = sidebar_was_visible;
2841
 
          priv->original_width_before_fs = original_width;
2842
 
          priv->original_height_before_fs = original_height;
2843
 
 
2844
 
          if (priv->video_output_motion_handler_id == 0 &&
2845
 
                priv->video_output != NULL)
2846
 
            {
2847
 
              priv->video_output_motion_handler_id = g_signal_connect (
2848
 
                  G_OBJECT (priv->video_output), "motion-notify-event",
2849
 
                  G_CALLBACK (empathy_call_window_video_output_motion_notify),
2850
 
                  window);
2851
 
            }
2852
 
        }
2853
 
      else
2854
 
        {
2855
 
          disconnect_video_output_motion_handler (window);
2856
 
        }
2857
 
 
2858
 
      empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
2859
 
          set_fullscreen);
2860
 
      show_controls (window, set_fullscreen);
2861
 
      show_borders (window, set_fullscreen);
2862
 
      gtk_action_set_stock_id (priv->menu_fullscreen,
2863
 
          (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
2864
 
      priv->is_fullscreen = set_fullscreen;
2865
 
  }
2866
 
 
2867
 
  return FALSE;
2868
 
}
2869
 
 
2870
 
static void
2871
 
empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
2872
 
  EmpathyCallWindow *window)
2873
 
{
2874
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
2875
 
  GtkWidget *arrow;
2876
 
  int w, h, handle_size;
2877
 
  GtkAllocation allocation, sidebar_allocation;
2878
 
 
2879
 
  gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2880
 
  w = allocation.width;
2881
 
  h = allocation.height;
2882
 
 
2883
 
  gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
2884
 
 
2885
 
  gtk_widget_get_allocation (priv->sidebar, &sidebar_allocation);
2886
 
  if (gtk_toggle_button_get_active (toggle))
2887
 
    {
2888
 
      arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
2889
 
      gtk_widget_show (priv->sidebar);
2890
 
      w += sidebar_allocation.width + handle_size;
2891
 
    }
2892
 
  else
2893
 
    {
2894
 
      arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
2895
 
      w -= sidebar_allocation.width + handle_size;
2896
 
      gtk_widget_hide (priv->sidebar);
2897
 
    }
2898
 
 
2899
 
  gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
2900
 
 
2901
 
  if (w > 0 && h > 0)
2902
 
    gtk_window_resize (GTK_WINDOW (window), w, h);
2903
 
}
2904
 
 
2905
 
static void
2906
 
empathy_call_window_set_send_video (EmpathyCallWindow *window,
2907
 
  CameraState state)
2908
 
{
2909
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
2910
 
  TpyCallChannel *call;
2911
 
 
2912
 
  priv->sending_video = (state == CAMERA_STATE_ON);
2913
 
 
2914
 
  if (state == CAMERA_STATE_PREVIEW ||
2915
 
      state == CAMERA_STATE_ON)
2916
 
    {
2917
 
      /* When we start sending video, we want to show the video preview by
2918
 
         default. */
2919
 
      display_video_preview (window, TRUE);
2920
 
    }
2921
 
  else
2922
 
    {
2923
 
      display_video_preview (window, FALSE);
2924
 
    }
2925
 
 
2926
 
  if (priv->call_state != CONNECTED)
2927
 
    return;
2928
 
 
2929
 
  g_object_get (priv->handler, "call-channel", &call, NULL);
2930
 
  DEBUG ("%s sending video", priv->sending_video ? "start": "stop");
2931
 
  tpy_call_channel_send_video (call, priv->sending_video);
2932
 
  g_object_unref (call);
2933
 
}
2934
 
 
2935
 
static void
2936
 
empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
2937
 
  EmpathyCallWindow *window)
2938
 
{
2939
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
2940
 
  gboolean active;
2941
 
 
2942
 
  active = (gtk_toggle_tool_button_get_active (toggle));
2943
 
 
2944
 
  if (active)
2945
 
    {
2946
 
      empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2947
 
        priv->volume);
2948
 
      gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
2949
 
    }
2950
 
  else
2951
 
    {
2952
 
      /* TODO, Instead of setting the input volume to 0 we should probably
2953
 
       * stop sending but this would cause the audio call to drop if both
2954
 
       * sides mute at the same time on certain CMs AFAIK. Need to revisit this
2955
 
       * in the future. GNOME #574574
2956
 
       */
2957
 
      empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2958
 
        0);
2959
 
      gtk_adjustment_set_value (priv->audio_input_adj, 0);
2960
 
    }
2961
 
}
2962
 
 
2963
 
static void
2964
 
empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
2965
 
  EmpathyCallWindow *window)
2966
 
{
2967
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
2968
 
 
2969
 
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2970
 
    FALSE);
2971
 
}
2972
 
 
2973
 
static void
2974
 
empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
2975
 
  EmpathyCallWindow *window)
2976
 
{
2977
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
2978
 
 
2979
 
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2980
 
    TRUE);
2981
 
}
2982
 
 
2983
 
static void
2984
 
empathy_call_window_hangup_cb (gpointer object,
2985
 
                               EmpathyCallWindow *window)
2986
 
{
2987
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
2988
 
 
2989
 
  empathy_call_handler_stop_call (priv->handler);
2990
 
 
2991
 
  if (empathy_call_window_disconnected (window, FALSE))
2992
 
    gtk_widget_destroy (GTK_WIDGET (window));
2993
 
}
2994
 
 
2995
 
static void
2996
 
empathy_call_window_restart_call (EmpathyCallWindow *window)
2997
 
{
2998
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
2999
 
 
3000
 
  /* Remove error info bars */
3001
 
  gtk_container_forall (GTK_CONTAINER (priv->errors_vbox),
3002
 
      (GtkCallback) gtk_widget_destroy, NULL);
3003
 
 
3004
 
  create_video_output_widget (window);
3005
 
 
3006
 
  g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
3007
 
      G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
3008
 
 
3009
 
  /* While the call was disconnected, the input volume might have changed.
3010
 
   * However, since the audio_input source was destroyed, its volume has not
3011
 
   * been updated during that time. That's why we manually update it here */
3012
 
  empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
3013
 
 
3014
 
  priv->outgoing = TRUE;
3015
 
  empathy_call_window_set_state_connecting (window);
3016
 
 
3017
 
  if (priv->pipeline_playing)
3018
 
    start_call (window);
3019
 
  else
3020
 
    /* call will be started when the pipeline is ready */
3021
 
    priv->start_call_when_playing = TRUE;
3022
 
 
3023
 
 
3024
 
  empathy_call_window_setup_avatars (window, priv->handler);
3025
 
 
3026
 
  gtk_action_set_sensitive (priv->redial, FALSE);
3027
 
  gtk_widget_set_sensitive (priv->redial_button, FALSE);
3028
 
}
3029
 
 
3030
 
static void
3031
 
empathy_call_window_redial_cb (gpointer object,
3032
 
    EmpathyCallWindow *window)
3033
 
{
3034
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
3035
 
 
3036
 
  if (priv->call_state == CONNECTED)
3037
 
    priv->call_state = REDIALING;
3038
 
 
3039
 
  empathy_call_handler_stop_call (priv->handler);
3040
 
 
3041
 
  if (priv->call_state != CONNECTED)
3042
 
    empathy_call_window_restart_call (window);
3043
 
}
3044
 
 
3045
 
static void
3046
 
empathy_call_window_fullscreen_cb (gpointer object,
3047
 
                                   EmpathyCallWindow *window)
3048
 
{
3049
 
  empathy_call_window_fullscreen_toggle (window);
3050
 
}
3051
 
 
3052
 
static void
3053
 
empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
3054
 
{
3055
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
3056
 
 
3057
 
  if (priv->is_fullscreen)
3058
 
    gtk_window_unfullscreen (GTK_WINDOW (window));
3059
 
  else
3060
 
    gtk_window_fullscreen (GTK_WINDOW (window));
3061
 
}
3062
 
 
3063
 
static gboolean
3064
 
empathy_call_window_video_button_press_cb (GtkWidget *video_output,
3065
 
  GdkEventButton *event, EmpathyCallWindow *window)
3066
 
{
3067
 
  if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
3068
 
    {
3069
 
      empathy_call_window_video_menu_popup (window, event->button);
3070
 
      return TRUE;
3071
 
    }
3072
 
 
3073
 
  return FALSE;
3074
 
}
3075
 
 
3076
 
static gboolean
3077
 
empathy_call_window_key_press_cb (GtkWidget *video_output,
3078
 
  GdkEventKey *event, EmpathyCallWindow *window)
3079
 
{
3080
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
3081
 
 
3082
 
  if (priv->is_fullscreen && event->keyval == GDK_KEY_Escape)
3083
 
    {
3084
 
      /* Since we are in fullscreen mode, toggling will bring us back to
3085
 
         normal mode. */
3086
 
      empathy_call_window_fullscreen_toggle (window);
3087
 
      return TRUE;
3088
 
    }
3089
 
 
3090
 
  return FALSE;
3091
 
}
3092
 
 
3093
 
static gboolean
3094
 
empathy_call_window_video_output_motion_notify (GtkWidget *widget,
3095
 
    GdkEventMotion *event, EmpathyCallWindow *window)
3096
 
{
3097
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
3098
 
 
3099
 
  if (priv->is_fullscreen)
3100
 
    {
3101
 
      empathy_call_window_fullscreen_show_popup (priv->fullscreen);
3102
 
      return TRUE;
3103
 
    }
3104
 
  return FALSE;
3105
 
}
3106
 
 
3107
 
static void
3108
 
empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
3109
 
  guint button)
3110
 
{
3111
 
  GtkWidget *menu;
3112
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
3113
 
 
3114
 
  menu = gtk_ui_manager_get_widget (priv->ui_manager,
3115
 
            "/video-popup");
3116
 
  gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
3117
 
      button, gtk_get_current_event_time ());
3118
 
  gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
3119
 
}
3120
 
 
3121
 
static void
3122
 
empathy_call_window_status_message (EmpathyCallWindow *window,
3123
 
  gchar *message)
3124
 
{
3125
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
3126
 
 
3127
 
  if (priv->context_id == 0)
3128
 
    {
3129
 
      priv->context_id = gtk_statusbar_get_context_id (
3130
 
        GTK_STATUSBAR (priv->statusbar), "voip call status messages");
3131
 
    }
3132
 
  else
3133
 
    {
3134
 
      gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
3135
 
    }
3136
 
 
3137
 
  gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
3138
 
    message);
3139
 
}
3140
 
 
3141
 
static void
3142
 
empathy_call_window_volume_changed_cb (GtkScaleButton *button,
3143
 
  gdouble value, EmpathyCallWindow *window)
3144
 
{
3145
 
  EmpathyCallWindowPriv *priv = GET_PRIV (window);
3146
 
 
3147
 
  if (priv->audio_output == NULL)
3148
 
    return;
3149
 
 
3150
 
  empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),
3151
 
    value);
3152
 
}
3153
 
 
3154
 
/* block all the signals related to camera control widgets. This is useful
3155
 
 * when we are manually updating the UI and so don't want to fire the
3156
 
 * callbacks */
3157
 
static void
3158
 
block_camera_control_signals (EmpathyCallWindow *self)
3159
 
{
3160
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
3161
 
 
3162
 
  g_signal_handlers_block_by_func (priv->tool_button_camera_off,
3163
 
      tool_button_camera_off_toggled_cb, self);
3164
 
  g_signal_handlers_block_by_func (priv->tool_button_camera_preview,
3165
 
      tool_button_camera_preview_toggled_cb, self);
3166
 
  g_signal_handlers_block_by_func (priv->tool_button_camera_on,
3167
 
      tool_button_camera_on_toggled_cb, self);
3168
 
  g_signal_handlers_block_by_func (priv->action_camera_on,
3169
 
      action_camera_change_cb, self);
3170
 
}
3171
 
 
3172
 
static void
3173
 
unblock_camera_control_signals (EmpathyCallWindow *self)
3174
 
{
3175
 
  EmpathyCallWindowPriv *priv = GET_PRIV (self);
3176
 
 
3177
 
  g_signal_handlers_unblock_by_func (priv->tool_button_camera_off,
3178
 
      tool_button_camera_off_toggled_cb, self);
3179
 
  g_signal_handlers_unblock_by_func (priv->tool_button_camera_preview,
3180
 
      tool_button_camera_preview_toggled_cb, self);
3181
 
  g_signal_handlers_unblock_by_func (priv->tool_button_camera_on,
3182
 
      tool_button_camera_on_toggled_cb, self);
3183
 
  g_signal_handlers_unblock_by_func (priv->action_camera_on,
3184
 
      action_camera_change_cb, self);
3185
 
}