2 * empathy-call-window.c - Source for EmpathyCallWindow
3 * Copyright (C) 2008-2009 Collabora Ltd.
4 * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
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.
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.
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
27 #include <gdk/gdkkeysyms.h>
30 #include <glib/gi18n.h>
32 #include <telepathy-farsight/channel.h>
34 #include <gst/farsight/fs-element-added-notifier.h>
36 #include <libempathy/empathy-tp-contact-factory.h>
37 #include <libempathy/empathy-call-factory.h>
38 #include <libempathy/empathy-utils.h>
39 #include <libempathy-gtk/empathy-avatar-image.h>
40 #include <libempathy-gtk/empathy-video-widget.h>
41 #include <libempathy-gtk/empathy-audio-src.h>
42 #include <libempathy-gtk/empathy-audio-sink.h>
43 #include <libempathy-gtk/empathy-video-src.h>
44 #include <libempathy-gtk/empathy-ui-utils.h>
45 #include <libempathy-gtk/empathy-sound.h>
47 #include "empathy-call-window.h"
48 #include "empathy-call-window-fullscreen.h"
49 #include "empathy-sidebar.h"
51 #define BUTTON_ID "empathy-call-dtmf-button-id"
53 #define CONTENT_HBOX_BORDER_WIDTH 6
54 #define CONTENT_HBOX_SPACING 3
55 #define CONTENT_HBOX_CHILDREN_PACKING_PADDING 3
57 #define SELF_VIDEO_SECTION_WIDTH 160
58 #define SELF_VIDEO_SECTION_HEIGTH 120
60 /* The avatar's default width and height are set to the same value because we
61 want a square icon. */
62 #define REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
63 #define REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT \
64 EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
66 /* If an video input error occurs, the error message will start with "v4l" */
67 #define VIDEO_INPUT_ERROR_PREFIX "v4l"
69 /* The time interval in milliseconds between 2 outgoing rings */
70 #define MS_BETWEEN_RING 500
72 G_DEFINE_TYPE(EmpathyCallWindow, empathy_call_window, GTK_TYPE_WINDOW)
81 static guint signals[LAST_SIGNAL] = {0};
85 PROP_CALL_HANDLER = 1,
95 /* private structure */
96 typedef struct _EmpathyCallWindowPriv EmpathyCallWindowPriv;
98 struct _EmpathyCallWindowPriv
100 gboolean dispose_has_run;
101 EmpathyCallHandler *handler;
102 EmpathyContact *contact;
107 GtkUIManager *ui_manager;
108 GtkWidget *errors_vbox;
109 GtkWidget *video_output;
110 GtkWidget *video_preview;
111 GtkWidget *remote_user_avatar_widget;
112 GtkWidget *self_user_avatar_widget;
114 GtkWidget *sidebar_button;
115 GtkWidget *statusbar;
116 GtkWidget *volume_button;
117 GtkWidget *redial_button;
118 GtkWidget *mic_button;
119 GtkWidget *camera_button;
122 GtkAction *show_preview;
123 GtkAction *send_video;
125 GtkAction *menu_fullscreen;
127 /* The frames and boxes that contain self and remote avatar and video
128 input/output. When we redial, we destroy and re-create the boxes */
129 GtkWidget *remote_user_output_frame;
130 GtkWidget *self_user_output_frame;
131 GtkWidget *remote_user_output_hbox;
132 GtkWidget *self_user_output_hbox;
134 /* We keep a reference on the hbox which contains the main content so we can
135 easilly repack everything when toggling fullscreen */
136 GtkWidget *content_hbox;
138 /* This vbox is contained in the content_hbox and it contains the
139 self_user_output_frame and the sidebar button. When toggling fullscreen,
140 it needs to be repacked. We keep a reference on it for easier access. */
143 gulong video_output_motion_handler_id;
144 guint bus_message_source_id;
147 GtkWidget *volume_scale;
148 GtkWidget *volume_progress_bar;
149 GtkAdjustment *audio_input_adj;
151 GtkWidget *dtmf_panel;
153 GstElement *video_input;
154 GstElement *audio_input;
155 GstElement *audio_output;
156 GstElement *pipeline;
157 GstElement *video_tee;
160 GstElement *liveadder;
162 FsElementAddedNotifier *fsnotifier;
169 GtkWidget *video_contrast;
170 GtkWidget *video_brightness;
171 GtkWidget *video_gamma;
174 gboolean call_started;
175 gboolean sending_video;
177 EmpathyCallWindowFullscreen *fullscreen;
178 gboolean is_fullscreen;
180 /* Those fields represent the state of the window before it actually was in
182 gboolean sidebar_was_visible_before_fs;
183 gint original_width_before_fs;
184 gint original_height_before_fs;
187 #define GET_PRIV(o) \
188 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_CALL_WINDOW, \
189 EmpathyCallWindowPriv))
191 static void empathy_call_window_realized_cb (GtkWidget *widget,
192 EmpathyCallWindow *window);
194 static gboolean empathy_call_window_delete_cb (GtkWidget *widget,
195 GdkEvent *event, EmpathyCallWindow *window);
197 static gboolean empathy_call_window_state_event_cb (GtkWidget *widget,
198 GdkEventWindowState *event, EmpathyCallWindow *window);
200 static void empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
201 EmpathyCallWindow *window);
203 static void empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
204 EmpathyCallWindow *window);
206 static void empathy_call_window_set_send_video (EmpathyCallWindow *window,
209 static void empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
210 EmpathyCallWindow *window);
212 static void empathy_call_window_show_preview_toggled_cb (
213 GtkToggleAction *toggle, EmpathyCallWindow *window);
215 static void empathy_call_window_mic_toggled_cb (
216 GtkToggleToolButton *toggle, EmpathyCallWindow *window);
218 static void empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
219 EmpathyCallWindow *window);
221 static void empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
222 EmpathyCallWindow *window);
224 static void empathy_call_window_hangup_cb (gpointer object,
225 EmpathyCallWindow *window);
227 static void empathy_call_window_fullscreen_cb (gpointer object,
228 EmpathyCallWindow *window);
230 static void empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window);
232 static gboolean empathy_call_window_video_button_press_cb (
233 GtkWidget *video_output, GdkEventButton *event, EmpathyCallWindow *window);
235 static gboolean empathy_call_window_key_press_cb (GtkWidget *video_output,
236 GdkEventKey *event, EmpathyCallWindow *window);
238 static gboolean empathy_call_window_video_output_motion_notify (
239 GtkWidget *widget, GdkEventMotion *event, EmpathyCallWindow *window);
241 static void empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
244 static void empathy_call_window_redial_cb (gpointer object,
245 EmpathyCallWindow *window);
247 static void empathy_call_window_restart_call (EmpathyCallWindow *window);
249 static void empathy_call_window_status_message (EmpathyCallWindow *window,
252 static void empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
253 EmpathyCallWindow *window);
255 static gboolean empathy_call_window_bus_message (GstBus *bus,
256 GstMessage *message, gpointer user_data);
259 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
260 gdouble value, EmpathyCallWindow *window);
263 empathy_call_window_setup_toolbar (EmpathyCallWindow *self)
265 EmpathyCallWindowPriv *priv = GET_PRIV (self);
266 GtkToolItem *tool_item;
268 /* Add an empty expanded GtkToolItem so the volume button is at the end of
270 tool_item = gtk_tool_item_new ();
271 gtk_tool_item_set_expand (tool_item, TRUE);
272 gtk_widget_show (GTK_WIDGET (tool_item));
273 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
275 priv->volume_button = gtk_volume_button_new ();
276 /* FIXME listen to the audiosinks signals and update the button according to
277 * that, for now starting out at 1.0 and assuming only the app changes the
279 gtk_scale_button_set_value (GTK_SCALE_BUTTON (priv->volume_button), 1.0);
280 g_signal_connect (G_OBJECT (priv->volume_button), "value-changed",
281 G_CALLBACK (empathy_call_window_volume_changed_cb), self);
283 tool_item = gtk_tool_item_new ();
284 gtk_container_add (GTK_CONTAINER (tool_item), priv->volume_button);
285 gtk_widget_show_all (GTK_WIDGET (tool_item));
286 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
290 dtmf_button_pressed_cb (GtkButton *button, EmpathyCallWindow *window)
292 EmpathyCallWindowPriv *priv = GET_PRIV (window);
297 g_object_get (priv->handler, "tp-call", &call, NULL);
299 button_quark = g_quark_from_static_string (BUTTON_ID);
300 event = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (button),
303 empathy_tp_call_start_tone (call, event);
305 g_object_unref (call);
309 dtmf_button_released_cb (GtkButton *button, EmpathyCallWindow *window)
311 EmpathyCallWindowPriv *priv = GET_PRIV (window);
314 g_object_get (priv->handler, "tp-call", &call, NULL);
316 empathy_tp_call_stop_tone (call);
318 g_object_unref (call);
322 empathy_call_window_create_dtmf (EmpathyCallWindow *self)
330 } dtmfbuttons[] = { { "1", TP_DTMF_EVENT_DIGIT_1 },
331 { "2", TP_DTMF_EVENT_DIGIT_2 },
332 { "3", TP_DTMF_EVENT_DIGIT_3 },
333 { "4", TP_DTMF_EVENT_DIGIT_4 },
334 { "5", TP_DTMF_EVENT_DIGIT_5 },
335 { "6", TP_DTMF_EVENT_DIGIT_6 },
336 { "7", TP_DTMF_EVENT_DIGIT_7 },
337 { "8", TP_DTMF_EVENT_DIGIT_8 },
338 { "9", TP_DTMF_EVENT_DIGIT_9 },
339 { "#", TP_DTMF_EVENT_HASH },
340 { "0", TP_DTMF_EVENT_DIGIT_0 },
341 { "*", TP_DTMF_EVENT_ASTERISK },
344 button_quark = g_quark_from_static_string (BUTTON_ID);
346 table = gtk_table_new (4, 3, TRUE);
348 for (i = 0; dtmfbuttons[i].label != NULL; i++)
350 GtkWidget *button = gtk_button_new_with_label (dtmfbuttons[i].label);
351 gtk_table_attach (GTK_TABLE (table), button, i % 3, i % 3 + 1,
352 i/3, i/3 + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
354 g_object_set_qdata (G_OBJECT (button), button_quark,
355 GUINT_TO_POINTER (dtmfbuttons[i].event));
357 g_signal_connect (G_OBJECT (button), "pressed",
358 G_CALLBACK (dtmf_button_pressed_cb), self);
359 g_signal_connect (G_OBJECT (button), "released",
360 G_CALLBACK (dtmf_button_released_cb), self);
367 empathy_call_window_create_video_input_add_slider (EmpathyCallWindow *self,
368 gchar *label_text, GtkWidget *bin)
370 GtkWidget *vbox = gtk_vbox_new (FALSE, 2);
371 GtkWidget *scale = gtk_vscale_new_with_range (0, 100, 10);
372 GtkWidget *label = gtk_label_new (label_text);
374 gtk_widget_set_sensitive (scale, FALSE);
376 gtk_container_add (GTK_CONTAINER (bin), vbox);
378 gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
379 gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 0);
380 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
386 empathy_call_window_video_contrast_changed_cb (GtkAdjustment *adj,
387 EmpathyCallWindow *self)
390 EmpathyCallWindowPriv *priv = GET_PRIV (self);
392 empathy_video_src_set_channel (priv->video_input,
393 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST, gtk_adjustment_get_value (adj));
397 empathy_call_window_video_brightness_changed_cb (GtkAdjustment *adj,
398 EmpathyCallWindow *self)
401 EmpathyCallWindowPriv *priv = GET_PRIV (self);
403 empathy_video_src_set_channel (priv->video_input,
404 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS, gtk_adjustment_get_value (adj));
408 empathy_call_window_video_gamma_changed_cb (GtkAdjustment *adj,
409 EmpathyCallWindow *self)
412 EmpathyCallWindowPriv *priv = GET_PRIV (self);
414 empathy_video_src_set_channel (priv->video_input,
415 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA, gtk_adjustment_get_value (adj));
420 empathy_call_window_create_video_input (EmpathyCallWindow *self)
422 EmpathyCallWindowPriv *priv = GET_PRIV (self);
425 hbox = gtk_hbox_new (TRUE, 3);
427 priv->video_contrast = empathy_call_window_create_video_input_add_slider (
428 self, _("Contrast"), hbox);
430 priv->video_brightness = empathy_call_window_create_video_input_add_slider (
431 self, _("Brightness"), hbox);
433 priv->video_gamma = empathy_call_window_create_video_input_add_slider (
434 self, _("Gamma"), hbox);
440 empathy_call_window_setup_video_input (EmpathyCallWindow *self)
442 EmpathyCallWindowPriv *priv = GET_PRIV (self);
446 supported = empathy_video_src_get_supported_channels (priv->video_input);
448 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST)
450 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_contrast));
452 gtk_adjustment_set_value (adj,
453 empathy_video_src_get_channel (priv->video_input,
454 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST));
456 g_signal_connect (G_OBJECT (adj), "value-changed",
457 G_CALLBACK (empathy_call_window_video_contrast_changed_cb), self);
459 gtk_widget_set_sensitive (priv->video_contrast, TRUE);
462 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS)
464 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_brightness));
466 gtk_adjustment_set_value (adj,
467 empathy_video_src_get_channel (priv->video_input,
468 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS));
470 g_signal_connect (G_OBJECT (adj), "value-changed",
471 G_CALLBACK (empathy_call_window_video_brightness_changed_cb), self);
472 gtk_widget_set_sensitive (priv->video_brightness, TRUE);
475 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA)
477 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_gamma));
479 gtk_adjustment_set_value (adj,
480 empathy_video_src_get_channel (priv->video_input,
481 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA));
483 g_signal_connect (G_OBJECT (adj), "value-changed",
484 G_CALLBACK (empathy_call_window_video_gamma_changed_cb), self);
485 gtk_widget_set_sensitive (priv->video_gamma, TRUE);
490 empathy_call_window_mic_volume_changed_cb (GtkAdjustment *adj,
491 EmpathyCallWindow *self)
493 EmpathyCallWindowPriv *priv = GET_PRIV (self);
496 if (priv->audio_input == NULL)
499 volume = gtk_adjustment_get_value (adj)/100.0;
501 /* Don't store the volume because of muting */
502 if (volume > 0 || gtk_toggle_tool_button_get_active (
503 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
504 priv->volume = volume;
506 /* Ensure that the toggle button is active if the volume is > 0 and inactive
507 * if it's smaller than 0 */
508 if ((volume > 0) != gtk_toggle_tool_button_get_active (
509 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
510 gtk_toggle_tool_button_set_active (
511 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), volume > 0);
513 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
518 empathy_call_window_audio_input_level_changed_cb (EmpathyGstAudioSrc *src,
519 gdouble level, EmpathyCallWindow *window)
522 EmpathyCallWindowPriv *priv = GET_PRIV (window);
524 value = CLAMP (pow (10, level / 20), 0.0, 1.0);
525 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
530 empathy_call_window_create_audio_input (EmpathyCallWindow *self)
532 EmpathyCallWindowPriv *priv = GET_PRIV (self);
533 GtkWidget *hbox, *vbox, *label;
535 hbox = gtk_hbox_new (TRUE, 3);
537 vbox = gtk_vbox_new (FALSE, 3);
538 gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 3);
540 priv->volume_scale = gtk_vscale_new_with_range (0, 150, 100);
541 gtk_range_set_inverted (GTK_RANGE (priv->volume_scale), TRUE);
542 label = gtk_label_new (_("Volume"));
544 priv->audio_input_adj = gtk_range_get_adjustment (
545 GTK_RANGE (priv->volume_scale));
546 priv->volume = empathy_audio_src_get_volume (EMPATHY_GST_AUDIO_SRC
547 (priv->audio_input));
548 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
550 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
551 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), self);
553 gtk_box_pack_start (GTK_BOX (vbox), priv->volume_scale, TRUE, TRUE, 3);
554 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 3);
556 priv->volume_progress_bar = gtk_progress_bar_new ();
557 gtk_progress_bar_set_orientation (
558 GTK_PROGRESS_BAR (priv->volume_progress_bar),
559 GTK_PROGRESS_BOTTOM_TO_TOP);
560 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
563 gtk_box_pack_start (GTK_BOX (hbox), priv->volume_progress_bar, FALSE, FALSE,
570 empathy_call_window_setup_remote_frame (GstBus *bus, EmpathyCallWindow *self)
572 EmpathyCallWindowPriv *priv = GET_PRIV (self);
574 /* Initializing all the content (UI and output gst elements) related to the
576 priv->remote_user_output_hbox = gtk_hbox_new (FALSE, 0);
578 priv->remote_user_avatar_widget = gtk_image_new ();
579 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
580 priv->remote_user_avatar_widget, TRUE, TRUE, 0);
582 priv->video_output = empathy_video_widget_new (bus);
583 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
584 priv->video_output, TRUE, TRUE, 0);
586 gtk_widget_add_events (priv->video_output,
587 GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
588 g_signal_connect (G_OBJECT (priv->video_output), "button-press-event",
589 G_CALLBACK (empathy_call_window_video_button_press_cb), self);
591 gtk_container_add (GTK_CONTAINER (priv->remote_user_output_frame),
592 priv->remote_user_output_hbox);
594 priv->audio_output = empathy_audio_sink_new ();
595 gst_object_ref (priv->audio_output);
596 gst_object_sink (priv->audio_output);
600 empathy_call_window_setup_self_frame (GstBus *bus, EmpathyCallWindow *self)
602 EmpathyCallWindowPriv *priv = GET_PRIV (self);
604 /* Initializing all the content (UI and input gst elements) related to the
605 self contact, except for the video preview widget. This widget is only
606 initialized when the "show video preview" option is activated */
607 priv->self_user_output_hbox = gtk_hbox_new (FALSE, 0);
609 priv->self_user_avatar_widget = gtk_image_new ();
610 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
611 priv->self_user_avatar_widget, TRUE, TRUE, 0);
613 gtk_container_add (GTK_CONTAINER (priv->self_user_output_frame),
614 priv->self_user_output_hbox);
616 priv->video_input = empathy_video_src_new ();
617 gst_object_ref (priv->video_input);
618 gst_object_sink (priv->video_input);
620 priv->audio_input = empathy_audio_src_new ();
621 gst_object_ref (priv->audio_input);
622 gst_object_sink (priv->audio_input);
624 empathy_signal_connect_weak (priv->audio_input, "peak-level-changed",
625 G_CALLBACK (empathy_call_window_audio_input_level_changed_cb),
630 empathy_call_window_setup_video_preview (EmpathyCallWindow *window)
632 EmpathyCallWindowPriv *priv = GET_PRIV (window);
634 GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
636 if (priv->video_preview != NULL)
638 /* Since the video preview and the video tee are initialized and freed
639 at the same time, if one is initialized, then the other one should
641 g_assert (priv->video_tee != NULL);
645 g_assert (priv->video_tee == NULL);
647 priv->video_tee = gst_element_factory_make ("tee", NULL);
648 gst_object_ref (priv->video_tee);
649 gst_object_sink (priv->video_tee);
651 priv->video_preview = empathy_video_widget_new_with_size (bus,
652 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
653 g_object_set (priv->video_preview, "sync", FALSE, "async", TRUE, NULL);
654 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
655 priv->video_preview, TRUE, TRUE, 0);
657 preview = empathy_video_widget_get_element (
658 EMPATHY_VIDEO_WIDGET (priv->video_preview));
659 gst_bin_add_many (GST_BIN (priv->pipeline), priv->video_input,
660 priv->video_tee, preview, NULL);
661 gst_element_link_many (priv->video_input, priv->video_tee,
664 g_object_unref (bus);
666 gst_element_set_state (preview, GST_STATE_PLAYING);
667 gst_element_set_state (priv->video_input, GST_STATE_PLAYING);
668 gst_element_set_state (priv->video_tee, GST_STATE_PLAYING);
672 empathy_call_window_set_state_connecting (EmpathyCallWindow *window)
674 EmpathyCallWindowPriv *priv = GET_PRIV (window);
676 empathy_call_window_status_message (window, _("Connecting..."));
677 priv->call_state = CONNECTING;
680 empathy_sound_start_playing (GTK_WIDGET (window),
681 EMPATHY_SOUND_PHONE_OUTGOING, MS_BETWEEN_RING);
685 empathy_call_window_init (EmpathyCallWindow *self)
687 EmpathyCallWindowPriv *priv = GET_PRIV (self);
696 GError *error = NULL;
698 filename = empathy_file_lookup ("empathy-call-window.ui", "src");
699 gui = empathy_builder_get_file (filename,
700 "call_window_vbox", &top_vbox,
701 "errors_vbox", &priv->errors_vbox,
703 "statusbar", &priv->statusbar,
704 "redial", &priv->redial_button,
705 "microphone", &priv->mic_button,
706 "camera", &priv->camera_button,
707 "toolbar", &priv->toolbar,
708 "send_video", &priv->send_video,
709 "menuredial", &priv->redial,
710 "show_preview", &priv->show_preview,
711 "ui_manager", &priv->ui_manager,
712 "menufullscreen", &priv->menu_fullscreen,
716 empathy_builder_connect (gui, self,
717 "menuhangup", "activate", empathy_call_window_hangup_cb,
718 "hangup", "clicked", empathy_call_window_hangup_cb,
719 "menuredial", "activate", empathy_call_window_redial_cb,
720 "redial", "clicked", empathy_call_window_redial_cb,
721 "microphone", "toggled", empathy_call_window_mic_toggled_cb,
722 "camera", "toggled", empathy_call_window_camera_toggled_cb,
723 "send_video", "toggled", empathy_call_window_send_video_toggled_cb,
724 "show_preview", "toggled", empathy_call_window_show_preview_toggled_cb,
725 "menufullscreen", "activate", empathy_call_window_fullscreen_cb,
728 priv->lock = g_mutex_new ();
730 gtk_container_add (GTK_CONTAINER (self), top_vbox);
732 priv->content_hbox = gtk_hbox_new (FALSE, CONTENT_HBOX_SPACING);
733 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
734 CONTENT_HBOX_BORDER_WIDTH);
735 gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
737 priv->pipeline = gst_pipeline_new (NULL);
738 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
739 priv->bus_message_source_id = gst_bus_add_watch (bus,
740 empathy_call_window_bus_message, self);
742 priv->fsnotifier = fs_element_added_notifier_new ();
743 fs_element_added_notifier_add (priv->fsnotifier, GST_BIN (priv->pipeline));
745 keyfile = g_key_file_new ();
746 filename = empathy_file_lookup ("element-properties", "data");
747 if (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error))
749 fs_element_added_notifier_set_properties_from_keyfile (priv->fsnotifier,
754 g_warning ("Could not load element-properties file: %s", error->message);
755 g_key_file_free (keyfile);
756 g_clear_error (&error);
761 priv->remote_user_output_frame = gtk_frame_new (NULL);
762 gtk_widget_set_size_request (priv->remote_user_output_frame,
763 EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT);
764 gtk_box_pack_start (GTK_BOX (priv->content_hbox),
765 priv->remote_user_output_frame, TRUE, TRUE,
766 CONTENT_HBOX_CHILDREN_PACKING_PADDING);
767 empathy_call_window_setup_remote_frame (bus, self);
769 priv->self_user_output_frame = gtk_frame_new (NULL);
770 gtk_widget_set_size_request (priv->self_user_output_frame,
771 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
773 priv->vbox = gtk_vbox_new (FALSE, 3);
774 gtk_box_pack_start (GTK_BOX (priv->content_hbox), priv->vbox,
775 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
776 gtk_box_pack_start (GTK_BOX (priv->vbox), priv->self_user_output_frame,
778 empathy_call_window_setup_self_frame (bus, self);
780 empathy_call_window_setup_toolbar (self);
782 g_object_unref (bus);
784 priv->sidebar_button = gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
785 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
786 g_signal_connect (G_OBJECT (priv->sidebar_button), "toggled",
787 G_CALLBACK (empathy_call_window_sidebar_toggled_cb), self);
789 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
791 h = gtk_hbox_new (FALSE, 3);
792 gtk_box_pack_end (GTK_BOX (priv->vbox), h, FALSE, FALSE, 3);
793 gtk_box_pack_end (GTK_BOX (h), priv->sidebar_button, FALSE, FALSE, 3);
795 priv->sidebar = empathy_sidebar_new ();
796 g_signal_connect (G_OBJECT (priv->sidebar),
797 "hide", G_CALLBACK (empathy_call_window_sidebar_hidden_cb), self);
798 g_signal_connect (G_OBJECT (priv->sidebar),
799 "show", G_CALLBACK (empathy_call_window_sidebar_shown_cb), self);
800 gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
802 priv->dtmf_panel = empathy_call_window_create_dtmf (self);
803 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Dialpad"),
806 gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
808 page = empathy_call_window_create_audio_input (self);
809 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Audio input"),
812 page = empathy_call_window_create_video_input (self);
813 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Video input"),
816 gtk_widget_show_all (top_vbox);
818 gtk_widget_hide (priv->sidebar);
820 priv->fullscreen = empathy_call_window_fullscreen_new (self);
821 empathy_call_window_fullscreen_set_video_widget (priv->fullscreen,
823 g_signal_connect (G_OBJECT (priv->fullscreen->leave_fullscreen_button),
824 "clicked", G_CALLBACK (empathy_call_window_fullscreen_cb), self);
826 g_signal_connect (G_OBJECT (self), "realize",
827 G_CALLBACK (empathy_call_window_realized_cb), self);
829 g_signal_connect (G_OBJECT (self), "delete-event",
830 G_CALLBACK (empathy_call_window_delete_cb), self);
832 g_signal_connect (G_OBJECT (self), "window-state-event",
833 G_CALLBACK (empathy_call_window_state_event_cb), self);
835 g_signal_connect (G_OBJECT (self), "key-press-event",
836 G_CALLBACK (empathy_call_window_key_press_cb), self);
838 priv->timer = g_timer_new ();
840 g_object_ref (priv->ui_manager);
841 g_object_unref (gui);
844 /* Instead of specifying a width and a height, we specify only one size. That's
845 because we want a square avatar icon. */
847 init_contact_avatar_with_size (EmpathyContact *contact,
848 GtkWidget *image_widget,
851 GdkPixbuf *pixbuf_avatar = NULL;
855 pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact,
859 if (pixbuf_avatar == NULL)
861 pixbuf_avatar = empathy_pixbuf_from_icon_name_sized ("stock_person",
865 gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget), pixbuf_avatar);
869 set_window_title (EmpathyCallWindow *self)
871 EmpathyCallWindowPriv *priv = GET_PRIV (self);
874 /* translators: Call is a noun and %s is the contact name. This string
875 * is used in the window title */
876 tmp = g_strdup_printf (_("Call with %s"),
877 empathy_contact_get_name (priv->contact));
878 gtk_window_set_title (GTK_WINDOW (self), tmp);
883 contact_name_changed_cb (EmpathyContact *contact,
884 GParamSpec *pspec, EmpathyCallWindow *self)
886 set_window_title (self);
890 contact_avatar_changed_cb (EmpathyContact *contact,
891 GParamSpec *pspec, GtkWidget *avatar_widget)
895 size = avatar_widget->allocation.height;
899 /* the widget is not allocated yet, set a default size */
900 size = MIN (REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT,
901 REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH);
904 init_contact_avatar_with_size (contact, avatar_widget, size);
908 empathy_call_window_got_self_contact_cb (EmpathyTpContactFactory *factory,
909 EmpathyContact *contact, const GError *error, gpointer user_data,
910 GObject *weak_object)
912 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
913 EmpathyCallWindowPriv *priv = GET_PRIV (self);
915 init_contact_avatar_with_size (contact, priv->self_user_avatar_widget,
916 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
918 g_signal_connect (contact, "notify::avatar",
919 G_CALLBACK (contact_avatar_changed_cb), priv->self_user_avatar_widget);
923 empathy_call_window_setup_avatars (EmpathyCallWindow *self,
924 EmpathyCallHandler *handler)
926 EmpathyCallWindowPriv *priv = GET_PRIV (self);
928 g_object_get (handler, "contact", &(priv->contact), NULL);
930 if (priv->contact != NULL)
932 TpConnection *connection;
933 EmpathyTpContactFactory *factory;
935 set_window_title (self);
937 g_signal_connect (priv->contact, "notify::name",
938 G_CALLBACK (contact_name_changed_cb), self);
939 g_signal_connect (priv->contact, "notify::avatar",
940 G_CALLBACK (contact_avatar_changed_cb),
941 priv->remote_user_avatar_widget);
943 /* Retreiving the self avatar */
944 connection = empathy_contact_get_connection (priv->contact);
945 factory = empathy_tp_contact_factory_dup_singleton (connection);
946 empathy_tp_contact_factory_get_from_handle (factory,
947 tp_connection_get_self_handle (connection),
948 empathy_call_window_got_self_contact_cb, self, NULL, G_OBJECT (self));
950 g_object_unref (factory);
954 g_warning ("call handler doesn't have a contact");
955 /* translators: Call is a noun. This string is used in the window
957 gtk_window_set_title (GTK_WINDOW (self), _("Call"));
959 /* Since we can't access the remote contact, we can't get a connection
960 to it and can't get the self contact (and its avatar). This means
961 that we have to manually set the self avatar. */
962 init_contact_avatar_with_size (NULL, priv->self_user_avatar_widget,
963 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
966 init_contact_avatar_with_size (priv->contact,
967 priv->remote_user_avatar_widget,
968 MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
969 REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
971 /* The remote avatar is shown by default and will be hidden when we receive
972 video from the remote side. */
973 gtk_widget_hide (priv->video_output);
974 gtk_widget_show (priv->remote_user_avatar_widget);
978 empathy_call_window_setup_video_preview_visibility (EmpathyCallWindow *self,
979 EmpathyCallHandler *handler)
981 EmpathyCallWindowPriv *priv = GET_PRIV (self);
982 gboolean initial_video =
983 empathy_call_handler_has_initial_video (priv->handler);
985 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
990 empathy_call_window_constructed (GObject *object)
992 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
993 EmpathyCallWindowPriv *priv = GET_PRIV (self);
996 g_assert (priv->handler != NULL);
998 g_object_get (priv->handler, "tp-call", &call, NULL);
999 priv->outgoing = (call == NULL);
1001 g_object_unref (call);
1003 empathy_call_window_setup_avatars (self, priv->handler);
1004 empathy_call_window_setup_video_preview_visibility (self, priv->handler);
1005 empathy_call_window_set_state_connecting (self);
1008 static void empathy_call_window_dispose (GObject *object);
1009 static void empathy_call_window_finalize (GObject *object);
1012 empathy_call_window_set_property (GObject *object,
1013 guint property_id, const GValue *value, GParamSpec *pspec)
1015 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1017 switch (property_id)
1019 case PROP_CALL_HANDLER:
1020 priv->handler = g_value_dup_object (value);
1023 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1028 empathy_call_window_get_property (GObject *object,
1029 guint property_id, GValue *value, GParamSpec *pspec)
1031 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1033 switch (property_id)
1035 case PROP_CALL_HANDLER:
1036 g_value_set_object (value, priv->handler);
1039 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1044 empathy_call_window_class_init (
1045 EmpathyCallWindowClass *empathy_call_window_class)
1047 GObjectClass *object_class = G_OBJECT_CLASS (empathy_call_window_class);
1048 GParamSpec *param_spec;
1050 g_type_class_add_private (empathy_call_window_class,
1051 sizeof (EmpathyCallWindowPriv));
1053 object_class->constructed = empathy_call_window_constructed;
1054 object_class->set_property = empathy_call_window_set_property;
1055 object_class->get_property = empathy_call_window_get_property;
1057 object_class->dispose = empathy_call_window_dispose;
1058 object_class->finalize = empathy_call_window_finalize;
1060 param_spec = g_param_spec_object ("handler",
1061 "handler", "The call handler",
1062 EMPATHY_TYPE_CALL_HANDLER,
1063 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1064 g_object_class_install_property (object_class,
1065 PROP_CALL_HANDLER, param_spec);
1069 empathy_call_window_video_stream_changed_cb (EmpathyTpCall *call,
1070 GParamSpec *property, EmpathyCallWindow *self)
1072 empathy_call_window_update_avatars_visibility (call, self);
1076 empathy_call_window_dispose (GObject *object)
1078 EmpathyTpCall *call;
1079 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1080 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1082 if (priv->dispose_has_run)
1085 priv->dispose_has_run = TRUE;
1087 g_object_get (priv->handler, "tp-call", &call, NULL);
1091 g_signal_handlers_disconnect_by_func (call,
1092 empathy_call_window_video_stream_changed_cb, object);
1093 g_object_unref (call);
1096 if (priv->handler != NULL)
1097 g_object_unref (priv->handler);
1098 priv->handler = NULL;
1100 if (priv->pipeline != NULL)
1101 g_object_unref (priv->pipeline);
1102 priv->pipeline = NULL;
1104 if (priv->video_input != NULL)
1105 g_object_unref (priv->video_input);
1106 priv->video_input = NULL;
1108 if (priv->audio_input != NULL)
1109 g_object_unref (priv->audio_input);
1110 priv->audio_input = NULL;
1112 if (priv->audio_output != NULL)
1113 g_object_unref (priv->audio_output);
1114 priv->audio_output = NULL;
1116 if (priv->video_tee != NULL)
1117 g_object_unref (priv->video_tee);
1118 priv->video_tee = NULL;
1120 if (priv->fsnotifier != NULL)
1121 g_object_unref (priv->fsnotifier);
1122 priv->fsnotifier = NULL;
1124 if (priv->timer_id != 0)
1125 g_source_remove (priv->timer_id);
1128 if (priv->ui_manager != NULL)
1129 g_object_unref (priv->ui_manager);
1130 priv->ui_manager = NULL;
1132 if (priv->contact != NULL)
1134 g_signal_handlers_disconnect_by_func (priv->contact,
1135 contact_name_changed_cb, self);
1136 g_object_unref (priv->contact);
1137 priv->contact = NULL;
1140 /* release any references held by the object here */
1141 if (G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose)
1142 G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
1146 empathy_call_window_finalize (GObject *object)
1148 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1149 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1151 if (priv->video_output_motion_handler_id != 0)
1153 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1154 priv->video_output_motion_handler_id);
1155 priv->video_output_motion_handler_id = 0;
1158 if (priv->bus_message_source_id != 0)
1160 g_source_remove (priv->bus_message_source_id);
1161 priv->bus_message_source_id = 0;
1164 /* free any data held directly by the object here */
1165 g_mutex_free (priv->lock);
1167 g_timer_destroy (priv->timer);
1169 G_OBJECT_CLASS (empathy_call_window_parent_class)->finalize (object);
1174 empathy_call_window_new (EmpathyCallHandler *handler)
1176 return EMPATHY_CALL_WINDOW (
1177 g_object_new (EMPATHY_TYPE_CALL_WINDOW, "handler", handler, NULL));
1181 empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
1182 GstElement *conference, gpointer user_data)
1184 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1185 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1187 gst_bin_add (GST_BIN (priv->pipeline), conference);
1189 gst_element_set_state (conference, GST_STATE_PLAYING);
1193 empathy_call_window_request_resource_cb (EmpathyCallHandler *handler,
1194 FsMediaType type, FsStreamDirection direction, gpointer user_data)
1196 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1197 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1199 if (type != TP_MEDIA_STREAM_TYPE_VIDEO)
1202 if (direction == FS_DIRECTION_RECV)
1205 /* video and direction is send */
1206 return priv->video_input != NULL;
1210 empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
1212 GstStateChangeReturn state_change_return;
1213 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1215 if (priv->pipeline == NULL)
1218 if (priv->bus_message_source_id != 0)
1220 g_source_remove (priv->bus_message_source_id);
1221 priv->bus_message_source_id = 0;
1224 state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1226 if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1227 state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1229 if (priv->pipeline != NULL)
1230 g_object_unref (priv->pipeline);
1231 priv->pipeline = NULL;
1233 if (priv->video_input != NULL)
1234 g_object_unref (priv->video_input);
1235 priv->video_input = NULL;
1237 if (priv->audio_input != NULL)
1238 g_object_unref (priv->audio_input);
1239 priv->audio_input = NULL;
1241 g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
1242 empathy_call_window_mic_volume_changed_cb, self);
1244 if (priv->audio_output != NULL)
1245 g_object_unref (priv->audio_output);
1246 priv->audio_output = NULL;
1248 if (priv->video_tee != NULL)
1249 g_object_unref (priv->video_tee);
1250 priv->video_tee = NULL;
1252 if (priv->video_preview != NULL)
1253 gtk_widget_destroy (priv->video_preview);
1254 priv->video_preview = NULL;
1256 priv->liveadder = NULL;
1257 priv->funnel = NULL;
1263 g_message ("Error: could not destroy pipeline. Closing call window");
1264 gtk_widget_destroy (GTK_WIDGET (self));
1271 empathy_call_window_disconnected (EmpathyCallWindow *self)
1273 gboolean could_disconnect = FALSE;
1274 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1275 gboolean could_reset_pipeline = empathy_call_window_reset_pipeline (self);
1277 if (priv->call_state == CONNECTING)
1278 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1280 if (priv->call_state != REDIALING)
1281 priv->call_state = DISCONNECTED;
1283 if (could_reset_pipeline)
1285 gboolean initial_video = empathy_call_handler_has_initial_video (
1287 g_mutex_lock (priv->lock);
1289 g_timer_stop (priv->timer);
1291 if (priv->timer_id != 0)
1292 g_source_remove (priv->timer_id);
1295 g_mutex_unlock (priv->lock);
1297 empathy_call_window_status_message (self, _("Disconnected"));
1299 gtk_action_set_sensitive (priv->redial, TRUE);
1300 gtk_widget_set_sensitive (priv->redial_button, TRUE);
1302 /* Reseting the send_video, camera_buton and mic_button to their
1304 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1305 gtk_widget_set_sensitive (priv->mic_button, FALSE);
1306 gtk_action_set_sensitive (priv->send_video, FALSE);
1307 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1309 gtk_toggle_tool_button_set_active (
1310 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), initial_video);
1311 gtk_toggle_tool_button_set_active (
1312 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
1314 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1316 gtk_action_set_sensitive (priv->show_preview, FALSE);
1318 gtk_progress_bar_set_fraction (
1319 GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
1321 gtk_widget_hide (priv->video_output);
1322 gtk_widget_show (priv->remote_user_avatar_widget);
1324 priv->sending_video = FALSE;
1325 priv->call_started = FALSE;
1327 could_disconnect = TRUE;
1330 return could_disconnect;
1335 empathy_call_window_channel_closed_cb (EmpathyCallHandler *handler,
1338 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1339 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1341 if (empathy_call_window_disconnected (self) && priv->call_state == REDIALING)
1342 empathy_call_window_restart_call (self);
1347 empathy_call_window_channel_stream_closed_cb (EmpathyCallHandler *handler,
1348 TfStream *stream, gpointer user_data)
1350 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1351 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1354 g_object_get (stream, "media-type", &media_type, NULL);
1357 * This assumes that there is only one video stream per channel...
1360 if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO)
1362 if (priv->funnel != NULL)
1366 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1367 (priv->video_output));
1369 gst_element_set_state (output, GST_STATE_NULL);
1370 gst_element_set_state (priv->funnel, GST_STATE_NULL);
1372 gst_bin_remove (GST_BIN (priv->pipeline), output);
1373 gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
1374 priv->funnel = NULL;
1377 else if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO)
1379 if (priv->liveadder != NULL)
1381 gst_element_set_state (priv->audio_output, GST_STATE_NULL);
1382 gst_element_set_state (priv->liveadder, GST_STATE_NULL);
1384 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
1385 gst_bin_remove (GST_BIN (priv->pipeline), priv->liveadder);
1386 priv->liveadder = NULL;
1391 /* Called with global lock held */
1393 empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
1395 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1398 if (priv->funnel == NULL)
1402 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1403 (priv->video_output));
1405 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
1407 gst_bin_add (GST_BIN (priv->pipeline), priv->funnel);
1408 gst_bin_add (GST_BIN (priv->pipeline), output);
1410 gst_element_link (priv->funnel, output);
1412 gst_element_set_state (priv->funnel, GST_STATE_PLAYING);
1413 gst_element_set_state (output, GST_STATE_PLAYING);
1416 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
1421 /* Called with global lock held */
1423 empathy_call_window_get_audio_sink_pad (EmpathyCallWindow *self)
1425 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1428 if (priv->liveadder == NULL)
1430 priv->liveadder = gst_element_factory_make ("liveadder", NULL);
1432 gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder);
1433 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output);
1435 gst_element_link (priv->liveadder, priv->audio_output);
1437 gst_element_set_state (priv->liveadder, GST_STATE_PLAYING);
1438 gst_element_set_state (priv->audio_output, GST_STATE_PLAYING);
1441 pad = gst_element_get_request_pad (priv->liveadder, "sink%d");
1447 empathy_call_window_update_timer (gpointer user_data)
1449 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1450 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1454 time_ = g_timer_elapsed (priv->timer, NULL);
1456 /* Translators: number of minutes:seconds the caller has been connected */
1457 str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time_ / 60,
1459 empathy_call_window_status_message (self, str);
1466 display_error (EmpathyCallWindow *self,
1467 EmpathyTpCall *call,
1471 const gchar *details)
1473 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1474 GtkWidget *info_bar;
1475 GtkWidget *content_area;
1482 /* Create info bar */
1483 info_bar = gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1486 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
1488 content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
1490 /* hbox containing the image and the messages vbox */
1491 hbox = gtk_hbox_new (FALSE, 3);
1492 gtk_container_add (GTK_CONTAINER (content_area), hbox);
1495 image = gtk_image_new_from_icon_name (img, GTK_ICON_SIZE_DIALOG);
1496 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
1498 /* vbox containing the main message and the details expander */
1499 vbox = gtk_vbox_new (FALSE, 3);
1500 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1503 txt = g_strdup_printf ("<b>%s</b>\n%s", title, desc);
1505 label = gtk_label_new (NULL);
1506 gtk_label_set_markup (GTK_LABEL (label), txt);
1507 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1508 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1511 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
1514 if (details != NULL)
1516 GtkWidget *expander;
1518 expander = gtk_expander_new (_("Technical Details"));
1520 txt = g_strdup_printf ("<i>%s</i>", details);
1522 label = gtk_label_new (NULL);
1523 gtk_label_set_markup (GTK_LABEL (label), txt);
1524 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1525 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
1528 gtk_container_add (GTK_CONTAINER (expander), label);
1529 gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
1532 g_signal_connect (info_bar, "response",
1533 G_CALLBACK (gtk_widget_destroy), NULL);
1535 gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar,
1536 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1537 gtk_widget_show_all (info_bar);
1541 media_stream_error_to_txt (EmpathyCallWindow *self,
1542 EmpathyTpCall *call,
1544 TpMediaStreamError error)
1546 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1553 case TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED:
1555 return g_strdup_printf (
1556 _("%s's software does not understand any of the audio formats "
1557 "supported by your computer"),
1558 empathy_contact_get_name (priv->contact));
1560 return g_strdup_printf (
1561 _("%s's software does not understand any of the video formats "
1562 "supported by your computer"),
1563 empathy_contact_get_name (priv->contact));
1565 case TP_MEDIA_STREAM_ERROR_CONNECTION_FAILED:
1566 return g_strdup_printf (
1567 _("Can't establish a connection to %s. "
1568 "One of you might be on a network that does not allow "
1569 "direct connections."),
1570 empathy_contact_get_name (priv->contact));
1572 case TP_MEDIA_STREAM_ERROR_NETWORK_ERROR:
1573 return g_strdup (_("There was a failure on the network"));
1575 case TP_MEDIA_STREAM_ERROR_NO_CODECS:
1577 return g_strdup (_("The audio formats necessary for this call "
1578 "are not installed on your computer"));
1580 return g_strdup (_("The video formats necessary for this call "
1581 "are not installed on your computer"));
1583 case TP_MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR:
1584 cm = empathy_tp_call_get_connection_manager (call);
1586 url = g_strdup_printf ("http://bugs.freedesktop.org/enter_bug.cgi?"
1587 "product=Telepathy&component=%s", cm);
1589 result = g_strdup_printf (
1590 _("Something not expected happened in a Telepathy component. "
1591 "Please <a href=\"%s\">report this bug</a> and attach "
1592 "logs gathered from the 'Debug' window in the Help menu."), url);
1597 case TP_MEDIA_STREAM_ERROR_MEDIA_ERROR:
1598 return g_strdup (_("There was a failure in the call engine"));
1606 empathy_call_window_stream_error (EmpathyCallWindow *self,
1607 EmpathyTpCall *call,
1616 desc = media_stream_error_to_txt (self, call, audio, code);
1619 /* No description, use the error message. That's not great as it's not
1620 * localized but it's better than nothing. */
1621 display_error (self, call, icon, title, msg, NULL);
1625 display_error (self, call, icon, title, desc, msg);
1631 empathy_call_window_audio_stream_error (EmpathyTpCall *call,
1634 EmpathyCallWindow *self)
1636 empathy_call_window_stream_error (self, call, TRUE, code, msg,
1637 "gnome-stock-mic", _("Can't establish audio stream"));
1641 empathy_call_window_video_stream_error (EmpathyTpCall *call,
1644 EmpathyCallWindow *self)
1646 empathy_call_window_stream_error (self, call, FALSE, code, msg,
1647 "camera-web", _("Can't establish video stream"));
1651 empathy_call_window_connected (gpointer user_data)
1653 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1654 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1655 EmpathyTpCall *call;
1656 gboolean can_send_video;
1658 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1660 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
1661 empathy_contact_can_voip_video (priv->contact);
1663 g_object_get (priv->handler, "tp-call", &call, NULL);
1665 g_signal_connect (call, "notify::video-stream",
1666 G_CALLBACK (empathy_call_window_video_stream_changed_cb), self);
1668 if (empathy_tp_call_has_dtmf (call))
1669 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
1671 if (priv->video_input == NULL)
1672 empathy_call_window_set_send_video (self, FALSE);
1674 priv->sending_video = can_send_video ?
1675 empathy_tp_call_is_sending_video (call) : FALSE;
1677 gtk_action_set_sensitive (priv->show_preview, TRUE);
1678 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1680 || gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (
1681 priv->show_preview)));
1682 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1683 priv->sending_video && priv->video_input != NULL);
1684 gtk_toggle_tool_button_set_active (
1685 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button),
1686 priv->sending_video && priv->video_input != NULL);
1687 gtk_widget_set_sensitive (priv->camera_button, can_send_video);
1688 gtk_action_set_sensitive (priv->send_video, can_send_video);
1690 gtk_action_set_sensitive (priv->redial, FALSE);
1691 gtk_widget_set_sensitive (priv->redial_button, FALSE);
1693 gtk_widget_set_sensitive (priv->mic_button, TRUE);
1695 empathy_call_window_update_avatars_visibility (call, self);
1697 g_object_unref (call);
1699 g_mutex_lock (priv->lock);
1701 priv->timer_id = g_timeout_add_seconds (1,
1702 empathy_call_window_update_timer, self);
1704 g_mutex_unlock (priv->lock);
1706 empathy_call_window_update_timer (self);
1712 /* Called from the streaming thread */
1714 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
1715 GstPad *src, guint media_type, gpointer user_data)
1717 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1718 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1722 g_mutex_lock (priv->lock);
1724 if (priv->call_state != CONNECTED)
1726 g_timer_start (priv->timer);
1727 priv->timer_id = g_idle_add (empathy_call_window_connected, self);
1728 priv->call_state = CONNECTED;
1733 case TP_MEDIA_STREAM_TYPE_AUDIO:
1734 pad = empathy_call_window_get_audio_sink_pad (self);
1736 case TP_MEDIA_STREAM_TYPE_VIDEO:
1737 gtk_widget_hide (priv->remote_user_avatar_widget);
1738 gtk_widget_show (priv->video_output);
1739 pad = empathy_call_window_get_video_sink_pad (self);
1742 g_assert_not_reached ();
1745 gst_pad_link (src, pad);
1746 gst_object_unref (pad);
1748 g_mutex_unlock (priv->lock);
1751 /* Called from the streaming thread */
1753 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
1754 GstPad *sink, guint media_type, gpointer user_data)
1756 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1757 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1762 case TP_MEDIA_STREAM_TYPE_AUDIO:
1763 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
1765 pad = gst_element_get_static_pad (priv->audio_input, "src");
1766 gst_pad_link (pad, sink);
1768 gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
1770 case TP_MEDIA_STREAM_TYPE_VIDEO:
1771 if (priv->video_input != NULL)
1773 EmpathyTpCall *call;
1774 g_object_get (priv->handler, "tp-call", &call, NULL);
1776 if (empathy_tp_call_is_sending_video (call))
1778 empathy_call_window_setup_video_preview (self);
1780 gtk_toggle_action_set_active (
1781 GTK_TOGGLE_ACTION (priv->show_preview), TRUE);
1783 if (priv->video_preview != NULL)
1784 gtk_widget_show (priv->video_preview);
1785 gtk_widget_hide (priv->self_user_avatar_widget);
1788 g_object_unref (call);
1790 if (priv->video_tee != NULL)
1792 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
1793 gst_pad_link (pad, sink);
1798 g_assert_not_reached ();
1804 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
1806 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1807 GstElement *preview;
1809 preview = empathy_video_widget_get_element (
1810 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1812 gst_element_set_state (priv->video_input, GST_STATE_NULL);
1813 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
1814 gst_element_set_state (preview, GST_STATE_NULL);
1816 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
1817 priv->video_tee, preview, NULL);
1819 g_object_unref (priv->video_input);
1820 priv->video_input = NULL;
1821 g_object_unref (priv->video_tee);
1822 priv->video_tee = NULL;
1823 gtk_widget_destroy (priv->video_preview);
1824 priv->video_preview = NULL;
1826 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), FALSE);
1827 gtk_toggle_tool_button_set_active (
1828 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), FALSE);
1829 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1830 gtk_action_set_sensitive (priv->send_video, FALSE);
1832 gtk_widget_show (priv->self_user_avatar_widget);
1837 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
1840 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1841 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1844 empathy_call_handler_bus_message (priv->handler, bus, message);
1846 switch (GST_MESSAGE_TYPE (message))
1848 case GST_MESSAGE_STATE_CHANGED:
1849 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
1851 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1852 if (newstate == GST_STATE_PAUSED)
1853 empathy_call_window_setup_video_input (self);
1855 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
1856 !priv->call_started)
1858 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1859 if (newstate == GST_STATE_PAUSED)
1861 priv->call_started = TRUE;
1862 empathy_call_handler_start_call (priv->handler);
1863 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1867 case GST_MESSAGE_ERROR:
1869 GError *error = NULL;
1870 GstElement *gst_error;
1873 gst_message_parse_error (message, &error, &debug);
1874 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
1876 g_message ("Element error: %s -- %s\n", error->message, debug);
1878 if (g_str_has_prefix (gst_element_get_name (gst_error),
1879 VIDEO_INPUT_ERROR_PREFIX))
1881 /* Remove the video input and continue */
1882 if (priv->video_input != NULL)
1883 empathy_call_window_remove_video_input (self);
1884 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1888 empathy_call_window_disconnected (self);
1890 g_error_free (error);
1901 empathy_call_window_update_self_avatar_visibility (EmpathyCallWindow *window)
1903 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1905 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (priv->show_preview)))
1907 if (priv->video_preview != NULL)
1909 gtk_widget_hide (priv->self_user_avatar_widget);
1910 gtk_widget_show (priv->video_preview);
1914 if (priv->video_preview != NULL)
1915 gtk_widget_hide (priv->video_preview);
1917 gtk_widget_show (priv->self_user_avatar_widget);
1923 empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
1924 EmpathyCallWindow *window)
1926 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1928 if (empathy_tp_call_is_receiving_video (call))
1930 gtk_widget_hide (priv->remote_user_avatar_widget);
1931 gtk_widget_show (priv->video_output);
1935 gtk_widget_hide (priv->video_output);
1936 gtk_widget_show (priv->remote_user_avatar_widget);
1939 empathy_call_window_update_self_avatar_visibility (window);
1943 call_handler_notify_tp_call_cb (EmpathyCallHandler *handler,
1945 EmpathyCallWindow *self)
1947 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1948 EmpathyTpCall *call;
1950 g_object_get (priv->handler, "tp-call", &call, NULL);
1954 empathy_signal_connect_weak (call, "audio-stream-error",
1955 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (self));
1956 empathy_signal_connect_weak (call, "video-stream-error",
1957 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (self));
1959 g_object_unref (call);
1963 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
1965 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1966 EmpathyTpCall *call;
1968 g_signal_connect (priv->handler, "conference-added",
1969 G_CALLBACK (empathy_call_window_conference_added_cb), window);
1970 g_signal_connect (priv->handler, "request-resource",
1971 G_CALLBACK (empathy_call_window_request_resource_cb), window);
1972 g_signal_connect (priv->handler, "closed",
1973 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
1974 g_signal_connect (priv->handler, "src-pad-added",
1975 G_CALLBACK (empathy_call_window_src_added_cb), window);
1976 g_signal_connect (priv->handler, "sink-pad-added",
1977 G_CALLBACK (empathy_call_window_sink_added_cb), window);
1978 g_signal_connect (priv->handler, "stream-closed",
1979 G_CALLBACK (empathy_call_window_channel_stream_closed_cb), window);
1981 g_object_get (priv->handler, "tp-call", &call, NULL);
1984 empathy_signal_connect_weak (call, "audio-stream-error",
1985 G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (window));
1986 empathy_signal_connect_weak (call, "video-stream-error",
1987 G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (window));
1989 g_object_unref (call);
1993 /* tp-call doesn't exist yet, we'll connect signals once it has been
1995 g_signal_connect (priv->handler, "notify::tp-call",
1996 G_CALLBACK (call_handler_notify_tp_call_cb), window);
1999 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
2003 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
2004 EmpathyCallWindow *window)
2006 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2008 if (priv->pipeline != NULL)
2010 if (priv->bus_message_source_id != 0)
2012 g_source_remove (priv->bus_message_source_id);
2013 priv->bus_message_source_id = 0;
2016 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
2019 if (priv->call_state == CONNECTING)
2020 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
2026 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
2029 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2031 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2036 gtk_widget_hide (priv->sidebar);
2037 gtk_widget_hide (menu);
2038 gtk_widget_hide (priv->vbox);
2039 gtk_widget_hide (priv->statusbar);
2040 gtk_widget_hide (priv->toolbar);
2044 if (priv->sidebar_was_visible_before_fs)
2045 gtk_widget_show (priv->sidebar);
2047 gtk_widget_show (menu);
2048 gtk_widget_show (priv->vbox);
2049 gtk_widget_show (priv->statusbar);
2050 gtk_widget_show (priv->toolbar);
2052 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
2053 priv->original_height_before_fs);
2058 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
2060 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2062 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
2063 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
2064 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
2065 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
2066 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2067 priv->video_output, TRUE, TRUE,
2068 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2070 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2071 priv->vbox, TRUE, TRUE,
2072 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2077 empathy_call_window_state_event_cb (GtkWidget *widget,
2078 GdkEventWindowState *event, EmpathyCallWindow *window)
2080 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
2082 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2083 gboolean set_fullscreen = event->new_window_state &
2084 GDK_WINDOW_STATE_FULLSCREEN;
2088 gboolean sidebar_was_visible;
2089 gint original_width = GTK_WIDGET (window)->allocation.width;
2090 gint original_height = GTK_WIDGET (window)->allocation.height;
2092 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
2094 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
2095 priv->original_width_before_fs = original_width;
2096 priv->original_height_before_fs = original_height;
2098 if (priv->video_output_motion_handler_id == 0 &&
2099 priv->video_output != NULL)
2101 priv->video_output_motion_handler_id = g_signal_connect (
2102 G_OBJECT (priv->video_output), "motion-notify-event",
2103 G_CALLBACK (empathy_call_window_video_output_motion_notify),
2109 if (priv->video_output_motion_handler_id != 0)
2111 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
2112 priv->video_output_motion_handler_id);
2113 priv->video_output_motion_handler_id = 0;
2117 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
2119 show_controls (window, set_fullscreen);
2120 show_borders (window, set_fullscreen);
2121 gtk_action_set_stock_id (priv->menu_fullscreen,
2122 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
2123 priv->is_fullscreen = set_fullscreen;
2130 empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
2131 EmpathyCallWindow *window)
2133 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2135 int w,h, handle_size;
2137 w = GTK_WIDGET (window)->allocation.width;
2138 h = GTK_WIDGET (window)->allocation.height;
2140 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
2142 if (gtk_toggle_button_get_active (toggle))
2144 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
2145 gtk_widget_show (priv->sidebar);
2146 w += priv->sidebar->allocation.width + handle_size;
2150 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
2151 w -= priv->sidebar->allocation.width + handle_size;
2152 gtk_widget_hide (priv->sidebar);
2155 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
2158 gtk_window_resize (GTK_WINDOW (window), w, h);
2162 empathy_call_window_set_send_video (EmpathyCallWindow *window,
2165 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2166 EmpathyTpCall *call;
2168 priv->sending_video = send;
2170 /* When we start sending video, we want to show the video preview by
2174 empathy_call_window_setup_video_preview (window);
2175 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
2179 g_object_get (priv->handler, "tp-call", &call, NULL);
2180 empathy_tp_call_request_video_stream_direction (call, send);
2181 g_object_unref (call);
2185 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
2186 EmpathyCallWindow *window)
2188 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2191 if (priv->call_state != CONNECTED)
2194 active = (gtk_toggle_tool_button_get_active (toggle));
2196 if (priv->sending_video == active)
2199 empathy_call_window_set_send_video (window, active);
2200 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), active);
2204 empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
2205 EmpathyCallWindow *window)
2207 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2210 if (priv->call_state != CONNECTED)
2213 active = (gtk_toggle_action_get_active (toggle));
2215 if (priv->sending_video == active)
2218 empathy_call_window_set_send_video (window, active);
2219 gtk_toggle_tool_button_set_active (
2220 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), active);
2224 empathy_call_window_show_preview_toggled_cb (GtkToggleAction *toggle,
2225 EmpathyCallWindow *window)
2227 gboolean show_preview_toggled;
2228 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2230 show_preview_toggled = gtk_toggle_action_get_active (toggle);
2232 if (show_preview_toggled)
2234 empathy_call_window_setup_video_preview (window);
2235 gtk_widget_show (priv->self_user_output_frame);
2236 empathy_call_window_update_self_avatar_visibility (window);
2240 gtk_widget_hide (priv->self_user_output_frame);
2245 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
2246 EmpathyCallWindow *window)
2248 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2251 if (priv->audio_input == NULL)
2254 active = (gtk_toggle_tool_button_get_active (toggle));
2258 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2260 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
2264 /* TODO, Instead of setting the input volume to 0 we should probably
2265 * stop sending but this would cause the audio call to drop if both
2266 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
2267 * in the future. GNOME #574574
2269 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2271 gtk_adjustment_set_value (priv->audio_input_adj, 0);
2276 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
2277 EmpathyCallWindow *window)
2279 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2281 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2286 empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
2287 EmpathyCallWindow *window)
2289 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2291 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2296 empathy_call_window_hangup_cb (gpointer object,
2297 EmpathyCallWindow *window)
2299 if (empathy_call_window_disconnected (window))
2300 gtk_widget_destroy (GTK_WIDGET (window));
2304 empathy_call_window_restart_call (EmpathyCallWindow *window)
2307 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2309 gtk_widget_destroy (priv->remote_user_output_hbox);
2310 gtk_widget_destroy (priv->self_user_output_hbox);
2312 priv->pipeline = gst_pipeline_new (NULL);
2313 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
2314 priv->bus_message_source_id = gst_bus_add_watch (bus,
2315 empathy_call_window_bus_message, window);
2317 empathy_call_window_setup_remote_frame (bus, window);
2318 empathy_call_window_setup_self_frame (bus, window);
2320 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
2321 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
2323 /* While the call was disconnected, the input volume might have changed.
2324 * However, since the audio_input source was destroyed, its volume has not
2325 * been updated during that time. That's why we manually update it here */
2326 empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
2328 g_object_unref (bus);
2330 gtk_widget_show_all (priv->content_hbox);
2332 if (!empathy_call_handler_has_initial_video (priv->handler))
2333 gtk_widget_hide (priv->self_user_output_frame);
2335 priv->outgoing = TRUE;
2336 empathy_call_window_set_state_connecting (window);
2338 priv->call_started = TRUE;
2339 empathy_call_handler_start_call (priv->handler);
2340 empathy_call_window_setup_avatars (window, priv->handler);
2341 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2343 gtk_action_set_sensitive (priv->redial, FALSE);
2344 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2348 empathy_call_window_redial_cb (gpointer object,
2349 EmpathyCallWindow *window)
2351 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2353 if (priv->call_state == CONNECTED)
2354 priv->call_state = REDIALING;
2356 empathy_call_handler_stop_call (priv->handler);
2358 if (priv->call_state != CONNECTED)
2359 empathy_call_window_restart_call (window);
2363 empathy_call_window_fullscreen_cb (gpointer object,
2364 EmpathyCallWindow *window)
2366 empathy_call_window_fullscreen_toggle (window);
2370 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
2372 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2374 if (priv->is_fullscreen)
2375 gtk_window_unfullscreen (GTK_WINDOW (window));
2377 gtk_window_fullscreen (GTK_WINDOW (window));
2381 empathy_call_window_video_button_press_cb (GtkWidget *video_output,
2382 GdkEventButton *event, EmpathyCallWindow *window)
2384 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2386 empathy_call_window_video_menu_popup (window, event->button);
2394 empathy_call_window_key_press_cb (GtkWidget *video_output,
2395 GdkEventKey *event, EmpathyCallWindow *window)
2397 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2399 if (priv->is_fullscreen && event->keyval == GDK_Escape)
2401 /* Since we are in fullscreen mode, toggling will bring us back to
2403 empathy_call_window_fullscreen_toggle (window);
2411 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
2412 GdkEventMotion *event, EmpathyCallWindow *window)
2414 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2416 if (priv->is_fullscreen)
2418 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
2425 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
2429 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2431 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2433 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
2434 button, gtk_get_current_event_time ());
2435 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
2439 empathy_call_window_status_message (EmpathyCallWindow *window,
2442 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2444 if (priv->context_id == 0)
2446 priv->context_id = gtk_statusbar_get_context_id (
2447 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
2451 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
2454 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
2459 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
2460 gdouble value, EmpathyCallWindow *window)
2462 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2464 if (priv->audio_output == NULL)
2467 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),