2 * empathy-call-window.c - Source for EmpathyCallWindow
3 * Copyright (C) 2008-2009 Collabora Ltd.
4 * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27 #include <gdk/gdkkeysyms.h>
30 #include <glib/gi18n.h>
32 #include <telepathy-farsight/channel.h>
34 #include <libempathy/empathy-tp-contact-factory.h>
35 #include <libempathy/empathy-utils.h>
36 #include <libempathy-gtk/empathy-avatar-image.h>
37 #include <libempathy-gtk/empathy-video-widget.h>
38 #include <libempathy-gtk/empathy-audio-src.h>
39 #include <libempathy-gtk/empathy-audio-sink.h>
40 #include <libempathy-gtk/empathy-video-src.h>
41 #include <libempathy-gtk/empathy-ui-utils.h>
43 #include "empathy-call-window.h"
45 #include "empathy-call-window-fullscreen.h"
46 #include "empathy-sidebar.h"
48 #define BUTTON_ID "empathy-call-dtmf-button-id"
50 #define CONTENT_HBOX_BORDER_WIDTH 6
51 #define CONTENT_HBOX_SPACING 3
52 #define CONTENT_HBOX_CHILDREN_PACKING_PADDING 3
54 #define SELF_VIDEO_SECTION_WIDTH 160
55 #define SELF_VIDEO_SECTION_HEIGTH 120
57 /* The avatar's default width and height are set to the same value because we
58 want a square icon. */
59 #define REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
60 #define REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
62 G_DEFINE_TYPE(EmpathyCallWindow, empathy_call_window, GTK_TYPE_WINDOW)
71 static guint signals[LAST_SIGNAL] = {0};
75 PROP_CALL_HANDLER = 1,
78 /* private structure */
79 typedef struct _EmpathyCallWindowPriv EmpathyCallWindowPriv;
81 struct _EmpathyCallWindowPriv
83 gboolean dispose_has_run;
84 EmpathyCallHandler *handler;
85 EmpathyContact *contact;
89 GtkUIManager *ui_manager;
90 GtkWidget *video_output;
91 GtkWidget *video_preview;
92 GtkWidget *remote_user_avatar_widget;
93 GtkWidget *self_user_avatar_widget;
95 GtkWidget *sidebar_button;
97 GtkWidget *volume_button;
98 GtkWidget *mic_button;
99 GtkWidget *camera_button;
102 GtkAction *send_video;
103 GtkAction *menu_fullscreen;
105 /* We keep a reference on the hbox which contains the main content so we can
106 easilly repack everything when toggling fullscreen */
107 GtkWidget *content_hbox;
109 /* This vbox is contained in the content_hbox. When toggling fullscreen,
110 it needs to be repacked. We keep a reference on it for easier access. */
113 gulong video_output_motion_handler_id;
116 GtkAdjustment *audio_input_adj;
118 GtkWidget *dtmf_panel;
120 GstElement *video_input;
121 GstElement *audio_input;
122 GstElement *audio_output;
123 GstElement *pipeline;
124 GstElement *video_tee;
127 GstElement *liveadder;
134 GtkWidget *video_contrast;
135 GtkWidget *video_brightness;
136 GtkWidget *video_gamma;
139 gboolean call_started;
140 gboolean sending_video;
142 EmpathyCallWindowFullscreen *fullscreen;
143 gboolean is_fullscreen;
145 /* Those fields represent the state of the window before it actually was in
147 gboolean sidebar_was_visible_before_fs;
148 gint original_width_before_fs;
149 gint original_height_before_fs;
152 #define GET_PRIV(o) \
153 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_CALL_WINDOW, \
154 EmpathyCallWindowPriv))
156 static void empathy_call_window_realized_cb (GtkWidget *widget,
157 EmpathyCallWindow *window);
159 static gboolean empathy_call_window_delete_cb (GtkWidget *widget,
160 GdkEvent *event, EmpathyCallWindow *window);
162 static gboolean empathy_call_window_state_event_cb (GtkWidget *widget,
163 GdkEventWindowState *event, EmpathyCallWindow *window);
165 static void empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
166 EmpathyCallWindow *window);
168 static void empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
169 EmpathyCallWindow *window);
171 static void empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
172 EmpathyCallWindow *window);
174 static void empathy_call_window_show_preview_toggled_cb (GtkToggleAction *toggle,
175 EmpathyCallWindow *window);
177 static void empathy_call_window_mic_toggled_cb (
178 GtkToggleToolButton *toggle, EmpathyCallWindow *window);
180 static void empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
181 EmpathyCallWindow *window);
183 static void empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
184 EmpathyCallWindow *window);
186 static void empathy_call_window_hangup_cb (gpointer object,
187 EmpathyCallWindow *window);
189 static void empathy_call_window_fullscreen_cb (gpointer object,
190 EmpathyCallWindow *window);
192 static void empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window);
194 static gboolean empathy_call_window_video_button_press_cb (GtkWidget *video_output,
195 GdkEventButton *event, EmpathyCallWindow *window);
197 static gboolean empathy_call_window_key_press_cb (GtkWidget *video_output,
198 GdkEventKey *event, EmpathyCallWindow *window);
200 static gboolean empathy_call_window_video_output_motion_notify (GtkWidget *widget,
201 GdkEventMotion *event, EmpathyCallWindow *window);
203 static void empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
206 static void empathy_call_window_status_message (EmpathyCallWindow *window,
209 static gboolean empathy_call_window_bus_message (GstBus *bus,
210 GstMessage *message, gpointer user_data);
213 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
214 gdouble value, EmpathyCallWindow *window);
217 empathy_call_window_setup_toolbar (EmpathyCallWindow *self)
219 EmpathyCallWindowPriv *priv = GET_PRIV (self);
220 GtkToolItem *tool_item;
222 /* Add an empty expanded GtkToolItem so the volume button is at the end of
224 tool_item = gtk_tool_item_new ();
225 gtk_tool_item_set_expand (tool_item, TRUE);
226 gtk_widget_show (GTK_WIDGET (tool_item));
227 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
229 priv->volume_button = gtk_volume_button_new ();
230 /* FIXME listen to the audiosinks signals and update the button according to
231 * that, for now starting out at 1.0 and assuming only the app changes the
233 gtk_scale_button_set_value (GTK_SCALE_BUTTON (priv->volume_button), 1.0);
234 g_signal_connect (G_OBJECT (priv->volume_button), "value-changed",
235 G_CALLBACK (empathy_call_window_volume_changed_cb), self);
237 tool_item = gtk_tool_item_new ();
238 gtk_container_add (GTK_CONTAINER (tool_item), priv->volume_button);
239 gtk_widget_show_all (GTK_WIDGET (tool_item));
240 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
244 dtmf_button_pressed_cb (GtkButton *button, EmpathyCallWindow *window)
246 EmpathyCallWindowPriv *priv = GET_PRIV (window);
251 g_object_get (priv->handler, "tp-call", &call, NULL);
253 button_quark = g_quark_from_static_string (BUTTON_ID);
254 event = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (button),
257 empathy_tp_call_start_tone (call, event);
259 g_object_unref (call);
263 dtmf_button_released_cb (GtkButton *button, EmpathyCallWindow *window)
265 EmpathyCallWindowPriv *priv = GET_PRIV (window);
268 g_object_get (priv->handler, "tp-call", &call, NULL);
270 empathy_tp_call_stop_tone (call);
272 g_object_unref (call);
276 empathy_call_window_create_dtmf (EmpathyCallWindow *self)
284 } dtmfbuttons[] = { { "1", TP_DTMF_EVENT_DIGIT_1 },
285 { "2", TP_DTMF_EVENT_DIGIT_2 },
286 { "3", TP_DTMF_EVENT_DIGIT_3 },
287 { "4", TP_DTMF_EVENT_DIGIT_4 },
288 { "5", TP_DTMF_EVENT_DIGIT_5 },
289 { "6", TP_DTMF_EVENT_DIGIT_6 },
290 { "7", TP_DTMF_EVENT_DIGIT_7 },
291 { "8", TP_DTMF_EVENT_DIGIT_8 },
292 { "9", TP_DTMF_EVENT_DIGIT_9 },
293 { "#", TP_DTMF_EVENT_HASH },
294 { "0", TP_DTMF_EVENT_DIGIT_0 },
295 { "*", TP_DTMF_EVENT_ASTERISK },
298 button_quark = g_quark_from_static_string (BUTTON_ID);
300 table = gtk_table_new (4, 3, TRUE);
302 for (i = 0; dtmfbuttons[i].label != NULL; i++)
304 GtkWidget *button = gtk_button_new_with_label (dtmfbuttons[i].label);
305 gtk_table_attach (GTK_TABLE (table), button, i % 3, i % 3 + 1,
306 i/3, i/3 + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
308 g_object_set_qdata (G_OBJECT (button), button_quark,
309 GUINT_TO_POINTER (dtmfbuttons[i].event));
311 g_signal_connect (G_OBJECT (button), "pressed",
312 G_CALLBACK (dtmf_button_pressed_cb), self);
313 g_signal_connect (G_OBJECT (button), "released",
314 G_CALLBACK (dtmf_button_released_cb), self);
321 empathy_call_window_create_video_input_add_slider (EmpathyCallWindow *self,
322 gchar *label_text, GtkWidget *bin)
324 GtkWidget *vbox = gtk_vbox_new (FALSE, 2);
325 GtkWidget *scale = gtk_vscale_new_with_range (0, 100, 10);
326 GtkWidget *label = gtk_label_new (label_text);
328 gtk_widget_set_sensitive (scale, FALSE);
330 gtk_container_add (GTK_CONTAINER (bin), vbox);
332 gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
333 gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 0);
334 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
340 empathy_call_window_video_contrast_changed_cb (GtkAdjustment *adj,
341 EmpathyCallWindow *self)
344 EmpathyCallWindowPriv *priv = GET_PRIV (self);
346 empathy_video_src_set_channel (priv->video_input,
347 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST, gtk_adjustment_get_value (adj));
351 empathy_call_window_video_brightness_changed_cb (GtkAdjustment *adj,
352 EmpathyCallWindow *self)
355 EmpathyCallWindowPriv *priv = GET_PRIV (self);
357 empathy_video_src_set_channel (priv->video_input,
358 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS, gtk_adjustment_get_value (adj));
362 empathy_call_window_video_gamma_changed_cb (GtkAdjustment *adj,
363 EmpathyCallWindow *self)
366 EmpathyCallWindowPriv *priv = GET_PRIV (self);
368 empathy_video_src_set_channel (priv->video_input,
369 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA, gtk_adjustment_get_value (adj));
374 empathy_call_window_create_video_input (EmpathyCallWindow *self)
376 EmpathyCallWindowPriv *priv = GET_PRIV (self);
379 hbox = gtk_hbox_new (TRUE, 3);
381 priv->video_contrast = empathy_call_window_create_video_input_add_slider (
382 self, _("Contrast"), hbox);
384 priv->video_brightness = empathy_call_window_create_video_input_add_slider (
385 self, _("Brightness"), hbox);
387 priv->video_gamma = empathy_call_window_create_video_input_add_slider (
388 self, _("Gamma"), hbox);
394 empathy_call_window_setup_video_input (EmpathyCallWindow *self)
396 EmpathyCallWindowPriv *priv = GET_PRIV (self);
400 supported = empathy_video_src_get_supported_channels (priv->video_input);
402 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST)
404 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_contrast));
406 gtk_adjustment_set_value (adj,
407 empathy_video_src_get_channel (priv->video_input,
408 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST));
410 g_signal_connect (G_OBJECT (adj), "value-changed",
411 G_CALLBACK (empathy_call_window_video_contrast_changed_cb), self);
413 gtk_widget_set_sensitive (priv->video_contrast, TRUE);
416 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS)
418 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_brightness));
420 gtk_adjustment_set_value (adj,
421 empathy_video_src_get_channel (priv->video_input,
422 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS));
424 g_signal_connect (G_OBJECT (adj), "value-changed",
425 G_CALLBACK (empathy_call_window_video_brightness_changed_cb), self);
426 gtk_widget_set_sensitive (priv->video_brightness, TRUE);
429 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA)
431 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_gamma));
433 gtk_adjustment_set_value (adj,
434 empathy_video_src_get_channel (priv->video_input,
435 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA));
437 g_signal_connect (G_OBJECT (adj), "value-changed",
438 G_CALLBACK (empathy_call_window_video_gamma_changed_cb), self);
439 gtk_widget_set_sensitive (priv->video_gamma, TRUE);
444 empathy_call_window_mic_volume_changed_cb (GtkAdjustment *adj,
445 EmpathyCallWindow *self)
447 EmpathyCallWindowPriv *priv = GET_PRIV (self);
450 volume = gtk_adjustment_get_value (adj)/100.0;
452 /* Don't store the volume because of muting */
453 if (volume > 0 || gtk_toggle_tool_button_get_active (
454 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
455 priv->volume = volume;
457 /* Ensure that the toggle button is active if the volume is > 0 and inactive
458 * if it's smaller then 0 */
459 if ((volume > 0) != gtk_toggle_tool_button_get_active (
460 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
461 gtk_toggle_tool_button_set_active (
462 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), volume > 0);
464 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
469 empathy_call_window_audio_input_level_changed_cb (EmpathyGstAudioSrc *src,
470 gdouble level, GtkProgressBar *bar)
474 value = CLAMP (pow (10, level / 20), 0.0, 1.0);
475 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (bar), value);
479 empathy_call_window_create_audio_input (EmpathyCallWindow *self)
481 EmpathyCallWindowPriv *priv = GET_PRIV (self);
482 GtkWidget *hbox, *vbox, *scale, *progress, *label;
485 hbox = gtk_hbox_new (TRUE, 3);
487 vbox = gtk_vbox_new (FALSE, 3);
488 gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 3);
490 scale = gtk_vscale_new_with_range (0, 150, 100);
491 gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
492 label = gtk_label_new (_("Volume"));
494 priv->audio_input_adj = adj = gtk_range_get_adjustment (GTK_RANGE (scale));
495 priv->volume = empathy_audio_src_get_volume (EMPATHY_GST_AUDIO_SRC
496 (priv->audio_input));
497 gtk_adjustment_set_value (adj, priv->volume * 100);
499 g_signal_connect (G_OBJECT (adj), "value-changed",
500 G_CALLBACK (empathy_call_window_mic_volume_changed_cb), self);
502 gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 3);
503 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 3);
505 progress = gtk_progress_bar_new ();
506 gtk_progress_bar_set_orientation (GTK_PROGRESS_BAR (progress),
507 GTK_PROGRESS_BOTTOM_TO_TOP);
508 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress), 0);
510 g_signal_connect (priv->audio_input, "peak-level-changed",
511 G_CALLBACK (empathy_call_window_audio_input_level_changed_cb), progress);
513 gtk_box_pack_start (GTK_BOX (hbox), progress, FALSE, FALSE, 3);
519 empathy_call_window_init (EmpathyCallWindow *self)
521 EmpathyCallWindowPriv *priv = GET_PRIV (self);
527 GtkWidget *remote_user_output_frame, *self_user_output_frame;
528 GtkWidget *remote_user_output_hbox, *self_user_output_hbox;
532 filename = empathy_file_lookup ("empathy-call-window.ui", "src");
533 gui = empathy_builder_get_file (filename,
534 "call_window_vbox", &top_vbox,
536 "statusbar", &priv->statusbar,
537 "microphone", &priv->mic_button,
538 "camera", &priv->camera_button,
539 "toolbar", &priv->toolbar,
540 "send_video", &priv->send_video,
541 "ui_manager", &priv->ui_manager,
542 "menufullscreen", &priv->menu_fullscreen,
545 empathy_builder_connect (gui, self,
546 "menuhangup", "activate", empathy_call_window_hangup_cb,
547 "hangup", "clicked", empathy_call_window_hangup_cb,
548 "microphone", "toggled", empathy_call_window_mic_toggled_cb,
549 "camera", "toggled", empathy_call_window_camera_toggled_cb,
550 "send_video", "toggled", empathy_call_window_send_video_toggled_cb,
551 "show_preview", "toggled", empathy_call_window_show_preview_toggled_cb,
552 "menufullscreen", "activate", empathy_call_window_fullscreen_cb,
555 priv->lock = g_mutex_new ();
557 gtk_container_add (GTK_CONTAINER (self), top_vbox);
559 empathy_call_window_setup_toolbar (self);
561 priv->pipeline = gst_pipeline_new (NULL);
563 priv->content_hbox = gtk_hbox_new (FALSE, CONTENT_HBOX_SPACING);
564 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
565 CONTENT_HBOX_BORDER_WIDTH);
566 gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
568 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
570 gst_bus_add_watch (bus, empathy_call_window_bus_message, self);
572 remote_user_output_frame = gtk_frame_new (NULL);
573 gtk_widget_set_size_request (remote_user_output_frame,
574 EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT);
575 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 (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 (remote_user_output_hbox),
583 priv->video_output, TRUE, TRUE, 0);
585 gtk_container_add (GTK_CONTAINER (remote_user_output_frame),
586 remote_user_output_hbox);
588 gtk_widget_add_events (priv->video_output,
589 GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
590 g_signal_connect (G_OBJECT (priv->video_output), "button-press-event",
591 G_CALLBACK (empathy_call_window_video_button_press_cb), self);
592 gtk_box_pack_start (GTK_BOX (priv->content_hbox), remote_user_output_frame,
593 TRUE, TRUE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
595 priv->video_tee = gst_element_factory_make ("tee", NULL);
596 gst_object_ref (priv->video_tee);
597 gst_object_sink (priv->video_tee);
599 priv->vbox = gtk_vbox_new (FALSE, 3);
600 gtk_box_pack_start (GTK_BOX (priv->content_hbox), priv->vbox,
601 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
603 self_user_output_frame = gtk_frame_new (NULL);
604 gtk_widget_set_size_request (self_user_output_frame, SELF_VIDEO_SECTION_WIDTH,
605 SELF_VIDEO_SECTION_HEIGTH);
606 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 (self_user_output_hbox),
610 priv->self_user_avatar_widget, TRUE, TRUE, 0);
612 priv->video_preview = empathy_video_widget_new_with_size (bus,
613 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
614 g_object_set (priv->video_preview, "sync", FALSE, "async", TRUE, NULL);
615 gtk_box_pack_start (GTK_BOX (self_user_output_hbox), priv->video_preview,
618 gtk_container_add (GTK_CONTAINER (self_user_output_frame),
619 self_user_output_hbox);
621 gtk_box_pack_start (GTK_BOX (priv->vbox), self_user_output_frame, FALSE,
624 priv->video_input = empathy_video_src_new ();
625 gst_object_ref (priv->video_input);
626 gst_object_sink (priv->video_input);
628 priv->audio_input = empathy_audio_src_new ();
629 gst_object_ref (priv->audio_input);
630 gst_object_sink (priv->audio_input);
632 priv->audio_output = empathy_audio_sink_new ();
633 gst_object_ref (priv->audio_output);
634 gst_object_sink (priv->audio_output);
636 g_object_unref (bus);
638 priv->sidebar_button = gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
639 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
640 g_signal_connect (G_OBJECT (priv->sidebar_button), "toggled",
641 G_CALLBACK (empathy_call_window_sidebar_toggled_cb), self);
643 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
645 h = gtk_hbox_new (FALSE, 3);
646 gtk_box_pack_end (GTK_BOX (priv->vbox), h, FALSE, FALSE, 3);
647 gtk_box_pack_end (GTK_BOX (h), priv->sidebar_button, FALSE, FALSE, 3);
649 priv->sidebar = empathy_sidebar_new ();
650 g_signal_connect (G_OBJECT (priv->sidebar),
651 "hide", G_CALLBACK (empathy_call_window_sidebar_hidden_cb), self);
652 g_signal_connect (G_OBJECT (priv->sidebar),
653 "show", G_CALLBACK (empathy_call_window_sidebar_shown_cb), self);
654 gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
656 priv->dtmf_panel = empathy_call_window_create_dtmf (self);
657 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Dialpad"),
660 gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
662 page = empathy_call_window_create_audio_input (self);
663 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Audio input"),
666 page = empathy_call_window_create_video_input (self);
667 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Video input"),
670 gtk_widget_show_all (top_vbox);
672 gtk_widget_hide (priv->sidebar);
674 priv->fullscreen = empathy_call_window_fullscreen_new (self);
675 empathy_call_window_fullscreen_set_video_widget (priv->fullscreen, priv->video_output);
676 g_signal_connect (G_OBJECT (priv->fullscreen->leave_fullscreen_button),
677 "clicked", G_CALLBACK (empathy_call_window_fullscreen_cb), self);
679 g_signal_connect (G_OBJECT (self), "realize",
680 G_CALLBACK (empathy_call_window_realized_cb), self);
682 g_signal_connect (G_OBJECT (self), "delete-event",
683 G_CALLBACK (empathy_call_window_delete_cb), self);
685 g_signal_connect (G_OBJECT (self), "window-state-event",
686 G_CALLBACK (empathy_call_window_state_event_cb), self);
688 g_signal_connect (G_OBJECT (self), "key-press-event",
689 G_CALLBACK (empathy_call_window_key_press_cb), self);
691 empathy_call_window_status_message (self, _("Connecting..."));
693 priv->timer = g_timer_new ();
695 g_object_ref (priv->ui_manager);
696 g_object_unref (gui);
700 /* Instead of specifying a width and a height, we specify only one size. That's
701 because we want a square avatar icon. */
703 init_contact_avatar_with_size (EmpathyContact *contact, GtkWidget *image_widget,
707 GdkPixbuf *pixbuf_avatar;
711 pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact,
715 if (pixbuf_avatar == NULL)
717 pixbuf_avatar = empathy_pixbuf_from_icon_name_sized ("stock_person",
721 gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget), pixbuf_avatar);
725 set_window_title (EmpathyCallWindow *self)
727 EmpathyCallWindowPriv *priv = GET_PRIV (self);
730 tmp = g_strdup_printf (_("Call with %s"),
731 empathy_contact_get_name (priv->contact));
732 gtk_window_set_title (GTK_WINDOW (self), tmp);
737 contact_name_changed_cb (EmpathyContact *contact,
738 GParamSpec *pspec, EmpathyCallWindow *self)
740 set_window_title (self);
744 contact_avatar_changed_cb (EmpathyContact *contact,
745 GParamSpec *pspec, GtkWidget *avatar_widget)
747 init_contact_avatar_with_size (contact, avatar_widget,
748 avatar_widget->allocation.height);
752 empathy_call_window_got_self_contact_cb (EmpathyTpContactFactory *factory,
753 EmpathyContact *contact, const GError *error, gpointer user_data,
754 GObject *weak_object)
756 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
757 EmpathyCallWindowPriv *priv = GET_PRIV (self);
759 init_contact_avatar_with_size (contact, priv->self_user_avatar_widget,
760 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
762 g_signal_connect (contact, "notify::avatar",
763 G_CALLBACK (contact_avatar_changed_cb), priv->self_user_avatar_widget);
767 empathy_call_window_constructed (GObject *object)
769 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
770 EmpathyCallWindowPriv *priv = GET_PRIV (self);
772 g_assert (priv->handler != NULL);
774 g_object_get (priv->handler, "contact", &(priv->contact), NULL);
776 if (priv->contact != NULL)
778 TpConnection *connection;
779 EmpathyTpContactFactory *factory;
781 set_window_title (self);
783 g_signal_connect (priv->contact, "notify::name",
784 G_CALLBACK (contact_name_changed_cb), self);
785 g_signal_connect (priv->contact, "notify::avatar",
786 G_CALLBACK (contact_avatar_changed_cb),
787 priv->remote_user_avatar_widget);
789 /* Retreiving the self avatar */
790 connection = empathy_contact_get_connection (priv->contact);
791 factory = empathy_tp_contact_factory_dup_singleton(connection);
792 empathy_tp_contact_factory_get_from_handle (factory,
793 tp_connection_get_self_handle (connection),
794 empathy_call_window_got_self_contact_cb, self, NULL, NULL);
796 g_object_unref (factory);
800 g_warning ("call handler doesn't have a contact");
801 gtk_window_set_title (GTK_WINDOW (self), _("Call"));
803 /* Since we can't access the remote contact, we can't get a connection
804 to it and can't get the self contact (and its avatar). This means
805 that we have to manually set the self avatar. */
806 init_contact_avatar_with_size (NULL, priv->self_user_avatar_widget,
807 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
810 init_contact_avatar_with_size (priv->contact,
811 priv->remote_user_avatar_widget, MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
812 REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
814 /* We hide the self avatar. It will be shown if a problem is
815 encountered when we try to send video. As for the remote avatar, it
816 is shown by default and will be hidden when we receive video from
818 gtk_widget_hide (priv->self_user_avatar_widget);
819 gtk_widget_hide (priv->video_output);
820 gtk_widget_show (priv->remote_user_avatar_widget);
823 static void empathy_call_window_dispose (GObject *object);
824 static void empathy_call_window_finalize (GObject *object);
827 empathy_call_window_set_property (GObject *object,
828 guint property_id, const GValue *value, GParamSpec *pspec)
830 EmpathyCallWindowPriv *priv = GET_PRIV (object);
834 case PROP_CALL_HANDLER:
835 priv->handler = g_value_dup_object (value);
838 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
843 empathy_call_window_get_property (GObject *object,
844 guint property_id, GValue *value, GParamSpec *pspec)
846 EmpathyCallWindowPriv *priv = GET_PRIV (object);
850 case PROP_CALL_HANDLER:
851 g_value_set_object (value, priv->handler);
854 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
859 empathy_call_window_class_init (
860 EmpathyCallWindowClass *empathy_call_window_class)
862 GObjectClass *object_class = G_OBJECT_CLASS (empathy_call_window_class);
863 GParamSpec *param_spec;
865 g_type_class_add_private (empathy_call_window_class,
866 sizeof (EmpathyCallWindowPriv));
868 object_class->constructed = empathy_call_window_constructed;
869 object_class->set_property = empathy_call_window_set_property;
870 object_class->get_property = empathy_call_window_get_property;
872 object_class->dispose = empathy_call_window_dispose;
873 object_class->finalize = empathy_call_window_finalize;
875 param_spec = g_param_spec_object ("handler",
876 "handler", "The call handler",
877 EMPATHY_TYPE_CALL_HANDLER,
878 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
879 g_object_class_install_property (object_class,
880 PROP_CALL_HANDLER, param_spec);
885 empathy_call_window_dispose (GObject *object)
887 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
888 EmpathyCallWindowPriv *priv = GET_PRIV (self);
890 if (priv->dispose_has_run)
893 priv->dispose_has_run = TRUE;
895 if (priv->handler != NULL)
896 g_object_unref (priv->handler);
898 priv->handler = NULL;
900 if (priv->pipeline != NULL)
901 g_object_unref (priv->pipeline);
902 priv->pipeline = NULL;
904 if (priv->video_input != NULL)
905 g_object_unref (priv->video_input);
906 priv->video_input = NULL;
908 if (priv->audio_input != NULL)
909 g_object_unref (priv->audio_input);
910 priv->audio_input = NULL;
912 if (priv->audio_output != NULL)
913 g_object_unref (priv->audio_output);
914 priv->audio_output = NULL;
916 if (priv->video_tee != NULL)
917 g_object_unref (priv->video_tee);
918 priv->video_tee = NULL;
920 if (priv->timer_id != 0)
921 g_source_remove (priv->timer_id);
924 if (priv->ui_manager != NULL)
925 g_object_unref (priv->ui_manager);
926 priv->ui_manager = NULL;
928 if (priv->contact != NULL)
930 g_signal_handlers_disconnect_by_func (priv->contact,
931 contact_name_changed_cb, self);
932 g_object_unref (priv->contact);
933 priv->contact = NULL;
936 /* release any references held by the object here */
937 if (G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose)
938 G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
942 empathy_call_window_finalize (GObject *object)
944 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
945 EmpathyCallWindowPriv *priv = GET_PRIV (self);
947 if (priv->video_output_motion_handler_id != 0)
949 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
950 priv->video_output_motion_handler_id);
951 priv->video_output_motion_handler_id = 0;
954 /* free any data held directly by the object here */
955 g_mutex_free (priv->lock);
957 g_timer_destroy (priv->timer);
959 G_OBJECT_CLASS (empathy_call_window_parent_class)->finalize (object);
964 empathy_call_window_new (EmpathyCallHandler *handler)
966 return EMPATHY_CALL_WINDOW (
967 g_object_new (EMPATHY_TYPE_CALL_WINDOW, "handler", handler, NULL));
971 empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
972 GstElement *conference, gpointer user_data)
974 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
975 EmpathyCallWindowPriv *priv = GET_PRIV (self);
977 gst_bin_add (GST_BIN (priv->pipeline), conference);
979 gst_element_set_state (conference, GST_STATE_PLAYING);
983 empathy_call_window_request_resource_cb (EmpathyCallHandler *handler,
984 FsMediaType type, FsStreamDirection direction, gpointer user_data)
986 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
987 EmpathyCallWindowPriv *priv = GET_PRIV (self);
989 if (type != TP_MEDIA_STREAM_TYPE_VIDEO)
992 if (direction == FS_DIRECTION_RECV)
995 /* video and direction is send */
996 return priv->video_input != NULL;
1000 empathy_call_window_disconnected (EmpathyCallWindow *self)
1002 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1004 g_mutex_lock (priv->lock);
1006 g_timer_stop (priv->timer);
1008 if (priv->timer_id != 0)
1009 g_source_remove (priv->timer_id);
1012 g_mutex_unlock (priv->lock);
1014 empathy_call_window_status_message (self, _("Disconnected"));
1016 gtk_widget_set_sensitive (priv->camera_button, FALSE);
1017 gtk_action_set_sensitive (priv->send_video, FALSE);
1018 priv->sending_video = FALSE;
1023 empathy_call_window_channel_closed_cb (TfChannel *channel, gpointer user_data)
1025 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1027 empathy_call_window_disconnected (self);
1030 /* Called with global lock held */
1032 empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
1034 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1037 if (priv->funnel == NULL)
1041 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1042 (priv->video_output));
1044 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
1046 gst_bin_add (GST_BIN (priv->pipeline), priv->funnel);
1047 gst_bin_add (GST_BIN (priv->pipeline), output);
1049 gst_element_link (priv->funnel, output);
1051 gst_element_set_state (priv->funnel, GST_STATE_PLAYING);
1052 gst_element_set_state (output, GST_STATE_PLAYING);
1055 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
1060 /* Called with global lock held */
1062 empathy_call_window_get_audio_sink_pad (EmpathyCallWindow *self)
1064 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1067 if (priv->liveadder == NULL)
1069 priv->liveadder = gst_element_factory_make ("liveadder", NULL);
1071 gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder);
1072 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output);
1074 gst_element_link (priv->liveadder, priv->audio_output);
1076 gst_element_set_state (priv->liveadder, GST_STATE_PLAYING);
1077 gst_element_set_state (priv->audio_output, GST_STATE_PLAYING);
1080 pad = gst_element_get_request_pad (priv->liveadder, "sink%d");
1086 empathy_call_window_update_timer (gpointer user_data)
1088 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1089 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1093 time = g_timer_elapsed (priv->timer, NULL);
1095 /* Translators: number of minutes:seconds the caller has been connected */
1096 str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time / 60,
1098 empathy_call_window_status_message (self, str);
1105 empathy_call_window_connected (gpointer user_data)
1107 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1108 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1109 EmpathyTpCall *call;
1111 g_object_get (priv->handler, "tp-call", &call, NULL);
1113 if (empathy_tp_call_has_dtmf (call))
1114 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
1116 if (priv->video_input != NULL)
1118 gtk_widget_set_sensitive (priv->camera_button, TRUE);
1119 gtk_action_set_sensitive (priv->send_video, TRUE);
1122 g_object_unref (call);
1124 g_mutex_lock (priv->lock);
1126 priv->timer_id = g_timeout_add_seconds (1,
1127 empathy_call_window_update_timer, self);
1129 g_mutex_unlock (priv->lock);
1131 empathy_call_window_update_timer (self);
1137 /* Called from the streaming thread */
1139 empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
1140 GstPad *src, guint media_type, gpointer user_data)
1142 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1143 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1147 g_mutex_lock (priv->lock);
1149 if (priv->connected == FALSE)
1151 g_timer_start (priv->timer);
1152 priv->timer_id = g_idle_add (empathy_call_window_connected, self);
1153 priv->connected = TRUE;
1158 case TP_MEDIA_STREAM_TYPE_AUDIO:
1159 pad = empathy_call_window_get_audio_sink_pad (self);
1161 case TP_MEDIA_STREAM_TYPE_VIDEO:
1162 gtk_widget_hide (priv->remote_user_avatar_widget);
1163 gtk_widget_show (priv->video_output);
1164 pad = empathy_call_window_get_video_sink_pad (self);
1167 g_assert_not_reached ();
1170 gst_pad_link (src, pad);
1171 gst_object_unref (pad);
1173 g_mutex_unlock (priv->lock);
1176 /* Called from the streaming thread */
1178 empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
1179 GstPad *sink, guint media_type, gpointer user_data)
1181 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1182 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1187 case TP_MEDIA_STREAM_TYPE_AUDIO:
1188 gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
1190 pad = gst_element_get_static_pad (priv->audio_input, "src");
1191 gst_pad_link (pad, sink);
1193 gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
1195 case TP_MEDIA_STREAM_TYPE_VIDEO:
1196 if (priv->video_input != NULL)
1198 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
1199 gst_pad_link (pad, sink);
1203 g_assert_not_reached ();
1209 empathy_gst_bin_has_child (GstBin *bin, GstElement *element)
1212 gboolean ret = FALSE;
1215 it = gst_bin_iterate_recurse (bin);
1219 switch (gst_iterator_next (it, (gpointer *)&item))
1221 case GST_ITERATOR_OK:
1222 if (item == element)
1224 gst_object_unref (GST_OBJECT (item));
1228 gst_object_unref (GST_OBJECT (item));
1230 case GST_ITERATOR_RESYNC:
1231 gst_iterator_resync (it);
1233 case GST_ITERATOR_ERROR:
1234 g_assert_not_reached ();
1236 case GST_ITERATOR_DONE:
1241 gst_iterator_free (it);
1248 empathy_call_window_remove_video_input (EmpathyCallWindow *self)
1250 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1251 GstElement *preview;
1253 preview = empathy_video_widget_get_element (
1254 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1256 gst_element_set_state (priv->video_input, GST_STATE_NULL);
1257 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
1258 gst_element_set_state (preview, GST_STATE_NULL);
1260 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
1261 priv->video_tee, preview, NULL);
1263 g_object_unref (priv->video_input);
1264 priv->video_input = NULL;
1265 g_object_unref (priv->video_tee);
1266 priv->video_tee = NULL;
1268 gtk_widget_hide (priv->video_preview);
1269 gtk_widget_show (priv->self_user_avatar_widget);
1274 empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
1277 EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1278 EmpathyCallWindowPriv *priv = GET_PRIV (self);
1281 empathy_call_handler_bus_message (priv->handler, bus, message);
1283 switch (GST_MESSAGE_TYPE (message))
1285 case GST_MESSAGE_STATE_CHANGED:
1286 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
1288 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1289 if (newstate == GST_STATE_PAUSED)
1290 empathy_call_window_setup_video_input (self);
1292 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
1293 !priv->call_started)
1295 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
1296 if (newstate == GST_STATE_PAUSED)
1298 priv->call_started = TRUE;
1299 empathy_call_handler_start_call (priv->handler);
1300 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
1304 case GST_MESSAGE_ERROR:
1306 GError *error = NULL;
1309 gst_message_parse_error (message, &error, &debug);
1311 g_message ("Element error: %s -- %s\n", error->message, debug);
1313 if (priv->video_input != NULL &&
1314 empathy_gst_bin_has_child (GST_BIN (priv->video_input),
1315 GST_ELEMENT (GST_MESSAGE_SRC (message))))
1317 /* Remove the video input and continue */
1318 empathy_call_window_remove_video_input (self);
1319 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1323 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1324 empathy_call_window_disconnected (self);
1326 g_error_free (error);
1337 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
1339 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1340 GstElement *preview;
1342 g_signal_connect (priv->handler, "conference-added",
1343 G_CALLBACK (empathy_call_window_conference_added_cb), window);
1344 g_signal_connect (priv->handler, "request-resource",
1345 G_CALLBACK (empathy_call_window_request_resource_cb), window);
1346 g_signal_connect (priv->handler, "closed",
1347 G_CALLBACK (empathy_call_window_channel_closed_cb), window);
1348 g_signal_connect (priv->handler, "src-pad-added",
1349 G_CALLBACK (empathy_call_window_src_added_cb), window);
1350 g_signal_connect (priv->handler, "sink-pad-added",
1351 G_CALLBACK (empathy_call_window_sink_added_cb), window);
1354 preview = empathy_video_widget_get_element (
1355 EMPATHY_VIDEO_WIDGET (priv->video_preview));
1357 gst_bin_add_many (GST_BIN (priv->pipeline), priv->video_input,
1358 priv->video_tee, preview, NULL);
1359 gst_element_link_many (priv->video_input, priv->video_tee,
1362 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1366 empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
1367 EmpathyCallWindow *window)
1369 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1371 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1377 show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
1380 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1382 menu = gtk_ui_manager_get_widget (priv->ui_manager,
1387 gtk_widget_hide (priv->sidebar);
1388 gtk_widget_hide (menu);
1389 gtk_widget_hide (priv->vbox);
1390 gtk_widget_hide (priv->statusbar);
1391 gtk_widget_hide (priv->toolbar);
1395 if (priv->sidebar_was_visible_before_fs)
1396 gtk_widget_show (priv->sidebar);
1398 gtk_widget_show (menu);
1399 gtk_widget_show (priv->vbox);
1400 gtk_widget_show (priv->statusbar);
1401 gtk_widget_show (priv->toolbar);
1403 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
1404 priv->original_height_before_fs);
1409 show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
1411 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1413 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
1414 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
1415 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
1416 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
1417 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
1418 priv->video_output, TRUE, TRUE,
1419 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
1421 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
1422 priv->vbox, TRUE, TRUE,
1423 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
1428 empathy_call_window_state_event_cb (GtkWidget *widget,
1429 GdkEventWindowState *event, EmpathyCallWindow *window)
1431 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
1433 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1434 gboolean set_fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
1438 gboolean sidebar_was_visible;
1439 gint original_width = GTK_WIDGET (window)->allocation.width;
1440 gint original_height = GTK_WIDGET (window)->allocation.height;
1442 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
1444 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
1445 priv->original_width_before_fs = original_width;
1446 priv->original_height_before_fs = original_height;
1448 if (priv->video_output_motion_handler_id == 0 &&
1449 priv->video_output != NULL)
1451 priv->video_output_motion_handler_id = g_signal_connect (
1452 G_OBJECT (priv->video_output), "motion-notify-event",
1453 G_CALLBACK (empathy_call_window_video_output_motion_notify), window);
1458 if (priv->video_output_motion_handler_id != 0)
1460 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1461 priv->video_output_motion_handler_id);
1462 priv->video_output_motion_handler_id = 0;
1466 empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
1468 show_controls (window, set_fullscreen);
1469 show_borders (window, set_fullscreen);
1470 gtk_action_set_stock_id (priv->menu_fullscreen,
1471 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
1472 priv->is_fullscreen = set_fullscreen;
1479 empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
1480 EmpathyCallWindow *window)
1482 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1484 int w,h, handle_size;
1486 w = GTK_WIDGET (window)->allocation.width;
1487 h = GTK_WIDGET (window)->allocation.height;
1489 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
1491 if (gtk_toggle_button_get_active (toggle))
1493 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
1494 gtk_widget_show (priv->sidebar);
1495 w += priv->sidebar->allocation.width + handle_size;
1499 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
1500 w -= priv->sidebar->allocation.width + handle_size;
1501 gtk_widget_hide (priv->sidebar);
1504 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
1507 gtk_window_resize (GTK_WINDOW (window), w, h);
1511 empathy_call_window_set_send_video (EmpathyCallWindow *window,
1514 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1515 EmpathyTpCall *call;
1517 g_object_get (priv->handler, "tp-call", &call, NULL);
1518 empathy_tp_call_request_video_stream_direction (call, send);
1519 g_object_unref (call);
1523 empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
1524 EmpathyCallWindow *window)
1526 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1529 active = (gtk_toggle_tool_button_get_active (toggle));
1531 if (priv->sending_video == active)
1533 priv->sending_video = active;
1535 empathy_call_window_set_send_video (window, active);
1536 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priv->send_video), active);
1540 empathy_call_window_send_video_toggled_cb (GtkToggleAction *toggle,
1541 EmpathyCallWindow *window)
1543 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1546 active = (gtk_toggle_action_get_active (toggle));
1548 if (priv->sending_video == active)
1550 priv->sending_video = active;
1552 empathy_call_window_set_send_video (window, active);
1553 gtk_toggle_tool_button_set_active (
1554 GTK_TOGGLE_TOOL_BUTTON (priv->camera_button), active);
1558 empathy_call_window_show_preview_toggled_cb (GtkToggleAction *toggle,
1559 EmpathyCallWindow *window)
1561 /* FIXME: Not implemented */
1565 empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
1566 EmpathyCallWindow *window)
1568 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1571 active = (gtk_toggle_tool_button_get_active (toggle));
1575 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
1577 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
1581 /* TODO, Instead of setting the input volume to 0 we should probably
1582 * stop sending but this would cause the audio call to drop if both
1583 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
1584 * in the future. GNOME #574574
1586 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
1588 gtk_adjustment_set_value (priv->audio_input_adj, 0);
1593 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
1594 EmpathyCallWindow *window)
1596 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1598 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
1603 empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
1604 EmpathyCallWindow *window)
1606 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1608 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
1613 empathy_call_window_hangup_cb (gpointer object,
1614 EmpathyCallWindow *window)
1616 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1618 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1619 gtk_widget_destroy (GTK_WIDGET (window));
1623 empathy_call_window_fullscreen_cb (gpointer object,
1624 EmpathyCallWindow *window)
1626 empathy_call_window_fullscreen_toggle (window);
1630 empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
1632 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1634 if (priv->is_fullscreen)
1635 gtk_window_unfullscreen (GTK_WINDOW (window));
1637 gtk_window_fullscreen (GTK_WINDOW (window));
1641 empathy_call_window_video_button_press_cb (GtkWidget *video_output,
1642 GdkEventButton *event, EmpathyCallWindow *window)
1644 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
1646 empathy_call_window_video_menu_popup (window, event->button);
1654 empathy_call_window_key_press_cb (GtkWidget *video_output,
1655 GdkEventKey *event, EmpathyCallWindow *window)
1657 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1659 if (priv->is_fullscreen && event->keyval == GDK_Escape)
1661 /* Since we are in fullscreen mode, toggling will bring us back to
1663 empathy_call_window_fullscreen_toggle (window);
1671 empathy_call_window_video_output_motion_notify (GtkWidget *widget,
1672 GdkEventMotion *event, EmpathyCallWindow *window)
1674 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1676 if (priv->is_fullscreen)
1678 empathy_call_window_fullscreen_show_popup (priv->fullscreen);
1686 empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
1690 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1692 menu = gtk_ui_manager_get_widget (priv->ui_manager,
1694 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
1695 button, gtk_get_current_event_time ());
1696 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
1700 empathy_call_window_status_message (EmpathyCallWindow *window,
1703 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1705 if (priv->context_id == 0)
1707 priv->context_id = gtk_statusbar_get_context_id (
1708 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
1712 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
1715 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
1720 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
1721 gdouble value, EmpathyCallWindow *window)
1723 EmpathyCallWindowPriv *priv = GET_PRIV (window);
1725 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),