1 /*
2 * @file gtksound.c GTK+ Sound
3 * @ingroup pidgin
4 */
5
6 /* pidgin
7 *
8 * Pidgin is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
25 *
26 */
27 #include "internal.h"
28 #include "pidgin.h"
29
30 #ifdef _WIN32
31 #include <windows.h>
32 #include <mmsystem.h>
33 #endif
34
35 #ifdef USE_GSTREAMER
36 # include <gst/gst.h>
37 #endif /* USE_GSTREAMER */
38
39 #include "debug.h"
40 #include "notify.h"
41 #include "prefs.h"
42 #include "sound.h"
43 #include "sound-theme.h"
44 #include "theme-manager.h"
45 #include "util.h"
46
47 #include "gtkconv.h"
48 #include "gtksound.h"
49
50 struct pidgin_sound_event {
51 char *label;
52 char *pref;
53 char *def;
54 };
55
56 static guint mute_login_sounds_timeout = 0;
57 static gboolean mute_login_sounds = FALSE;
58
59 #ifdef USE_GSTREAMER
60 static gboolean gst_init_failed;
61 #endif /* USE_GSTREAMER */
62
63 static const struct pidgin_sound_event sounds[PURPLE_NUM_SOUNDS] = {
64 {N_("Buddy logs in"), "login", "login.wav"},
65 {N_("Buddy logs out"), "logout", "logout.wav"},
66 {N_("Message received"), "im_recv", "receive.wav"},
67 {N_("Message received begins conversation"), "first_im_recv", "receive.wav"},
68 {N_("Message sent"), "send_im", "send.wav"},
69 {N_("Person enters chat"), "join_chat", "login.wav"},
70 {N_("Person leaves chat"), "left_chat", "logout.wav"},
71 {N_("You talk in chat"), "send_chat_msg", "send.wav"},
72 {N_("Others talk in chat"), "chat_msg_recv", "receive.wav"},
73 /* this isn't a terminator, it's the buddy pounce default sound event ;-) */
74 {NULL, "pounce_default", "alert.wav"},
75 {N_("Someone says your username in chat"), "nick_said", "alert.wav"},
76 {N_("Attention received"), "got_attention", "alert.wav"}
77 };
78
79 static gboolean
unmute_login_sounds_cb(gpointer data)80 unmute_login_sounds_cb(gpointer data)
81 {
82 mute_login_sounds = FALSE;
83 mute_login_sounds_timeout = 0;
84 return FALSE;
85 }
86
87 static gboolean
chat_nick_matches_name(PurpleConversation * conv,const char * aname)88 chat_nick_matches_name(PurpleConversation *conv, const char *aname)
89 {
90 PurpleConvChat *chat = NULL;
91 char *nick = NULL;
92 char *name = NULL;
93 gboolean ret = FALSE;
94 chat = purple_conversation_get_chat_data(conv);
95
96 if (chat==NULL)
97 return ret;
98
99 nick = g_strdup(purple_normalize(conv->account, chat->nick));
100 name = g_strdup(purple_normalize(conv->account, aname));
101
102 if (g_utf8_collate(nick, name) == 0)
103 ret = TRUE;
104
105 g_free(nick);
106 g_free(name);
107
108 return ret;
109 }
110
111 /*
112 * play a sound event for a conversation, honoring make_sound flag
113 * of conversation and checking for focus if conv_focus pref is set
114 */
115 static void
play_conv_event(PurpleConversation * conv,PurpleSoundEventID event)116 play_conv_event(PurpleConversation *conv, PurpleSoundEventID event)
117 {
118 /* If we should not play the sound for some reason, then exit early */
119 if (conv != NULL && PIDGIN_IS_PIDGIN_CONVERSATION(conv))
120 {
121 PidginConversation *gtkconv;
122 gboolean has_focus;
123
124 gtkconv = PIDGIN_CONVERSATION(conv);
125 has_focus = purple_conversation_has_focus(conv);
126
127 if (!gtkconv->make_sound ||
128 (has_focus && !purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/sound/conv_focus")))
129 {
130 return;
131 }
132 }
133
134 purple_sound_play_event(event, conv ? purple_conversation_get_account(conv) : NULL);
135 }
136
137 static void
buddy_state_cb(PurpleBuddy * buddy,PurpleSoundEventID event)138 buddy_state_cb(PurpleBuddy *buddy, PurpleSoundEventID event)
139 {
140 purple_sound_play_event(event, purple_buddy_get_account(buddy));
141 }
142
143 static void
im_msg_received_cb(PurpleAccount * account,char * sender,char * message,PurpleConversation * conv,PurpleMessageFlags flags,PurpleSoundEventID event)144 im_msg_received_cb(PurpleAccount *account, char *sender,
145 char *message, PurpleConversation *conv,
146 PurpleMessageFlags flags, PurpleSoundEventID event)
147 {
148 if (flags & PURPLE_MESSAGE_DELAYED || flags & PURPLE_MESSAGE_NOTIFY)
149 return;
150
151 if (conv==NULL)
152 purple_sound_play_event(PURPLE_SOUND_FIRST_RECEIVE, account);
153 else
154 play_conv_event(conv, event);
155 }
156
157 static void
im_msg_sent_cb(PurpleAccount * account,const char * receiver,const char * message,PurpleSoundEventID event)158 im_msg_sent_cb(PurpleAccount *account, const char *receiver,
159 const char *message, PurpleSoundEventID event)
160 {
161 PurpleConversation *conv = purple_find_conversation_with_account(
162 PURPLE_CONV_TYPE_IM, receiver, account);
163 play_conv_event(conv, event);
164 }
165
166 static void
chat_buddy_join_cb(PurpleConversation * conv,const char * name,PurpleConvChatBuddyFlags flags,gboolean new_arrival,PurpleSoundEventID event)167 chat_buddy_join_cb(PurpleConversation *conv, const char *name,
168 PurpleConvChatBuddyFlags flags, gboolean new_arrival,
169 PurpleSoundEventID event)
170 {
171 if (new_arrival && !chat_nick_matches_name(conv, name))
172 play_conv_event(conv, event);
173 }
174
175 static void
chat_buddy_left_cb(PurpleConversation * conv,const char * name,const char * reason,PurpleSoundEventID event)176 chat_buddy_left_cb(PurpleConversation *conv, const char *name,
177 const char *reason, PurpleSoundEventID event)
178 {
179 if (!chat_nick_matches_name(conv, name))
180 play_conv_event(conv, event);
181 }
182
183 static void
chat_msg_sent_cb(PurpleAccount * account,const char * message,int id,PurpleSoundEventID event)184 chat_msg_sent_cb(PurpleAccount *account, const char *message,
185 int id, PurpleSoundEventID event)
186 {
187 PurpleConnection *conn = purple_account_get_connection(account);
188 PurpleConversation *conv = NULL;
189
190 if (conn!=NULL)
191 conv = purple_find_chat(conn,id);
192
193 play_conv_event(conv, event);
194 }
195
196 static void
chat_msg_received_cb(PurpleAccount * account,char * sender,char * message,PurpleConversation * conv,PurpleMessageFlags flags,PurpleSoundEventID event)197 chat_msg_received_cb(PurpleAccount *account, char *sender,
198 char *message, PurpleConversation *conv,
199 PurpleMessageFlags flags, PurpleSoundEventID event)
200 {
201 PurpleConvChat *chat;
202
203 if (flags & PURPLE_MESSAGE_DELAYED || flags & PURPLE_MESSAGE_NOTIFY)
204 return;
205
206 chat = purple_conversation_get_chat_data(conv);
207 g_return_if_fail(chat != NULL);
208
209 if (purple_conv_chat_is_user_ignored(chat, sender))
210 return;
211
212 if (chat_nick_matches_name(conv, sender))
213 return;
214
215 if (flags & PURPLE_MESSAGE_NICK || purple_utf8_has_word(message, chat->nick))
216 /* This isn't quite right; if you have the PURPLE_SOUND_CHAT_NICK event disabled
217 * and the PURPLE_SOUND_CHAT_SAY event enabled, you won't get a sound at all */
218 play_conv_event(conv, PURPLE_SOUND_CHAT_NICK);
219 else
220 play_conv_event(conv, event);
221 }
222
223 static void
got_attention_cb(PurpleAccount * account,const char * who,PurpleConversation * conv,guint type,PurpleSoundEventID event)224 got_attention_cb(PurpleAccount *account, const char *who,
225 PurpleConversation *conv, guint type, PurpleSoundEventID event)
226 {
227 play_conv_event(conv, event);
228 }
229
230 /*
231 * We mute sounds for the 10 seconds after you log in so that
232 * you don't get flooded with sounds when the blist shows all
233 * your buddies logging in.
234 */
235 static void
account_signon_cb(PurpleConnection * gc,gpointer data)236 account_signon_cb(PurpleConnection *gc, gpointer data)
237 {
238 if (mute_login_sounds_timeout != 0)
239 purple_timeout_remove(mute_login_sounds_timeout);
240 mute_login_sounds = TRUE;
241 mute_login_sounds_timeout = purple_timeout_add_seconds(10, unmute_login_sounds_cb, NULL);
242 }
243
244 const char *
pidgin_sound_get_event_option(PurpleSoundEventID event)245 pidgin_sound_get_event_option(PurpleSoundEventID event)
246 {
247 if(event >= PURPLE_NUM_SOUNDS)
248 return 0;
249
250 return sounds[event].pref;
251 }
252
253 const char *
pidgin_sound_get_event_label(PurpleSoundEventID event)254 pidgin_sound_get_event_label(PurpleSoundEventID event)
255 {
256 if(event >= PURPLE_NUM_SOUNDS)
257 return NULL;
258
259 return sounds[event].label;
260 }
261
262 void *
pidgin_sound_get_handle()263 pidgin_sound_get_handle()
264 {
265 static int handle;
266
267 return &handle;
268 }
269
270 static void
pidgin_sound_init(void)271 pidgin_sound_init(void)
272 {
273 void *gtk_sound_handle = pidgin_sound_get_handle();
274 void *blist_handle = purple_blist_get_handle();
275 void *conv_handle = purple_conversations_get_handle();
276 #ifdef USE_GSTREAMER
277 GError *error = NULL;
278 #endif
279
280 purple_signal_connect(purple_connections_get_handle(), "signed-on",
281 gtk_sound_handle, PURPLE_CALLBACK(account_signon_cb),
282 NULL);
283
284 purple_prefs_add_none(PIDGIN_PREFS_ROOT "/sound");
285 purple_prefs_add_none(PIDGIN_PREFS_ROOT "/sound/enabled");
286 purple_prefs_add_none(PIDGIN_PREFS_ROOT "/sound/file");
287 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/login", TRUE);
288 purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/login", "");
289 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/logout", TRUE);
290 purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/logout", "");
291 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/im_recv", TRUE);
292 purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/im_recv", "");
293 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/first_im_recv", FALSE);
294 purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/first_im_recv", "");
295 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/send_im", TRUE);
296 purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/send_im", "");
297 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/join_chat", FALSE);
298 purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/join_chat", "");
299 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/left_chat", FALSE);
300 purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/left_chat", "");
301 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/send_chat_msg", FALSE);
302 purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/send_chat_msg", "");
303 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/chat_msg_recv", FALSE);
304 purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/chat_msg_recv", "");
305 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/nick_said", FALSE);
306 purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/nick_said", "");
307 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/pounce_default", TRUE);
308 purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/pounce_default", "");
309 purple_prefs_add_string(PIDGIN_PREFS_ROOT "/sound/theme", "");
310 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/sent_attention", TRUE);
311 purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/sent_attention", "");
312 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/got_attention", TRUE);
313 purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/got_attention", "");
314 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/conv_focus", TRUE);
315 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/mute", FALSE);
316 purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/command", "");
317 purple_prefs_add_string(PIDGIN_PREFS_ROOT "/sound/method", "automatic");
318 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/sound/volume", 50);
319
320 #ifdef USE_GSTREAMER
321 purple_debug_info("sound", "Initializing sound output drivers.\n");
322 #ifdef GST_CAN_DISABLE_FORKING
323 gst_registry_fork_set_enabled (FALSE);
324 #endif
325 if ((gst_init_failed = !gst_init_check(NULL, NULL, &error))) {
326 purple_notify_error(NULL, _("GStreamer Failure"),
327 _("GStreamer failed to initialize."),
328 error ? error->message : "");
329 if (error) {
330 g_error_free(error);
331 error = NULL;
332 }
333 }
334 #endif /* USE_GSTREAMER */
335
336 purple_signal_connect(blist_handle, "buddy-signed-on",
337 gtk_sound_handle, PURPLE_CALLBACK(buddy_state_cb),
338 GINT_TO_POINTER(PURPLE_SOUND_BUDDY_ARRIVE));
339 purple_signal_connect(blist_handle, "buddy-signed-off",
340 gtk_sound_handle, PURPLE_CALLBACK(buddy_state_cb),
341 GINT_TO_POINTER(PURPLE_SOUND_BUDDY_LEAVE));
342 purple_signal_connect(conv_handle, "received-im-msg",
343 gtk_sound_handle, PURPLE_CALLBACK(im_msg_received_cb),
344 GINT_TO_POINTER(PURPLE_SOUND_RECEIVE));
345 purple_signal_connect(conv_handle, "sent-im-msg",
346 gtk_sound_handle, PURPLE_CALLBACK(im_msg_sent_cb),
347 GINT_TO_POINTER(PURPLE_SOUND_SEND));
348 purple_signal_connect(conv_handle, "chat-buddy-joined",
349 gtk_sound_handle, PURPLE_CALLBACK(chat_buddy_join_cb),
350 GINT_TO_POINTER(PURPLE_SOUND_CHAT_JOIN));
351 purple_signal_connect(conv_handle, "chat-buddy-left",
352 gtk_sound_handle, PURPLE_CALLBACK(chat_buddy_left_cb),
353 GINT_TO_POINTER(PURPLE_SOUND_CHAT_LEAVE));
354 purple_signal_connect(conv_handle, "sent-chat-msg",
355 gtk_sound_handle, PURPLE_CALLBACK(chat_msg_sent_cb),
356 GINT_TO_POINTER(PURPLE_SOUND_CHAT_YOU_SAY));
357 purple_signal_connect(conv_handle, "received-chat-msg",
358 gtk_sound_handle, PURPLE_CALLBACK(chat_msg_received_cb),
359 GINT_TO_POINTER(PURPLE_SOUND_CHAT_SAY));
360 purple_signal_connect(conv_handle, "got-attention", gtk_sound_handle,
361 PURPLE_CALLBACK(got_attention_cb),
362 GINT_TO_POINTER(PURPLE_SOUND_GOT_ATTENTION));
363 /* for the time being, don't handle sent-attention here, since playing a
364 sound would result induplicate sounds. And fixing that would require changing the
365 conversation signal for msg-recv */
366 }
367
368 static void
pidgin_sound_uninit(void)369 pidgin_sound_uninit(void)
370 {
371 #ifdef USE_GSTREAMER
372 if (!gst_init_failed)
373 gst_deinit();
374 #endif
375
376 purple_signals_disconnect_by_handle(pidgin_sound_get_handle());
377 }
378
379 #ifdef USE_GSTREAMER
380 static gboolean
bus_call(GstBus * bus,GstMessage * msg,gpointer data)381 bus_call (GstBus *bus,
382 GstMessage *msg,
383 gpointer data)
384 {
385 GstElement *play = data;
386 GError *err = NULL;
387
388 switch (GST_MESSAGE_TYPE (msg)) {
389 case GST_MESSAGE_ERROR:
390 gst_message_parse_error(msg, &err, NULL);
391 purple_debug_error("gstreamer", "%s\n", err->message);
392 g_error_free(err);
393 /* fall-through and clean up */
394 case GST_MESSAGE_EOS:
395 gst_element_set_state(play, GST_STATE_NULL);
396 gst_object_unref(GST_OBJECT(play));
397 return FALSE;
398 break;
399 case GST_MESSAGE_WARNING:
400 gst_message_parse_warning(msg, &err, NULL);
401 purple_debug_warning("gstreamer", "%s\n", err->message);
402 g_error_free(err);
403 break;
404 default:
405 break;
406 }
407 return TRUE;
408 }
409 #endif
410
411 #ifndef _WIN32
412 static gboolean
expire_old_child(gpointer data)413 expire_old_child(gpointer data)
414 {
415 pid_t pid = GPOINTER_TO_INT(data);
416
417 if (waitpid(pid, NULL, WNOHANG | WUNTRACED) < 0) {
418 if (errno == ECHILD)
419 return FALSE;
420 else
421 purple_debug_warning("gtksound", "Child is ill, pid: %d (%s)\n", pid, strerror(errno));
422 }
423
424 if (kill(pid, SIGKILL) < 0)
425 purple_debug_error("gtksound", "Killing process %d failed (%s)\n", pid, strerror(errno));
426
427 return FALSE;
428 }
429 #endif
430
431 static void
pidgin_sound_play_file(const char * filename)432 pidgin_sound_play_file(const char *filename)
433 {
434 const char *method;
435 #ifdef USE_GSTREAMER
436 float volume;
437 char *uri;
438 GstElement *sink = NULL;
439 GstElement *play = NULL;
440 GstBus *bus = NULL;
441 #endif
442
443 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/sound/mute"))
444 return;
445
446 method = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method");
447
448 if (purple_strequal(method, "none")) {
449 return;
450 } else if (purple_strequal(method, "beep")) {
451 gdk_beep();
452 return;
453 }
454
455 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
456 purple_debug_error("gtksound", "sound file (%s) does not exist.\n", filename);
457 return;
458 }
459
460 #ifndef _WIN32
461 if (purple_strequal(method, "custom")) {
462 const char *sound_cmd;
463 char *command;
464 char *esc_filename;
465 char **argv = NULL;
466 GError *error = NULL;
467 GPid pid;
468
469 sound_cmd = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/sound/command");
470
471 if (!sound_cmd || *sound_cmd == '\0') {
472 purple_debug_error("gtksound",
473 "'Command' sound method has been chosen, "
474 "but no command has been set.\n");
475 return;
476 }
477
478 esc_filename = g_shell_quote(filename);
479
480 if(strstr(sound_cmd, "%s"))
481 command = purple_strreplace(sound_cmd, "%s", esc_filename);
482 else
483 command = g_strdup_printf("%s %s", sound_cmd, esc_filename);
484
485 if (!g_shell_parse_argv(command, NULL, &argv, &error)) {
486 purple_debug_error("gtksound", "error parsing command %s (%s)\n",
487 command, error->message);
488 g_error_free(error);
489 g_free(esc_filename);
490 g_free(command);
491 return;
492 }
493
494 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
495 NULL, NULL, &pid, &error)) {
496 purple_debug_error("gtksound", "sound command could not be launched: %s\n",
497 error->message);
498 g_error_free(error);
499 } else {
500 purple_timeout_add_seconds(15, expire_old_child, GINT_TO_POINTER(pid));
501 }
502
503 g_strfreev(argv);
504 g_free(esc_filename);
505 g_free(command);
506 return;
507 }
508 #endif /* _WIN32 */
509
510 #ifdef USE_GSTREAMER
511 if (gst_init_failed) /* Perhaps do gdk_beep instead? */
512 return;
513 volume = (float)(CLAMP(purple_prefs_get_int(PIDGIN_PREFS_ROOT "/sound/volume"),0,100)) / 50;
514 if (purple_strequal(method, "automatic")) {
515 sink = gst_element_factory_make("gconfaudiosink", "sink");
516 }
517 #ifndef _WIN32
518 else if (purple_strequal(method, "esd")) {
519 sink = gst_element_factory_make("esdsink", "sink");
520 } else if (purple_strequal(method, "alsa")) {
521 sink = gst_element_factory_make("alsasink", "sink");
522 }
523 #endif
524 else {
525 purple_debug_error("sound", "Unknown sound method '%s'\n", method);
526 return;
527 }
528
529 if (!purple_strequal(method, "automatic") && !sink) {
530 purple_debug_error("sound", "Unable to create GStreamer audiosink.\n");
531 return;
532 }
533
534 play = gst_element_factory_make("playbin", "play");
535
536 if (play == NULL) {
537 return;
538 }
539
540 uri = g_strdup_printf("file://%s", filename);
541
542 g_object_set(G_OBJECT(play), "uri", uri,
543 "volume", volume,
544 "audio-sink", sink, NULL);
545
546 bus = gst_pipeline_get_bus(GST_PIPELINE(play));
547 gst_bus_add_watch(bus, bus_call, play);
548
549 gst_element_set_state(play, GST_STATE_PLAYING);
550
551 gst_object_unref(bus);
552 g_free(uri);
553
554 #else /* #ifdef USE_GSTREAMER */
555
556 #ifndef _WIN32
557 gdk_beep();
558 #else /* _WIN32 */
559 purple_debug_info("sound", "Playing %s\n", filename);
560
561 {
562 wchar_t *wc_filename = g_utf8_to_utf16(filename,
563 -1, NULL, NULL, NULL);
564 if (!PlaySoundW(wc_filename, NULL, SND_ASYNC | SND_FILENAME))
565 purple_debug(PURPLE_DEBUG_ERROR, "sound", "Error playing sound.\n");
566 g_free(wc_filename);
567 }
568 #endif /* _WIN32 */
569
570 #endif /* USE_GSTREAMER */
571 }
572
573 static void
pidgin_sound_play_event(PurpleSoundEventID event)574 pidgin_sound_play_event(PurpleSoundEventID event)
575 {
576 char *enable_pref;
577 char *file_pref;
578 const char *theme_name;
579 PurpleSoundTheme *theme;
580
581 if ((event == PURPLE_SOUND_BUDDY_ARRIVE) && mute_login_sounds)
582 return;
583
584 if (event >= PURPLE_NUM_SOUNDS) {
585 purple_debug_error("sound", "got request for unknown sound: %d\n", event);
586 return;
587 }
588
589 enable_pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/enabled/%s",
590 sounds[event].pref);
591 file_pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s", sounds[event].pref);
592
593 /* check NULL for sounds that don't have an option, ie buddy pounce */
594 if (purple_prefs_get_bool(enable_pref)) {
595 char *filename = g_strdup(purple_prefs_get_path(file_pref));
596 theme_name = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme");
597
598 if (theme_name && *theme_name && (!filename || !*filename)) {
599 /* Use theme */
600 g_free(filename);
601
602 theme = PURPLE_SOUND_THEME(purple_theme_manager_find_theme(theme_name, "sound"));
603 filename = purple_sound_theme_get_file_full(theme, sounds[event].pref);
604
605 if(!g_file_test(filename, G_FILE_TEST_IS_REGULAR)){ /* Use Default sound in this case */
606 purple_debug_error("sound", "The file: (%s) %s\n from theme: %s, was not found or wasn't readable\n",
607 sounds[event].pref, filename, theme_name);
608 g_free(filename);
609 filename = NULL;
610 }
611 }
612
613 if (!filename || !strlen(filename)) { /* Use Default sounds */
614 g_free(filename);
615
616 /* XXX Consider creating a constant for "sounds/purple" to be shared with Finch */
617 filename = g_build_filename(DATADIR, "sounds", "purple", sounds[event].def, NULL);
618 }
619
620 purple_sound_play_file(filename, NULL);
621
622 g_free(filename);
623 }
624
625 g_free(enable_pref);
626 g_free(file_pref);
627 }
628
629 gboolean
pidgin_sound_is_customized(void)630 pidgin_sound_is_customized(void)
631 {
632 gint i;
633 gchar *path;
634 const char *file;
635
636 for (i = 0; i < PURPLE_NUM_SOUNDS; i++) {
637 path = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s", sounds[i].pref);
638 file = purple_prefs_get_path(path);
639 g_free(path);
640
641 if (file && file[0] != '\0')
642 return TRUE;
643 }
644
645 return FALSE;
646
647 }
648
649 static PurpleSoundUiOps sound_ui_ops =
650 {
651 pidgin_sound_init,
652 pidgin_sound_uninit,
653 pidgin_sound_play_file,
654 pidgin_sound_play_event,
655 NULL,
656 NULL,
657 NULL,
658 NULL
659 };
660
661 PurpleSoundUiOps *
pidgin_sound_get_ui_ops(void)662 pidgin_sound_get_ui_ops(void)
663 {
664 return &sound_ui_ops;
665 }
666