prepare 2.30.1.1 release
[empathy-mirror.git] / libempathy-gtk / empathy-sound.c
blob74dcf6a2e852af681c81374299240b5353940997
1 /*
2 * empathy-sound.c - Various sound related utility functions.
3 * Copyright (C) 2009 Collabora Ltd.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 #include <config.h>
22 #include "empathy-sound.h"
24 #include <canberra-gtk.h>
25 #include <glib/gi18n-lib.h>
26 #include <gtk/gtk.h>
28 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
29 #include <libempathy/empathy-debug.h>
30 #include <libempathy/empathy-utils.h>
32 #include "empathy-conf.h"
34 typedef struct {
35 EmpathySound sound_id;
36 const char * event_ca_id;
37 const char * event_ca_description;
38 const char * gconf_key;
39 } EmpathySoundEntry;
41 typedef struct {
42 GtkWidget *widget;
43 gint sound_id;
44 guint play_interval;
45 guint replay_timeout_id;
46 } EmpathyRepeatableSound;
48 /* NOTE: these entries MUST be in the same order than EmpathySound enum */
49 static EmpathySoundEntry sound_entries[LAST_EMPATHY_SOUND] = {
50 { EMPATHY_SOUND_MESSAGE_INCOMING, "message-new-instant",
51 N_("Received an instant message"), EMPATHY_PREFS_SOUNDS_INCOMING_MESSAGE } ,
52 { EMPATHY_SOUND_MESSAGE_OUTGOING, "message-sent-instant",
53 N_("Sent an instant message"), EMPATHY_PREFS_SOUNDS_OUTGOING_MESSAGE } ,
54 { EMPATHY_SOUND_CONVERSATION_NEW, "message-new-instant",
55 N_("Incoming chat request"), EMPATHY_PREFS_SOUNDS_NEW_CONVERSATION },
56 { EMPATHY_SOUND_CONTACT_CONNECTED, "service-login",
57 N_("Contact connected"), EMPATHY_PREFS_SOUNDS_CONTACT_LOGIN },
58 { EMPATHY_SOUND_CONTACT_DISCONNECTED, "service-logout",
59 N_("Contact disconnected"), EMPATHY_PREFS_SOUNDS_CONTACT_LOGOUT },
60 { EMPATHY_SOUND_ACCOUNT_CONNECTED, "service-login",
61 N_("Connected to server"), EMPATHY_PREFS_SOUNDS_SERVICE_LOGIN },
62 { EMPATHY_SOUND_ACCOUNT_DISCONNECTED, "service-logout",
63 N_("Disconnected from server"), EMPATHY_PREFS_SOUNDS_SERVICE_LOGOUT },
64 { EMPATHY_SOUND_PHONE_INCOMING, "phone-incoming-call",
65 N_("Incoming voice call"), NULL },
66 { EMPATHY_SOUND_PHONE_OUTGOING, "phone-outgoing-calling",
67 N_("Outgoing voice call"), NULL },
68 { EMPATHY_SOUND_PHONE_HANGUP, "phone-hangup",
69 N_("Voice call ended"), NULL },
72 /* An hash table containing currently repeating sounds. The format is the
73 * following:
74 * Key: An EmpathySound
75 * Value : The EmpathyRepeatableSound associated with that EmpathySound. */
76 static GHashTable *repeating_sounds;
78 static gboolean
79 empathy_sound_pref_is_enabled (EmpathySound sound_id)
81 EmpathySoundEntry *entry;
82 EmpathyConf *conf;
83 gboolean res;
85 entry = &(sound_entries[sound_id]);
86 g_return_val_if_fail (entry->sound_id == sound_id, FALSE);
88 if (entry->gconf_key == NULL)
89 return TRUE;
91 conf = empathy_conf_get ();
92 res = FALSE;
94 empathy_conf_get_bool (conf, EMPATHY_PREFS_SOUNDS_ENABLED, &res);
96 if (!res)
97 return FALSE;
99 if (!empathy_check_available_state ())
101 empathy_conf_get_bool (conf, EMPATHY_PREFS_SOUNDS_DISABLED_AWAY, &res);
103 if (res)
104 return FALSE;
107 empathy_conf_get_bool (conf, entry->gconf_key, &res);
109 return res;
113 * empathy_sound_stop:
114 * @sound_id: The #EmpathySound to stop playing.
116 * Stop playing a sound. If it has been stated in loop with
117 * empathy_sound_start_playing(), it will also stop replaying.
119 void
120 empathy_sound_stop (EmpathySound sound_id)
122 EmpathySoundEntry *entry;
124 g_return_if_fail (sound_id < LAST_EMPATHY_SOUND);
126 entry = &(sound_entries[sound_id]);
127 g_return_if_fail (entry->sound_id == sound_id);
129 if (repeating_sounds != NULL)
131 EmpathyRepeatableSound *repeatable_sound;
133 repeatable_sound = g_hash_table_lookup (repeating_sounds,
134 GINT_TO_POINTER (sound_id));
135 if (repeatable_sound != NULL)
137 /* The sound must be stopped... If it is waiting for replay, remove
138 * it from hash table to cancel. Otherwise we'll cancel the sound
139 * being played. */
140 if (repeatable_sound->replay_timeout_id != 0)
142 g_hash_table_remove (repeating_sounds, GINT_TO_POINTER (sound_id));
143 return;
148 ca_context_cancel (ca_gtk_context_get (), entry->sound_id);
151 static gboolean
152 empathy_sound_play_internal (GtkWidget *widget, EmpathySound sound_id,
153 ca_finish_callback_t callback, gpointer user_data)
155 EmpathySoundEntry *entry;
156 ca_context *c;
157 ca_proplist *p = NULL;
159 entry = &(sound_entries[sound_id]);
160 g_return_val_if_fail (entry->sound_id == sound_id, FALSE);
162 c = ca_gtk_context_get ();
163 ca_context_cancel (c, entry->sound_id);
165 DEBUG ("Play sound \"%s\" (%s)",
166 entry->event_ca_id,
167 entry->event_ca_description);
169 if (ca_proplist_create (&p) < 0)
170 goto failed;
172 if (ca_proplist_sets (p, CA_PROP_EVENT_ID, entry->event_ca_id) < 0)
173 goto failed;
175 if (ca_proplist_sets (p, CA_PROP_EVENT_DESCRIPTION,
176 gettext (entry->event_ca_description)) < 0)
177 goto failed;
179 if (ca_gtk_proplist_set_for_widget (p, widget) < 0)
180 goto failed;
182 ca_context_play_full (ca_gtk_context_get (), entry->sound_id, p, callback,
183 user_data);
185 ca_proplist_destroy (p);
187 return TRUE;
189 failed:
190 if (p != NULL)
191 ca_proplist_destroy (p);
193 return FALSE;
197 * empathy_sound_play_full:
198 * @widget: The #GtkWidget from which the sound is originating.
199 * @sound_id: The #EmpathySound to play.
200 * @callback: The #ca_finish_callback_t function that will be called when the
201 * sound has stopped playing.
202 * @user_data: user data to pass to the function.
204 * Plays a sound.
206 * Returns %TRUE if the sound has successfully started playing, otherwise
207 * returning %FALSE and @callback won't be called.
209 * This function returns %FALSE if the sound is already playing in loop using
210 * %empathy_sound_start_playing.
212 * This function returns %FALSE if the sound is disabled in empathy preferences.
214 * Return value: %TRUE if the sound has successfully started playing, %FALSE
215 * otherwise.
217 gboolean
218 empathy_sound_play_full (GtkWidget *widget, EmpathySound sound_id,
219 ca_finish_callback_t callback, gpointer user_data)
221 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
222 g_return_val_if_fail (sound_id < LAST_EMPATHY_SOUND, FALSE);
224 if (!empathy_sound_pref_is_enabled (sound_id))
225 return FALSE;
227 /* The sound might already be playing repeatedly. If it's the case, we
228 * immediadely return since there's no need to make it play again */
229 if (repeating_sounds != NULL &&
230 g_hash_table_lookup (repeating_sounds, GINT_TO_POINTER (sound_id)) != NULL)
231 return FALSE;
233 return empathy_sound_play_internal (widget, sound_id, callback, user_data);
237 * empathy_sound_play:
238 * @widget: The #GtkWidget from which the sound is originating.
239 * @sound_id: The #EmpathySound to play.
241 * Plays a sound. See %empathy_sound_play_full for details.'
243 * Return value: %TRUE if the sound has successfully started playing, %FALSE
244 * otherwise.
246 gboolean
247 empathy_sound_play (GtkWidget *widget, EmpathySound sound_id)
249 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
250 g_return_val_if_fail (sound_id < LAST_EMPATHY_SOUND, FALSE);
252 return empathy_sound_play_full (widget, sound_id, NULL, NULL);
255 static void playing_finished_cb (ca_context *c, guint id, int error_code,
256 gpointer user_data);
258 static gboolean
259 playing_timeout_cb (gpointer data)
261 EmpathyRepeatableSound *repeatable_sound = data;
262 gboolean playing;
264 repeatable_sound->replay_timeout_id = 0;
266 playing = empathy_sound_play_internal (repeatable_sound->widget,
267 repeatable_sound->sound_id, playing_finished_cb, data);
269 if (!playing)
271 DEBUG ("Failed to replay sound, stop repeating");
272 g_hash_table_remove (repeating_sounds,
273 GINT_TO_POINTER (repeatable_sound->sound_id));
276 return FALSE;
279 static void
280 playing_finished_cb (ca_context *c, guint id, int error_code,
281 gpointer user_data)
283 EmpathyRepeatableSound *repeatable_sound = user_data;
285 if (error_code != CA_SUCCESS)
287 DEBUG ("Error: %s", ca_strerror (error_code));
288 g_hash_table_remove (repeating_sounds,
289 GINT_TO_POINTER (repeatable_sound->sound_id));
290 return;
293 repeatable_sound->replay_timeout_id = g_timeout_add (
294 repeatable_sound->play_interval, playing_timeout_cb, user_data);
297 static void
298 empathy_sound_widget_destroyed_cb (GtkWidget *widget, gpointer user_data)
300 EmpathyRepeatableSound *repeatable_sound = user_data;
302 /* The sound must be stopped... If it is waiting for replay, remove
303 * it from hash table to cancel. Otherwise playing_finished_cb will be
304 * called with an error. */
305 if (repeatable_sound->replay_timeout_id != 0)
307 g_hash_table_remove (repeating_sounds,
308 GINT_TO_POINTER (repeatable_sound->sound_id));
312 static void
313 repeating_sounds_item_delete (gpointer data)
315 EmpathyRepeatableSound *repeatable_sound = data;
317 if (repeatable_sound->replay_timeout_id != 0)
318 g_source_remove (repeatable_sound->replay_timeout_id);
320 g_signal_handlers_disconnect_by_func (repeatable_sound->widget,
321 empathy_sound_widget_destroyed_cb, repeatable_sound);
323 g_slice_free (EmpathyRepeatableSound, repeatable_sound);
327 * empathy_sound_start_playing:
328 * @widget: The #GtkWidget from which the sound is originating.
329 * @sound_id: The #EmpathySound to play.
330 * @timeout_before_replay: The amount of time, in milliseconds, between two
331 * consecutive play.
333 * Start playing a sound in loop. To stop the sound, call empathy_call_stop ()
334 * by passing it the same @sound_id. Note that if you start playing a sound
335 * multiple times, you'll have to call %empathy_sound_stop the same number of
336 * times.
338 * Return value: %TRUE if the sound has successfully started playing.
340 gboolean
341 empathy_sound_start_playing (GtkWidget *widget, EmpathySound sound_id,
342 guint timeout_before_replay)
344 EmpathyRepeatableSound *repeatable_sound;
345 gboolean playing = FALSE;
347 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
348 g_return_val_if_fail (sound_id < LAST_EMPATHY_SOUND, FALSE);
350 if (!empathy_sound_pref_is_enabled (sound_id))
351 return FALSE;
353 if (repeating_sounds == NULL)
355 repeating_sounds = g_hash_table_new_full (g_direct_hash, g_direct_equal,
356 NULL, repeating_sounds_item_delete);
358 else if (g_hash_table_lookup (repeating_sounds,
359 GINT_TO_POINTER (sound_id)) != NULL)
361 /* The sound is already playing in loop. No need to continue. */
362 return FALSE;
365 repeatable_sound = g_slice_new0 (EmpathyRepeatableSound);
366 repeatable_sound->widget = widget;
367 repeatable_sound->sound_id = sound_id;
368 repeatable_sound->play_interval = timeout_before_replay;
369 repeatable_sound->replay_timeout_id = 0;
371 g_hash_table_insert (repeating_sounds, GINT_TO_POINTER (sound_id),
372 repeatable_sound);
374 g_signal_connect (G_OBJECT (widget), "destroy",
375 G_CALLBACK (empathy_sound_widget_destroyed_cb),
376 repeatable_sound);
378 playing = empathy_sound_play_internal (widget, sound_id, playing_finished_cb,
379 repeatable_sound);
381 if (!playing)
382 g_hash_table_remove (repeating_sounds, GINT_TO_POINTER (sound_id));
384 return playing;