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