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 <libempathy/empathy-tp-contact-factory.h>
35 #include <libempathy/empathy-call-factory.h>
36 #include <libempathy/empathy-utils.h>
37 #include <libempathy-gtk/empathy-avatar-image.h>
38 #include <libempathy-gtk/empathy-video-widget.h>
39 #include <libempathy-gtk/empathy-audio-src.h>
40 #include <libempathy-gtk/empathy-audio-sink.h>
41 #include <libempathy-gtk/empathy-video-src.h>
42 #include <libempathy-gtk/empathy-ui-utils.h>
44 #include "empathy-call-window.h"
46 #include "empathy-call-window-fullscreen.h"
47 #include "empathy-sidebar.h"
49 #define BUTTON_ID "empathy-call-dtmf-button-id"
51 #define CONTENT_HBOX_BORDER_WIDTH 6
52 #define CONTENT_HBOX_SPACING 3
53 #define CONTENT_HBOX_CHILDREN_PACKING_PADDING 3
55 #define SELF_VIDEO_SECTION_WIDTH 160
56 #define SELF_VIDEO_SECTION_HEIGTH 120
58 /* The avatar's default width and height are set to the same value because we
59 want a square icon. */
60 #define REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
61 #define REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
63 #define CONNECTING_STATUS_TEXT _("Connecting...")
65 G_DEFINE_TYPE(EmpathyCallWindow, empathy_call_window, GTK_TYPE_WINDOW)
74 static guint signals[LAST_SIGNAL] = {0};
78 PROP_CALL_HANDLER = 1,
81 /* private structure */
82 typedef struct _EmpathyCallWindowPriv EmpathyCallWindowPriv;
84 struct _EmpathyCallWindowPriv
86 gboolean dispose_has_run;
87 EmpathyCallHandler *handler;
88 EmpathyContact *contact;
92 GtkUIManager *ui_manager;
93 GtkWidget *video_output;
94 GtkWidget *video_preview;
95 GtkWidget *remote_user_avatar_widget;
96 GtkWidget *self_user_avatar_widget;
98 GtkWidget *sidebar_button;
100 GtkWidget *volume_button;
101 GtkWidget *redial_button;
102 GtkWidget *mic_button;
103 GtkWidget *camera_button;
106 GtkAction *show_preview;
107 GtkAction *send_video;
109 GtkAction *menu_fullscreen;
111 /* The frames and boxes that contain self and remote avatar and video
112 input/output. When we redial, we destroy and re-create the boxes */
113 GtkWidget *remote_user_output_frame;
114 GtkWidget *self_user_output_frame;
115 GtkWidget *remote_user_output_hbox;
116 GtkWidget *self_user_output_hbox;
118 /* We keep a reference on the hbox which contains the main content so we can
119 easilly repack everything when toggling fullscreen */
120 GtkWidget *content_hbox;
122 /* This vbox is contained in the content_hbox and it contains the
123 self_user_output_frame and the sidebar button. When toggling fullscreen,
124 it needs to be repacked. We keep a reference on it for easier access. */
127 gulong video_output_motion_handler_id;
130 GtkWidget *volume_progress_bar;
131 GtkAdjustment *audio_input_adj;
133 GtkWidget *dtmf_panel;
135 GstElement *video_input;
136 GstElement *audio_input;
137 GstElement *audio_output;
138 GstElement *pipeline;
139 GstElement *video_tee;
142 GstElement *liveadder;
149 GtkWidget *video_contrast;
150 GtkWidget *video_brightness;
151 GtkWidget *video_gamma;
154 gboolean call_started;
155 gboolean sending_video;
157 EmpathyCallWindowFullscreen *fullscreen;
158 gboolean is_fullscreen;
160 /* Those fields represent the state of the window before it actually was in
162 gboolean sidebar_was_visible_before_fs;
163 gint original_width_before_fs;
164 gint original_height_before_fs;
166 /* Used to indicate if we are currently redialing. If we are, as soon as the
167 channel is closed, the call is automatically re-initiated.*/
171 #define GET_PRIV(o) \
172 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_CALL_WINDOW, \
173 EmpathyCallWindowPriv))
175 static void empathy_call_window_realized_cb (GtkWidget *widget,
176 EmpathyCallWindow *window);
178 static gboolean empathy_call_window_delete_cb (GtkWidget *widget,
179 GdkEvent *event, EmpathyCallWindow *window);
181 static gboolean empathy_call_window_state_event_cb (GtkWidget *widget,
182 GdkEventWindowState *event, EmpathyCallWindow *window);
184 static void empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
185 EmpathyCallWindow *window);
187 static void empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
188 EmpathyCallWindow *window);
190 static void empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
191 EmpathyCallWindow *window);
193 static void empathy_call_window_show_preview_toggled_cb (GtkToggleAction *toggle,
194 EmpathyCallWindow *window);
196 static void empathy_call_window_mic_toggled_cb (
197 GtkToggleToolButton *toggle, EmpathyCallWindow *window);
199 static void empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
200 EmpathyCallWindow *window);
202 static void empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
203 EmpathyCallWindow *window);
205 static void empathy_call_window_hangup_cb (gpointer object,
206 EmpathyCallWindow *window);
208 static void empathy_call_window_fullscreen_cb (gpointer object,
209 EmpathyCallWindow *window);
211 static void empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window);
213 static gboolean empathy_call_window_video_button_press_cb (GtkWidget *video_output,
214 GdkEventButton *event, EmpathyCallWindow *window);
216 static gboolean empathy_call_window_key_press_cb (GtkWidget *video_output,
217 GdkEventKey *event, EmpathyCallWindow *window);
219 static gboolean empathy_call_window_video_output_motion_notify (GtkWidget *widget,
220 GdkEventMotion *event, EmpathyCallWindow *window);
222 static void empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
225 static void empathy_call_window_redial_cb (gpointer object,
226 EmpathyCallWindow *window);
228 static void empathy_call_window_restart_call (EmpathyCallWindow *window);
230 static void empathy_call_window_status_message (EmpathyCallWindow *window,
233 static void empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
234 EmpathyCallWindow *window);
236 static gboolean empathy_call_window_bus_message (GstBus *bus,
237 GstMessage *message, gpointer user_data);
240 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
241 gdouble value, EmpathyCallWindow *window);
244 empathy_call_window_setup_toolbar (EmpathyCallWindow *self)
246 EmpathyCallWindowPriv *priv = GET_PRIV (self);
247 GtkToolItem *tool_item;
249 /* Add an empty expanded GtkToolItem so the volume button is at the end of
251 tool_item = gtk_tool_item_new ();
252 gtk_tool_item_set_expand (tool_item, TRUE);
253 gtk_widget_show (GTK_WIDGET (tool_item));
254 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
256 priv->volume_button = gtk_volume_button_new ();
257 /* FIXME listen to the audiosinks signals and update the button according to
258 * that, for now starting out at 1.0 and assuming only the app changes the
260 gtk_scale_button_set_value (GTK_SCALE_BUTTON (priv->volume_button), 1.0);
261 g_signal_connect (G_OBJECT (priv->volume_button), "value-changed",
262 G_CALLBACK (empathy_call_window_volume_changed_cb), self);
264 tool_item = gtk_tool_item_new ();
265 gtk_container_add (GTK_CONTAINER (tool_item), priv->volume_button);
266 gtk_widget_show_all (GTK_WIDGET (tool_item));
267 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
271 dtmf_button_pressed_cb (GtkButton *button, EmpathyCallWindow *window)
273 EmpathyCallWindowPriv *priv = GET_PRIV (window);
278 g_object_get (priv->handler, "tp-call", &call, NULL);
280 button_quark = g_quark_from_static_string (BUTTON_ID);
281 event = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (button),
284 empathy_tp_call_start_tone (call, event);
286 g_object_unref (call);
290 dtmf_button_released_cb (GtkButton *button, EmpathyCallWindow *window)
292 EmpathyCallWindowPriv *priv = GET_PRIV (window);
295 g_object_get (priv->handler, "tp-call", &call, NULL);
297 empathy_tp_call_stop_tone (call);
299 g_object_unref (call);
303 empathy_call_window_create_dtmf (EmpathyCallWindow *self)
311 } dtmfbuttons[] = { { "1", TP_DTMF_EVENT_DIGIT_1 },
312 { "2", TP_DTMF_EVENT_DIGIT_2 },
313 { "3", TP_DTMF_EVENT_DIGIT_3 },
314 { "4", TP_DTMF_EVENT_DIGIT_4 },
315 { "5", TP_DTMF_EVENT_DIGIT_5 },
316 { "6", TP_DTMF_EVENT_DIGIT_6 },
317 { "7", TP_DTMF_EVENT_DIGIT_7 },
318 { "8", TP_DTMF_EVENT_DIGIT_8 },
319 { "9", TP_DTMF_EVENT_DIGIT_9 },
320 { "#", TP_DTMF_EVENT_HASH },
321 { "0", TP_DTMF_EVENT_DIGIT_0 },
322 { "*", TP_DTMF_EVENT_ASTERISK },
325 button_quark = g_quark_from_static_string (BUTTON_ID);
327 table = gtk_table_new (4, 3, TRUE);
329 for (i = 0; dtmfbuttons[i].label != NULL; i++)
331 GtkWidget *button = gtk_button_new_with_label (dtmfbuttons[i].label);
332 gtk_table_attach (GTK_TABLE (table), button, i % 3, i % 3 + 1,
333 i/3, i/3 + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
335 g_object_set_qdata (G_OBJECT (button), button_quark,
336 GUINT_TO_POINTER (dtmfbuttons[i].event));
338 g_signal_connect (G_OBJECT (button), "pressed",
339 G_CALLBACK (dtmf_button_pressed_cb), self);
340 g_signal_connect (G_OBJECT (button), "released",
341 G_CALLBACK (dtmf_button_released_cb), self);
348 empathy_call_window_create_video_input_add_slider (EmpathyCallWindow *self,
349 gchar *label_text, GtkWidget *bin)
351 GtkWidget *vbox = gtk_vbox_new (FALSE, 2);
352 GtkWidget *scale = gtk_vscale_new_with_range (0, 100, 10);
353 GtkWidget *label = gtk_label_new (label_text);
355 gtk_widget_set_sensitive (scale, FALSE);
357 gtk_container_add (GTK_CONTAINER (bin), vbox);
359 gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
360 gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 0);
361 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
367 empathy_call_window_video_contrast_changed_cb (GtkAdjustment *adj,
368 EmpathyCallWindow *self)
371 EmpathyCallWindowPriv *priv = GET_PRIV (self);
373 empathy_video_src_set_channel (priv->video_input,
374 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST, gtk_adjustment_get_value (adj));
378 empathy_call_window_video_brightness_changed_cb (GtkAdjustment *adj,
379 EmpathyCallWindow *self)
382 EmpathyCallWindowPriv *priv = GET_PRIV (self);
384 empathy_video_src_set_channel (priv->video_input,
385 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS, gtk_adjustment_get_value (adj));
389 empathy_call_window_video_gamma_changed_cb (GtkAdjustment *adj,
390 EmpathyCallWindow *self)
393 EmpathyCallWindowPriv *priv = GET_PRIV (self);
395 empathy_video_src_set_channel (priv->video_input,
396 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA, gtk_adjustment_get_value (adj));
401 empathy_call_window_create_video_input (EmpathyCallWindow *self)
403 EmpathyCallWindowPriv *priv = GET_PRIV (self);
406 hbox = gtk_hbox_new (TRUE, 3);
408 priv->video_contrast = empathy_call_window_create_video_input_add_slider (
409 self, _("Contrast"), hbox);
411 priv->video_brightness = empathy_call_window_create_video_input_add_slider (
412 self, _("Brightness"), hbox);
414 priv->video_gamma = empathy_call_window_create_video_input_add_slider (
415 self, _("Gamma"), hbox);
421 empathy_call_window_setup_video_input (EmpathyCallWindow *self)
423 EmpathyCallWindowPriv *priv = GET_PRIV (self);
427 supported = empathy_video_src_get_supported_channels (priv->video_input);
429 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST)
431 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_contrast));
433 gtk_adjustment_set_value (adj,
434 empathy_video_src_get_channel (priv->video_input,
435 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST));
437 g_signal_connect (G_OBJECT (adj), "value-changed",
438 G_CALLBACK (empathy_call_window_video_contrast_changed_cb), self);
440 gtk_widget_set_sensitive (priv->video_contrast, TRUE);
443 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS)
445 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_brightness));
447 gtk_adjustment_set_value (adj,
448 empathy_video_src_get_channel (priv->video_input,
449 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS));
451 g_signal_connect (G_OBJECT (adj), "value-changed",
452 G_CALLBACK (empathy_call_window_video_brightness_changed_cb), self);
453 gtk_widget_set_sensitive (priv->video_brightness, TRUE);
456 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA)
458 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_gamma));
460 gtk_adjustment_set_value (adj,
461 empathy_video_src_get_channel (priv->video_input,
462 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA));
464 g_signal_connect (G_OBJECT (adj), "value-changed",
465 G_CALLBACK (empathy_call_window_video_gamma_changed_cb), self);
466 gtk_widget_set_sensitive (priv->video_gamma, TRUE);
471 empathy_call_window_mic_volume_changed_cb (GtkAdjustment *adj,
472 EmpathyCallWindow *self)
474 EmpathyCallWindowPriv *priv = GET_PRIV (self);
477 volume = gtk_adjustment_get_value (adj)/100.0;
479 /* Don't store the volume because of muting */
480 if (volume > 0 || gtk_toggle_tool_button_get_active (
481 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
482 priv->volume = volume;
484 /* Ensure that the toggle button is active if the volume is > 0 and inactive
485 * if it's smaller then 0 */
486 if ((volume > 0) != gtk_toggle_tool_button_get_active (
487 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
488 gtk_toggle_tool_button_set_active (
489 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), volume > 0);
491 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
496 empathy_call_window_audio_input_level_changed_cb (EmpathyGstAudioSrc *src,
497 gdouble level, EmpathyCallWindow *window)
500 EmpathyCallWindowPriv *priv = GET_PRIV (window);
502 value = CLAMP (pow (10, level / 20), 0.0, 1.0);
503 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar), value);
507 empathy_call_window_create_audio_input (EmpathyCallWindow *self)
509 EmpathyCallWindowPriv *priv = GET_PRIV (self);
510 GtkWidget *hbox, *vbox, *scale, *label;
513 hbox = gtk_hbox_new (TRUE, 3);
515 vbox = gtk_vbox_new (FALSE, 3);
516 gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 3);
518 scale = gtk_vscale_new_with_range (0, 150, 100);
519 gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
520 label = gtk_label_new (_("Volume"));
522 priv->audio_input_adj = adj = gtk_range_get_adjustment (GTK_RANGE (scale));
523 priv->volume = empathy_audio_src_get_volume (EMPATHY_GST_AUDIO_SRC
524 (priv->audio_input));
525 gtk_adjustment_set_value (adj, priv->volume * 100);
527 g_signal_connect (G_OBJECT (adj), "value-changed",
528 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), self);
530 gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 3);
531 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 3);
533 priv->volume_progress_bar = gtk_progress_bar_new ();
534 gtk_progress_bar_set_orientation (
535 GTK_PROGRESS_BAR (priv->volume_progress_bar), GTK_PROGRESS_BOTTOM_TO_TOP);
536 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
539 gtk_box_pack_start (GTK_BOX (hbox), priv->volume_progress_bar, FALSE, FALSE,
546 empathy_call_window_setup_remote_frame (GstBus *bus, EmpathyCallWindow *self)
548 EmpathyCallWindowPriv *priv = GET_PRIV (self);
550 /* Initializing all the content (UI and output gst elements) related to the
552 priv->remote_user_output_hbox = gtk_hbox_new (FALSE, 0);
554 priv->remote_user_avatar_widget = gtk_image_new ();
555 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
556 priv->remote_user_avatar_widget, TRUE, TRUE, 0);
558 priv->video_output = empathy_video_widget_new (bus);
559 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
560 priv->video_output, TRUE, TRUE, 0);
562 gtk_widget_add_events (priv->video_output,
563 GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
564 g_signal_connect (G_OBJECT (priv->video_output), "button-press-event",
565 G_CALLBACK (empathy_call_window_video_button_press_cb), self);
567 gtk_container_add (GTK_CONTAINER (priv->remote_user_output_frame),
568 priv->remote_user_output_hbox);
570 priv->audio_output = empathy_audio_sink_new ();
571 gst_object_ref (priv->audio_output);
572 gst_object_sink (priv->audio_output);
576 empathy_call_window_setup_self_frame (GstBus *bus, EmpathyCallWindow *self)
578 EmpathyCallWindowPriv *priv = GET_PRIV (self);
580 /* Initializing all the content (UI and input gst elements) related to the
581 self contact, except for the video preview widget. This widget is only
582 initialized when the "show video preview" option is activated */
583 priv->self_user_output_hbox = gtk_hbox_new (FALSE, 0);
585 priv->self_user_avatar_widget = gtk_image_new ();
586 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
587 priv->self_user_avatar_widget, TRUE, TRUE, 0);
589 gtk_container_add (GTK_CONTAINER (priv->self_user_output_frame),
590 priv->self_user_output_hbox);
592 priv->video_input = empathy_video_src_new ();
593 gst_object_ref (priv->video_input);
594 gst_object_sink (priv->video_input);
596 priv->audio_input = empathy_audio_src_new ();
597 gst_object_ref (priv->audio_input);
598 gst_object_sink (priv->audio_input);
600 g_signal_connect (priv->audio_input, "peak-level-changed",
601 G_CALLBACK (empathy_call_window_audio_input_level_changed_cb), self);
605 empathy_call_window_setup_video_preview (EmpathyCallWindow *window)
608 EmpathyCallWindowPriv *priv = GET_PRIV (window);
609 GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
611 priv->video_tee = gst_element_factory_make ("tee", NULL);
612 gst_object_ref (priv->video_tee);
613 gst_object_sink (priv->video_tee);
615 priv->video_preview = empathy_video_widget_new_with_size (bus,
616 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
617 g_object_set (priv->video_preview, "sync", FALSE, "async", TRUE, NULL);
618 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
619 priv->video_preview, TRUE, TRUE, 0);
621 g_object_unref (bus);
623 preview = empathy_video_widget_get_element (
624 EMPATHY_VIDEO_WIDGET (priv->video_preview));
625 gst_bin_add_many (GST_BIN (priv->pipeline), priv->video_input,
626 priv->video_tee, preview, NULL);
627 gst_element_link_many (priv->video_input, priv->video_tee,
630 gst_element_set_state (preview, GST_STATE_PLAYING);
631 gst_element_set_state (priv->video_input, GST_STATE_PLAYING);
632 gst_element_set_state (priv->video_tee, GST_STATE_PLAYING);
636 empathy_call_window_init (EmpathyCallWindow *self)
638 EmpathyCallWindowPriv *priv = GET_PRIV (self);
647 filename = empathy_file_lookup ("empathy-call-window.ui", "src");
648 gui = empathy_builder_get_file (filename,
649 "call_window_vbox", &top_vbox,
651 "statusbar", &priv->statusbar,
652 "redial", &priv->redial_button,
653 "microphone", &priv->mic_button,
654 "camera", &priv->camera_button,
655 "toolbar", &priv->toolbar,
656 "send_video", &priv->send_video,
657 "menuredial", &priv->redial,
658 "show_preview", &priv->show_preview,
659 "ui_manager", &priv->ui_manager,
660 "menufullscreen", &priv->menu_fullscreen,
663 empathy_builder_connect (gui, self,
664 "menuhangup", "activate", empathy_call_window_hangup_cb,
665 "hangup", "clicked", empathy_call_window_hangup_cb,
666 "menuredial", "activate", empathy_call_window_redial_cb,
667 "redial", "clicked", empathy_call_window_redial_cb,
668 "microphone", "toggled", empathy_call_window_mic_toggled_cb,
669 "camera", "toggled", empathy_call_window_camera_toggled_cb,
670 "send_video", "toggled", empathy_call_window_send_video_toggled_cb,
671 "show_preview", "toggled", empathy_call_window_show_preview_toggled_cb,
672 "menufullscreen", "activate", empathy_call_window_fullscreen_cb,
675 priv->lock = g_mutex_new ();
677 gtk_container_add (GTK_CONTAINER (self), top_vbox);
679 empathy_call_window_setup_toolbar (self);
681 priv->content_hbox = gtk_hbox_new (FALSE, CONTENT_HBOX_SPACING);
682 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
683 CONTENT_HBOX_BORDER_WIDTH);
684 gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
686 priv->pipeline = gst_pipeline_new (NULL);
687 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
688 gst_bus_add_watch (bus, empathy_call_window_bus_message, self);
690 priv->remote_user_output_frame = gtk_frame_new (NULL);
691 gtk_widget_set_size_request (priv->remote_user_output_frame,
692 EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT);
693 gtk_box_pack_start (GTK_BOX (priv->content_hbox),
694 priv->remote_user_output_frame, TRUE, TRUE,
695 CONTENT_HBOX_CHILDREN_PACKING_PADDING);
696 empathy_call_window_setup_remote_frame (bus, self);
698 priv->self_user_output_frame = gtk_frame_new (NULL);
699 gtk_widget_set_size_request (priv->self_user_output_frame,
700 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
702 priv->vbox = gtk_vbox_new (FALSE, 3);
703 gtk_box_pack_start (GTK_BOX (priv->content_hbox), priv->vbox,
704 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
705 gtk_box_pack_start (GTK_BOX (priv->vbox), priv->self_user_output_frame, FALSE,
707 empathy_call_window_setup_self_frame (bus, self);
709 g_object_unref (bus);
711 priv->sidebar_button = gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
712 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
713 g_signal_connect (G_OBJECT (priv->sidebar_button), "toggled",
714 G_CALLBACK (empathy_call_window_sidebar_toggled_cb), self);
716 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
718 h = gtk_hbox_new (FALSE, 3);
719 gtk_box_pack_end (GTK_BOX (priv->vbox), h, FALSE, FALSE, 3);
720 gtk_box_pack_end (GTK_BOX (h), priv->sidebar_button, FALSE, FALSE, 3);
722 priv->sidebar = empathy_sidebar_new ();
723 g_signal_connect (G_OBJECT (priv->sidebar),
724 "hide", G_CALLBACK (empathy_call_window_sidebar_hidden_cb), self);
725 g_signal_connect (G_OBJECT (priv->sidebar),
726 "show", G_CALLBACK (empathy_call_window_sidebar_shown_cb), self);
727 gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
729 priv->dtmf_panel = empathy_call_window_create_dtmf (self);
730 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Dialpad"),
733 gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
735 page = empathy_call_window_create_audio_input (self);
736 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Audio input"),
739 page = empathy_call_window_create_video_input (self);
740 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Video input"),
743 gtk_widget_show_all (top_vbox);
745 gtk_widget_hide (priv->sidebar);
747 priv->fullscreen = empathy_call_window_fullscreen_new (self);
748 empathy_call_window_fullscreen_set_video_widget (priv->fullscreen, priv->video_output);
749 g_signal_connect (G_OBJECT (priv->fullscreen->leave_fullscreen_button),
750 "clicked", G_CALLBACK (empathy_call_window_fullscreen_cb), self);
752 g_signal_connect (G_OBJECT (self), "realize",
753 G_CALLBACK (empathy_call_window_realized_cb), self);
755 g_signal_connect (G_OBJECT (self), "delete-event",
756 G_CALLBACK (empathy_call_window_delete_cb), self);
758 g_signal_connect (G_OBJECT (self), "window-state-event",
759 G_CALLBACK (empathy_call_window_state_event_cb), self);
761 g_signal_connect (G_OBJECT (self), "key-press-event",
762 G_CALLBACK (empathy_call_window_key_press_cb), self);
764 empathy_call_window_status_message (self, CONNECTING_STATUS_TEXT);
766 priv->timer = g_timer_new ();
768 g_object_ref (priv->ui_manager);
769 g_object_unref (gui);
773 /* Instead of specifying a width and a height, we specify only one size. That's
774 because we want a square avatar icon. */
776 init_contact_avatar_with_size (EmpathyContact *contact, GtkWidget *image_widget,
780 GdkPixbuf *pixbuf_avatar = NULL;
784 pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact,
788 if (pixbuf_avatar == NULL)
790 pixbuf_avatar = empathy_pixbuf_from_icon_name_sized ("stock_person",
794 gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget), pixbuf_avatar);
798 set_window_title (EmpathyCallWindow *self)
800 EmpathyCallWindowPriv *priv = GET_PRIV (self);
803 tmp = g_strdup_printf (_("Call with %s"),
804 empathy_contact_get_name (priv->contact));
805 gtk_window_set_title (GTK_WINDOW (self), tmp);
810 contact_name_changed_cb (EmpathyContact *contact,
811 GParamSpec *pspec, EmpathyCallWindow *self)
813 set_window_title (self);
817 contact_avatar_changed_cb (EmpathyContact *contact,
818 GParamSpec *pspec, GtkWidget *avatar_widget)
820 init_contact_avatar_with_size (contact, avatar_widget,
821 avatar_widget->allocation.height);
825 empathy_call_window_got_self_contact_cb (EmpathyTpContactFactory *factory,
826 EmpathyContact *contact, const GError *error, gpointer user_data,
827 GObject *weak_object)
829 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
830 EmpathyCallWindowPriv *priv = GET_PRIV (self);
832 init_contact_avatar_with_size (contact, priv->self_user_avatar_widget,
833 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
835 g_signal_connect (contact, "notify::avatar",
836 G_CALLBACK (contact_avatar_changed_cb), priv->self_user_avatar_widget);
840 empathy_call_window_setup_avatars (EmpathyCallWindow *self,
841 EmpathyCallHandler *handler)
843 EmpathyCallWindowPriv *priv = GET_PRIV (self);
845 g_object_get (handler, "contact", &(priv->contact), NULL);
847 if (priv->contact != NULL)
849 TpConnection *connection;
850 EmpathyTpContactFactory *factory;
852 set_window_title (self);
854 g_signal_connect (priv->contact, "notify::name",
855 G_CALLBACK (contact_name_changed_cb), self);
856 g_signal_connect (priv->contact, "notify::avatar",
857 G_CALLBACK (contact_avatar_changed_cb),
858 priv->remote_user_avatar_widget);
860 /* Retreiving the self avatar */
861 connection = empathy_contact_get_connection (priv->contact);
862 factory = empathy_tp_contact_factory_dup_singleton (connection);
863 empathy_tp_contact_factory_get_from_handle (factory,
864 tp_connection_get_self_handle (connection),
865 empathy_call_window_got_self_contact_cb, self, NULL, NULL);
867 g_object_unref (factory);
871 g_warning ("call handler doesn't have a contact");
872 gtk_window_set_title (GTK_WINDOW (self), _("Call"));
874 /* Since we can't access the remote contact, we can't get a connection
875 to it and can't get the self contact (and its avatar). This means
876 that we have to manually set the self avatar. */
877 init_contact_avatar_with_size (NULL, priv->self_user_avatar_widget,
878 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
881 init_contact_avatar_with_size (priv->contact,
882 priv->remote_user_avatar_widget, MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
883 REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
885 /* The remote avatar is shown by default and will be hidden when we receive
886 video from the remote side. */
887 gtk_widget_hide (priv->video_output);
888 gtk_widget_show (priv->remote_user_avatar_widget);
892 empathy_call_window_setup_video_preview_visibility (EmpathyCallWindow *self,
893 EmpathyCallHandler *handler)
895 EmpathyCallWindowPriv *priv = GET_PRIV (self);
896 gboolean initial_video = empathy_call_handler_has_initial_video (priv->handler);
900 empathy_call_window_setup_video_preview (self);
901 gtk_widget_hide (priv->self_user_avatar_widget);
902 gtk_widget_show (priv->video_preview);
905 gtk_widget_show (priv->self_user_avatar_widget);
907 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
912 empathy_call_window_constructed (GObject *object)
914 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
915 EmpathyCallWindowPriv *priv = GET_PRIV (self);
917 g_assert (priv->handler != NULL);
918 empathy_call_window_setup_avatars (self, priv->handler);
919 empathy_call_window_setup_video_preview_visibility (self, priv->handler);
922 static void empathy_call_window_dispose (GObject *object);
923 static void empathy_call_window_finalize (GObject *object);
926 empathy_call_window_set_property (GObject *object,
927 guint property_id, const GValue *value, GParamSpec *pspec)
929 EmpathyCallWindowPriv *priv = GET_PRIV (object);
933 case PROP_CALL_HANDLER:
934 priv->handler = g_value_dup_object (value);
937 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
942 empathy_call_window_get_property (GObject *object,
943 guint property_id, GValue *value, GParamSpec *pspec)
945 EmpathyCallWindowPriv *priv = GET_PRIV (object);
949 case PROP_CALL_HANDLER:
950 g_value_set_object (value, priv->handler);
953 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
958 empathy_call_window_class_init (
959 EmpathyCallWindowClass *empathy_call_window_class)
961 GObjectClass *object_class = G_OBJECT_CLASS (empathy_call_window_class);
962 GParamSpec *param_spec;
964 g_type_class_add_private (empathy_call_window_class,
965 sizeof (EmpathyCallWindowPriv));
967 object_class->constructed = empathy_call_window_constructed;
968 object_class->set_property = empathy_call_window_set_property;
969 object_class->get_property = empathy_call_window_get_property;
971 object_class->dispose = empathy_call_window_dispose;
972 object_class->finalize = empathy_call_window_finalize;
974 param_spec = g_param_spec_object ("handler",
975 "handler", "The call handler",
976 EMPATHY_TYPE_CALL_HANDLER,
977 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
978 g_object_class_install_property (object_class,
979 PROP_CALL_HANDLER, param_spec);
984 empathy_call_window_dispose (GObject *object)
986 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
987 EmpathyCallWindowPriv *priv = GET_PRIV (self);
989 if (priv->dispose_has_run)
992 priv->dispose_has_run = TRUE;
994 if (priv->handler != NULL)
995 g_object_unref (priv->handler);
997 priv->handler = NULL;
999 if (priv->pipeline != NULL)
1000 g_object_unref (priv->pipeline);
1001 priv->pipeline = NULL;
1003 if (priv->video_input != NULL)
1004 g_object_unref (priv->video_input);
1005 priv->video_input = NULL;
1007 if (priv->audio_input != NULL)
1008 g_object_unref (priv->audio_input);
1009 priv->audio_input = NULL;
1011 if (priv->audio_output != NULL)
1012 g_object_unref (priv->audio_output);
1013 priv->audio_output = NULL;
1015 if (priv->video_tee != NULL)
1016 g_object_unref (priv->video_tee);
1017 priv->video_tee = NULL;
1019 if (priv->timer_id != 0)
1020 g_source_remove (priv->timer_id);
1023 if (priv->ui_manager != NULL)
1024 g_object_unref (priv->ui_manager);
1025 priv->ui_manager = NULL;
1027 if (priv->contact != NULL)
1029 g_signal_handlers_disconnect_by_func (priv->contact,
1030 contact_name_changed_cb, self);
1031 g_object_unref (priv->contact);
1032 priv->contact = NULL;
1035 /* release any references held by the object here */
1036 if (G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose)
1037 G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
1041 empathy_call_window_finalize (GObject *object)
1043 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1044 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1046 if (priv->video_output_motion_handler_id != 0)
1048 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1049 priv->video_output_motion_handler_id);
1050 priv->video_output_motion_handler_id = 0;
1053 /* free any data held directly by the object here */
1054 g_mutex_free (priv->lock);
1056 g_timer_destroy (priv->timer);
1058 G_OBJECT_CLASS (empathy_call_window_parent_class)->finalize (object);
1063 empathy_call_window_new (EmpathyCallHandler *handler)
1065 return EMPATHY_CALL_WINDOW (
1066 g_object_new (EMPATHY_TYPE_CALL_WINDOW, "handler", handler, NULL));
1070 empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
1071 GstElement *conference, gpointer user_data)
1073 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1074 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1076 gst_bin_add (GST_BIN (priv->pipeline), conference);
1078 gst_element_set_state (conference, GST_STATE_PLAYING);
1082 empathy_call_window_request_resource_cb (EmpathyCallHandler *handler,
1083 FsMediaType type, FsStreamDirection direction, gpointer user_data)
1085 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1086 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1088 if (type != TP_MEDIA_STREAM_TYPE_VIDEO)
1091 if (direction == FS_DIRECTION_RECV)
1094 /* video and direction is send */
1095 return priv->video_input != NULL;
1099 empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
1101 GstStateChangeReturn state_change_return;
1102 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1104 if (priv->pipeline == NULL)
1107 state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1109 if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1110 state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1112 priv->liveadder = NULL;
1113 priv->funnel = NULL;
1114 g_object_unref (priv->pipeline);
1115 priv->pipeline = NULL;
1121 g_message ("Error: could not destroy pipeline. Closing call window");
1122 gtk_widget_destroy (GTK_WIDGET (self));
1129 empathy_call_window_disconnected (EmpathyCallWindow *self)
1131 gboolean could_disconnect = FALSE;
1132 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1133 gboolean could_reset_pipeline = empathy_call_window_reset_pipeline (self);
1135 if (could_reset_pipeline)
1137 g_mutex_lock (priv->lock);
1139 g_timer_stop (priv->timer);
1141 if (priv->timer_id != 0)
1142 g_source_remove (priv->timer_id);
1145 g_mutex_unlock (priv->lock);
1147 empathy_call_window_status_message (self, _("Disconnected"));
1149 gtk_action_set_sensitive (priv->redial, TRUE);
1150 gtk_widget_set_sensitive (priv->redial_button, TRUE);
1151 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1152 gtk_action_set_sensitive (priv->send_video, FALSE);
1153 priv->sending_video = FALSE;
1154 priv->connected = FALSE;
1155 priv->call_started = FALSE;
1157 could_disconnect = TRUE;
1160 return could_disconnect;
1165 empathy_call_window_channel_closed_cb (TfChannel *channel, gpointer user_data)
1167 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1168 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1170 if (empathy_call_window_disconnected (self) && priv->redialing)
1172 empathy_call_window_restart_call (self);
1173 priv->redialing = FALSE;
1177 /* Called with global lock held */
1179 empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
1181 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1184 if (priv->funnel == NULL)
1188 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1189 (priv->video_output));
1191 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
1193 gst_bin_add (GST_BIN (priv->pipeline), priv->funnel);
1194 gst_bin_add (GST_BIN (priv->pipeline), output);
1196 gst_element_link (priv->funnel, output);
1198 gst_element_set_state (priv->funnel, GST_STATE_PLAYING);
1199 gst_element_set_state (output, GST_STATE_PLAYING);
1202 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
1207 /* Called with global lock held */
1209 empathy_call_window_get_audio_sink_pad (EmpathyCallWindow *self)
1211 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1214 if (priv->liveadder == NULL)
1216 priv->liveadder = gst_element_factory_make ("liveadder", NULL);
1218 gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder);
1219 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output);
1221 gst_element_link (priv->liveadder, priv->audio_output);
1223 gst_element_set_state (priv->liveadder, GST_STATE_PLAYING);
1224 gst_element_set_state (priv->audio_output, GST_STATE_PLAYING);
1227 pad = gst_element_get_request_pad (priv->liveadder, "sink%d");
1233 empathy_call_window_update_timer (gpointer user_data)
1235 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1236 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1240 time = g_timer_elapsed (priv->timer, NULL);
1242 /* Translators: number of minutes:seconds the caller has been connected */
1243 str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time / 60,
1245 empathy_call_window_status_message (self, str);
1252 empathy_call_window_connected (gpointer user_data)
1254 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1255 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1256 EmpathyTpCall *call;
1258 g_object_get (priv->handler, "tp-call", &call, NULL);
1260 if (empathy_tp_call_has_dtmf (call))
1261 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
1263 priv->sending_video = empathy_tp_call_is_sending_video (call);
1265 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1266 priv->sending_video);
1267 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1268 priv->sending_video);
1269 gtk_toggle_tool_button_set_active (
1270 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), priv->sending_video);
1272 if (priv->video_input != NULL)
1274 gtk_widget_set_sensitive (priv->camera_button, TRUE);
1275 gtk_action_set_sensitive (priv->send_video, TRUE);
1278 gtk_action_set_sensitive (priv->redial, FALSE);
1279 gtk_widget_set_sensitive (priv->redial_button, FALSE);
1281 empathy_call_window_update_avatars_visibility (call, self);
1283 g_object_unref (call);
1285 g_mutex_lock (priv->lock);
1287 priv->timer_id = g_timeout_add_seconds (1,
1288 empathy_call_window_update_timer, self);
1290 g_mutex_unlock (priv->lock);
1292 empathy_call_window_update_timer (self);
1298 /* Called from the streaming thread */
1300 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
1301 GstPad *src, guint media_type, gpointer user_data)
1303 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1304 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1308 g_mutex_lock (priv->lock);
1310 if (priv->connected == FALSE)
1312 g_timer_start (priv->timer);
1313 priv->timer_id = g_idle_add (empathy_call_window_connected, self);
1314 priv->connected = TRUE;
1319 case TP_MEDIA_STREAM_TYPE_AUDIO:
1320 pad = empathy_call_window_get_audio_sink_pad (self);
1322 case TP_MEDIA_STREAM_TYPE_VIDEO:
1323 gtk_widget_hide (priv->remote_user_avatar_widget);
1324 gtk_widget_show (priv->video_output);
1325 pad = empathy_call_window_get_video_sink_pad (self);
1328 g_assert_not_reached ();
1331 gst_pad_link (src, pad);
1332 gst_object_unref (pad);
1334 g_mutex_unlock (priv->lock);
1337 /* Called from the streaming thread */
1339 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
1340 GstPad *sink, guint media_type, gpointer user_data)
1342 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1343 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1348 case TP_MEDIA_STREAM_TYPE_AUDIO:
1349 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
1351 pad = gst_element_get_static_pad (priv->audio_input, "src");
1352 gst_pad_link (pad, sink);
1354 gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
1356 case TP_MEDIA_STREAM_TYPE_VIDEO:
1357 if (priv->video_input != NULL)
1359 if (priv->video_tee == NULL)
1360 empathy_call_window_setup_video_preview (self);
1362 gtk_toggle_action_set_active (
1363 GTK_TOGGLE_ACTION (priv->show_preview),TRUE);
1364 gtk_widget_show (priv->video_preview);
1365 gtk_widget_hide (priv->self_user_avatar_widget);
1367 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
1368 gst_pad_link (pad, sink);
1372 g_assert_not_reached ();
1378 empathy_gst_bin_has_child (GstBin *bin, GstElement *element)
1381 gboolean ret = FALSE;
1384 it = gst_bin_iterate_recurse (bin);
1388 switch (gst_iterator_next (it, (gpointer *)&item))
1390 case GST_ITERATOR_OK:
1391 if (item == element)
1393 gst_object_unref (GST_OBJECT (item));
1397 gst_object_unref (GST_OBJECT (item));
1399 case GST_ITERATOR_RESYNC:
1400 gst_iterator_resync (it);
1402 case GST_ITERATOR_ERROR:
1403 g_assert_not_reached ();
1405 case GST_ITERATOR_DONE:
1410 gst_iterator_free (it);
1417 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
1419 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1420 GstElement *preview;
1422 preview = empathy_video_widget_get_element (
1423 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1425 gst_element_set_state (priv->video_input, GST_STATE_NULL);
1426 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
1427 gst_element_set_state (preview, GST_STATE_NULL);
1429 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
1430 priv->video_tee, preview, NULL);
1432 g_object_unref (priv->video_input);
1433 priv->video_input = NULL;
1434 g_object_unref (priv->video_tee);
1435 priv->video_tee = NULL;
1437 gtk_widget_hide (priv->video_preview);
1438 gtk_widget_show (priv->self_user_avatar_widget);
1443 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
1446 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1447 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1450 empathy_call_handler_bus_message (priv->handler, bus, message);
1452 switch (GST_MESSAGE_TYPE (message))
1454 case GST_MESSAGE_STATE_CHANGED:
1455 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
1457 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1458 if (newstate == GST_STATE_PAUSED)
1459 empathy_call_window_setup_video_input (self);
1461 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
1462 !priv->call_started)
1464 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1465 if (newstate == GST_STATE_PAUSED)
1467 priv->call_started = TRUE;
1468 empathy_call_handler_start_call (priv->handler);
1469 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1473 case GST_MESSAGE_ERROR:
1475 GError *error = NULL;
1478 gst_message_parse_error (message, &error, &debug);
1480 g_message ("Element error: %s -- %s\n", error->message, debug);
1482 if (priv->video_input != NULL &&
1483 empathy_gst_bin_has_child (GST_BIN (priv->video_input),
1484 GST_ELEMENT (GST_MESSAGE_SRC (message))))
1486 /* Remove the video input and continue */
1487 empathy_call_window_remove_video_input (self);
1488 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1492 empathy_call_window_disconnected (self);
1494 g_error_free (error);
1505 empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
1506 EmpathyCallWindow *window)
1508 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1510 if (empathy_tp_call_is_receiving_video (call))
1512 gtk_widget_hide (priv->remote_user_avatar_widget);
1513 gtk_widget_show (priv->video_output);
1517 gtk_widget_hide (priv->video_output);
1518 gtk_widget_show (priv->remote_user_avatar_widget);
1521 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (priv->show_preview)))
1523 if (empathy_tp_call_is_sending_video (call))
1525 gtk_widget_hide (priv->self_user_avatar_widget);
1526 gtk_widget_show (priv->video_preview);
1530 gtk_widget_hide (priv->video_preview);
1531 gtk_widget_show (priv->self_user_avatar_widget);
1537 empathy_call_window_video_stream_changed_cb (EmpathyCallHandler *handler,
1538 EmpathyCallWindow *window)
1540 EmpathyTpCall *call;
1541 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1543 g_object_get (priv->handler, "tp-call", &call, NULL);
1545 empathy_call_window_update_avatars_visibility (call, window);
1546 g_object_unref (call);
1550 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
1552 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1554 g_signal_connect (priv->handler, "conference-added",
1555 G_CALLBACK (empathy_call_window_conference_added_cb), window);
1556 g_signal_connect (priv->handler, "request-resource",
1557 G_CALLBACK (empathy_call_window_request_resource_cb), window);
1558 g_signal_connect (priv->handler, "closed",
1559 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
1560 g_signal_connect (priv->handler, "src-pad-added",
1561 G_CALLBACK (empathy_call_window_src_added_cb), window);
1562 g_signal_connect (priv->handler, "sink-pad-added",
1563 G_CALLBACK (empathy_call_window_sink_added_cb), window);
1564 g_signal_connect (priv->handler, "video-stream-changed",
1565 G_CALLBACK (empathy_call_window_video_stream_changed_cb), window);
1567 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1571 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
1572 EmpathyCallWindow *window)
1574 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1576 if (priv->pipeline != NULL)
1577 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1583 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
1586 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1588 menu = gtk_ui_manager_get_widget (priv->ui_manager,
1593 gtk_widget_hide (priv->sidebar);
1594 gtk_widget_hide (menu);
1595 gtk_widget_hide (priv->vbox);
1596 gtk_widget_hide (priv->statusbar);
1597 gtk_widget_hide (priv->toolbar);
1601 if (priv->sidebar_was_visible_before_fs)
1602 gtk_widget_show (priv->sidebar);
1604 gtk_widget_show (menu);
1605 gtk_widget_show (priv->vbox);
1606 gtk_widget_show (priv->statusbar);
1607 gtk_widget_show (priv->toolbar);
1609 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
1610 priv->original_height_before_fs);
1615 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
1617 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1619 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
1620 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
1621 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
1622 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
1623 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
1624 priv->video_output, TRUE, TRUE,
1625 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
1627 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
1628 priv->vbox, TRUE, TRUE,
1629 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
1634 empathy_call_window_state_event_cb (GtkWidget *widget,
1635 GdkEventWindowState *event, EmpathyCallWindow *window)
1637 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
1639 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1640 gboolean set_fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
1644 gboolean sidebar_was_visible;
1645 gint original_width = GTK_WIDGET (window)->allocation.width;
1646 gint original_height = GTK_WIDGET (window)->allocation.height;
1648 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
1650 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
1651 priv->original_width_before_fs = original_width;
1652 priv->original_height_before_fs = original_height;
1654 if (priv->video_output_motion_handler_id == 0 &&
1655 priv->video_output != NULL)
1657 priv->video_output_motion_handler_id = g_signal_connect (
1658 G_OBJECT (priv->video_output), "motion-notify-event",
1659 G_CALLBACK (empathy_call_window_video_output_motion_notify), window);
1664 if (priv->video_output_motion_handler_id != 0)
1666 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1667 priv->video_output_motion_handler_id);
1668 priv->video_output_motion_handler_id = 0;
1672 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
1674 show_controls (window, set_fullscreen);
1675 show_borders (window, set_fullscreen);
1676 gtk_action_set_stock_id (priv->menu_fullscreen,
1677 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
1678 priv->is_fullscreen = set_fullscreen;
1685 empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
1686 EmpathyCallWindow *window)
1688 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1690 int w,h, handle_size;
1692 w = GTK_WIDGET (window)->allocation.width;
1693 h = GTK_WIDGET (window)->allocation.height;
1695 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
1697 if (gtk_toggle_button_get_active (toggle))
1699 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
1700 gtk_widget_show (priv->sidebar);
1701 w += priv->sidebar->allocation.width + handle_size;
1705 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
1706 w -= priv->sidebar->allocation.width + handle_size;
1707 gtk_widget_hide (priv->sidebar);
1710 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
1713 gtk_window_resize (GTK_WINDOW (window), w, h);
1717 empathy_call_window_set_send_video (EmpathyCallWindow *window,
1720 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1721 EmpathyTpCall *call;
1723 priv->sending_video = send;
1725 /* When we start sending video, we want to show the video preview by
1729 if (priv->video_preview == NULL)
1730 empathy_call_window_setup_video_preview (window);
1732 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1736 g_object_get (priv->handler, "tp-call", &call, NULL);
1737 empathy_tp_call_request_video_stream_direction (call, send);
1738 g_object_unref (call);
1742 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
1743 EmpathyCallWindow *window)
1745 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1748 active = (gtk_toggle_tool_button_get_active (toggle));
1750 if (priv->sending_video == active)
1753 empathy_call_window_set_send_video (window, active);
1754 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), active);
1758 empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
1759 EmpathyCallWindow *window)
1761 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1764 active = (gtk_toggle_action_get_active (toggle));
1766 if (priv->sending_video == active)
1769 empathy_call_window_set_send_video (window, active);
1770 gtk_toggle_tool_button_set_active (
1771 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), active);
1775 empathy_call_window_show_preview_toggled_cb (GtkToggleAction *toggle,
1776 EmpathyCallWindow *window)
1778 gboolean show_preview_toggled;
1779 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1781 show_preview_toggled = gtk_toggle_action_get_active (toggle);
1783 if (show_preview_toggled)
1784 gtk_widget_show (priv->self_user_output_frame);
1786 gtk_widget_hide (priv->self_user_output_frame);
1790 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
1791 EmpathyCallWindow *window)
1793 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1796 active = (gtk_toggle_tool_button_get_active (toggle));
1800 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
1802 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
1806 /* TODO, Instead of setting the input volume to 0 we should probably
1807 * stop sending but this would cause the audio call to drop if both
1808 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
1809 * in the future. GNOME #574574
1811 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
1813 gtk_adjustment_set_value (priv->audio_input_adj, 0);
1818 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
1819 EmpathyCallWindow *window)
1821 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1823 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
1828 empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
1829 EmpathyCallWindow *window)
1831 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1833 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
1838 empathy_call_window_hangup_cb (gpointer object,
1839 EmpathyCallWindow *window)
1841 if (empathy_call_window_disconnected (window))
1842 gtk_widget_destroy (GTK_WIDGET (window));
1846 empathy_call_window_restart_call (EmpathyCallWindow *window)
1849 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1851 gtk_widget_destroy (priv->remote_user_output_hbox);
1852 gtk_widget_destroy (priv->self_user_output_hbox);
1854 priv->pipeline = gst_pipeline_new (NULL);
1855 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
1856 gst_bus_add_watch (bus, empathy_call_window_bus_message, window);
1858 empathy_call_window_setup_remote_frame (bus, window);
1859 empathy_call_window_setup_self_frame (bus, window);
1860 empathy_call_window_setup_video_preview (window);
1862 g_object_unref (bus);
1864 gtk_widget_show_all (priv->content_hbox);
1866 empathy_call_window_status_message (window, CONNECTING_STATUS_TEXT);
1867 priv->call_started = TRUE;
1868 empathy_call_handler_start_call (priv->handler);
1869 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1871 gtk_action_set_sensitive (priv->redial, FALSE);
1872 gtk_widget_set_sensitive (priv->redial_button, FALSE);
1876 empathy_call_window_redial_cb (gpointer object,
1877 EmpathyCallWindow *window)
1879 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1881 if (priv->connected)
1882 priv->redialing = TRUE;
1884 empathy_call_handler_stop_call (priv->handler);
1886 if (!priv->connected)
1887 empathy_call_window_restart_call (window);
1891 empathy_call_window_fullscreen_cb (gpointer object,
1892 EmpathyCallWindow *window)
1894 empathy_call_window_fullscreen_toggle (window);
1898 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
1900 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1902 if (priv->is_fullscreen)
1903 gtk_window_unfullscreen (GTK_WINDOW (window));
1905 gtk_window_fullscreen (GTK_WINDOW (window));
1909 empathy_call_window_video_button_press_cb (GtkWidget *video_output,
1910 GdkEventButton *event, EmpathyCallWindow *window)
1912 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
1914 empathy_call_window_video_menu_popup (window, event->button);
1922 empathy_call_window_key_press_cb (GtkWidget *video_output,
1923 GdkEventKey *event, EmpathyCallWindow *window)
1925 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1927 if (priv->is_fullscreen && event->keyval == GDK_Escape)
1929 /* Since we are in fullscreen mode, toggling will bring us back to
1931 empathy_call_window_fullscreen_toggle (window);
1939 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
1940 GdkEventMotion *event, EmpathyCallWindow *window)
1942 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1944 if (priv->is_fullscreen)
1946 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
1953 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
1957 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1959 menu = gtk_ui_manager_get_widget (priv->ui_manager,
1961 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
1962 button, gtk_get_current_event_time ());
1963 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
1967 empathy_call_window_status_message (EmpathyCallWindow *window,
1970 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1972 if (priv->context_id == 0)
1974 priv->context_id = gtk_statusbar_get_context_id (
1975 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
1979 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
1982 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
1987 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
1988 gdouble value, EmpathyCallWindow *window)
1990 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1992 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),