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