]> git.0d.be Git - empathy.git/blob - src/empathy-call-window.c
Initial port from telepathy-stream-engine to telepathy-farsight
[empathy.git] / src / empathy-call-window.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007 Elliot Fairweather
4  * Copyright (C) 2007-2008 Collabora Ltd.
5  *
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.
10  *
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.
15  *
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
19  *
20  * Authors: Elliot Fairweather <elliot.fairweather@collabora.co.uk>
21  *          Xavier Claessens <xclaesse@gmail.com>
22  */
23
24 #include <string.h>
25
26 #include <glade/glade.h>
27 #include <glib/gi18n.h>
28
29 #include <telepathy-glib/enums.h>
30
31 #include <libempathy/empathy-contact.h>
32 #include <libempathy/empathy-tp-call.h>
33 #include <libempathy/empathy-utils.h>
34 #include <libempathy-gtk/empathy-ui-utils.h>
35
36 #include "empathy-call-window.h"
37
38 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
39 #include <libempathy/empathy-debug.h>
40
41 typedef struct
42 {
43   EmpathyTpCall *call;
44   GTimeVal start_time;
45   guint timeout_event_id;
46   gboolean is_drawing;
47   guint status; 
48
49   GtkWidget *window;
50   GtkWidget *main_hbox;
51   GtkWidget *controls_vbox;
52   GtkWidget *volume_hbox;
53   GtkWidget *status_label;
54   GtkWidget *input_volume_button;
55   GtkWidget *output_volume_button;
56   GtkWidget *preview_video_socket;
57   GtkWidget *output_video_socket;
58   GtkWidget *video_button;
59   GtkWidget *hang_up_button;
60   GtkWidget *confirmation_dialog;
61   GtkWidget *keypad_expander;
62 } EmpathyCallWindow;
63
64 static void call_window_update (EmpathyCallWindow *window);
65
66 static GSList *windows = NULL;
67
68 static gboolean
69 call_window_update_timer (gpointer data)
70 {
71   EmpathyCallWindow *window = data;
72   GTimeVal current;
73   gchar *str;
74   glong now, then;
75   glong time, seconds, minutes, hours;
76
77   g_get_current_time (&current);
78
79   now = current.tv_sec;
80   then = (window->start_time).tv_sec;
81
82   time = now - then;
83
84   seconds = time % 60;
85   time /= 60;
86   minutes = time % 60;
87   time /= 60;
88   hours = time % 60;
89
90   if (hours > 0)
91       str = g_strdup_printf ("Connected  -  %02ld : %02ld : %02ld", hours,
92           minutes, seconds);
93   else
94       str = g_strdup_printf ("Connected  -  %02ld : %02ld", minutes, seconds);
95
96   gtk_label_set_text (GTK_LABEL (window->status_label), str);
97
98   g_free (str);
99
100   return TRUE;
101 }
102
103 static void
104 call_window_stop_timeout (EmpathyCallWindow *window)
105 {
106   DEBUG ("Timer stopped");
107
108   if (window->timeout_event_id)
109     {
110       g_source_remove (window->timeout_event_id);
111       window->timeout_event_id = 0;
112     }
113 }
114
115 static void
116 call_window_set_output_video_is_drawing (EmpathyCallWindow *window,
117                                          gboolean is_drawing)
118 {
119   DEBUG ("Setting output video is drawing - %d", is_drawing);
120
121   if (is_drawing && !window->is_drawing)
122     {
123       gtk_window_set_resizable (GTK_WINDOW (window->window), TRUE);
124       gtk_box_pack_end (GTK_BOX (window->main_hbox),
125           window->output_video_socket, TRUE, TRUE, 0);
126       empathy_tp_call_add_output_video (window->call,
127           gtk_socket_get_id (GTK_SOCKET (window->output_video_socket)));
128     }
129   if (!is_drawing && window->is_drawing)
130     {
131       gtk_window_set_resizable (GTK_WINDOW (window->window), FALSE);
132       empathy_tp_call_add_output_video (window->call, 0);
133       gtk_container_remove (GTK_CONTAINER (window->main_hbox),
134           window->output_video_socket);
135     }
136
137   window->is_drawing = is_drawing;
138 }
139
140 static void
141 call_window_finalize (EmpathyCallWindow *window)
142 {
143   gtk_label_set_text (GTK_LABEL (window->status_label), _("Closed"));
144   gtk_widget_set_sensitive (window->hang_up_button, FALSE);
145   gtk_widget_set_sensitive (window->video_button, FALSE);
146   gtk_widget_set_sensitive (window->output_volume_button, FALSE);
147   gtk_widget_set_sensitive (window->input_volume_button, FALSE);
148   gtk_widget_set_sensitive (window->keypad_expander, FALSE);
149
150   if (window->call)
151     { 
152       call_window_stop_timeout (window);
153       call_window_set_output_video_is_drawing (window, FALSE);
154       empathy_tp_call_remove_preview_video (window->call,
155           gtk_socket_get_id (GTK_SOCKET (window->preview_video_socket)));
156
157       g_signal_handlers_disconnect_by_func (window->call,
158         call_window_update, window);
159
160       empathy_tp_call_close (window->call);
161       g_object_unref (window->call);
162       window->call = NULL;
163     }
164
165   if (window->confirmation_dialog)
166       gtk_widget_destroy (window->confirmation_dialog);
167 }
168
169 static void
170 call_window_socket_realized_cb (GtkWidget *widget,
171                                 EmpathyCallWindow *window)
172 {
173   if (widget == window->preview_video_socket)
174     {
175       DEBUG ("Preview socket realized");
176       empathy_tp_call_add_preview_video (window->call,
177           gtk_socket_get_id (GTK_SOCKET (window->preview_video_socket)));
178     }
179   else
180       DEBUG ("Output socket realized");
181 }
182
183 static void
184 call_window_video_button_toggled_cb (GtkWidget *button,
185                                      EmpathyCallWindow *window)
186 {
187   gboolean is_sending;
188   guint status;
189
190   is_sending = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
191
192   DEBUG ("Send video toggled - %d", is_sending);
193
194   g_object_get (window->call, "status", &status, NULL);
195   if (status == EMPATHY_TP_CALL_STATUS_ACCEPTED)
196       empathy_tp_call_request_video_stream_direction (window->call, is_sending);
197 }
198
199 static void
200 call_window_hang_up_button_clicked_cb (GtkWidget *widget,
201                                        EmpathyCallWindow *window)
202 {
203   empathy_sound_play (GTK_WIDGET (window->window),
204                       EMPATHY_SOUND_PHONE_HANGUP);
205
206   DEBUG ("Call clicked, end call");
207   call_window_finalize (window);
208 }
209
210 static void
211 call_window_output_volume_changed_cb (GtkScaleButton *button,
212                                       gdouble value,
213                                       EmpathyCallWindow *window)
214 {
215   if (!window->call)
216       return;
217
218   if (value <= 0)
219       empathy_tp_call_mute_output (window->call, TRUE);
220   else
221     {
222       empathy_tp_call_mute_output (window->call, FALSE);
223       empathy_tp_call_set_output_volume (window->call, value * 100);
224     }
225 }
226
227 static void
228 call_window_input_volume_changed_cb (GtkScaleButton    *button,
229                                      gdouble            value,
230                                      EmpathyCallWindow *window)
231 {
232   if (!window->call)
233       return;
234
235   if (value <= 0)
236       empathy_tp_call_mute_input (window->call, TRUE);
237   else
238     {
239       empathy_tp_call_mute_input (window->call, FALSE);
240       /* FIXME: Not implemented?
241       empathy_tp_call_set_input_volume (window->call, value * 100);*/
242     }
243 }
244
245 static gboolean
246 call_window_delete_event_cb (GtkWidget *widget,
247                              GdkEvent *event,
248                              EmpathyCallWindow *window)
249 {
250   GtkWidget *dialog;
251   gint result;
252   guint status = EMPATHY_TP_CALL_STATUS_CLOSED;
253
254   DEBUG ("Delete event occurred");
255
256   if (window->call)
257       g_object_get (window->call, "status", &status, NULL);
258
259   if (status == EMPATHY_TP_CALL_STATUS_ACCEPTED)
260     {
261       dialog = gtk_message_dialog_new (GTK_WINDOW (window->window),
262           GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
263           GTK_MESSAGE_WARNING, GTK_BUTTONS_CANCEL, _("End this call?"));
264       gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
265           _("Closing this window will end the call in progress."));
266       gtk_dialog_add_button (GTK_DIALOG (dialog), _("_End Call"), GTK_RESPONSE_OK);
267       gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
268
269       result = gtk_dialog_run (GTK_DIALOG (dialog));
270       gtk_widget_destroy (dialog);
271
272       if (result != GTK_RESPONSE_OK)
273           return TRUE;
274     }
275
276   return FALSE;
277 }
278
279 static void
280 call_window_destroy_cb (GtkWidget *widget,
281                         EmpathyCallWindow *window)
282 {
283   call_window_finalize (window);
284
285   g_object_unref (window->output_video_socket);
286   g_object_unref (window->preview_video_socket);
287
288   windows = g_slist_remove (windows, window);
289   g_slice_free (EmpathyCallWindow, window);
290 }
291
292 static void
293 call_window_confirmation_dialog_response_cb (GtkDialog *dialog,
294                                              gint response,
295                                              EmpathyCallWindow *window)
296 {
297   if (response == GTK_RESPONSE_OK && window->call)
298       empathy_tp_call_accept_incoming_call (window->call);
299   else
300       call_window_finalize (window);
301
302   gtk_widget_destroy (window->confirmation_dialog);
303   window->confirmation_dialog = NULL;
304 }
305
306 static void
307 call_window_show_confirmation_dialog (EmpathyCallWindow *window)
308 {
309   EmpathyContact *contact;
310   GtkWidget *button;
311   GtkWidget *image;
312
313   if (window->confirmation_dialog)
314       return;
315
316   g_object_get (window->call, "contact", &contact, NULL);
317
318   window->confirmation_dialog = gtk_message_dialog_new (GTK_WINDOW (window->window),
319       GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
320       GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("Incoming call"));
321   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (window->confirmation_dialog),
322       _("%s is calling you, do you want to answer?"),
323       empathy_contact_get_name (contact));
324   gtk_dialog_set_default_response (GTK_DIALOG (window->confirmation_dialog),
325       GTK_RESPONSE_OK);
326
327   button = gtk_dialog_add_button (GTK_DIALOG (window->confirmation_dialog),
328       _("_Reject"), GTK_RESPONSE_CANCEL);
329   image = gtk_image_new_from_icon_name (GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
330   gtk_button_set_image (GTK_BUTTON (button), image);
331
332   button = gtk_dialog_add_button (GTK_DIALOG (window->confirmation_dialog),
333       _("_Answer"), GTK_RESPONSE_OK);
334   image = gtk_image_new_from_icon_name (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
335   gtk_button_set_image (GTK_BUTTON (button), image);
336
337   g_signal_connect (window->confirmation_dialog, "response",
338       G_CALLBACK (call_window_confirmation_dialog_response_cb),
339       window);
340
341   gtk_widget_show (window->confirmation_dialog);
342   g_object_unref (contact);
343 }
344
345 static void
346 call_window_update (EmpathyCallWindow *window)
347 {
348   EmpathyContact *contact;
349   guint stream_state;
350   EmpathyTpCallStream *audio_stream;
351   EmpathyTpCallStream *video_stream;
352   gboolean is_incoming;
353   gchar *title;
354
355   if (window->call == NULL)
356     return;
357
358   g_object_get (window->call,
359       "status", &window->status,
360       "audio-stream", &audio_stream,
361       "video-stream", &video_stream,
362       "contact", &contact,
363       "is-incoming", &is_incoming,
364       NULL);
365
366   if (video_stream->state > audio_stream->state)
367       stream_state = video_stream->state;
368   else
369       stream_state = audio_stream->state;
370
371   DEBUG ("Status changed - status: %d, stream state: %d, "
372       "is-incoming: %d video-stream direction: %d",
373       window->status, stream_state, is_incoming, video_stream->direction);
374
375   if (empathy_tp_call_has_dtmf (window->call))
376     {
377       gtk_widget_show (window->keypad_expander);
378     }
379   else
380     {
381       gtk_widget_hide (window->keypad_expander);
382     }
383
384   /* Depending on the status we have to set:
385    * - window's title
386    * - status's label
387    * - sensibility of all buttons
388    * */
389   if (window->status == EMPATHY_TP_CALL_STATUS_READYING)
390     {
391       gtk_window_set_title (GTK_WINDOW (window->window), _("Empathy Call"));
392       /* To translators: Readying is the first state of the call, it is
393        * preparing the connection and it does not yet ring. */
394       gtk_label_set_text (GTK_LABEL (window->status_label), _("Readying"));
395       gtk_widget_set_sensitive (window->video_button, FALSE);
396       gtk_widget_set_sensitive (window->output_volume_button, FALSE);
397       gtk_widget_set_sensitive (window->input_volume_button, FALSE);
398       gtk_widget_set_sensitive (window->hang_up_button, FALSE);
399       gtk_widget_set_sensitive (window->keypad_expander, FALSE);
400     }
401   else if (window->status == EMPATHY_TP_CALL_STATUS_PENDING)
402     {
403       title = g_strdup_printf (_("%s - Empathy Call"),
404           empathy_contact_get_name (contact));
405
406       gtk_window_set_title (GTK_WINDOW (window->window), title);
407       gtk_label_set_text (GTK_LABEL (window->status_label), _("Ringing"));
408       gtk_widget_set_sensitive (window->hang_up_button, TRUE);
409
410       if (is_incoming)
411         {
412           call_window_show_confirmation_dialog (window);
413           empathy_sound_play (GTK_WIDGET (window->window),
414                               EMPATHY_SOUND_PHONE_INCOMING);
415         }
416       else
417         {
418           empathy_sound_play (GTK_WIDGET (window->window),
419                               EMPATHY_SOUND_PHONE_OUTGOING);
420         }
421     }
422   else if (window->status == EMPATHY_TP_CALL_STATUS_ACCEPTED)
423     {
424       gboolean receiving_video;
425       gboolean sending_video;
426
427       if (stream_state == TP_MEDIA_STREAM_STATE_DISCONNECTED)
428           gtk_label_set_text (GTK_LABEL (window->status_label), _("Disconnected"));
429       if (stream_state == TP_MEDIA_STREAM_STATE_CONNECTING)
430           gtk_label_set_text (GTK_LABEL (window->status_label), _("Connecting"));
431       else if (stream_state == TP_MEDIA_STREAM_STATE_CONNECTED &&
432                window->timeout_event_id == 0)
433         {
434           /* The call started, launch the timer */
435           g_get_current_time (&(window->start_time));
436           window->timeout_event_id = g_timeout_add_seconds (1,
437               call_window_update_timer, window);
438           call_window_update_timer (window);
439         }
440
441       receiving_video = video_stream->direction & TP_MEDIA_STREAM_DIRECTION_RECEIVE;
442       sending_video = video_stream->direction & TP_MEDIA_STREAM_DIRECTION_SEND;
443       call_window_set_output_video_is_drawing (window, receiving_video);
444       g_signal_handlers_block_by_func (window->video_button,
445           call_window_video_button_toggled_cb, window);
446       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (window->video_button),
447           sending_video);
448       g_signal_handlers_unblock_by_func (window->video_button,
449           call_window_video_button_toggled_cb, window);
450
451       gtk_widget_set_sensitive (window->video_button, TRUE);
452       gtk_widget_set_sensitive (window->output_volume_button, TRUE);
453       gtk_widget_set_sensitive (window->input_volume_button, TRUE);
454       gtk_widget_set_sensitive (window->hang_up_button, TRUE);
455       gtk_widget_set_sensitive (window->keypad_expander, TRUE);
456     }
457   else if (window->status == EMPATHY_TP_CALL_STATUS_CLOSED)
458       call_window_finalize (window);
459
460   if (contact)
461       g_object_unref (contact);
462 }
463
464 static gboolean
465 call_window_dtmf_button_release_event_cb (GtkWidget *widget,
466                                           GdkEventButton *event,
467                                           EmpathyCallWindow *window)
468 {
469   empathy_tp_call_stop_tone (window->call);
470   return FALSE;
471 }
472
473 static gboolean
474 call_window_dtmf_button_press_event_cb (GtkWidget *widget,
475                                         GdkEventButton *event,
476                                         EmpathyCallWindow *window)
477 {
478   TpDTMFEvent dtmf_event;
479
480   dtmf_event = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), "code"));
481   empathy_tp_call_start_tone (window->call, dtmf_event);
482   return FALSE;
483 }
484
485 static void
486 call_window_dtmf_connect (GladeXML *glade,
487                           EmpathyCallWindow *window,
488                           const gchar *name,
489                           TpDTMFEvent event)
490 {
491   GtkWidget *widget;
492
493   widget = glade_xml_get_widget (glade, name);
494   g_object_set_data (G_OBJECT (widget), "code", GUINT_TO_POINTER (event));
495   g_signal_connect (widget, "button-press-event",
496       G_CALLBACK (call_window_dtmf_button_press_event_cb), window);
497   g_signal_connect (widget, "button-release-event",
498       G_CALLBACK (call_window_dtmf_button_release_event_cb), window);
499   /* FIXME: Connect "key-[press/release]-event" to*/
500 }
501
502 GtkWidget *
503 empathy_call_window_new (EmpathyTpCall *call)
504 {
505   EmpathyCallWindow *window;
506   GladeXML *glade;
507   gchar *filename;
508   const gchar *icons[] = {"audio-input-microphone", NULL};
509
510   g_return_val_if_fail (EMPATHY_IS_TP_CALL (call), NULL);
511
512   if (windows)
513     {
514       window = (EmpathyCallWindow*) windows->data;
515       if (!window->call)
516         {
517           window->call = g_object_ref (call);
518           g_signal_connect_swapped (G_OBJECT (window->call), "notify",
519               G_CALLBACK (call_window_update), window);
520           call_window_update (window);
521
522           if (GTK_WIDGET_REALIZED (GTK_WIDGET (window->preview_video_socket)))
523             {
524               call_window_socket_realized_cb (window->preview_video_socket,
525                   window);
526             }
527         }
528       else
529         {
530           GtkWidget *dialog;
531           EmpathyContact *contact;
532           TpChannel *channel;
533
534           g_object_get ( G_OBJECT (call),
535             "contact", &contact,
536             "channel", &channel,
537             NULL);
538
539           empathy_contact_run_until_ready (contact, EMPATHY_CONTACT_READY_NAME,
540               NULL);
541
542           /* We don't want to have multiple calls running.
543            * FIXME: We should use the hold interface... */
544           tp_cli_channel_call_close (channel, -1, NULL, NULL, NULL, NULL);
545
546           dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_INFO,
547               GTK_BUTTONS_CLOSE,
548               _("Incoming call from %s rejected because there is already a"
549                 " running call."), empathy_contact_get_name (contact));
550
551           g_object_unref (contact);
552           g_object_unref (channel);
553
554           g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy),
555               NULL);
556           gtk_widget_show (dialog);
557         }
558
559       gtk_window_present (GTK_WINDOW (window->window));
560       return window->window;
561     }
562
563   window = g_slice_new0 (EmpathyCallWindow);
564   windows = g_slist_prepend (windows, window);
565   window->call = g_object_ref (call);
566
567   filename = empathy_file_lookup ("empathy-call-window.glade", "src");
568   glade = empathy_glade_get_file (filename,
569       "window",
570       NULL,
571       "window", &window->window,
572       "main_hbox", &window->main_hbox,
573       "controls_vbox", &window->controls_vbox,
574       "volume_hbox", &window->volume_hbox,
575       "status_label", &window->status_label,
576       "video_button", &window->video_button,
577       "hang_up_button", &window->hang_up_button,
578       "keypad_expander", &window->keypad_expander,
579       NULL);
580   g_free (filename);
581
582   empathy_glade_connect (glade,
583       window,
584       "window", "destroy", call_window_destroy_cb,
585       "window", "delete_event", call_window_delete_event_cb,
586       "hang_up_button", "clicked", call_window_hang_up_button_clicked_cb,
587       "video_button", "toggled", call_window_video_button_toggled_cb,
588       NULL);
589
590   /* Setup DTMF buttons */
591   call_window_dtmf_connect (glade, window, "button_0", TP_DTMF_EVENT_DIGIT_0);
592   call_window_dtmf_connect (glade, window, "button_1", TP_DTMF_EVENT_DIGIT_1);
593   call_window_dtmf_connect (glade, window, "button_2", TP_DTMF_EVENT_DIGIT_2);
594   call_window_dtmf_connect (glade, window, "button_3", TP_DTMF_EVENT_DIGIT_3);
595   call_window_dtmf_connect (glade, window, "button_4", TP_DTMF_EVENT_DIGIT_4);
596   call_window_dtmf_connect (glade, window, "button_5", TP_DTMF_EVENT_DIGIT_5);
597   call_window_dtmf_connect (glade, window, "button_6", TP_DTMF_EVENT_DIGIT_6);
598   call_window_dtmf_connect (glade, window, "button_7", TP_DTMF_EVENT_DIGIT_7);
599   call_window_dtmf_connect (glade, window, "button_8", TP_DTMF_EVENT_DIGIT_8);
600   call_window_dtmf_connect (glade, window, "button_9", TP_DTMF_EVENT_DIGIT_9);
601   call_window_dtmf_connect (glade, window, "button_asterisk", TP_DTMF_EVENT_ASTERISK);
602   call_window_dtmf_connect (glade, window, "button_hash", TP_DTMF_EVENT_HASH);
603
604   g_object_unref (glade);
605
606   /* Output volume button */
607   window->output_volume_button = gtk_volume_button_new ();
608   gtk_scale_button_set_value (GTK_SCALE_BUTTON (window->output_volume_button), 1);
609   gtk_box_pack_start (GTK_BOX (window->volume_hbox),
610       window->output_volume_button, FALSE, FALSE, 0);
611   gtk_widget_show (window->output_volume_button);
612   g_signal_connect (window->output_volume_button, "value-changed",
613       G_CALLBACK (call_window_output_volume_changed_cb), window);
614
615   /* Input volume button */
616   window->input_volume_button = gtk_volume_button_new ();
617   gtk_scale_button_set_icons (GTK_SCALE_BUTTON (window->input_volume_button),
618       icons);
619   gtk_scale_button_set_value (GTK_SCALE_BUTTON (window->input_volume_button), 1);
620   gtk_box_pack_start (GTK_BOX (window->volume_hbox),
621       window->input_volume_button, FALSE, FALSE, 0);
622   gtk_widget_show (window->input_volume_button);
623   g_signal_connect (window->input_volume_button, "value-changed",
624       G_CALLBACK (call_window_input_volume_changed_cb), window);
625
626   /* Output video socket */
627   window->output_video_socket = g_object_ref (gtk_socket_new ());
628   gtk_widget_set_size_request (window->output_video_socket, 400, 300);
629   g_signal_connect (GTK_OBJECT (window->output_video_socket), "realize",
630       G_CALLBACK (call_window_socket_realized_cb), window);
631   gtk_widget_show (window->output_video_socket);
632
633   /* Preview video socket */
634   window->preview_video_socket = g_object_ref (gtk_socket_new ());
635   gtk_widget_set_size_request (window->preview_video_socket, 176, 144);
636   g_signal_connect (GTK_OBJECT (window->preview_video_socket), "realize",
637       G_CALLBACK (call_window_socket_realized_cb), window);
638   gtk_widget_show (window->preview_video_socket);
639
640   /* FIXME: We shouldn't do this if there is no video input */
641   gtk_box_pack_start (GTK_BOX (window->controls_vbox),
642       window->preview_video_socket, FALSE, FALSE, 0);
643   gtk_box_reorder_child (GTK_BOX (window->controls_vbox),
644       window->preview_video_socket, 0);
645
646   g_signal_connect_swapped (G_OBJECT (window->call), "notify",
647       G_CALLBACK (call_window_update),
648       window);
649
650   call_window_update (window);
651   gtk_widget_show (window->window);
652
653   return window->window;
654 }
655