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 *video_output;
109 GtkWidget *video_preview;
110 GtkWidget *remote_user_avatar_widget;
111 GtkWidget *self_user_avatar_widget;
113 GtkWidget *sidebar_button;
114 GtkWidget *statusbar;
115 GtkWidget *volume_button;
116 GtkWidget *redial_button;
117 GtkWidget *mic_button;
118 GtkWidget *camera_button;
121 GtkAction *show_preview;
122 GtkAction *send_video;
124 GtkAction *menu_fullscreen;
126 /* The frames and boxes that contain self and remote avatar and video
127 input/output. When we redial, we destroy and re-create the boxes */
128 GtkWidget *remote_user_output_frame;
129 GtkWidget *self_user_output_frame;
130 GtkWidget *remote_user_output_hbox;
131 GtkWidget *self_user_output_hbox;
133 /* We keep a reference on the hbox which contains the main content so we can
134 easilly repack everything when toggling fullscreen */
135 GtkWidget *content_hbox;
137 /* This vbox is contained in the content_hbox and it contains the
138 self_user_output_frame and the sidebar button. When toggling fullscreen,
139 it needs to be repacked. We keep a reference on it for easier access. */
142 gulong video_output_motion_handler_id;
143 guint bus_message_source_id;
146 GtkWidget *volume_scale;
147 GtkWidget *volume_progress_bar;
148 GtkAdjustment *audio_input_adj;
150 GtkWidget *dtmf_panel;
152 GstElement *video_input;
153 GstElement *audio_input;
154 GstElement *audio_output;
155 GstElement *pipeline;
156 GstElement *video_tee;
159 GstElement *liveadder;
161 FsElementAddedNotifier *fsnotifier;
168 GtkWidget *video_contrast;
169 GtkWidget *video_brightness;
170 GtkWidget *video_gamma;
173 gboolean call_started;
174 gboolean sending_video;
176 EmpathyCallWindowFullscreen *fullscreen;
177 gboolean is_fullscreen;
179 /* Those fields represent the state of the window before it actually was in
181 gboolean sidebar_was_visible_before_fs;
182 gint original_width_before_fs;
183 gint original_height_before_fs;
186 #define GET_PRIV(o) \
187 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_CALL_WINDOW, \
188 EmpathyCallWindowPriv))
190 static void empathy_call_window_realized_cb (GtkWidget *widget,
191 EmpathyCallWindow *window);
193 static gboolean empathy_call_window_delete_cb (GtkWidget *widget,
194 GdkEvent *event, EmpathyCallWindow *window);
196 static gboolean empathy_call_window_state_event_cb (GtkWidget *widget,
197 GdkEventWindowState *event, EmpathyCallWindow *window);
199 static void empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
200 EmpathyCallWindow *window);
202 static void empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
203 EmpathyCallWindow *window);
205 static void empathy_call_window_set_send_video (EmpathyCallWindow *window,
208 static void empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
209 EmpathyCallWindow *window);
211 static void empathy_call_window_show_preview_toggled_cb (
212 GtkToggleAction *toggle, EmpathyCallWindow *window);
214 static void empathy_call_window_mic_toggled_cb (
215 GtkToggleToolButton *toggle, EmpathyCallWindow *window);
217 static void empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
218 EmpathyCallWindow *window);
220 static void empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
221 EmpathyCallWindow *window);
223 static void empathy_call_window_hangup_cb (gpointer object,
224 EmpathyCallWindow *window);
226 static void empathy_call_window_fullscreen_cb (gpointer object,
227 EmpathyCallWindow *window);
229 static void empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window);
231 static gboolean empathy_call_window_video_button_press_cb (
232 GtkWidget *video_output, GdkEventButton *event, EmpathyCallWindow *window);
234 static gboolean empathy_call_window_key_press_cb (GtkWidget *video_output,
235 GdkEventKey *event, EmpathyCallWindow *window);
237 static gboolean empathy_call_window_video_output_motion_notify (
238 GtkWidget *widget, GdkEventMotion *event, EmpathyCallWindow *window);
240 static void empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
243 static void empathy_call_window_redial_cb (gpointer object,
244 EmpathyCallWindow *window);
246 static void empathy_call_window_restart_call (EmpathyCallWindow *window);
248 static void empathy_call_window_status_message (EmpathyCallWindow *window,
251 static void empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
252 EmpathyCallWindow *window);
254 static gboolean empathy_call_window_bus_message (GstBus *bus,
255 GstMessage *message, gpointer user_data);
258 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
259 gdouble value, EmpathyCallWindow *window);
262 empathy_call_window_setup_toolbar (EmpathyCallWindow *self)
264 EmpathyCallWindowPriv *priv = GET_PRIV (self);
265 GtkToolItem *tool_item;
267 /* Add an empty expanded GtkToolItem so the volume button is at the end of
269 tool_item = gtk_tool_item_new ();
270 gtk_tool_item_set_expand (tool_item, TRUE);
271 gtk_widget_show (GTK_WIDGET (tool_item));
272 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
274 priv->volume_button = gtk_volume_button_new ();
275 /* FIXME listen to the audiosinks signals and update the button according to
276 * that, for now starting out at 1.0 and assuming only the app changes the
278 gtk_scale_button_set_value (GTK_SCALE_BUTTON (priv->volume_button), 1.0);
279 g_signal_connect (G_OBJECT (priv->volume_button), "value-changed",
280 G_CALLBACK (empathy_call_window_volume_changed_cb), self);
282 tool_item = gtk_tool_item_new ();
283 gtk_container_add (GTK_CONTAINER (tool_item), priv->volume_button);
284 gtk_widget_show_all (GTK_WIDGET (tool_item));
285 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
289 dtmf_button_pressed_cb (GtkButton *button, EmpathyCallWindow *window)
291 EmpathyCallWindowPriv *priv = GET_PRIV (window);
296 g_object_get (priv->handler, "tp-call", &call, NULL);
298 button_quark = g_quark_from_static_string (BUTTON_ID);
299 event = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (button),
302 empathy_tp_call_start_tone (call, event);
304 g_object_unref (call);
308 dtmf_button_released_cb (GtkButton *button, EmpathyCallWindow *window)
310 EmpathyCallWindowPriv *priv = GET_PRIV (window);
313 g_object_get (priv->handler, "tp-call", &call, NULL);
315 empathy_tp_call_stop_tone (call);
317 g_object_unref (call);
321 empathy_call_window_create_dtmf (EmpathyCallWindow *self)
329 } dtmfbuttons[] = { { "1", TP_DTMF_EVENT_DIGIT_1 },
330 { "2", TP_DTMF_EVENT_DIGIT_2 },
331 { "3", TP_DTMF_EVENT_DIGIT_3 },
332 { "4", TP_DTMF_EVENT_DIGIT_4 },
333 { "5", TP_DTMF_EVENT_DIGIT_5 },
334 { "6", TP_DTMF_EVENT_DIGIT_6 },
335 { "7", TP_DTMF_EVENT_DIGIT_7 },
336 { "8", TP_DTMF_EVENT_DIGIT_8 },
337 { "9", TP_DTMF_EVENT_DIGIT_9 },
338 { "#", TP_DTMF_EVENT_HASH },
339 { "0", TP_DTMF_EVENT_DIGIT_0 },
340 { "*", TP_DTMF_EVENT_ASTERISK },
343 button_quark = g_quark_from_static_string (BUTTON_ID);
345 table = gtk_table_new (4, 3, TRUE);
347 for (i = 0; dtmfbuttons[i].label != NULL; i++)
349 GtkWidget *button = gtk_button_new_with_label (dtmfbuttons[i].label);
350 gtk_table_attach (GTK_TABLE (table), button, i % 3, i % 3 + 1,
351 i/3, i/3 + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
353 g_object_set_qdata (G_OBJECT (button), button_quark,
354 GUINT_TO_POINTER (dtmfbuttons[i].event));
356 g_signal_connect (G_OBJECT (button), "pressed",
357 G_CALLBACK (dtmf_button_pressed_cb), self);
358 g_signal_connect (G_OBJECT (button), "released",
359 G_CALLBACK (dtmf_button_released_cb), self);
366 empathy_call_window_create_video_input_add_slider (EmpathyCallWindow *self,
367 gchar *label_text, GtkWidget *bin)
369 GtkWidget *vbox = gtk_vbox_new (FALSE, 2);
370 GtkWidget *scale = gtk_vscale_new_with_range (0, 100, 10);
371 GtkWidget *label = gtk_label_new (label_text);
373 gtk_widget_set_sensitive (scale, FALSE);
375 gtk_container_add (GTK_CONTAINER (bin), vbox);
377 gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
378 gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 0);
379 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
385 empathy_call_window_video_contrast_changed_cb (GtkAdjustment *adj,
386 EmpathyCallWindow *self)
389 EmpathyCallWindowPriv *priv = GET_PRIV (self);
391 empathy_video_src_set_channel (priv->video_input,
392 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST, gtk_adjustment_get_value (adj));
396 empathy_call_window_video_brightness_changed_cb (GtkAdjustment *adj,
397 EmpathyCallWindow *self)
400 EmpathyCallWindowPriv *priv = GET_PRIV (self);
402 empathy_video_src_set_channel (priv->video_input,
403 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS, gtk_adjustment_get_value (adj));
407 empathy_call_window_video_gamma_changed_cb (GtkAdjustment *adj,
408 EmpathyCallWindow *self)
411 EmpathyCallWindowPriv *priv = GET_PRIV (self);
413 empathy_video_src_set_channel (priv->video_input,
414 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA, gtk_adjustment_get_value (adj));
419 empathy_call_window_create_video_input (EmpathyCallWindow *self)
421 EmpathyCallWindowPriv *priv = GET_PRIV (self);
424 hbox = gtk_hbox_new (TRUE, 3);
426 priv->video_contrast = empathy_call_window_create_video_input_add_slider (
427 self, _("Contrast"), hbox);
429 priv->video_brightness = empathy_call_window_create_video_input_add_slider (
430 self, _("Brightness"), hbox);
432 priv->video_gamma = empathy_call_window_create_video_input_add_slider (
433 self, _("Gamma"), hbox);
439 empathy_call_window_setup_video_input (EmpathyCallWindow *self)
441 EmpathyCallWindowPriv *priv = GET_PRIV (self);
445 supported = empathy_video_src_get_supported_channels (priv->video_input);
447 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST)
449 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_contrast));
451 gtk_adjustment_set_value (adj,
452 empathy_video_src_get_channel (priv->video_input,
453 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST));
455 g_signal_connect (G_OBJECT (adj), "value-changed",
456 G_CALLBACK (empathy_call_window_video_contrast_changed_cb), self);
458 gtk_widget_set_sensitive (priv->video_contrast, TRUE);
461 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS)
463 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_brightness));
465 gtk_adjustment_set_value (adj,
466 empathy_video_src_get_channel (priv->video_input,
467 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS));
469 g_signal_connect (G_OBJECT (adj), "value-changed",
470 G_CALLBACK (empathy_call_window_video_brightness_changed_cb), self);
471 gtk_widget_set_sensitive (priv->video_brightness, TRUE);
474 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA)
476 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_gamma));
478 gtk_adjustment_set_value (adj,
479 empathy_video_src_get_channel (priv->video_input,
480 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA));
482 g_signal_connect (G_OBJECT (adj), "value-changed",
483 G_CALLBACK (empathy_call_window_video_gamma_changed_cb), self);
484 gtk_widget_set_sensitive (priv->video_gamma, TRUE);
489 empathy_call_window_mic_volume_changed_cb (GtkAdjustment *adj,
490 EmpathyCallWindow *self)
492 EmpathyCallWindowPriv *priv = GET_PRIV (self);
495 if (priv->audio_input == NULL)
498 volume = gtk_adjustment_get_value (adj)/100.0;
500 /* Don't store the volume because of muting */
501 if (volume > 0 || gtk_toggle_tool_button_get_active (
502 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
503 priv->volume = volume;
505 /* Ensure that the toggle button is active if the volume is > 0 and inactive
506 * if it's smaller than 0 */
507 if ((volume > 0) != gtk_toggle_tool_button_get_active (
508 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
509 gtk_toggle_tool_button_set_active (
510 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), volume > 0);
512 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
517 empathy_call_window_audio_input_level_changed_cb (EmpathyGstAudioSrc *src,
518 gdouble level, EmpathyCallWindow *window)
521 EmpathyCallWindowPriv *priv = GET_PRIV (window);
523 value = CLAMP (pow (10, level / 20), 0.0, 1.0);
524 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
529 empathy_call_window_create_audio_input (EmpathyCallWindow *self)
531 EmpathyCallWindowPriv *priv = GET_PRIV (self);
532 GtkWidget *hbox, *vbox, *label;
534 hbox = gtk_hbox_new (TRUE, 3);
536 vbox = gtk_vbox_new (FALSE, 3);
537 gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 3);
539 priv->volume_scale = gtk_vscale_new_with_range (0, 150, 100);
540 gtk_range_set_inverted (GTK_RANGE (priv->volume_scale), TRUE);
541 label = gtk_label_new (_("Volume"));
543 priv->audio_input_adj = gtk_range_get_adjustment (
544 GTK_RANGE (priv->volume_scale));
545 priv->volume = empathy_audio_src_get_volume (EMPATHY_GST_AUDIO_SRC
546 (priv->audio_input));
547 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
549 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
550 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), self);
552 gtk_box_pack_start (GTK_BOX (vbox), priv->volume_scale, TRUE, TRUE, 3);
553 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 3);
555 priv->volume_progress_bar = gtk_progress_bar_new ();
556 gtk_progress_bar_set_orientation (
557 GTK_PROGRESS_BAR (priv->volume_progress_bar),
558 GTK_PROGRESS_BOTTOM_TO_TOP);
559 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
562 gtk_box_pack_start (GTK_BOX (hbox), priv->volume_progress_bar, FALSE, FALSE,
569 empathy_call_window_setup_remote_frame (GstBus *bus, EmpathyCallWindow *self)
571 EmpathyCallWindowPriv *priv = GET_PRIV (self);
573 /* Initializing all the content (UI and output gst elements) related to the
575 priv->remote_user_output_hbox = gtk_hbox_new (FALSE, 0);
577 priv->remote_user_avatar_widget = gtk_image_new ();
578 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
579 priv->remote_user_avatar_widget, TRUE, TRUE, 0);
581 priv->video_output = empathy_video_widget_new (bus);
582 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
583 priv->video_output, TRUE, TRUE, 0);
585 gtk_widget_add_events (priv->video_output,
586 GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
587 g_signal_connect (G_OBJECT (priv->video_output), "button-press-event",
588 G_CALLBACK (empathy_call_window_video_button_press_cb), self);
590 gtk_container_add (GTK_CONTAINER (priv->remote_user_output_frame),
591 priv->remote_user_output_hbox);
593 priv->audio_output = empathy_audio_sink_new ();
594 gst_object_ref (priv->audio_output);
595 gst_object_sink (priv->audio_output);
599 empathy_call_window_setup_self_frame (GstBus *bus, EmpathyCallWindow *self)
601 EmpathyCallWindowPriv *priv = GET_PRIV (self);
603 /* Initializing all the content (UI and input gst elements) related to the
604 self contact, except for the video preview widget. This widget is only
605 initialized when the "show video preview" option is activated */
606 priv->self_user_output_hbox = gtk_hbox_new (FALSE, 0);
608 priv->self_user_avatar_widget = gtk_image_new ();
609 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
610 priv->self_user_avatar_widget, TRUE, TRUE, 0);
612 gtk_container_add (GTK_CONTAINER (priv->self_user_output_frame),
613 priv->self_user_output_hbox);
615 priv->video_input = empathy_video_src_new ();
616 gst_object_ref (priv->video_input);
617 gst_object_sink (priv->video_input);
619 priv->audio_input = empathy_audio_src_new ();
620 gst_object_ref (priv->audio_input);
621 gst_object_sink (priv->audio_input);
623 g_signal_connect (priv->audio_input, "peak-level-changed",
624 G_CALLBACK (empathy_call_window_audio_input_level_changed_cb), self);
628 empathy_call_window_setup_video_preview (EmpathyCallWindow *window)
630 EmpathyCallWindowPriv *priv = GET_PRIV (window);
632 GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
634 if (priv->video_preview != NULL)
636 /* Since the video preview and the video tee are initialized and freed
637 at the same time, if one is initialized, then the other one should
639 g_assert (priv->video_tee != NULL);
643 g_assert (priv->video_tee == NULL);
645 priv->video_tee = gst_element_factory_make ("tee", NULL);
646 gst_object_ref (priv->video_tee);
647 gst_object_sink (priv->video_tee);
649 priv->video_preview = empathy_video_widget_new_with_size (bus,
650 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
651 g_object_set (priv->video_preview, "sync", FALSE, "async", TRUE, NULL);
652 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
653 priv->video_preview, TRUE, TRUE, 0);
655 preview = empathy_video_widget_get_element (
656 EMPATHY_VIDEO_WIDGET (priv->video_preview));
657 gst_bin_add_many (GST_BIN (priv->pipeline), priv->video_input,
658 priv->video_tee, preview, NULL);
659 gst_element_link_many (priv->video_input, priv->video_tee,
662 g_object_unref (bus);
664 gst_element_set_state (preview, GST_STATE_PLAYING);
665 gst_element_set_state (priv->video_input, GST_STATE_PLAYING);
666 gst_element_set_state (priv->video_tee, GST_STATE_PLAYING);
670 empathy_call_window_set_state_connecting (EmpathyCallWindow *window)
672 EmpathyCallWindowPriv *priv = GET_PRIV (window);
674 empathy_call_window_status_message (window, _("Connecting..."));
675 priv->call_state = CONNECTING;
678 empathy_sound_start_playing (GTK_WIDGET (window),
679 EMPATHY_SOUND_PHONE_OUTGOING, MS_BETWEEN_RING);
683 empathy_call_window_init (EmpathyCallWindow *self)
685 EmpathyCallWindowPriv *priv = GET_PRIV (self);
694 GError *error = NULL;
696 filename = empathy_file_lookup ("empathy-call-window.ui", "src");
697 gui = empathy_builder_get_file (filename,
698 "call_window_vbox", &top_vbox,
700 "statusbar", &priv->statusbar,
701 "redial", &priv->redial_button,
702 "microphone", &priv->mic_button,
703 "camera", &priv->camera_button,
704 "toolbar", &priv->toolbar,
705 "send_video", &priv->send_video,
706 "menuredial", &priv->redial,
707 "show_preview", &priv->show_preview,
708 "ui_manager", &priv->ui_manager,
709 "menufullscreen", &priv->menu_fullscreen,
713 empathy_builder_connect (gui, self,
714 "menuhangup", "activate", empathy_call_window_hangup_cb,
715 "hangup", "clicked", empathy_call_window_hangup_cb,
716 "menuredial", "activate", empathy_call_window_redial_cb,
717 "redial", "clicked", empathy_call_window_redial_cb,
718 "microphone", "toggled", empathy_call_window_mic_toggled_cb,
719 "camera", "toggled", empathy_call_window_camera_toggled_cb,
720 "send_video", "toggled", empathy_call_window_send_video_toggled_cb,
721 "show_preview", "toggled", empathy_call_window_show_preview_toggled_cb,
722 "menufullscreen", "activate", empathy_call_window_fullscreen_cb,
725 priv->lock = g_mutex_new ();
727 gtk_container_add (GTK_CONTAINER (self), top_vbox);
729 priv->content_hbox = gtk_hbox_new (FALSE, CONTENT_HBOX_SPACING);
730 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
731 CONTENT_HBOX_BORDER_WIDTH);
732 gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
734 priv->pipeline = gst_pipeline_new (NULL);
735 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
736 priv->bus_message_source_id = gst_bus_add_watch (bus,
737 empathy_call_window_bus_message, self);
739 priv->fsnotifier = fs_element_added_notifier_new ();
740 fs_element_added_notifier_add (priv->fsnotifier, GST_BIN (priv->pipeline));
742 keyfile = g_key_file_new ();
743 filename = empathy_file_lookup ("element-properties", "data");
744 if (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error))
746 fs_element_added_notifier_set_properties_from_keyfile (priv->fsnotifier,
751 g_warning ("Could not load element-properties file: %s", error->message);
752 g_key_file_free (keyfile);
753 g_clear_error (&error);
758 priv->remote_user_output_frame = gtk_frame_new (NULL);
759 gtk_widget_set_size_request (priv->remote_user_output_frame,
760 EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT);
761 gtk_box_pack_start (GTK_BOX (priv->content_hbox),
762 priv->remote_user_output_frame, TRUE, TRUE,
763 CONTENT_HBOX_CHILDREN_PACKING_PADDING);
764 empathy_call_window_setup_remote_frame (bus, self);
766 priv->self_user_output_frame = gtk_frame_new (NULL);
767 gtk_widget_set_size_request (priv->self_user_output_frame,
768 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
770 priv->vbox = gtk_vbox_new (FALSE, 3);
771 gtk_box_pack_start (GTK_BOX (priv->content_hbox), priv->vbox,
772 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
773 gtk_box_pack_start (GTK_BOX (priv->vbox), priv->self_user_output_frame,
775 empathy_call_window_setup_self_frame (bus, self);
777 empathy_call_window_setup_toolbar (self);
779 g_object_unref (bus);
781 priv->sidebar_button = gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
782 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
783 g_signal_connect (G_OBJECT (priv->sidebar_button), "toggled",
784 G_CALLBACK (empathy_call_window_sidebar_toggled_cb), self);
786 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
788 h = gtk_hbox_new (FALSE, 3);
789 gtk_box_pack_end (GTK_BOX (priv->vbox), h, FALSE, FALSE, 3);
790 gtk_box_pack_end (GTK_BOX (h), priv->sidebar_button, FALSE, FALSE, 3);
792 priv->sidebar = empathy_sidebar_new ();
793 g_signal_connect (G_OBJECT (priv->sidebar),
794 "hide", G_CALLBACK (empathy_call_window_sidebar_hidden_cb), self);
795 g_signal_connect (G_OBJECT (priv->sidebar),
796 "show", G_CALLBACK (empathy_call_window_sidebar_shown_cb), self);
797 gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
799 priv->dtmf_panel = empathy_call_window_create_dtmf (self);
800 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Dialpad"),
803 gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
805 page = empathy_call_window_create_audio_input (self);
806 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Audio input"),
809 page = empathy_call_window_create_video_input (self);
810 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Video input"),
813 gtk_widget_show_all (top_vbox);
815 gtk_widget_hide (priv->sidebar);
817 priv->fullscreen = empathy_call_window_fullscreen_new (self);
818 empathy_call_window_fullscreen_set_video_widget (priv->fullscreen,
820 g_signal_connect (G_OBJECT (priv->fullscreen->leave_fullscreen_button),
821 "clicked", G_CALLBACK (empathy_call_window_fullscreen_cb), self);
823 g_signal_connect (G_OBJECT (self), "realize",
824 G_CALLBACK (empathy_call_window_realized_cb), self);
826 g_signal_connect (G_OBJECT (self), "delete-event",
827 G_CALLBACK (empathy_call_window_delete_cb), self);
829 g_signal_connect (G_OBJECT (self), "window-state-event",
830 G_CALLBACK (empathy_call_window_state_event_cb), self);
832 g_signal_connect (G_OBJECT (self), "key-press-event",
833 G_CALLBACK (empathy_call_window_key_press_cb), self);
835 priv->timer = g_timer_new ();
837 g_object_ref (priv->ui_manager);
838 g_object_unref (gui);
841 /* Instead of specifying a width and a height, we specify only one size. That's
842 because we want a square avatar icon. */
844 init_contact_avatar_with_size (EmpathyContact *contact,
845 GtkWidget *image_widget,
848 GdkPixbuf *pixbuf_avatar = NULL;
852 pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact,
856 if (pixbuf_avatar == NULL)
858 pixbuf_avatar = empathy_pixbuf_from_icon_name_sized ("stock_person",
862 gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget), pixbuf_avatar);
866 set_window_title (EmpathyCallWindow *self)
868 EmpathyCallWindowPriv *priv = GET_PRIV (self);
871 /* translators: Call is a noun and %s is the contact name. This string
872 * is used in the window title */
873 tmp = g_strdup_printf (_("Call with %s"),
874 empathy_contact_get_name (priv->contact));
875 gtk_window_set_title (GTK_WINDOW (self), tmp);
880 contact_name_changed_cb (EmpathyContact *contact,
881 GParamSpec *pspec, EmpathyCallWindow *self)
883 set_window_title (self);
887 contact_avatar_changed_cb (EmpathyContact *contact,
888 GParamSpec *pspec, GtkWidget *avatar_widget)
892 size = avatar_widget->allocation.height;
896 /* the widget is not allocated yet, set a default size */
897 size = MIN (REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT,
898 REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH);
901 init_contact_avatar_with_size (contact, avatar_widget, size);
905 empathy_call_window_got_self_contact_cb (EmpathyTpContactFactory *factory,
906 EmpathyContact *contact, const GError *error, gpointer user_data,
907 GObject *weak_object)
909 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
910 EmpathyCallWindowPriv *priv = GET_PRIV (self);
912 init_contact_avatar_with_size (contact, priv->self_user_avatar_widget,
913 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
915 g_signal_connect (contact, "notify::avatar",
916 G_CALLBACK (contact_avatar_changed_cb), priv->self_user_avatar_widget);
920 empathy_call_window_setup_avatars (EmpathyCallWindow *self,
921 EmpathyCallHandler *handler)
923 EmpathyCallWindowPriv *priv = GET_PRIV (self);
925 g_object_get (handler, "contact", &(priv->contact), NULL);
927 if (priv->contact != NULL)
929 TpConnection *connection;
930 EmpathyTpContactFactory *factory;
932 set_window_title (self);
934 g_signal_connect (priv->contact, "notify::name",
935 G_CALLBACK (contact_name_changed_cb), self);
936 g_signal_connect (priv->contact, "notify::avatar",
937 G_CALLBACK (contact_avatar_changed_cb),
938 priv->remote_user_avatar_widget);
940 /* Retreiving the self avatar */
941 connection = empathy_contact_get_connection (priv->contact);
942 factory = empathy_tp_contact_factory_dup_singleton (connection);
943 empathy_tp_contact_factory_get_from_handle (factory,
944 tp_connection_get_self_handle (connection),
945 empathy_call_window_got_self_contact_cb, self, NULL, NULL);
947 g_object_unref (factory);
951 g_warning ("call handler doesn't have a contact");
952 /* translators: Call is a noun. This string is used in the window
954 gtk_window_set_title (GTK_WINDOW (self), _("Call"));
956 /* Since we can't access the remote contact, we can't get a connection
957 to it and can't get the self contact (and its avatar). This means
958 that we have to manually set the self avatar. */
959 init_contact_avatar_with_size (NULL, priv->self_user_avatar_widget,
960 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
963 init_contact_avatar_with_size (priv->contact,
964 priv->remote_user_avatar_widget,
965 MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
966 REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
968 /* The remote avatar is shown by default and will be hidden when we receive
969 video from the remote side. */
970 gtk_widget_hide (priv->video_output);
971 gtk_widget_show (priv->remote_user_avatar_widget);
975 empathy_call_window_setup_video_preview_visibility (EmpathyCallWindow *self,
976 EmpathyCallHandler *handler)
978 EmpathyCallWindowPriv *priv = GET_PRIV (self);
979 gboolean initial_video =
980 empathy_call_handler_has_initial_video (priv->handler);
982 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
987 empathy_call_window_constructed (GObject *object)
989 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
990 EmpathyCallWindowPriv *priv = GET_PRIV (self);
993 g_assert (priv->handler != NULL);
995 g_object_get (priv->handler, "tp-call", &call, NULL);
996 priv->outgoing = (call == NULL);
998 g_object_unref (call);
1000 empathy_call_window_setup_avatars (self, priv->handler);
1001 empathy_call_window_setup_video_preview_visibility (self, priv->handler);
1002 empathy_call_window_set_state_connecting (self);
1005 static void empathy_call_window_dispose (GObject *object);
1006 static void empathy_call_window_finalize (GObject *object);
1009 empathy_call_window_set_property (GObject *object,
1010 guint property_id, const GValue *value, GParamSpec *pspec)
1012 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1014 switch (property_id)
1016 case PROP_CALL_HANDLER:
1017 priv->handler = g_value_dup_object (value);
1020 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1025 empathy_call_window_get_property (GObject *object,
1026 guint property_id, GValue *value, GParamSpec *pspec)
1028 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1030 switch (property_id)
1032 case PROP_CALL_HANDLER:
1033 g_value_set_object (value, priv->handler);
1036 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1041 empathy_call_window_class_init (
1042 EmpathyCallWindowClass *empathy_call_window_class)
1044 GObjectClass *object_class = G_OBJECT_CLASS (empathy_call_window_class);
1045 GParamSpec *param_spec;
1047 g_type_class_add_private (empathy_call_window_class,
1048 sizeof (EmpathyCallWindowPriv));
1050 object_class->constructed = empathy_call_window_constructed;
1051 object_class->set_property = empathy_call_window_set_property;
1052 object_class->get_property = empathy_call_window_get_property;
1054 object_class->dispose = empathy_call_window_dispose;
1055 object_class->finalize = empathy_call_window_finalize;
1057 param_spec = g_param_spec_object ("handler",
1058 "handler", "The call handler",
1059 EMPATHY_TYPE_CALL_HANDLER,
1060 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1061 g_object_class_install_property (object_class,
1062 PROP_CALL_HANDLER, param_spec);
1066 empathy_call_window_video_stream_changed_cb (EmpathyTpCall *call,
1067 GParamSpec *property, EmpathyCallWindow *self)
1069 empathy_call_window_update_avatars_visibility (call, self);
1073 empathy_call_window_dispose (GObject *object)
1075 EmpathyTpCall *call;
1076 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1077 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1079 if (priv->dispose_has_run)
1082 priv->dispose_has_run = TRUE;
1084 g_object_get (priv->handler, "tp-call", &call, NULL);
1088 g_signal_handlers_disconnect_by_func (call,
1089 empathy_call_window_video_stream_changed_cb, object);
1090 g_object_unref (call);
1093 if (priv->handler != NULL)
1094 g_object_unref (priv->handler);
1095 priv->handler = NULL;
1097 if (priv->pipeline != NULL)
1098 g_object_unref (priv->pipeline);
1099 priv->pipeline = NULL;
1101 if (priv->video_input != NULL)
1102 g_object_unref (priv->video_input);
1103 priv->video_input = NULL;
1105 if (priv->audio_input != NULL)
1106 g_object_unref (priv->audio_input);
1107 priv->audio_input = NULL;
1109 if (priv->audio_output != NULL)
1110 g_object_unref (priv->audio_output);
1111 priv->audio_output = NULL;
1113 if (priv->video_tee != NULL)
1114 g_object_unref (priv->video_tee);
1115 priv->video_tee = NULL;
1117 if (priv->fsnotifier != NULL)
1118 g_object_unref (priv->fsnotifier);
1119 priv->fsnotifier = NULL;
1121 if (priv->timer_id != 0)
1122 g_source_remove (priv->timer_id);
1125 if (priv->ui_manager != NULL)
1126 g_object_unref (priv->ui_manager);
1127 priv->ui_manager = NULL;
1129 if (priv->contact != NULL)
1131 g_signal_handlers_disconnect_by_func (priv->contact,
1132 contact_name_changed_cb, self);
1133 g_object_unref (priv->contact);
1134 priv->contact = NULL;
1137 /* release any references held by the object here */
1138 if (G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose)
1139 G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
1143 empathy_call_window_finalize (GObject *object)
1145 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1146 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1148 if (priv->video_output_motion_handler_id != 0)
1150 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1151 priv->video_output_motion_handler_id);
1152 priv->video_output_motion_handler_id = 0;
1155 if (priv->bus_message_source_id != 0)
1157 g_source_remove (priv->bus_message_source_id);
1158 priv->bus_message_source_id = 0;
1161 /* free any data held directly by the object here */
1162 g_mutex_free (priv->lock);
1164 g_timer_destroy (priv->timer);
1166 G_OBJECT_CLASS (empathy_call_window_parent_class)->finalize (object);
1171 empathy_call_window_new (EmpathyCallHandler *handler)
1173 return EMPATHY_CALL_WINDOW (
1174 g_object_new (EMPATHY_TYPE_CALL_WINDOW, "handler", handler, NULL));
1178 empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
1179 GstElement *conference, gpointer user_data)
1181 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1182 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1184 gst_bin_add (GST_BIN (priv->pipeline), conference);
1186 gst_element_set_state (conference, GST_STATE_PLAYING);
1190 empathy_call_window_request_resource_cb (EmpathyCallHandler *handler,
1191 FsMediaType type, FsStreamDirection direction, gpointer user_data)
1193 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1194 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1196 if (type != TP_MEDIA_STREAM_TYPE_VIDEO)
1199 if (direction == FS_DIRECTION_RECV)
1202 /* video and direction is send */
1203 return priv->video_input != NULL;
1207 empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
1209 GstStateChangeReturn state_change_return;
1210 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1212 if (priv->pipeline == NULL)
1215 if (priv->bus_message_source_id != 0)
1217 g_source_remove (priv->bus_message_source_id);
1218 priv->bus_message_source_id = 0;
1221 state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1223 if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1224 state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1226 if (priv->pipeline != NULL)
1227 g_object_unref (priv->pipeline);
1228 priv->pipeline = NULL;
1230 if (priv->video_input != NULL)
1231 g_object_unref (priv->video_input);
1232 priv->video_input = NULL;
1234 if (priv->audio_input != NULL)
1235 g_object_unref (priv->audio_input);
1236 priv->audio_input = NULL;
1238 g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
1239 empathy_call_window_mic_volume_changed_cb, self);
1241 if (priv->audio_output != NULL)
1242 g_object_unref (priv->audio_output);
1243 priv->audio_output = NULL;
1245 if (priv->video_tee != NULL)
1246 g_object_unref (priv->video_tee);
1247 priv->video_tee = NULL;
1249 if (priv->video_preview != NULL)
1250 gtk_widget_destroy (priv->video_preview);
1251 priv->video_preview = NULL;
1253 priv->liveadder = NULL;
1254 priv->funnel = NULL;
1260 g_message ("Error: could not destroy pipeline. Closing call window");
1261 gtk_widget_destroy (GTK_WIDGET (self));
1268 empathy_call_window_disconnected (EmpathyCallWindow *self)
1270 gboolean could_disconnect = FALSE;
1271 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1272 gboolean could_reset_pipeline = empathy_call_window_reset_pipeline (self);
1274 if (priv->call_state == CONNECTING)
1275 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1277 if (priv->call_state != REDIALING)
1278 priv->call_state = DISCONNECTED;
1280 if (could_reset_pipeline)
1282 gboolean initial_video = empathy_call_handler_has_initial_video (
1284 g_mutex_lock (priv->lock);
1286 g_timer_stop (priv->timer);
1288 if (priv->timer_id != 0)
1289 g_source_remove (priv->timer_id);
1292 g_mutex_unlock (priv->lock);
1294 empathy_call_window_status_message (self, _("Disconnected"));
1296 gtk_action_set_sensitive (priv->redial, TRUE);
1297 gtk_widget_set_sensitive (priv->redial_button, TRUE);
1299 /* Reseting the send_video, camera_buton and mic_button to their
1301 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1302 gtk_widget_set_sensitive (priv->mic_button, FALSE);
1303 gtk_action_set_sensitive (priv->send_video, FALSE);
1304 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1306 gtk_toggle_tool_button_set_active (
1307 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), initial_video);
1308 gtk_toggle_tool_button_set_active (
1309 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
1311 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1313 gtk_action_set_sensitive (priv->show_preview, FALSE);
1315 gtk_progress_bar_set_fraction (
1316 GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
1318 gtk_widget_hide (priv->video_output);
1319 gtk_widget_show (priv->remote_user_avatar_widget);
1321 priv->sending_video = FALSE;
1322 priv->call_started = FALSE;
1324 could_disconnect = TRUE;
1327 return could_disconnect;
1332 empathy_call_window_channel_closed_cb (EmpathyCallHandler *handler,
1335 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1336 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1338 if (empathy_call_window_disconnected (self) && priv->call_state == REDIALING)
1339 empathy_call_window_restart_call (self);
1344 empathy_call_window_channel_stream_closed_cb (EmpathyCallHandler *handler,
1345 TfStream *stream, gpointer user_data)
1347 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1348 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1351 g_object_get (stream, "media-type", &media_type, NULL);
1354 * This assumes that there is only one video stream per channel...
1357 if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO)
1359 if (priv->funnel != NULL)
1363 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1364 (priv->video_output));
1366 gst_element_set_state (output, GST_STATE_NULL);
1367 gst_element_set_state (priv->funnel, GST_STATE_NULL);
1369 gst_bin_remove (GST_BIN (priv->pipeline), output);
1370 gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
1371 priv->funnel = NULL;
1374 else if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO)
1376 if (priv->liveadder != NULL)
1378 gst_element_set_state (priv->audio_output, GST_STATE_NULL);
1379 gst_element_set_state (priv->liveadder, GST_STATE_NULL);
1381 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
1382 gst_bin_remove (GST_BIN (priv->pipeline), priv->liveadder);
1383 priv->liveadder = NULL;
1388 /* Called with global lock held */
1390 empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
1392 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1395 if (priv->funnel == NULL)
1399 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1400 (priv->video_output));
1402 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
1404 gst_bin_add (GST_BIN (priv->pipeline), priv->funnel);
1405 gst_bin_add (GST_BIN (priv->pipeline), output);
1407 gst_element_link (priv->funnel, output);
1409 gst_element_set_state (priv->funnel, GST_STATE_PLAYING);
1410 gst_element_set_state (output, GST_STATE_PLAYING);
1413 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
1418 /* Called with global lock held */
1420 empathy_call_window_get_audio_sink_pad (EmpathyCallWindow *self)
1422 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1425 if (priv->liveadder == NULL)
1427 priv->liveadder = gst_element_factory_make ("liveadder", NULL);
1429 gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder);
1430 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output);
1432 gst_element_link (priv->liveadder, priv->audio_output);
1434 gst_element_set_state (priv->liveadder, GST_STATE_PLAYING);
1435 gst_element_set_state (priv->audio_output, GST_STATE_PLAYING);
1438 pad = gst_element_get_request_pad (priv->liveadder, "sink%d");
1444 empathy_call_window_update_timer (gpointer user_data)
1446 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1447 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1451 time = g_timer_elapsed (priv->timer, NULL);
1453 /* Translators: number of minutes:seconds the caller has been connected */
1454 str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time / 60,
1456 empathy_call_window_status_message (self, str);
1463 empathy_call_window_connected (gpointer user_data)
1465 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1466 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1467 EmpathyTpCall *call;
1468 gboolean can_send_video;
1470 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1472 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
1473 empathy_contact_can_voip_video (priv->contact);
1475 g_object_get (priv->handler, "tp-call", &call, NULL);
1477 g_signal_connect (call, "notify::video-stream",
1478 G_CALLBACK (empathy_call_window_video_stream_changed_cb), self);
1480 if (empathy_tp_call_has_dtmf (call))
1481 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
1483 if (priv->video_input == NULL)
1484 empathy_call_window_set_send_video (self, FALSE);
1486 priv->sending_video = can_send_video ?
1487 empathy_tp_call_is_sending_video (call) : FALSE;
1489 gtk_action_set_sensitive (priv->show_preview, TRUE);
1490 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1492 || gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (
1493 priv->show_preview)));
1494 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1495 priv->sending_video && priv->video_input != NULL);
1496 gtk_toggle_tool_button_set_active (
1497 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button),
1498 priv->sending_video && priv->video_input != NULL);
1499 gtk_widget_set_sensitive (priv->camera_button, can_send_video);
1500 gtk_action_set_sensitive (priv->send_video, can_send_video);
1502 gtk_action_set_sensitive (priv->redial, FALSE);
1503 gtk_widget_set_sensitive (priv->redial_button, FALSE);
1505 gtk_widget_set_sensitive (priv->mic_button, TRUE);
1507 empathy_call_window_update_avatars_visibility (call, self);
1509 g_object_unref (call);
1511 g_mutex_lock (priv->lock);
1513 priv->timer_id = g_timeout_add_seconds (1,
1514 empathy_call_window_update_timer, self);
1516 g_mutex_unlock (priv->lock);
1518 empathy_call_window_update_timer (self);
1524 /* Called from the streaming thread */
1526 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
1527 GstPad *src, guint media_type, gpointer user_data)
1529 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1530 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1534 g_mutex_lock (priv->lock);
1536 if (priv->call_state != CONNECTED)
1538 g_timer_start (priv->timer);
1539 priv->timer_id = g_idle_add (empathy_call_window_connected, self);
1540 priv->call_state = CONNECTED;
1545 case TP_MEDIA_STREAM_TYPE_AUDIO:
1546 pad = empathy_call_window_get_audio_sink_pad (self);
1548 case TP_MEDIA_STREAM_TYPE_VIDEO:
1549 gtk_widget_hide (priv->remote_user_avatar_widget);
1550 gtk_widget_show (priv->video_output);
1551 pad = empathy_call_window_get_video_sink_pad (self);
1554 g_assert_not_reached ();
1557 gst_pad_link (src, pad);
1558 gst_object_unref (pad);
1560 g_mutex_unlock (priv->lock);
1563 /* Called from the streaming thread */
1565 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
1566 GstPad *sink, guint media_type, gpointer user_data)
1568 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1569 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1574 case TP_MEDIA_STREAM_TYPE_AUDIO:
1575 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
1577 pad = gst_element_get_static_pad (priv->audio_input, "src");
1578 gst_pad_link (pad, sink);
1580 gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
1582 case TP_MEDIA_STREAM_TYPE_VIDEO:
1583 if (priv->video_input != NULL)
1585 EmpathyTpCall *call;
1586 g_object_get (priv->handler, "tp-call", &call, NULL);
1588 if (empathy_tp_call_is_sending_video (call))
1590 empathy_call_window_setup_video_preview (self);
1592 gtk_toggle_action_set_active (
1593 GTK_TOGGLE_ACTION (priv->show_preview), TRUE);
1595 if (priv->video_preview != NULL)
1596 gtk_widget_show (priv->video_preview);
1597 gtk_widget_hide (priv->self_user_avatar_widget);
1600 g_object_unref (call);
1602 if (priv->video_tee != NULL)
1604 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
1605 gst_pad_link (pad, sink);
1610 g_assert_not_reached ();
1616 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
1618 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1619 GstElement *preview;
1621 preview = empathy_video_widget_get_element (
1622 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1624 gst_element_set_state (priv->video_input, GST_STATE_NULL);
1625 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
1626 gst_element_set_state (preview, GST_STATE_NULL);
1628 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
1629 priv->video_tee, preview, NULL);
1631 g_object_unref (priv->video_input);
1632 priv->video_input = NULL;
1633 g_object_unref (priv->video_tee);
1634 priv->video_tee = NULL;
1635 gtk_widget_destroy (priv->video_preview);
1636 priv->video_preview = NULL;
1638 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), FALSE);
1639 gtk_toggle_tool_button_set_active (
1640 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), FALSE);
1641 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1642 gtk_action_set_sensitive (priv->send_video, FALSE);
1644 gtk_widget_show (priv->self_user_avatar_widget);
1649 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
1652 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1653 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1656 empathy_call_handler_bus_message (priv->handler, bus, message);
1658 switch (GST_MESSAGE_TYPE (message))
1660 case GST_MESSAGE_STATE_CHANGED:
1661 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
1663 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1664 if (newstate == GST_STATE_PAUSED)
1665 empathy_call_window_setup_video_input (self);
1667 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
1668 !priv->call_started)
1670 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1671 if (newstate == GST_STATE_PAUSED)
1673 priv->call_started = TRUE;
1674 empathy_call_handler_start_call (priv->handler);
1675 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1679 case GST_MESSAGE_ERROR:
1681 GError *error = NULL;
1682 GstElement *gst_error;
1685 gst_message_parse_error (message, &error, &debug);
1686 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
1688 g_message ("Element error: %s -- %s\n", error->message, debug);
1690 if (g_str_has_prefix (gst_element_get_name (gst_error),
1691 VIDEO_INPUT_ERROR_PREFIX))
1693 /* Remove the video input and continue */
1694 if (priv->video_input != NULL)
1695 empathy_call_window_remove_video_input (self);
1696 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1700 empathy_call_window_disconnected (self);
1702 g_error_free (error);
1713 empathy_call_window_update_self_avatar_visibility (EmpathyCallWindow *window)
1715 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1717 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (priv->show_preview)))
1719 if (priv->video_preview != NULL)
1721 gtk_widget_hide (priv->self_user_avatar_widget);
1722 gtk_widget_show (priv->video_preview);
1726 if (priv->video_preview != NULL)
1727 gtk_widget_hide (priv->video_preview);
1729 gtk_widget_show (priv->self_user_avatar_widget);
1735 empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
1736 EmpathyCallWindow *window)
1738 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1740 if (empathy_tp_call_is_receiving_video (call))
1742 gtk_widget_hide (priv->remote_user_avatar_widget);
1743 gtk_widget_show (priv->video_output);
1747 gtk_widget_hide (priv->video_output);
1748 gtk_widget_show (priv->remote_user_avatar_widget);
1751 empathy_call_window_update_self_avatar_visibility (window);
1755 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
1757 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1759 g_signal_connect (priv->handler, "conference-added",
1760 G_CALLBACK (empathy_call_window_conference_added_cb), window);
1761 g_signal_connect (priv->handler, "request-resource",
1762 G_CALLBACK (empathy_call_window_request_resource_cb), window);
1763 g_signal_connect (priv->handler, "closed",
1764 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
1765 g_signal_connect (priv->handler, "src-pad-added",
1766 G_CALLBACK (empathy_call_window_src_added_cb), window);
1767 g_signal_connect (priv->handler, "sink-pad-added",
1768 G_CALLBACK (empathy_call_window_sink_added_cb), window);
1769 g_signal_connect (priv->handler, "stream-closed",
1770 G_CALLBACK (empathy_call_window_channel_stream_closed_cb), window);
1772 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1776 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
1777 EmpathyCallWindow *window)
1779 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1781 if (priv->pipeline != NULL)
1783 if (priv->bus_message_source_id != 0)
1785 g_source_remove (priv->bus_message_source_id);
1786 priv->bus_message_source_id = 0;
1789 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1792 if (priv->call_state == CONNECTING)
1793 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1799 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
1802 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1804 menu = gtk_ui_manager_get_widget (priv->ui_manager,
1809 gtk_widget_hide (priv->sidebar);
1810 gtk_widget_hide (menu);
1811 gtk_widget_hide (priv->vbox);
1812 gtk_widget_hide (priv->statusbar);
1813 gtk_widget_hide (priv->toolbar);
1817 if (priv->sidebar_was_visible_before_fs)
1818 gtk_widget_show (priv->sidebar);
1820 gtk_widget_show (menu);
1821 gtk_widget_show (priv->vbox);
1822 gtk_widget_show (priv->statusbar);
1823 gtk_widget_show (priv->toolbar);
1825 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
1826 priv->original_height_before_fs);
1831 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
1833 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1835 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
1836 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
1837 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
1838 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
1839 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
1840 priv->video_output, TRUE, TRUE,
1841 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
1843 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
1844 priv->vbox, TRUE, TRUE,
1845 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
1850 empathy_call_window_state_event_cb (GtkWidget *widget,
1851 GdkEventWindowState *event, EmpathyCallWindow *window)
1853 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
1855 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1856 gboolean set_fullscreen = event->new_window_state &
1857 GDK_WINDOW_STATE_FULLSCREEN;
1861 gboolean sidebar_was_visible;
1862 gint original_width = GTK_WIDGET (window)->allocation.width;
1863 gint original_height = GTK_WIDGET (window)->allocation.height;
1865 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
1867 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
1868 priv->original_width_before_fs = original_width;
1869 priv->original_height_before_fs = original_height;
1871 if (priv->video_output_motion_handler_id == 0 &&
1872 priv->video_output != NULL)
1874 priv->video_output_motion_handler_id = g_signal_connect (
1875 G_OBJECT (priv->video_output), "motion-notify-event",
1876 G_CALLBACK (empathy_call_window_video_output_motion_notify),
1882 if (priv->video_output_motion_handler_id != 0)
1884 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1885 priv->video_output_motion_handler_id);
1886 priv->video_output_motion_handler_id = 0;
1890 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
1892 show_controls (window, set_fullscreen);
1893 show_borders (window, set_fullscreen);
1894 gtk_action_set_stock_id (priv->menu_fullscreen,
1895 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
1896 priv->is_fullscreen = set_fullscreen;
1903 empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
1904 EmpathyCallWindow *window)
1906 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1908 int w,h, handle_size;
1910 w = GTK_WIDGET (window)->allocation.width;
1911 h = GTK_WIDGET (window)->allocation.height;
1913 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
1915 if (gtk_toggle_button_get_active (toggle))
1917 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
1918 gtk_widget_show (priv->sidebar);
1919 w += priv->sidebar->allocation.width + handle_size;
1923 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
1924 w -= priv->sidebar->allocation.width + handle_size;
1925 gtk_widget_hide (priv->sidebar);
1928 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
1931 gtk_window_resize (GTK_WINDOW (window), w, h);
1935 empathy_call_window_set_send_video (EmpathyCallWindow *window,
1938 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1939 EmpathyTpCall *call;
1941 priv->sending_video = send;
1943 /* When we start sending video, we want to show the video preview by
1947 empathy_call_window_setup_video_preview (window);
1948 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1952 g_object_get (priv->handler, "tp-call", &call, NULL);
1953 empathy_tp_call_request_video_stream_direction (call, send);
1954 g_object_unref (call);
1958 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
1959 EmpathyCallWindow *window)
1961 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1964 if (priv->call_state != CONNECTED)
1967 active = (gtk_toggle_tool_button_get_active (toggle));
1969 if (priv->sending_video == active)
1972 empathy_call_window_set_send_video (window, active);
1973 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), active);
1977 empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
1978 EmpathyCallWindow *window)
1980 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1983 if (priv->call_state != CONNECTED)
1986 active = (gtk_toggle_action_get_active (toggle));
1988 if (priv->sending_video == active)
1991 empathy_call_window_set_send_video (window, active);
1992 gtk_toggle_tool_button_set_active (
1993 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), active);
1997 empathy_call_window_show_preview_toggled_cb (GtkToggleAction *toggle,
1998 EmpathyCallWindow *window)
2000 gboolean show_preview_toggled;
2001 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2003 show_preview_toggled = gtk_toggle_action_get_active (toggle);
2005 if (show_preview_toggled)
2007 empathy_call_window_setup_video_preview (window);
2008 gtk_widget_show (priv->self_user_output_frame);
2009 empathy_call_window_update_self_avatar_visibility (window);
2013 gtk_widget_hide (priv->self_user_output_frame);
2018 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
2019 EmpathyCallWindow *window)
2021 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2024 if (priv->audio_input == NULL)
2027 active = (gtk_toggle_tool_button_get_active (toggle));
2031 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2033 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
2037 /* TODO, Instead of setting the input volume to 0 we should probably
2038 * stop sending but this would cause the audio call to drop if both
2039 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
2040 * in the future. GNOME #574574
2042 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2044 gtk_adjustment_set_value (priv->audio_input_adj, 0);
2049 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
2050 EmpathyCallWindow *window)
2052 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2054 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2059 empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
2060 EmpathyCallWindow *window)
2062 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2064 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2069 empathy_call_window_hangup_cb (gpointer object,
2070 EmpathyCallWindow *window)
2072 if (empathy_call_window_disconnected (window))
2073 gtk_widget_destroy (GTK_WIDGET (window));
2077 empathy_call_window_restart_call (EmpathyCallWindow *window)
2080 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2082 gtk_widget_destroy (priv->remote_user_output_hbox);
2083 gtk_widget_destroy (priv->self_user_output_hbox);
2085 priv->pipeline = gst_pipeline_new (NULL);
2086 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
2087 priv->bus_message_source_id = gst_bus_add_watch (bus,
2088 empathy_call_window_bus_message, window);
2090 empathy_call_window_setup_remote_frame (bus, window);
2091 empathy_call_window_setup_self_frame (bus, window);
2093 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
2094 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
2096 /* While the call was disconnected, the input volume might have changed.
2097 * However, since the audio_input source was destroyed, its volume has not
2098 * been updated during that time. That's why we manually update it here */
2099 empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
2101 g_object_unref (bus);
2103 gtk_widget_show_all (priv->content_hbox);
2105 if (!empathy_call_handler_has_initial_video (priv->handler))
2106 gtk_widget_hide (priv->self_user_output_frame);
2108 priv->outgoing = TRUE;
2109 empathy_call_window_set_state_connecting (window);
2111 priv->call_started = TRUE;
2112 empathy_call_handler_start_call (priv->handler);
2113 empathy_call_window_setup_avatars (window, priv->handler);
2114 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2116 gtk_action_set_sensitive (priv->redial, FALSE);
2117 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2121 empathy_call_window_redial_cb (gpointer object,
2122 EmpathyCallWindow *window)
2124 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2126 if (priv->call_state == CONNECTED)
2127 priv->call_state = REDIALING;
2129 empathy_call_handler_stop_call (priv->handler);
2131 if (priv->call_state != CONNECTED)
2132 empathy_call_window_restart_call (window);
2136 empathy_call_window_fullscreen_cb (gpointer object,
2137 EmpathyCallWindow *window)
2139 empathy_call_window_fullscreen_toggle (window);
2143 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
2145 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2147 if (priv->is_fullscreen)
2148 gtk_window_unfullscreen (GTK_WINDOW (window));
2150 gtk_window_fullscreen (GTK_WINDOW (window));
2154 empathy_call_window_video_button_press_cb (GtkWidget *video_output,
2155 GdkEventButton *event, EmpathyCallWindow *window)
2157 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2159 empathy_call_window_video_menu_popup (window, event->button);
2167 empathy_call_window_key_press_cb (GtkWidget *video_output,
2168 GdkEventKey *event, EmpathyCallWindow *window)
2170 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2172 if (priv->is_fullscreen && event->keyval == GDK_Escape)
2174 /* Since we are in fullscreen mode, toggling will bring us back to
2176 empathy_call_window_fullscreen_toggle (window);
2184 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
2185 GdkEventMotion *event, EmpathyCallWindow *window)
2187 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2189 if (priv->is_fullscreen)
2191 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
2198 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
2202 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2204 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2206 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
2207 button, gtk_get_current_event_time ());
2208 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
2212 empathy_call_window_status_message (EmpathyCallWindow *window,
2215 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2217 if (priv->context_id == 0)
2219 priv->context_id = gtk_statusbar_get_context_id (
2220 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
2224 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
2227 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
2232 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
2233 gdouble value, EmpathyCallWindow *window)
2235 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2237 if (priv->audio_output == NULL)
2240 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),