Fix includes
[empathy.git] / src / empathy-call-window.c
1 /*
2  *  Copyright (C) 2007 Elliot Fairweather
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Lesser General Public
6  *  License as published by the Free Software Foundation; either
7  *  version 2.1 of the License, or (at your option) any later version.
8  *
9  *  This library is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public
15  *  License along with this library; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  *  Authors: Elliot Fairweather <elliot.fairweather@collabora.co.uk>
19  */
20
21 #include <string.h>
22
23 #include <libtelepathy/tp-chan.h>
24
25 #include <libmissioncontrol/mc-account.h>
26 #include <libmissioncontrol/mc-account-monitor.h>
27 #include <libmissioncontrol/mission-control.h>
28
29 #include <libempathy/empathy-contact.h>
30 #include <libempathy/empathy-tp-call.h>
31 #include <libempathy/empathy-chandler.h>
32 #include <libempathy/empathy-debug.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_DOMAIN "CallWindow"
39
40 typedef struct 
41 {
42   GtkWidget *window;
43   GtkWidget *status_label;
44   GtkWidget *start_call_button;
45   GtkWidget *end_call_button;
46   GtkWidget *input_volume_scale;
47   GtkWidget *output_volume_scale;
48   GtkWidget *input_mute_button;
49   GtkWidget *output_mute_button;
50   GtkWidget *preview_video_frame;
51   GtkWidget *output_video_frame;
52   GtkWidget *preview_video_socket;
53   GtkWidget *output_video_socket;
54   GtkWidget *video_button;
55   GtkWidget *output_video_label;
56
57   EmpathyTpCall *call;
58
59   GTimeVal start_time;
60   guint timeout_event_id;
61
62   gboolean is_drawing;
63 } EmpathyCallWindow;
64
65 static gboolean
66 call_window_update_timer (gpointer data)
67 {
68   EmpathyCallWindow *window = data;
69   GTimeVal current;
70   gchar *str;
71   glong now, then;
72   glong time, seconds, minutes, hours;
73
74   g_get_current_time (&current);
75
76   now = current.tv_sec;
77   then = (window->start_time).tv_sec;
78
79   time = now - then;
80
81   seconds = time % 60;
82   time /= 60;
83   minutes = time % 60;
84   time /= 60;
85   hours = time % 60;
86
87   if (hours > 0)
88     {
89       str = g_strdup_printf ("Connected  -  %02ld : %02ld : %02ld", hours,
90           minutes, seconds);
91     }
92   else
93     {
94       str = g_strdup_printf ("Connected  -  %02ld : %02ld", minutes, seconds);
95     }
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   GMainContext *context;
108   GSource *source;
109
110   context = g_main_context_default ();
111
112   empathy_debug (DEBUG_DOMAIN, "Timer stopped");
113
114   if (window->timeout_event_id)
115     {
116       source = g_main_context_find_source_by_id (context,
117           window->timeout_event_id);
118       g_source_destroy (source);
119       window->timeout_event_id = 0;
120     }
121 }
122
123 static void
124 call_window_set_output_video_is_drawing (EmpathyCallWindow *window,
125                                          gboolean is_drawing)
126 {
127   GtkWidget* child;
128
129   child = gtk_bin_get_child (GTK_BIN (window->output_video_frame));
130
131   empathy_debug (DEBUG_DOMAIN,
132       "Setting output video is drawing - %d", is_drawing);
133
134   if (is_drawing)
135     {
136       if (!window->is_drawing)
137         {
138           if (child)
139             {
140               gtk_container_remove (GTK_CONTAINER (window->output_video_frame),
141                   child);
142             }
143           gtk_container_add (GTK_CONTAINER (window->output_video_frame),
144               window->output_video_socket);
145           gtk_widget_show (window->output_video_socket);
146           empathy_tp_call_add_output_video (window->call,
147               gtk_socket_get_id (GTK_SOCKET (window->output_video_socket)));
148           window->is_drawing = is_drawing;
149         }
150     }
151   else
152     {
153       if (window->is_drawing)
154         {
155           empathy_tp_call_add_output_video (window->call, 0);
156           if (child)
157             {
158               gtk_container_remove (GTK_CONTAINER (window->output_video_frame),
159                   child);
160             }
161           gtk_container_add (GTK_CONTAINER (window->output_video_frame),
162               window->output_video_label);
163           gtk_widget_show (window->output_video_label);
164           window->is_drawing = is_drawing;
165         }
166     }
167 }
168
169 static gboolean
170 call_window_delete_event_cb (GtkWidget *widget,
171                              GdkEvent *event,
172                              EmpathyCallWindow *window)
173 {
174   GtkWidget *dialog;
175   gint result;
176   guint status;
177
178   empathy_debug (DEBUG_DOMAIN, "Delete event occurred");
179
180   g_object_get (G_OBJECT (window->call), "status", &status, NULL);
181
182   if (status != EMPATHY_TP_CALL_STATUS_CLOSED)
183     {
184       dialog = gtk_message_dialog_new (GTK_WINDOW (window->window),
185           GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
186           GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
187           "This call will be ended. Continue?");
188
189       result = gtk_dialog_run (GTK_DIALOG (dialog));
190       gtk_widget_destroy (dialog);
191
192       switch (result)
193         {
194         case GTK_RESPONSE_YES:
195           call_window_stop_timeout (window);
196           call_window_set_output_video_is_drawing (window, FALSE);
197           empathy_tp_call_close_channel (window->call);
198           empathy_tp_call_remove_preview_video (window->call,
199               gtk_socket_get_id (GTK_SOCKET (window->preview_video_socket)));
200           return FALSE;
201         default:
202           return TRUE;
203         }
204     }
205   else
206     {
207       empathy_tp_call_remove_preview_video (window->call,
208           gtk_socket_get_id (GTK_SOCKET (window->preview_video_socket)));
209       return FALSE;
210     }
211 }
212
213 static void
214 call_window_video_button_toggled_cb (GtkWidget *button,
215                                      EmpathyCallWindow *window)
216 {
217   gboolean is_sending;
218
219   is_sending = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
220
221   empathy_debug (DEBUG_DOMAIN, "Send video toggled - %d", is_sending);
222
223   empathy_tp_call_request_video_stream_direction (window->call, is_sending);
224 }
225
226 static void
227 call_window_status_changed_cb (EmpathyTpCall *call,
228                                EmpathyCallWindow *window)
229 {
230   EmpathyContact *contact;
231   guint status;
232   guint stream_state;
233   EmpathyTpCallStream *audio_stream;
234   EmpathyTpCallStream *video_stream;
235   gboolean is_incoming;
236   gchar *title;
237
238   g_object_get (G_OBJECT (window->call), "status", &status, NULL);
239   g_object_get (G_OBJECT (window->call), "audio-stream", &audio_stream, NULL);
240   g_object_get (G_OBJECT (window->call), "video-stream", &video_stream, NULL);
241
242   if (video_stream->state > audio_stream->state)
243     {
244       stream_state = video_stream->state;
245     }
246   else
247     {
248       stream_state = audio_stream->state;
249     }
250
251   empathy_debug (DEBUG_DOMAIN, "Status changed - status: %d, stream state: %d",
252       status, stream_state);
253
254   if (window->timeout_event_id)
255     {
256       call_window_stop_timeout (window);
257     }
258
259   if (status == EMPATHY_TP_CALL_STATUS_CLOSED)
260     {
261       gtk_label_set_text (GTK_LABEL (window->status_label), "Closed");
262       gtk_widget_set_sensitive (window->end_call_button, FALSE);
263       gtk_widget_set_sensitive (window->start_call_button, FALSE);
264
265       call_window_set_output_video_is_drawing (window, FALSE);
266     }
267   else if (stream_state == TP_MEDIA_STREAM_STATE_DISCONNECTED)
268     {
269       gtk_label_set_text (GTK_LABEL (window->status_label), "Disconnected");
270     }
271   else if (status == EMPATHY_TP_CALL_STATUS_PENDING)
272     {
273       g_object_get (G_OBJECT (window->call), "contact", &contact, NULL);
274
275       title = g_strdup_printf ("%s - Empathy Call",
276           empathy_contact_get_name (contact));
277       gtk_window_set_title (GTK_WINDOW (window->window), title);
278
279       gtk_label_set_text (GTK_LABEL (window->status_label), "Ringing");
280       gtk_widget_set_sensitive (window->end_call_button, TRUE);
281       gtk_widget_set_sensitive (window->video_button, TRUE);
282
283       g_object_get (G_OBJECT (window->call), "is-incoming", &is_incoming, NULL);
284       if (is_incoming)
285         {
286           gtk_widget_set_sensitive (window->start_call_button, TRUE);
287         }
288       else
289         {
290           g_signal_connect (GTK_OBJECT (window->video_button), "toggled",
291               G_CALLBACK (call_window_video_button_toggled_cb),
292               window);
293         }
294     }
295   else if (status == EMPATHY_TP_CALL_STATUS_ACCEPTED)
296     {
297       if (stream_state == TP_MEDIA_STREAM_STATE_CONNECTING)
298         {
299           gtk_label_set_text (GTK_LABEL (window->status_label), "Connecting");
300         }
301       else if (stream_state == TP_MEDIA_STREAM_STATE_CONNECTED)
302         {
303           if ((window->start_time).tv_sec == 0)
304             {
305               g_get_current_time (&(window->start_time));
306             }
307           window->timeout_event_id = g_timeout_add (1000,
308               call_window_update_timer, window);
309           empathy_debug (DEBUG_DOMAIN, "Timer started");
310         }
311     }
312 }
313
314 static void
315 call_window_receiving_video_cb (EmpathyTpCall *call,
316                                 gboolean receiving_video,
317                                 EmpathyCallWindow *window)
318 {
319   empathy_debug (DEBUG_DOMAIN, "Receiving video signal received");
320
321   call_window_set_output_video_is_drawing (window, receiving_video);
322 }
323
324 static void
325 call_window_sending_video_cb (EmpathyTpCall *call,
326                               gboolean sending_video,
327                               EmpathyCallWindow *window)
328 {
329   empathy_debug (DEBUG_DOMAIN, "Sending video signal received");
330
331   g_signal_handlers_block_by_func (window->video_button,
332       call_window_video_button_toggled_cb, window);
333   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (window->video_button),
334       sending_video);
335   g_signal_handlers_unblock_by_func (window->video_button,
336       call_window_video_button_toggled_cb, window);
337 }
338
339 static void
340 call_window_socket_realized_cb (GtkWidget *widget,
341                                 EmpathyCallWindow *window)
342 {
343   if (widget == window->preview_video_socket)
344     {
345       empathy_debug (DEBUG_DOMAIN, "Preview socket realized");
346       empathy_tp_call_add_preview_video (window->call,
347           gtk_socket_get_id (GTK_SOCKET (window->preview_video_socket)));
348     }
349   else
350     {
351       empathy_debug (DEBUG_DOMAIN, "Output socket realized");
352     }
353 }
354
355 static void
356 call_window_start_call_button_clicked_cb (GtkWidget *widget,
357                                           EmpathyCallWindow *window)
358 {
359   gboolean send_video;
360   gboolean is_incoming;
361
362   empathy_debug (DEBUG_DOMAIN, "Start call clicked");
363
364   gtk_widget_set_sensitive (window->start_call_button, FALSE);
365   g_object_get (G_OBJECT (window->call), "is-incoming", &is_incoming, NULL);
366   if (is_incoming)
367     {
368       empathy_tp_call_accept_incoming_call (window->call);
369       send_video = gtk_toggle_button_get_active
370           (GTK_TOGGLE_BUTTON (window->video_button));
371       empathy_tp_call_request_video_stream_direction (window->call, send_video);
372       g_signal_connect (GTK_OBJECT (window->video_button), "toggled",
373           G_CALLBACK (call_window_video_button_toggled_cb), window);
374     }
375 }
376
377 static void
378 call_window_end_call_button_clicked_cb (GtkWidget *widget,
379                                         EmpathyCallWindow *window)
380 {
381   empathy_debug (DEBUG_DOMAIN, "End call clicked");
382
383   call_window_set_output_video_is_drawing (window, FALSE);
384   empathy_tp_call_close_channel (window->call);
385   gtk_widget_set_sensitive (window->end_call_button, FALSE);
386   gtk_widget_set_sensitive (window->start_call_button, FALSE);
387 }
388
389 static void
390 call_window_output_volume_changed_cb (GtkWidget *scale,
391                                       EmpathyCallWindow *window)
392 {
393   guint volume;
394
395   volume = (guint) gtk_range_get_value (GTK_RANGE (scale));
396
397   empathy_debug (DEBUG_DOMAIN, "Output volume changed - %u", volume);
398
399   empathy_tp_call_set_output_volume (window->call, volume);
400 }
401
402 static void
403 call_window_output_mute_button_toggled_cb (GtkWidget *button,
404                                            EmpathyCallWindow *window)
405 {
406   gboolean is_muted;
407
408   is_muted = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
409
410   empathy_debug (DEBUG_DOMAIN, "Mute output toggled - %d", is_muted);
411
412   empathy_tp_call_mute_output (window->call, is_muted);
413 }
414
415 static void
416 call_window_input_mute_button_toggled_cb (GtkWidget *button,
417                                           EmpathyCallWindow *window)
418 {
419   gboolean is_muted;
420
421   is_muted = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
422
423   empathy_debug (DEBUG_DOMAIN, "Mute input toggled - %d", is_muted);
424
425   empathy_tp_call_mute_input (window->call, is_muted);
426 }
427
428 static void
429 call_window_destroy_cb (GtkWidget *widget,
430                         EmpathyCallWindow *window)
431 {
432   g_signal_handlers_disconnect_by_func (window->call,
433       call_window_status_changed_cb, window);
434   g_signal_handlers_disconnect_by_func (window->call,
435       call_window_receiving_video_cb, window);
436   g_signal_handlers_disconnect_by_func (window->call,
437       call_window_sending_video_cb, window);
438
439   g_object_unref (window->call);
440   g_object_unref (window->output_video_socket);
441   g_object_unref (window->preview_video_socket);
442   g_object_unref (window->output_video_label);
443
444   g_slice_free (EmpathyCallWindow, window);
445 }
446
447 GtkWidget *
448 empathy_call_window_new (EmpathyTpCall *call)
449 {
450   EmpathyCallWindow *window;
451   GladeXML *glade;
452   guint status;
453   gchar *filename;
454
455   g_return_val_if_fail (EMPATHY_IS_TP_CALL (call), NULL);
456
457   window = g_slice_new0 (EmpathyCallWindow);
458   window->call = g_object_ref (call);
459
460   filename = empathy_file_lookup ("empathy-call-window.glade", "src");
461   glade = empathy_glade_get_file (filename,
462       "window",
463       NULL,
464       "window", &window->window,
465       "status_label", &window->status_label,
466       "start_call_button", &window->start_call_button,
467       "end_call_button", &window->end_call_button,
468       "input_volume_scale", &window->input_volume_scale,
469       "output_volume_scale", &window->output_volume_scale,
470       "input_mute_button", &window->input_mute_button,
471       "output_mute_button", &window->output_mute_button,
472       "preview_video_frame", &window->preview_video_frame,
473       "output_video_frame", &window->output_video_frame,
474       "video_button", &window->video_button,
475       NULL);
476   g_free (filename);
477
478   empathy_glade_connect (glade,
479       window,
480       "window", "destroy", call_window_destroy_cb,
481       "window", "delete_event", call_window_delete_event_cb,
482       "input_mute_button", "toggled", call_window_input_mute_button_toggled_cb,
483       "output_mute_button", "toggled", call_window_output_mute_button_toggled_cb,
484       "output_volume_scale", "value-changed", call_window_output_volume_changed_cb,
485       "start_call_button", "clicked", call_window_start_call_button_clicked_cb,
486       "end_call_button", "clicked", call_window_end_call_button_clicked_cb,
487       NULL);
488
489   g_object_unref (glade);
490
491   /* Output video label */
492   window->output_video_label = g_object_ref (gtk_label_new ("No video output"));
493   gtk_container_add (GTK_CONTAINER (window->output_video_frame),
494       window->output_video_label);
495   gtk_widget_show (window->output_video_label);
496
497   /* Output video socket */
498   window->output_video_socket = g_object_ref (gtk_socket_new ());
499   g_signal_connect (GTK_OBJECT (window->output_video_socket), "realize",
500       G_CALLBACK (call_window_socket_realized_cb), window);
501   gtk_widget_show (window->output_video_socket);
502
503   /* Preview video socket */
504   window->preview_video_socket = g_object_ref (gtk_socket_new ());
505   g_signal_connect (GTK_OBJECT (window->preview_video_socket), "realize",
506       G_CALLBACK (call_window_socket_realized_cb), window);
507   gtk_container_add (GTK_CONTAINER (window->preview_video_frame),
508       window->preview_video_socket);
509   gtk_widget_show (window->preview_video_socket);
510
511   g_signal_connect (G_OBJECT (window->call), "status-changed",
512       G_CALLBACK (call_window_status_changed_cb),
513       window);
514   g_signal_connect (G_OBJECT (window->call), "receiving-video",
515       G_CALLBACK (call_window_receiving_video_cb),
516       window);
517   g_signal_connect (G_OBJECT (window->call), "sending-video",
518       G_CALLBACK (call_window_sending_video_cb),
519       window);
520
521   window->is_drawing = FALSE;
522
523   g_object_get (G_OBJECT (window->call), "status", &status, NULL);
524
525   if (status == EMPATHY_TP_CALL_STATUS_READYING)
526     {
527       gtk_window_set_title (GTK_WINDOW (window->window), "Empathy Call");
528       gtk_label_set_text (GTK_LABEL (window->status_label), "Readying");
529     }
530
531   gtk_widget_show (window->window);
532
533   return window->window;
534 }
535