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 EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
65 /* If an video input error occurs, the error message will start with "v4l" */
66 #define VIDEO_INPUT_ERROR_PREFIX "v4l"
68 /* The time interval in milliseconds between 2 outgoing rings */
69 #define MS_BETWEEN_RING 500
71 G_DEFINE_TYPE(EmpathyCallWindow, empathy_call_window, GTK_TYPE_WINDOW)
80 static guint signals[LAST_SIGNAL] = {0};
84 PROP_CALL_HANDLER = 1,
94 /* private structure */
95 typedef struct _EmpathyCallWindowPriv EmpathyCallWindowPriv;
97 struct _EmpathyCallWindowPriv
99 gboolean dispose_has_run;
100 EmpathyCallHandler *handler;
101 EmpathyContact *contact;
106 GtkUIManager *ui_manager;
107 GtkWidget *video_output;
108 GtkWidget *video_preview;
109 GtkWidget *remote_user_avatar_widget;
110 GtkWidget *self_user_avatar_widget;
112 GtkWidget *sidebar_button;
113 GtkWidget *statusbar;
114 GtkWidget *volume_button;
115 GtkWidget *redial_button;
116 GtkWidget *mic_button;
117 GtkWidget *camera_button;
120 GtkAction *show_preview;
121 GtkAction *send_video;
123 GtkAction *menu_fullscreen;
125 /* The frames and boxes that contain self and remote avatar and video
126 input/output. When we redial, we destroy and re-create the boxes */
127 GtkWidget *remote_user_output_frame;
128 GtkWidget *self_user_output_frame;
129 GtkWidget *remote_user_output_hbox;
130 GtkWidget *self_user_output_hbox;
132 /* We keep a reference on the hbox which contains the main content so we can
133 easilly repack everything when toggling fullscreen */
134 GtkWidget *content_hbox;
136 /* This vbox is contained in the content_hbox and it contains the
137 self_user_output_frame and the sidebar button. When toggling fullscreen,
138 it needs to be repacked. We keep a reference on it for easier access. */
141 gulong video_output_motion_handler_id;
142 guint bus_message_source_id;
145 GtkWidget *volume_scale;
146 GtkWidget *volume_progress_bar;
147 GtkAdjustment *audio_input_adj;
149 GtkWidget *dtmf_panel;
151 GstElement *video_input;
152 GstElement *audio_input;
153 GstElement *audio_output;
154 GstElement *pipeline;
155 GstElement *video_tee;
158 GstElement *liveadder;
160 FsElementAddedNotifier *fsnotifier;
167 GtkWidget *video_contrast;
168 GtkWidget *video_brightness;
169 GtkWidget *video_gamma;
172 gboolean call_started;
173 gboolean sending_video;
175 EmpathyCallWindowFullscreen *fullscreen;
176 gboolean is_fullscreen;
178 /* Those fields represent the state of the window before it actually was in
180 gboolean sidebar_was_visible_before_fs;
181 gint original_width_before_fs;
182 gint original_height_before_fs;
185 #define GET_PRIV(o) \
186 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_CALL_WINDOW, \
187 EmpathyCallWindowPriv))
189 static void empathy_call_window_realized_cb (GtkWidget *widget,
190 EmpathyCallWindow *window);
192 static gboolean empathy_call_window_delete_cb (GtkWidget *widget,
193 GdkEvent *event, EmpathyCallWindow *window);
195 static gboolean empathy_call_window_state_event_cb (GtkWidget *widget,
196 GdkEventWindowState *event, EmpathyCallWindow *window);
198 static void empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
199 EmpathyCallWindow *window);
201 static void empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
202 EmpathyCallWindow *window);
204 static void empathy_call_window_set_send_video (EmpathyCallWindow *window,
207 static void empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
208 EmpathyCallWindow *window);
210 static void empathy_call_window_show_preview_toggled_cb (GtkToggleAction *toggle,
211 EmpathyCallWindow *window);
213 static void empathy_call_window_mic_toggled_cb (
214 GtkToggleToolButton *toggle, EmpathyCallWindow *window);
216 static void empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
217 EmpathyCallWindow *window);
219 static void empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
220 EmpathyCallWindow *window);
222 static void empathy_call_window_hangup_cb (gpointer object,
223 EmpathyCallWindow *window);
225 static void empathy_call_window_fullscreen_cb (gpointer object,
226 EmpathyCallWindow *window);
228 static void empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window);
230 static gboolean empathy_call_window_video_button_press_cb (GtkWidget *video_output,
231 GdkEventButton *event, EmpathyCallWindow *window);
233 static gboolean empathy_call_window_key_press_cb (GtkWidget *video_output,
234 GdkEventKey *event, EmpathyCallWindow *window);
236 static gboolean empathy_call_window_video_output_motion_notify (GtkWidget *widget,
237 GdkEventMotion *event, EmpathyCallWindow *window);
239 static void empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
242 static void empathy_call_window_redial_cb (gpointer object,
243 EmpathyCallWindow *window);
245 static void empathy_call_window_restart_call (EmpathyCallWindow *window);
247 static void empathy_call_window_status_message (EmpathyCallWindow *window,
250 static void empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
251 EmpathyCallWindow *window);
253 static gboolean empathy_call_window_bus_message (GstBus *bus,
254 GstMessage *message, gpointer user_data);
257 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
258 gdouble value, EmpathyCallWindow *window);
261 empathy_call_window_setup_toolbar (EmpathyCallWindow *self)
263 EmpathyCallWindowPriv *priv = GET_PRIV (self);
264 GtkToolItem *tool_item;
266 /* Add an empty expanded GtkToolItem so the volume button is at the end of
268 tool_item = gtk_tool_item_new ();
269 gtk_tool_item_set_expand (tool_item, TRUE);
270 gtk_widget_show (GTK_WIDGET (tool_item));
271 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
273 priv->volume_button = gtk_volume_button_new ();
274 /* FIXME listen to the audiosinks signals and update the button according to
275 * that, for now starting out at 1.0 and assuming only the app changes the
277 gtk_scale_button_set_value (GTK_SCALE_BUTTON (priv->volume_button), 1.0);
278 g_signal_connect (G_OBJECT (priv->volume_button), "value-changed",
279 G_CALLBACK (empathy_call_window_volume_changed_cb), self);
281 tool_item = gtk_tool_item_new ();
282 gtk_container_add (GTK_CONTAINER (tool_item), priv->volume_button);
283 gtk_widget_show_all (GTK_WIDGET (tool_item));
284 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
288 dtmf_button_pressed_cb (GtkButton *button, EmpathyCallWindow *window)
290 EmpathyCallWindowPriv *priv = GET_PRIV (window);
295 g_object_get (priv->handler, "tp-call", &call, NULL);
297 button_quark = g_quark_from_static_string (BUTTON_ID);
298 event = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (button),
301 empathy_tp_call_start_tone (call, event);
303 g_object_unref (call);
307 dtmf_button_released_cb (GtkButton *button, EmpathyCallWindow *window)
309 EmpathyCallWindowPriv *priv = GET_PRIV (window);
312 g_object_get (priv->handler, "tp-call", &call, NULL);
314 empathy_tp_call_stop_tone (call);
316 g_object_unref (call);
320 empathy_call_window_create_dtmf (EmpathyCallWindow *self)
328 } dtmfbuttons[] = { { "1", TP_DTMF_EVENT_DIGIT_1 },
329 { "2", TP_DTMF_EVENT_DIGIT_2 },
330 { "3", TP_DTMF_EVENT_DIGIT_3 },
331 { "4", TP_DTMF_EVENT_DIGIT_4 },
332 { "5", TP_DTMF_EVENT_DIGIT_5 },
333 { "6", TP_DTMF_EVENT_DIGIT_6 },
334 { "7", TP_DTMF_EVENT_DIGIT_7 },
335 { "8", TP_DTMF_EVENT_DIGIT_8 },
336 { "9", TP_DTMF_EVENT_DIGIT_9 },
337 { "#", TP_DTMF_EVENT_HASH },
338 { "0", TP_DTMF_EVENT_DIGIT_0 },
339 { "*", TP_DTMF_EVENT_ASTERISK },
342 button_quark = g_quark_from_static_string (BUTTON_ID);
344 table = gtk_table_new (4, 3, TRUE);
346 for (i = 0; dtmfbuttons[i].label != NULL; i++)
348 GtkWidget *button = gtk_button_new_with_label (dtmfbuttons[i].label);
349 gtk_table_attach (GTK_TABLE (table), button, i % 3, i % 3 + 1,
350 i/3, i/3 + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
352 g_object_set_qdata (G_OBJECT (button), button_quark,
353 GUINT_TO_POINTER (dtmfbuttons[i].event));
355 g_signal_connect (G_OBJECT (button), "pressed",
356 G_CALLBACK (dtmf_button_pressed_cb), self);
357 g_signal_connect (G_OBJECT (button), "released",
358 G_CALLBACK (dtmf_button_released_cb), self);
365 empathy_call_window_create_video_input_add_slider (EmpathyCallWindow *self,
366 gchar *label_text, GtkWidget *bin)
368 GtkWidget *vbox = gtk_vbox_new (FALSE, 2);
369 GtkWidget *scale = gtk_vscale_new_with_range (0, 100, 10);
370 GtkWidget *label = gtk_label_new (label_text);
372 gtk_widget_set_sensitive (scale, FALSE);
374 gtk_container_add (GTK_CONTAINER (bin), vbox);
376 gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
377 gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 0);
378 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
384 empathy_call_window_video_contrast_changed_cb (GtkAdjustment *adj,
385 EmpathyCallWindow *self)
388 EmpathyCallWindowPriv *priv = GET_PRIV (self);
390 empathy_video_src_set_channel (priv->video_input,
391 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST, gtk_adjustment_get_value (adj));
395 empathy_call_window_video_brightness_changed_cb (GtkAdjustment *adj,
396 EmpathyCallWindow *self)
399 EmpathyCallWindowPriv *priv = GET_PRIV (self);
401 empathy_video_src_set_channel (priv->video_input,
402 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS, gtk_adjustment_get_value (adj));
406 empathy_call_window_video_gamma_changed_cb (GtkAdjustment *adj,
407 EmpathyCallWindow *self)
410 EmpathyCallWindowPriv *priv = GET_PRIV (self);
412 empathy_video_src_set_channel (priv->video_input,
413 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA, gtk_adjustment_get_value (adj));
418 empathy_call_window_create_video_input (EmpathyCallWindow *self)
420 EmpathyCallWindowPriv *priv = GET_PRIV (self);
423 hbox = gtk_hbox_new (TRUE, 3);
425 priv->video_contrast = empathy_call_window_create_video_input_add_slider (
426 self, _("Contrast"), hbox);
428 priv->video_brightness = empathy_call_window_create_video_input_add_slider (
429 self, _("Brightness"), hbox);
431 priv->video_gamma = empathy_call_window_create_video_input_add_slider (
432 self, _("Gamma"), hbox);
438 empathy_call_window_setup_video_input (EmpathyCallWindow *self)
440 EmpathyCallWindowPriv *priv = GET_PRIV (self);
444 supported = empathy_video_src_get_supported_channels (priv->video_input);
446 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST)
448 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_contrast));
450 gtk_adjustment_set_value (adj,
451 empathy_video_src_get_channel (priv->video_input,
452 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST));
454 g_signal_connect (G_OBJECT (adj), "value-changed",
455 G_CALLBACK (empathy_call_window_video_contrast_changed_cb), self);
457 gtk_widget_set_sensitive (priv->video_contrast, TRUE);
460 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS)
462 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_brightness));
464 gtk_adjustment_set_value (adj,
465 empathy_video_src_get_channel (priv->video_input,
466 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS));
468 g_signal_connect (G_OBJECT (adj), "value-changed",
469 G_CALLBACK (empathy_call_window_video_brightness_changed_cb), self);
470 gtk_widget_set_sensitive (priv->video_brightness, TRUE);
473 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA)
475 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_gamma));
477 gtk_adjustment_set_value (adj,
478 empathy_video_src_get_channel (priv->video_input,
479 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA));
481 g_signal_connect (G_OBJECT (adj), "value-changed",
482 G_CALLBACK (empathy_call_window_video_gamma_changed_cb), self);
483 gtk_widget_set_sensitive (priv->video_gamma, TRUE);
488 empathy_call_window_mic_volume_changed_cb (GtkAdjustment *adj,
489 EmpathyCallWindow *self)
491 EmpathyCallWindowPriv *priv = GET_PRIV (self);
494 if (priv->audio_input == NULL)
497 volume = gtk_adjustment_get_value (adj)/100.0;
499 /* Don't store the volume because of muting */
500 if (volume > 0 || gtk_toggle_tool_button_get_active (
501 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
502 priv->volume = volume;
504 /* Ensure that the toggle button is active if the volume is > 0 and inactive
505 * if it's smaller than 0 */
506 if ((volume > 0) != gtk_toggle_tool_button_get_active (
507 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
508 gtk_toggle_tool_button_set_active (
509 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), volume > 0);
511 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
516 empathy_call_window_audio_input_level_changed_cb (EmpathyGstAudioSrc *src,
517 gdouble level, EmpathyCallWindow *window)
520 EmpathyCallWindowPriv *priv = GET_PRIV (window);
522 value = CLAMP (pow (10, level / 20), 0.0, 1.0);
523 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar), value);
527 empathy_call_window_create_audio_input (EmpathyCallWindow *self)
529 EmpathyCallWindowPriv *priv = GET_PRIV (self);
530 GtkWidget *hbox, *vbox, *label;
532 hbox = gtk_hbox_new (TRUE, 3);
534 vbox = gtk_vbox_new (FALSE, 3);
535 gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 3);
537 priv->volume_scale = gtk_vscale_new_with_range (0, 150, 100);
538 gtk_range_set_inverted (GTK_RANGE (priv->volume_scale), TRUE);
539 label = gtk_label_new (_("Volume"));
541 priv->audio_input_adj = gtk_range_get_adjustment (
542 GTK_RANGE (priv->volume_scale));
543 priv->volume = empathy_audio_src_get_volume (EMPATHY_GST_AUDIO_SRC
544 (priv->audio_input));
545 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
547 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
548 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), self);
550 gtk_box_pack_start (GTK_BOX (vbox), priv->volume_scale, TRUE, TRUE, 3);
551 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 3);
553 priv->volume_progress_bar = gtk_progress_bar_new ();
554 gtk_progress_bar_set_orientation (
555 GTK_PROGRESS_BAR (priv->volume_progress_bar), GTK_PROGRESS_BOTTOM_TO_TOP);
556 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
559 gtk_box_pack_start (GTK_BOX (hbox), priv->volume_progress_bar, FALSE, FALSE,
566 empathy_call_window_setup_remote_frame (GstBus *bus, EmpathyCallWindow *self)
568 EmpathyCallWindowPriv *priv = GET_PRIV (self);
570 /* Initializing all the content (UI and output gst elements) related to the
572 priv->remote_user_output_hbox = gtk_hbox_new (FALSE, 0);
574 priv->remote_user_avatar_widget = gtk_image_new ();
575 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
576 priv->remote_user_avatar_widget, TRUE, TRUE, 0);
578 priv->video_output = empathy_video_widget_new (bus);
579 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
580 priv->video_output, TRUE, TRUE, 0);
582 gtk_widget_add_events (priv->video_output,
583 GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
584 g_signal_connect (G_OBJECT (priv->video_output), "button-press-event",
585 G_CALLBACK (empathy_call_window_video_button_press_cb), self);
587 gtk_container_add (GTK_CONTAINER (priv->remote_user_output_frame),
588 priv->remote_user_output_hbox);
590 priv->audio_output = empathy_audio_sink_new ();
591 gst_object_ref (priv->audio_output);
592 gst_object_sink (priv->audio_output);
596 empathy_call_window_setup_self_frame (GstBus *bus, EmpathyCallWindow *self)
598 EmpathyCallWindowPriv *priv = GET_PRIV (self);
600 /* Initializing all the content (UI and input gst elements) related to the
601 self contact, except for the video preview widget. This widget is only
602 initialized when the "show video preview" option is activated */
603 priv->self_user_output_hbox = gtk_hbox_new (FALSE, 0);
605 priv->self_user_avatar_widget = gtk_image_new ();
606 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
607 priv->self_user_avatar_widget, TRUE, TRUE, 0);
609 gtk_container_add (GTK_CONTAINER (priv->self_user_output_frame),
610 priv->self_user_output_hbox);
612 priv->video_input = empathy_video_src_new ();
613 gst_object_ref (priv->video_input);
614 gst_object_sink (priv->video_input);
616 priv->audio_input = empathy_audio_src_new ();
617 gst_object_ref (priv->audio_input);
618 gst_object_sink (priv->audio_input);
620 g_signal_connect (priv->audio_input, "peak-level-changed",
621 G_CALLBACK (empathy_call_window_audio_input_level_changed_cb), self);
625 empathy_call_window_setup_video_preview (EmpathyCallWindow *window)
627 EmpathyCallWindowPriv *priv = GET_PRIV (window);
629 GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
631 if (priv->video_preview != NULL)
633 /* Since the video preview and the video tee are initialized and freed
634 at the same time, if one is initialized, then the other one should
636 g_assert (priv->video_tee != NULL);
640 g_assert (priv->video_tee == NULL);
642 priv->video_tee = gst_element_factory_make ("tee", NULL);
643 gst_object_ref (priv->video_tee);
644 gst_object_sink (priv->video_tee);
646 priv->video_preview = empathy_video_widget_new_with_size (bus,
647 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
648 g_object_set (priv->video_preview, "sync", FALSE, "async", TRUE, NULL);
649 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
650 priv->video_preview, TRUE, TRUE, 0);
652 preview = empathy_video_widget_get_element (
653 EMPATHY_VIDEO_WIDGET (priv->video_preview));
654 gst_bin_add_many (GST_BIN (priv->pipeline), priv->video_input,
655 priv->video_tee, preview, NULL);
656 gst_element_link_many (priv->video_input, priv->video_tee,
659 g_object_unref (bus);
661 gst_element_set_state (preview, GST_STATE_PLAYING);
662 gst_element_set_state (priv->video_input, GST_STATE_PLAYING);
663 gst_element_set_state (priv->video_tee, GST_STATE_PLAYING);
667 empathy_call_window_set_state_connecting (EmpathyCallWindow *window)
669 EmpathyCallWindowPriv *priv = GET_PRIV (window);
671 empathy_call_window_status_message (window, _("Connecting..."));
672 priv->call_state = CONNECTING;
675 empathy_sound_start_playing (GTK_WIDGET (window),
676 EMPATHY_SOUND_PHONE_OUTGOING, MS_BETWEEN_RING);
680 empathy_call_window_init (EmpathyCallWindow *self)
682 EmpathyCallWindowPriv *priv = GET_PRIV (self);
691 GError *error = NULL;
693 filename = empathy_file_lookup ("empathy-call-window.ui", "src");
694 gui = empathy_builder_get_file (filename,
695 "call_window_vbox", &top_vbox,
697 "statusbar", &priv->statusbar,
698 "redial", &priv->redial_button,
699 "microphone", &priv->mic_button,
700 "camera", &priv->camera_button,
701 "toolbar", &priv->toolbar,
702 "send_video", &priv->send_video,
703 "menuredial", &priv->redial,
704 "show_preview", &priv->show_preview,
705 "ui_manager", &priv->ui_manager,
706 "menufullscreen", &priv->menu_fullscreen,
710 empathy_builder_connect (gui, self,
711 "menuhangup", "activate", empathy_call_window_hangup_cb,
712 "hangup", "clicked", empathy_call_window_hangup_cb,
713 "menuredial", "activate", empathy_call_window_redial_cb,
714 "redial", "clicked", empathy_call_window_redial_cb,
715 "microphone", "toggled", empathy_call_window_mic_toggled_cb,
716 "camera", "toggled", empathy_call_window_camera_toggled_cb,
717 "send_video", "toggled", empathy_call_window_send_video_toggled_cb,
718 "show_preview", "toggled", empathy_call_window_show_preview_toggled_cb,
719 "menufullscreen", "activate", empathy_call_window_fullscreen_cb,
722 priv->lock = g_mutex_new ();
724 gtk_container_add (GTK_CONTAINER (self), top_vbox);
726 priv->content_hbox = gtk_hbox_new (FALSE, CONTENT_HBOX_SPACING);
727 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
728 CONTENT_HBOX_BORDER_WIDTH);
729 gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
731 priv->pipeline = gst_pipeline_new (NULL);
732 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
733 priv->bus_message_source_id = gst_bus_add_watch (bus,
734 empathy_call_window_bus_message, self);
736 priv->fsnotifier = fs_element_added_notifier_new ();
737 fs_element_added_notifier_add (priv->fsnotifier, GST_BIN (priv->pipeline));
739 keyfile = g_key_file_new ();
740 filename = empathy_file_lookup ("element-properties", "data");
741 if (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error))
743 fs_element_added_notifier_set_properties_from_keyfile (priv->fsnotifier,
748 g_warning ("Could not load element-properties file: %s", error->message);
749 g_key_file_free (keyfile);
750 g_clear_error (&error);
755 priv->remote_user_output_frame = gtk_frame_new (NULL);
756 gtk_widget_set_size_request (priv->remote_user_output_frame,
757 EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT);
758 gtk_box_pack_start (GTK_BOX (priv->content_hbox),
759 priv->remote_user_output_frame, TRUE, TRUE,
760 CONTENT_HBOX_CHILDREN_PACKING_PADDING);
761 empathy_call_window_setup_remote_frame (bus, self);
763 priv->self_user_output_frame = gtk_frame_new (NULL);
764 gtk_widget_set_size_request (priv->self_user_output_frame,
765 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
767 priv->vbox = gtk_vbox_new (FALSE, 3);
768 gtk_box_pack_start (GTK_BOX (priv->content_hbox), priv->vbox,
769 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
770 gtk_box_pack_start (GTK_BOX (priv->vbox), priv->self_user_output_frame, FALSE,
772 empathy_call_window_setup_self_frame (bus, self);
774 empathy_call_window_setup_toolbar (self);
776 g_object_unref (bus);
778 priv->sidebar_button = gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
779 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
780 g_signal_connect (G_OBJECT (priv->sidebar_button), "toggled",
781 G_CALLBACK (empathy_call_window_sidebar_toggled_cb), self);
783 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
785 h = gtk_hbox_new (FALSE, 3);
786 gtk_box_pack_end (GTK_BOX (priv->vbox), h, FALSE, FALSE, 3);
787 gtk_box_pack_end (GTK_BOX (h), priv->sidebar_button, FALSE, FALSE, 3);
789 priv->sidebar = empathy_sidebar_new ();
790 g_signal_connect (G_OBJECT (priv->sidebar),
791 "hide", G_CALLBACK (empathy_call_window_sidebar_hidden_cb), self);
792 g_signal_connect (G_OBJECT (priv->sidebar),
793 "show", G_CALLBACK (empathy_call_window_sidebar_shown_cb), self);
794 gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
796 priv->dtmf_panel = empathy_call_window_create_dtmf (self);
797 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Dialpad"),
800 gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
802 page = empathy_call_window_create_audio_input (self);
803 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Audio input"),
806 page = empathy_call_window_create_video_input (self);
807 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Video input"),
810 gtk_widget_show_all (top_vbox);
812 gtk_widget_hide (priv->sidebar);
814 priv->fullscreen = empathy_call_window_fullscreen_new (self);
815 empathy_call_window_fullscreen_set_video_widget (priv->fullscreen, priv->video_output);
816 g_signal_connect (G_OBJECT (priv->fullscreen->leave_fullscreen_button),
817 "clicked", G_CALLBACK (empathy_call_window_fullscreen_cb), self);
819 g_signal_connect (G_OBJECT (self), "realize",
820 G_CALLBACK (empathy_call_window_realized_cb), self);
822 g_signal_connect (G_OBJECT (self), "delete-event",
823 G_CALLBACK (empathy_call_window_delete_cb), self);
825 g_signal_connect (G_OBJECT (self), "window-state-event",
826 G_CALLBACK (empathy_call_window_state_event_cb), self);
828 g_signal_connect (G_OBJECT (self), "key-press-event",
829 G_CALLBACK (empathy_call_window_key_press_cb), self);
831 priv->timer = g_timer_new ();
833 g_object_ref (priv->ui_manager);
834 g_object_unref (gui);
837 /* Instead of specifying a width and a height, we specify only one size. That's
838 because we want a square avatar icon. */
840 init_contact_avatar_with_size (EmpathyContact *contact, GtkWidget *image_widget,
843 GdkPixbuf *pixbuf_avatar = NULL;
847 pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact,
851 if (pixbuf_avatar == NULL)
853 pixbuf_avatar = empathy_pixbuf_from_icon_name_sized ("stock_person",
857 gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget), pixbuf_avatar);
861 set_window_title (EmpathyCallWindow *self)
863 EmpathyCallWindowPriv *priv = GET_PRIV (self);
866 /* translators: Call is a noun and %s is the contact name. This string is used
867 * in the window title */
868 tmp = g_strdup_printf (_("Call with %s"),
869 empathy_contact_get_name (priv->contact));
870 gtk_window_set_title (GTK_WINDOW (self), tmp);
875 contact_name_changed_cb (EmpathyContact *contact,
876 GParamSpec *pspec, EmpathyCallWindow *self)
878 set_window_title (self);
882 contact_avatar_changed_cb (EmpathyContact *contact,
883 GParamSpec *pspec, GtkWidget *avatar_widget)
885 init_contact_avatar_with_size (contact, avatar_widget,
886 avatar_widget->allocation.height);
890 empathy_call_window_got_self_contact_cb (EmpathyTpContactFactory *factory,
891 EmpathyContact *contact, const GError *error, gpointer user_data,
892 GObject *weak_object)
894 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
895 EmpathyCallWindowPriv *priv = GET_PRIV (self);
897 init_contact_avatar_with_size (contact, priv->self_user_avatar_widget,
898 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
900 g_signal_connect (contact, "notify::avatar",
901 G_CALLBACK (contact_avatar_changed_cb), priv->self_user_avatar_widget);
905 empathy_call_window_setup_avatars (EmpathyCallWindow *self,
906 EmpathyCallHandler *handler)
908 EmpathyCallWindowPriv *priv = GET_PRIV (self);
910 g_object_get (handler, "contact", &(priv->contact), NULL);
912 if (priv->contact != NULL)
914 TpConnection *connection;
915 EmpathyTpContactFactory *factory;
917 set_window_title (self);
919 g_signal_connect (priv->contact, "notify::name",
920 G_CALLBACK (contact_name_changed_cb), self);
921 g_signal_connect (priv->contact, "notify::avatar",
922 G_CALLBACK (contact_avatar_changed_cb),
923 priv->remote_user_avatar_widget);
925 /* Retreiving the self avatar */
926 connection = empathy_contact_get_connection (priv->contact);
927 factory = empathy_tp_contact_factory_dup_singleton (connection);
928 empathy_tp_contact_factory_get_from_handle (factory,
929 tp_connection_get_self_handle (connection),
930 empathy_call_window_got_self_contact_cb, self, NULL, NULL);
932 g_object_unref (factory);
936 g_warning ("call handler doesn't have a contact");
937 /* translators: Call is a noun. This string is used in the window title */
938 gtk_window_set_title (GTK_WINDOW (self), _("Call"));
940 /* Since we can't access the remote contact, we can't get a connection
941 to it and can't get the self contact (and its avatar). This means
942 that we have to manually set the self avatar. */
943 init_contact_avatar_with_size (NULL, priv->self_user_avatar_widget,
944 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
947 init_contact_avatar_with_size (priv->contact,
948 priv->remote_user_avatar_widget, MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
949 REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
951 /* The remote avatar is shown by default and will be hidden when we receive
952 video from the remote side. */
953 gtk_widget_hide (priv->video_output);
954 gtk_widget_show (priv->remote_user_avatar_widget);
958 empathy_call_window_setup_video_preview_visibility (EmpathyCallWindow *self,
959 EmpathyCallHandler *handler)
961 EmpathyCallWindowPriv *priv = GET_PRIV (self);
962 gboolean initial_video = empathy_call_handler_has_initial_video (priv->handler);
964 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
969 empathy_call_window_constructed (GObject *object)
971 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
972 EmpathyCallWindowPriv *priv = GET_PRIV (self);
975 g_assert (priv->handler != NULL);
977 g_object_get (priv->handler, "tp-call", &call, NULL);
978 priv->outgoing = (call == NULL);
980 g_object_unref (call);
982 empathy_call_window_setup_avatars (self, priv->handler);
983 empathy_call_window_setup_video_preview_visibility (self, priv->handler);
984 empathy_call_window_set_state_connecting (self);
987 static void empathy_call_window_dispose (GObject *object);
988 static void empathy_call_window_finalize (GObject *object);
991 empathy_call_window_set_property (GObject *object,
992 guint property_id, const GValue *value, GParamSpec *pspec)
994 EmpathyCallWindowPriv *priv = GET_PRIV (object);
998 case PROP_CALL_HANDLER:
999 priv->handler = g_value_dup_object (value);
1002 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1007 empathy_call_window_get_property (GObject *object,
1008 guint property_id, GValue *value, GParamSpec *pspec)
1010 EmpathyCallWindowPriv *priv = GET_PRIV (object);
1012 switch (property_id)
1014 case PROP_CALL_HANDLER:
1015 g_value_set_object (value, priv->handler);
1018 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1023 empathy_call_window_class_init (
1024 EmpathyCallWindowClass *empathy_call_window_class)
1026 GObjectClass *object_class = G_OBJECT_CLASS (empathy_call_window_class);
1027 GParamSpec *param_spec;
1029 g_type_class_add_private (empathy_call_window_class,
1030 sizeof (EmpathyCallWindowPriv));
1032 object_class->constructed = empathy_call_window_constructed;
1033 object_class->set_property = empathy_call_window_set_property;
1034 object_class->get_property = empathy_call_window_get_property;
1036 object_class->dispose = empathy_call_window_dispose;
1037 object_class->finalize = empathy_call_window_finalize;
1039 param_spec = g_param_spec_object ("handler",
1040 "handler", "The call handler",
1041 EMPATHY_TYPE_CALL_HANDLER,
1042 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1043 g_object_class_install_property (object_class,
1044 PROP_CALL_HANDLER, param_spec);
1048 empathy_call_window_video_stream_changed_cb (EmpathyTpCall *call,
1049 GParamSpec *property, EmpathyCallWindow *self)
1051 empathy_call_window_update_avatars_visibility (call, self);
1055 empathy_call_window_dispose (GObject *object)
1057 EmpathyTpCall *call;
1058 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1059 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1061 if (priv->dispose_has_run)
1064 priv->dispose_has_run = TRUE;
1066 g_object_get (priv->handler, "tp-call", &call, NULL);
1070 g_signal_handlers_disconnect_by_func (call,
1071 empathy_call_window_video_stream_changed_cb, object);
1074 g_object_unref (call);
1076 if (priv->handler != NULL)
1077 g_object_unref (priv->handler);
1078 priv->handler = NULL;
1080 if (priv->pipeline != NULL)
1081 g_object_unref (priv->pipeline);
1082 priv->pipeline = NULL;
1084 if (priv->video_input != NULL)
1085 g_object_unref (priv->video_input);
1086 priv->video_input = NULL;
1088 if (priv->audio_input != NULL)
1089 g_object_unref (priv->audio_input);
1090 priv->audio_input = NULL;
1092 if (priv->audio_output != NULL)
1093 g_object_unref (priv->audio_output);
1094 priv->audio_output = NULL;
1096 if (priv->video_tee != NULL)
1097 g_object_unref (priv->video_tee);
1098 priv->video_tee = NULL;
1100 if (priv->fsnotifier != NULL)
1101 g_object_unref (priv->fsnotifier);
1102 priv->fsnotifier = NULL;
1104 if (priv->timer_id != 0)
1105 g_source_remove (priv->timer_id);
1108 if (priv->ui_manager != NULL)
1109 g_object_unref (priv->ui_manager);
1110 priv->ui_manager = NULL;
1112 if (priv->contact != NULL)
1114 g_signal_handlers_disconnect_by_func (priv->contact,
1115 contact_name_changed_cb, self);
1116 g_object_unref (priv->contact);
1117 priv->contact = NULL;
1120 /* release any references held by the object here */
1121 if (G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose)
1122 G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
1126 empathy_call_window_finalize (GObject *object)
1128 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1129 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1131 if (priv->video_output_motion_handler_id != 0)
1133 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1134 priv->video_output_motion_handler_id);
1135 priv->video_output_motion_handler_id = 0;
1138 if (priv->bus_message_source_id != 0)
1140 g_source_remove (priv->bus_message_source_id);
1141 priv->bus_message_source_id = 0;
1144 /* free any data held directly by the object here */
1145 g_mutex_free (priv->lock);
1147 g_timer_destroy (priv->timer);
1149 G_OBJECT_CLASS (empathy_call_window_parent_class)->finalize (object);
1154 empathy_call_window_new (EmpathyCallHandler *handler)
1156 return EMPATHY_CALL_WINDOW (
1157 g_object_new (EMPATHY_TYPE_CALL_WINDOW, "handler", handler, NULL));
1161 empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
1162 GstElement *conference, gpointer user_data)
1164 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1165 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1167 gst_bin_add (GST_BIN (priv->pipeline), conference);
1169 gst_element_set_state (conference, GST_STATE_PLAYING);
1173 empathy_call_window_request_resource_cb (EmpathyCallHandler *handler,
1174 FsMediaType type, FsStreamDirection direction, gpointer user_data)
1176 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1177 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1179 if (type != TP_MEDIA_STREAM_TYPE_VIDEO)
1182 if (direction == FS_DIRECTION_RECV)
1185 /* video and direction is send */
1186 return priv->video_input != NULL;
1190 empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
1192 GstStateChangeReturn state_change_return;
1193 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1195 if (priv->pipeline == NULL)
1198 if (priv->bus_message_source_id != 0)
1200 g_source_remove (priv->bus_message_source_id);
1201 priv->bus_message_source_id = 0;
1204 state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1206 if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1207 state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1209 if (priv->pipeline != NULL)
1210 g_object_unref (priv->pipeline);
1211 priv->pipeline = NULL;
1213 if (priv->video_input != NULL)
1214 g_object_unref (priv->video_input);
1215 priv->video_input = NULL;
1217 if (priv->audio_input != NULL)
1218 g_object_unref (priv->audio_input);
1219 priv->audio_input = NULL;
1221 g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
1222 empathy_call_window_mic_volume_changed_cb, self);
1224 if (priv->audio_output != NULL)
1225 g_object_unref (priv->audio_output);
1226 priv->audio_output = NULL;
1228 if (priv->video_tee != NULL)
1229 g_object_unref (priv->video_tee);
1230 priv->video_tee = NULL;
1232 if (priv->video_preview != NULL)
1233 gtk_widget_destroy (priv->video_preview);
1234 priv->video_preview = NULL;
1236 priv->liveadder = NULL;
1237 priv->funnel = NULL;
1243 g_message ("Error: could not destroy pipeline. Closing call window");
1244 gtk_widget_destroy (GTK_WIDGET (self));
1251 empathy_call_window_disconnected (EmpathyCallWindow *self)
1253 gboolean could_disconnect = FALSE;
1254 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1255 gboolean could_reset_pipeline = empathy_call_window_reset_pipeline (self);
1257 if (priv->call_state == CONNECTING)
1258 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1260 if (priv->call_state != REDIALING)
1261 priv->call_state = DISCONNECTED;
1263 if (could_reset_pipeline)
1265 gboolean initial_video = empathy_call_handler_has_initial_video (
1267 g_mutex_lock (priv->lock);
1269 g_timer_stop (priv->timer);
1271 if (priv->timer_id != 0)
1272 g_source_remove (priv->timer_id);
1275 g_mutex_unlock (priv->lock);
1277 empathy_call_window_status_message (self, _("Disconnected"));
1279 gtk_action_set_sensitive (priv->redial, TRUE);
1280 gtk_widget_set_sensitive (priv->redial_button, TRUE);
1282 /* Reseting the send_video, camera_buton and mic_button to their
1284 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1285 gtk_widget_set_sensitive (priv->mic_button, FALSE);
1286 gtk_action_set_sensitive (priv->send_video, FALSE);
1287 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1289 gtk_toggle_tool_button_set_active (
1290 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), initial_video);
1291 gtk_toggle_tool_button_set_active (
1292 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
1294 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1296 gtk_action_set_sensitive (priv->show_preview, FALSE);
1298 gtk_progress_bar_set_fraction (
1299 GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
1301 gtk_widget_hide (priv->video_output);
1302 gtk_widget_show (priv->remote_user_avatar_widget);
1304 priv->sending_video = FALSE;
1305 priv->call_started = FALSE;
1307 could_disconnect = TRUE;
1310 return could_disconnect;
1315 empathy_call_window_channel_closed_cb (EmpathyCallHandler *handler,
1318 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1319 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1321 if (empathy_call_window_disconnected (self) && priv->call_state == REDIALING)
1322 empathy_call_window_restart_call (self);
1327 empathy_call_window_channel_stream_closed_cb (EmpathyCallHandler *handler,
1328 TfStream *stream, gpointer user_data)
1330 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1331 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1334 g_object_get (stream, "media-type", &media_type, NULL);
1337 * This assumes that there is only one video stream per channel...
1340 if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO)
1342 if (priv->funnel != NULL)
1346 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1347 (priv->video_output));
1349 gst_element_set_state (output, GST_STATE_NULL);
1350 gst_element_set_state (priv->funnel, GST_STATE_NULL);
1352 gst_bin_remove (GST_BIN (priv->pipeline), output);
1353 gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
1354 priv->funnel = NULL;
1357 else if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO)
1359 if (priv->liveadder != NULL)
1361 gst_element_set_state (priv->audio_output, GST_STATE_NULL);
1362 gst_element_set_state (priv->liveadder, GST_STATE_NULL);
1364 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
1365 gst_bin_remove (GST_BIN (priv->pipeline), priv->liveadder);
1366 priv->liveadder = NULL;
1371 /* Called with global lock held */
1373 empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
1375 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1378 if (priv->funnel == NULL)
1382 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1383 (priv->video_output));
1385 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
1387 gst_bin_add (GST_BIN (priv->pipeline), priv->funnel);
1388 gst_bin_add (GST_BIN (priv->pipeline), output);
1390 gst_element_link (priv->funnel, output);
1392 gst_element_set_state (priv->funnel, GST_STATE_PLAYING);
1393 gst_element_set_state (output, GST_STATE_PLAYING);
1396 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
1401 /* Called with global lock held */
1403 empathy_call_window_get_audio_sink_pad (EmpathyCallWindow *self)
1405 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1408 if (priv->liveadder == NULL)
1410 priv->liveadder = gst_element_factory_make ("liveadder", NULL);
1412 gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder);
1413 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output);
1415 gst_element_link (priv->liveadder, priv->audio_output);
1417 gst_element_set_state (priv->liveadder, GST_STATE_PLAYING);
1418 gst_element_set_state (priv->audio_output, GST_STATE_PLAYING);
1421 pad = gst_element_get_request_pad (priv->liveadder, "sink%d");
1427 empathy_call_window_update_timer (gpointer user_data)
1429 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1430 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1434 time = g_timer_elapsed (priv->timer, NULL);
1436 /* Translators: number of minutes:seconds the caller has been connected */
1437 str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time / 60,
1439 empathy_call_window_status_message (self, str);
1446 empathy_call_window_connected (gpointer user_data)
1448 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1449 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1450 EmpathyTpCall *call;
1451 gboolean can_send_video;
1453 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1455 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
1456 empathy_contact_can_voip_video (priv->contact);
1458 g_object_get (priv->handler, "tp-call", &call, NULL);
1460 g_signal_connect (call, "notify::video-stream",
1461 G_CALLBACK (empathy_call_window_video_stream_changed_cb), self);
1463 if (empathy_tp_call_has_dtmf (call))
1464 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
1466 if (priv->video_input == NULL)
1467 empathy_call_window_set_send_video (self, FALSE);
1469 priv->sending_video = can_send_video ?
1470 empathy_tp_call_is_sending_video (call) : FALSE;
1472 gtk_action_set_sensitive (priv->show_preview, TRUE);
1473 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1475 || gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (priv->show_preview)));
1476 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video),
1477 priv->sending_video && priv->video_input != NULL);
1478 gtk_toggle_tool_button_set_active (
1479 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button),
1480 priv->sending_video && priv->video_input != NULL);
1481 gtk_widget_set_sensitive (priv->camera_button, can_send_video);
1482 gtk_action_set_sensitive (priv->send_video, can_send_video);
1484 gtk_action_set_sensitive (priv->redial, FALSE);
1485 gtk_widget_set_sensitive (priv->redial_button, FALSE);
1487 gtk_widget_set_sensitive (priv->mic_button, TRUE);
1489 empathy_call_window_update_avatars_visibility (call, self);
1491 g_object_unref (call);
1493 g_mutex_lock (priv->lock);
1495 priv->timer_id = g_timeout_add_seconds (1,
1496 empathy_call_window_update_timer, self);
1498 g_mutex_unlock (priv->lock);
1500 empathy_call_window_update_timer (self);
1506 /* Called from the streaming thread */
1508 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
1509 GstPad *src, guint media_type, gpointer user_data)
1511 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1512 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1516 g_mutex_lock (priv->lock);
1518 if (priv->call_state != CONNECTED)
1520 g_timer_start (priv->timer);
1521 priv->timer_id = g_idle_add (empathy_call_window_connected, self);
1522 priv->call_state = CONNECTED;
1527 case TP_MEDIA_STREAM_TYPE_AUDIO:
1528 pad = empathy_call_window_get_audio_sink_pad (self);
1530 case TP_MEDIA_STREAM_TYPE_VIDEO:
1531 gtk_widget_hide (priv->remote_user_avatar_widget);
1532 gtk_widget_show (priv->video_output);
1533 pad = empathy_call_window_get_video_sink_pad (self);
1536 g_assert_not_reached ();
1539 gst_pad_link (src, pad);
1540 gst_object_unref (pad);
1542 g_mutex_unlock (priv->lock);
1545 /* Called from the streaming thread */
1547 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
1548 GstPad *sink, guint media_type, gpointer user_data)
1550 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1551 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1556 case TP_MEDIA_STREAM_TYPE_AUDIO:
1557 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
1559 pad = gst_element_get_static_pad (priv->audio_input, "src");
1560 gst_pad_link (pad, sink);
1562 gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
1564 case TP_MEDIA_STREAM_TYPE_VIDEO:
1565 if (priv->video_input != NULL)
1567 EmpathyTpCall *call;
1568 g_object_get (priv->handler, "tp-call", &call, NULL);
1570 if (empathy_tp_call_is_sending_video (call))
1572 empathy_call_window_setup_video_preview (self);
1574 gtk_toggle_action_set_active (
1575 GTK_TOGGLE_ACTION (priv->show_preview), TRUE);
1577 if (priv->video_preview != NULL)
1578 gtk_widget_show (priv->video_preview);
1579 gtk_widget_hide (priv->self_user_avatar_widget);
1582 g_object_unref (call);
1584 if (priv->video_tee != NULL)
1586 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
1587 gst_pad_link (pad, sink);
1592 g_assert_not_reached ();
1598 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
1600 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1601 GstElement *preview;
1603 preview = empathy_video_widget_get_element (
1604 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1606 gst_element_set_state (priv->video_input, GST_STATE_NULL);
1607 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
1608 gst_element_set_state (preview, GST_STATE_NULL);
1610 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
1611 priv->video_tee, preview, NULL);
1613 g_object_unref (priv->video_input);
1614 priv->video_input = NULL;
1615 g_object_unref (priv->video_tee);
1616 priv->video_tee = NULL;
1617 gtk_widget_destroy (priv->video_preview);
1618 priv->video_preview = NULL;
1620 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), FALSE);
1621 gtk_toggle_tool_button_set_active (
1622 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), FALSE);
1623 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1624 gtk_action_set_sensitive (priv->send_video, FALSE);
1626 gtk_widget_show (priv->self_user_avatar_widget);
1631 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
1634 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1635 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1638 empathy_call_handler_bus_message (priv->handler, bus, message);
1640 switch (GST_MESSAGE_TYPE (message))
1642 case GST_MESSAGE_STATE_CHANGED:
1643 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
1645 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1646 if (newstate == GST_STATE_PAUSED)
1647 empathy_call_window_setup_video_input (self);
1649 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
1650 !priv->call_started)
1652 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1653 if (newstate == GST_STATE_PAUSED)
1655 priv->call_started = TRUE;
1656 empathy_call_handler_start_call (priv->handler);
1657 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1661 case GST_MESSAGE_ERROR:
1663 GError *error = NULL;
1664 GstElement *gst_error;
1667 gst_message_parse_error (message, &error, &debug);
1668 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
1670 g_message ("Element error: %s -- %s\n", error->message, debug);
1672 if (g_str_has_prefix (gst_element_get_name (gst_error),
1673 VIDEO_INPUT_ERROR_PREFIX))
1675 /* Remove the video input and continue */
1676 if (priv->video_input != NULL)
1677 empathy_call_window_remove_video_input (self);
1678 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1682 empathy_call_window_disconnected (self);
1684 g_error_free (error);
1695 empathy_call_window_update_self_avatar_visibility (EmpathyCallWindow *window)
1697 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1699 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (priv->show_preview)))
1701 if (priv->video_preview != NULL)
1703 gtk_widget_hide (priv->self_user_avatar_widget);
1704 gtk_widget_show (priv->video_preview);
1708 if (priv->video_preview != NULL)
1709 gtk_widget_hide (priv->video_preview);
1711 gtk_widget_show (priv->self_user_avatar_widget);
1717 empathy_call_window_update_avatars_visibility (EmpathyTpCall *call,
1718 EmpathyCallWindow *window)
1720 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1722 if (empathy_tp_call_is_receiving_video (call))
1724 gtk_widget_hide (priv->remote_user_avatar_widget);
1725 gtk_widget_show (priv->video_output);
1729 gtk_widget_hide (priv->video_output);
1730 gtk_widget_show (priv->remote_user_avatar_widget);
1733 empathy_call_window_update_self_avatar_visibility (window);
1737 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
1739 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1741 g_signal_connect (priv->handler, "conference-added",
1742 G_CALLBACK (empathy_call_window_conference_added_cb), window);
1743 g_signal_connect (priv->handler, "request-resource",
1744 G_CALLBACK (empathy_call_window_request_resource_cb), window);
1745 g_signal_connect (priv->handler, "closed",
1746 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
1747 g_signal_connect (priv->handler, "src-pad-added",
1748 G_CALLBACK (empathy_call_window_src_added_cb), window);
1749 g_signal_connect (priv->handler, "sink-pad-added",
1750 G_CALLBACK (empathy_call_window_sink_added_cb), window);
1751 g_signal_connect (priv->handler, "stream-closed",
1752 G_CALLBACK (empathy_call_window_channel_stream_closed_cb), window);
1754 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1758 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
1759 EmpathyCallWindow *window)
1761 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1763 if (priv->pipeline != NULL)
1765 if (priv->bus_message_source_id != 0)
1767 g_source_remove (priv->bus_message_source_id);
1768 priv->bus_message_source_id = 0;
1771 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1774 if (priv->call_state == CONNECTING)
1775 empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1781 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
1784 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1786 menu = gtk_ui_manager_get_widget (priv->ui_manager,
1791 gtk_widget_hide (priv->sidebar);
1792 gtk_widget_hide (menu);
1793 gtk_widget_hide (priv->vbox);
1794 gtk_widget_hide (priv->statusbar);
1795 gtk_widget_hide (priv->toolbar);
1799 if (priv->sidebar_was_visible_before_fs)
1800 gtk_widget_show (priv->sidebar);
1802 gtk_widget_show (menu);
1803 gtk_widget_show (priv->vbox);
1804 gtk_widget_show (priv->statusbar);
1805 gtk_widget_show (priv->toolbar);
1807 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
1808 priv->original_height_before_fs);
1813 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
1815 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1817 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
1818 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
1819 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
1820 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
1821 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
1822 priv->video_output, TRUE, TRUE,
1823 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
1825 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
1826 priv->vbox, TRUE, TRUE,
1827 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
1832 empathy_call_window_state_event_cb (GtkWidget *widget,
1833 GdkEventWindowState *event, EmpathyCallWindow *window)
1835 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
1837 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1838 gboolean set_fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
1842 gboolean sidebar_was_visible;
1843 gint original_width = GTK_WIDGET (window)->allocation.width;
1844 gint original_height = GTK_WIDGET (window)->allocation.height;
1846 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
1848 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
1849 priv->original_width_before_fs = original_width;
1850 priv->original_height_before_fs = original_height;
1852 if (priv->video_output_motion_handler_id == 0 &&
1853 priv->video_output != NULL)
1855 priv->video_output_motion_handler_id = g_signal_connect (
1856 G_OBJECT (priv->video_output), "motion-notify-event",
1857 G_CALLBACK (empathy_call_window_video_output_motion_notify), window);
1862 if (priv->video_output_motion_handler_id != 0)
1864 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1865 priv->video_output_motion_handler_id);
1866 priv->video_output_motion_handler_id = 0;
1870 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
1872 show_controls (window, set_fullscreen);
1873 show_borders (window, set_fullscreen);
1874 gtk_action_set_stock_id (priv->menu_fullscreen,
1875 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
1876 priv->is_fullscreen = set_fullscreen;
1883 empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
1884 EmpathyCallWindow *window)
1886 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1888 int w,h, handle_size;
1890 w = GTK_WIDGET (window)->allocation.width;
1891 h = GTK_WIDGET (window)->allocation.height;
1893 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
1895 if (gtk_toggle_button_get_active (toggle))
1897 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
1898 gtk_widget_show (priv->sidebar);
1899 w += priv->sidebar->allocation.width + handle_size;
1903 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
1904 w -= priv->sidebar->allocation.width + handle_size;
1905 gtk_widget_hide (priv->sidebar);
1908 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
1911 gtk_window_resize (GTK_WINDOW (window), w, h);
1915 empathy_call_window_set_send_video (EmpathyCallWindow *window,
1918 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1919 EmpathyTpCall *call;
1921 priv->sending_video = send;
1923 /* When we start sending video, we want to show the video preview by
1927 empathy_call_window_setup_video_preview (window);
1928 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->show_preview),
1932 g_object_get (priv->handler, "tp-call", &call, NULL);
1933 empathy_tp_call_request_video_stream_direction (call, send);
1934 g_object_unref (call);
1938 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
1939 EmpathyCallWindow *window)
1941 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1944 if (priv->call_state != CONNECTED)
1947 active = (gtk_toggle_tool_button_get_active (toggle));
1949 if (priv->sending_video == active)
1952 empathy_call_window_set_send_video (window, active);
1953 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), active);
1957 empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
1958 EmpathyCallWindow *window)
1960 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1963 if (priv->call_state != CONNECTED)
1966 active = (gtk_toggle_action_get_active (toggle));
1968 if (priv->sending_video == active)
1971 empathy_call_window_set_send_video (window, active);
1972 gtk_toggle_tool_button_set_active (
1973 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), active);
1977 empathy_call_window_show_preview_toggled_cb (GtkToggleAction *toggle,
1978 EmpathyCallWindow *window)
1980 gboolean show_preview_toggled;
1981 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1983 show_preview_toggled = gtk_toggle_action_get_active (toggle);
1985 if (show_preview_toggled)
1987 empathy_call_window_setup_video_preview (window);
1988 gtk_widget_show (priv->self_user_output_frame);
1989 empathy_call_window_update_self_avatar_visibility (window);
1993 gtk_widget_hide (priv->self_user_output_frame);
1998 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
1999 EmpathyCallWindow *window)
2001 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2004 if (priv->audio_input == NULL)
2007 active = (gtk_toggle_tool_button_get_active (toggle));
2011 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2013 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
2017 /* TODO, Instead of setting the input volume to 0 we should probably
2018 * stop sending but this would cause the audio call to drop if both
2019 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
2020 * in the future. GNOME #574574
2022 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2024 gtk_adjustment_set_value (priv->audio_input_adj, 0);
2029 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
2030 EmpathyCallWindow *window)
2032 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2034 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2039 empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
2040 EmpathyCallWindow *window)
2042 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2044 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2049 empathy_call_window_hangup_cb (gpointer object,
2050 EmpathyCallWindow *window)
2052 if (empathy_call_window_disconnected (window))
2053 gtk_widget_destroy (GTK_WIDGET (window));
2057 empathy_call_window_restart_call (EmpathyCallWindow *window)
2060 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2062 gtk_widget_destroy (priv->remote_user_output_hbox);
2063 gtk_widget_destroy (priv->self_user_output_hbox);
2065 priv->pipeline = gst_pipeline_new (NULL);
2066 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
2067 priv->bus_message_source_id = gst_bus_add_watch (bus,
2068 empathy_call_window_bus_message, window);
2070 empathy_call_window_setup_remote_frame (bus, window);
2071 empathy_call_window_setup_self_frame (bus, window);
2073 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
2074 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
2076 /* While the call was disconnected, the input volume might have changed.
2077 * However, since the audio_input source was destroyed, its volume has not
2078 * been updated during that time. That's why we manually update it here */
2079 empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
2081 g_object_unref (bus);
2083 gtk_widget_show_all (priv->content_hbox);
2085 if (!empathy_call_handler_has_initial_video (priv->handler))
2086 gtk_widget_hide (priv->self_user_output_frame);
2088 priv->outgoing = TRUE;
2089 empathy_call_window_set_state_connecting (window);
2091 priv->call_started = TRUE;
2092 empathy_call_handler_start_call (priv->handler);
2093 empathy_call_window_setup_avatars (window, priv->handler);
2094 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2096 gtk_action_set_sensitive (priv->redial, FALSE);
2097 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2101 empathy_call_window_redial_cb (gpointer object,
2102 EmpathyCallWindow *window)
2104 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2106 if (priv->call_state == CONNECTED)
2107 priv->call_state = REDIALING;
2109 empathy_call_handler_stop_call (priv->handler);
2111 if (priv->call_state != CONNECTED)
2112 empathy_call_window_restart_call (window);
2116 empathy_call_window_fullscreen_cb (gpointer object,
2117 EmpathyCallWindow *window)
2119 empathy_call_window_fullscreen_toggle (window);
2123 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
2125 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2127 if (priv->is_fullscreen)
2128 gtk_window_unfullscreen (GTK_WINDOW (window));
2130 gtk_window_fullscreen (GTK_WINDOW (window));
2134 empathy_call_window_video_button_press_cb (GtkWidget *video_output,
2135 GdkEventButton *event, EmpathyCallWindow *window)
2137 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
2139 empathy_call_window_video_menu_popup (window, event->button);
2147 empathy_call_window_key_press_cb (GtkWidget *video_output,
2148 GdkEventKey *event, EmpathyCallWindow *window)
2150 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2152 if (priv->is_fullscreen && event->keyval == GDK_Escape)
2154 /* Since we are in fullscreen mode, toggling will bring us back to
2156 empathy_call_window_fullscreen_toggle (window);
2164 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
2165 GdkEventMotion *event, EmpathyCallWindow *window)
2167 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2169 if (priv->is_fullscreen)
2171 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
2178 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
2182 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2184 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2186 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
2187 button, gtk_get_current_event_time ());
2188 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
2192 empathy_call_window_status_message (EmpathyCallWindow *window,
2195 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2197 if (priv->context_id == 0)
2199 priv->context_id = gtk_statusbar_get_context_id (
2200 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
2204 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
2207 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
2212 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
2213 gdouble value, EmpathyCallWindow *window)
2215 EmpathyCallWindowPriv *priv = GET_PRIV (window);
2217 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),