]> git.0d.be Git - empathy.git/blob - src/empathy-call-window.c
77f8508667855995e8a33864375c7ea062b51f82
[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   g_object_get (window->call,
356       "status", &window->status,
357       "audio-stream", &audio_stream,
358       "video-stream", &video_stream,
359       "contact", &contact,
360       "is-incoming", &is_incoming,
361       NULL);
362
363   if (video_stream->state > audio_stream->state)
364       stream_state = video_stream->state;
365   else
366       stream_state = audio_stream->state;
367
368   DEBUG ("Status changed - status: %d, stream state: %d, "
369       "is-incoming: %d video-stream direction: %d",
370       window->status, stream_state, is_incoming, video_stream->direction);
371
372   if (empathy_tp_call_has_dtmf (window->call))
373     {
374       gtk_widget_show (window->keypad_expander);
375     }
376   else
377     {
378       gtk_widget_hide (window->keypad_expander);
379     }
380
381   /* Depending on the status we have to set:
382    * - window's title
383    * - status's label
384    * - sensibility of all buttons
385    * */
386   if (window->status == EMPATHY_TP_CALL_STATUS_READYING)
387     {
388       gtk_window_set_title (GTK_WINDOW (window->window), _("Empathy Call"));
389       /* To translators: Readying is the first state of the call, it is
390        * preparing the connection and it does not yet ring. */
391       gtk_label_set_text (GTK_LABEL (window->status_label), _("Readying"));
392       gtk_widget_set_sensitive (window->video_button, FALSE);
393       gtk_widget_set_sensitive (window->output_volume_button, FALSE);
394       gtk_widget_set_sensitive (window->input_volume_button, FALSE);
395       gtk_widget_set_sensitive (window->hang_up_button, FALSE);
396       gtk_widget_set_sensitive (window->keypad_expander, FALSE);
397     }
398   else if (window->status == EMPATHY_TP_CALL_STATUS_PENDING)
399     {
400       title = g_strdup_printf (_("%s - Empathy Call"),
401           empathy_contact_get_name (contact));
402
403       gtk_window_set_title (GTK_WINDOW (window->window), title);
404       gtk_label_set_text (GTK_LABEL (window->status_label), _("Ringing"));
405       gtk_widget_set_sensitive (window->hang_up_button, TRUE);
406
407       if (is_incoming)
408         {
409           call_window_show_confirmation_dialog (window);
410           empathy_sound_play (GTK_WIDGET (window->window),
411                               EMPATHY_SOUND_PHONE_INCOMING);
412         }
413       else
414         {
415           empathy_sound_play (GTK_WIDGET (window->window),
416                               EMPATHY_SOUND_PHONE_OUTGOING);
417         }
418     }
419   else if (window->status == EMPATHY_TP_CALL_STATUS_ACCEPTED)
420     {
421       gboolean receiving_video;
422       gboolean sending_video;
423
424       if (stream_state == TP_MEDIA_STREAM_STATE_DISCONNECTED)
425           gtk_label_set_text (GTK_LABEL (window->status_label), _("Disconnected"));
426       if (stream_state == TP_MEDIA_STREAM_STATE_CONNECTING)
427           gtk_label_set_text (GTK_LABEL (window->status_label), _("Connecting"));
428       else if (stream_state == TP_MEDIA_STREAM_STATE_CONNECTED &&
429                window->timeout_event_id == 0)
430         {
431           /* The call started, launch the timer */
432           g_get_current_time (&(window->start_time));
433           window->timeout_event_id = g_timeout_add_seconds (1,
434               call_window_update_timer, window);
435           call_window_update_timer (window);
436         }
437
438       receiving_video = video_stream->direction & TP_MEDIA_STREAM_DIRECTION_RECEIVE;
439       sending_video = video_stream->direction & TP_MEDIA_STREAM_DIRECTION_SEND;
440       call_window_set_output_video_is_drawing (window, receiving_video);
441       g_signal_handlers_block_by_func (window->video_button,
442           call_window_video_button_toggled_cb, window);
443       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (window->video_button),
444           sending_video);
445       g_signal_handlers_unblock_by_func (window->video_button,
446           call_window_video_button_toggled_cb, window);
447
448       gtk_widget_set_sensitive (window->video_button, TRUE);
449       gtk_widget_set_sensitive (window->output_volume_button, TRUE);
450       gtk_widget_set_sensitive (window->input_volume_button, TRUE);
451       gtk_widget_set_sensitive (window->hang_up_button, TRUE);
452       gtk_widget_set_sensitive (window->keypad_expander, TRUE);
453     }
454   else if (window->status == EMPATHY_TP_CALL_STATUS_CLOSED)
455       call_window_finalize (window);
456
457   if (contact)
458       g_object_unref (contact);
459 }
460
461 static gboolean
462 call_window_dtmf_button_release_event_cb (GtkWidget *widget,
463                                           GdkEventButton *event,
464                                           EmpathyCallWindow *window)
465 {
466   empathy_tp_call_stop_tone (window->call);
467   return FALSE;
468 }
469
470 static gboolean
471 call_window_dtmf_button_press_event_cb (GtkWidget *widget,
472                                         GdkEventButton *event,
473                                         EmpathyCallWindow *window)
474 {
475   TpDTMFEvent dtmf_event;
476
477   dtmf_event = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), "code"));
478   empathy_tp_call_start_tone (window->call, dtmf_event);
479   return FALSE;
480 }
481
482 static void
483 call_window_dtmf_connect (GladeXML *glade,
484                           EmpathyCallWindow *window,
485                           const gchar *name,
486                           TpDTMFEvent event)
487 {
488   GtkWidget *widget;
489
490   widget = glade_xml_get_widget (glade, name);
491   g_object_set_data (G_OBJECT (widget), "code", GUINT_TO_POINTER (event));
492   g_signal_connect (widget, "button-press-event",
493       G_CALLBACK (call_window_dtmf_button_press_event_cb), window);
494   g_signal_connect (widget, "button-release-event",
495       G_CALLBACK (call_window_dtmf_button_release_event_cb), window);
496   /* FIXME: Connect "key-[press/release]-event" to*/
497 }
498
499 GtkWidget *
500 empathy_call_window_new (EmpathyTpCall *call)
501 {
502   EmpathyCallWindow *window;
503   GladeXML *glade;
504   gchar *filename;
505   const gchar *icons[] = {"audio-input-microphone", NULL};
506
507   g_return_val_if_fail (EMPATHY_IS_TP_CALL (call), NULL);
508
509   if (windows)
510     {
511       window = (EmpathyCallWindow*) windows->data;
512       if (!window->call)
513         {
514           window->call = g_object_ref (call);
515           g_signal_connect_swapped (G_OBJECT (window->call), "notify",
516               G_CALLBACK (call_window_update), window);
517           call_window_update (window);
518
519           if (GTK_WIDGET_REALIZED (GTK_WIDGET (window->preview_video_socket)))
520             {
521               call_window_socket_realized_cb (window->preview_video_socket,
522                   window);
523             }
524         }
525       else
526         {
527           GtkWidget *dialog;
528           EmpathyContact *contact;
529           TpChannel *channel;
530
531           g_object_get ( G_OBJECT (call),
532             "contact", &contact,
533             "channel", &channel,
534             NULL);
535
536           empathy_contact_run_until_ready (contact, EMPATHY_CONTACT_READY_NAME,
537               NULL);
538
539           /* We don't want to have multiple calls running.
540            * FIXME: We should use the hold interface... */
541           tp_cli_channel_call_close (channel, -1, NULL, NULL, NULL, NULL);
542
543           dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_INFO,
544               GTK_BUTTONS_CLOSE,
545               _("Incoming call from %s rejected because there is already a"
546                 " running call."), empathy_contact_get_name (contact));
547
548           g_object_unref (contact);
549           g_object_unref (channel);
550
551           g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy),
552               NULL);
553           gtk_widget_show (dialog);
554         }
555
556       gtk_window_present (GTK_WINDOW (window->window));
557       return window->window;
558     }
559
560   window = g_slice_new0 (EmpathyCallWindow);
561   windows = g_slist_prepend (windows, window);
562   window->call = g_object_ref (call);
563
564   filename = empathy_file_lookup ("empathy-call-window.glade", "src");
565   glade = empathy_glade_get_file (filename,
566       "window",
567       NULL,
568       "window", &window->window,
569       "main_hbox", &window->main_hbox,
570       "controls_vbox", &window->controls_vbox,
571       "volume_hbox", &window->volume_hbox,
572       "status_label", &window->status_label,
573       "video_button", &window->video_button,
574       "hang_up_button", &window->hang_up_button,
575       "keypad_expander", &window->keypad_expander,
576       NULL);
577   g_free (filename);
578
579   empathy_glade_connect (glade,
580       window,
581       "window", "destroy", call_window_destroy_cb,
582       "window", "delete_event", call_window_delete_event_cb,
583       "hang_up_button", "clicked", call_window_hang_up_button_clicked_cb,
584       "video_button", "toggled", call_window_video_button_toggled_cb,
585       NULL);
586
587   /* Setup DTMF buttons */
588   call_window_dtmf_connect (glade, window, "button_0", TP_DTMF_EVENT_DIGIT_0);
589   call_window_dtmf_connect (glade, window, "button_1", TP_DTMF_EVENT_DIGIT_1);
590   call_window_dtmf_connect (glade, window, "button_2", TP_DTMF_EVENT_DIGIT_2);
591   call_window_dtmf_connect (glade, window, "button_3", TP_DTMF_EVENT_DIGIT_3);
592   call_window_dtmf_connect (glade, window, "button_4", TP_DTMF_EVENT_DIGIT_4);
593   call_window_dtmf_connect (glade, window, "button_5", TP_DTMF_EVENT_DIGIT_5);
594   call_window_dtmf_connect (glade, window, "button_6", TP_DTMF_EVENT_DIGIT_6);
595   call_window_dtmf_connect (glade, window, "button_7", TP_DTMF_EVENT_DIGIT_7);
596   call_window_dtmf_connect (glade, window, "button_8", TP_DTMF_EVENT_DIGIT_8);
597   call_window_dtmf_connect (glade, window, "button_9", TP_DTMF_EVENT_DIGIT_9);
598   call_window_dtmf_connect (glade, window, "button_asterisk", TP_DTMF_EVENT_ASTERISK);
599   call_window_dtmf_connect (glade, window, "button_hash", TP_DTMF_EVENT_HASH);
600
601   g_object_unref (glade);
602
603   /* Output volume button */
604   window->output_volume_button = gtk_volume_button_new ();
605   gtk_scale_button_set_value (GTK_SCALE_BUTTON (window->output_volume_button), 1);
606   gtk_box_pack_start (GTK_BOX (window->volume_hbox),
607       window->output_volume_button, FALSE, FALSE, 0);
608   gtk_widget_show (window->output_volume_button);
609   g_signal_connect (window->output_volume_button, "value-changed",
610       G_CALLBACK (call_window_output_volume_changed_cb), window);
611
612   /* Input volume button */
613   window->input_volume_button = gtk_volume_button_new ();
614   gtk_scale_button_set_icons (GTK_SCALE_BUTTON (window->input_volume_button),
615       icons);
616   gtk_scale_button_set_value (GTK_SCALE_BUTTON (window->input_volume_button), 1);
617   gtk_box_pack_start (GTK_BOX (window->volume_hbox),
618       window->input_volume_button, FALSE, FALSE, 0);
619   gtk_widget_show (window->input_volume_button);
620   g_signal_connect (window->input_volume_button, "value-changed",
621       G_CALLBACK (call_window_input_volume_changed_cb), window);
622
623   /* Output video socket */
624   window->output_video_socket = g_object_ref (gtk_socket_new ());
625   gtk_widget_set_size_request (window->output_video_socket, 400, 300);
626   g_signal_connect (GTK_OBJECT (window->output_video_socket), "realize",
627       G_CALLBACK (call_window_socket_realized_cb), window);
628   gtk_widget_show (window->output_video_socket);
629
630   /* Preview video socket */
631   window->preview_video_socket = g_object_ref (gtk_socket_new ());
632   gtk_widget_set_size_request (window->preview_video_socket, 176, 144);
633   g_signal_connect (GTK_OBJECT (window->preview_video_socket), "realize",
634       G_CALLBACK (call_window_socket_realized_cb), window);
635   gtk_widget_show (window->preview_video_socket);
636
637   /* FIXME: We shouldn't do this if there is no video input */
638   gtk_box_pack_start (GTK_BOX (window->controls_vbox),
639       window->preview_video_socket, FALSE, FALSE, 0);
640   gtk_box_reorder_child (GTK_BOX (window->controls_vbox),
641       window->preview_video_socket, 0);
642
643   g_signal_connect_swapped (G_OBJECT (window->call), "notify",
644       G_CALLBACK (call_window_update),
645       window);
646
647   call_window_update (window);
648   gtk_widget_show (window->window);
649
650   return window->window;
651 }
652