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