Mercurial > pidgin.yaz
annotate src/gtksound.c @ 10284:f776e117c17b
[gaim-migrate @ 11454]
Several MSN memory leaks identified and fixed by Miah Gregory and Felipe
Contreras, plus my own fix for bug 1075347.
As normal, thank them for fixes, blame me for breakages.
Did I mention the new MSN icon? It rocks!
committer: Tailor Script <tailor@pidgin.im>
author | Stu Tomlinson <stu@nosnilmot.com> |
---|---|
date | Wed, 01 Dec 2004 02:30:47 +0000 |
parents | 5dd9c1df6459 |
children | ec140184437b |
rev | line source |
---|---|
5684 | 1 /* |
2 * gaim | |
3 * | |
8046 | 4 * Gaim is the legal property of its developers, whose names are too numerous |
5 * to list here. Please refer to the COPYRIGHT file distributed with this | |
6 * source distribution. | |
5684 | 7 * |
8 * This program is free software; you can redistribute it and/or modify | |
9 * it under the terms of the GNU General Public License as published by | |
10 * the Free Software Foundation; either version 2 of the License, or | |
11 * (at your option) any later version. | |
12 * | |
13 * This program is distributed in the hope that it will be useful, | |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 * GNU General Public License for more details. | |
17 * | |
18 * You should have received a copy of the GNU General Public License | |
19 * along with this program; if not, write to the Free Software | |
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 * | |
22 */ | |
9791 | 23 #include "internal.h" |
24 #include "gtkgaim.h" | |
5684 | 25 |
5872
059d95c67cda
[gaim-migrate @ 6304]
Christian Hammond <chipx86@chipx86.com>
parents:
5794
diff
changeset
|
26 #ifdef _WIN32 |
5684 | 27 #include <windows.h> |
28 #include <mmsystem.h> | |
29 #endif | |
30 | |
31 #ifdef USE_AO | |
5872
059d95c67cda
[gaim-migrate @ 6304]
Christian Hammond <chipx86@chipx86.com>
parents:
5794
diff
changeset
|
32 # include <ao/ao.h> |
059d95c67cda
[gaim-migrate @ 6304]
Christian Hammond <chipx86@chipx86.com>
parents:
5794
diff
changeset
|
33 # include <audiofile.h> |
5684 | 34 #endif /* USE_AO */ |
35 | |
36 #ifdef USE_NAS_AUDIO | |
5872
059d95c67cda
[gaim-migrate @ 6304]
Christian Hammond <chipx86@chipx86.com>
parents:
5794
diff
changeset
|
37 # include <audio/audiolib.h> |
059d95c67cda
[gaim-migrate @ 6304]
Christian Hammond <chipx86@chipx86.com>
parents:
5794
diff
changeset
|
38 # include <audio/soundlib.h> |
5684 | 39 #endif /* USE_NAS_AUDIO */ |
40 | |
5872
059d95c67cda
[gaim-migrate @ 6304]
Christian Hammond <chipx86@chipx86.com>
parents:
5794
diff
changeset
|
41 #include "debug.h" |
059d95c67cda
[gaim-migrate @ 6304]
Christian Hammond <chipx86@chipx86.com>
parents:
5794
diff
changeset
|
42 #include "notify.h" |
5684 | 43 #include "prefs.h" |
5872
059d95c67cda
[gaim-migrate @ 6304]
Christian Hammond <chipx86@chipx86.com>
parents:
5794
diff
changeset
|
44 #include "sound.h" |
7465 | 45 #include "util.h" |
5684 | 46 |
5872
059d95c67cda
[gaim-migrate @ 6304]
Christian Hammond <chipx86@chipx86.com>
parents:
5794
diff
changeset
|
47 #include "gtksound.h" |
5684 | 48 |
49 | |
50 struct gaim_sound_event { | |
51 char *label; | |
52 char *pref; | |
53 char *def; | |
54 }; | |
55 | |
10074 | 56 #define PLAY_SOUND_TIMEOUT 15000 |
5684 | 57 |
58 static gboolean mute_login_sounds = FALSE; | |
6199 | 59 static gboolean sound_initialized = FALSE; |
5684 | 60 |
61 static struct gaim_sound_event sounds[GAIM_NUM_SOUNDS] = { | |
10158 | 62 {N_("Buddy logs in"), "login", "login.wav"}, |
63 {N_("Buddy logs out"), "logout", "logout.wav"}, | |
5684 | 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"}, | |
10158 | 67 {N_("Person enters chat"), "join_chat", "login.wav"}, |
68 {N_("Person leaves chat"), "left_chat", "logout.wav"}, | |
5684 | 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 ;-) */ | |
10158 | 72 {NULL, "pounce_default", "alert.wav"}, |
73 {N_("Someone says your name in chat"), "nick_said", "alert.wav"} | |
5684 | 74 }; |
75 | |
76 #ifdef USE_AO | |
77 static int ao_driver = -1; | |
78 #endif /* USE_AO */ | |
79 | |
5794
5e93fc46d1af
[gaim-migrate @ 6219]
Christian Hammond <chipx86@chipx86.com>
parents:
5684
diff
changeset
|
80 static void _pref_sound_method_changed(const char *name, GaimPrefType type, |
5684 | 81 gpointer val, gpointer data); |
82 | |
83 static void gaim_gtk_sound_init(void) | |
84 { | |
85 gaim_prefs_add_none("/gaim/gtk/sound"); | |
86 gaim_prefs_add_none("/gaim/gtk/sound/enabled"); | |
87 gaim_prefs_add_none("/gaim/gtk/sound/file"); | |
88 gaim_prefs_add_bool("/gaim/gtk/sound/enabled/login", TRUE); | |
89 gaim_prefs_add_string("/gaim/gtk/sound/file/login", ""); | |
90 gaim_prefs_add_bool("/gaim/gtk/sound/enabled/logout", TRUE); | |
91 gaim_prefs_add_string("/gaim/gtk/sound/file/logout", ""); | |
92 gaim_prefs_add_bool("/gaim/gtk/sound/enabled/im_recv", TRUE); | |
93 gaim_prefs_add_string("/gaim/gtk/sound/file/im_recv", ""); | |
94 gaim_prefs_add_bool("/gaim/gtk/sound/enabled/first_im_recv", FALSE); | |
95 gaim_prefs_add_string("/gaim/gtk/sound/file/first_im_recv", ""); | |
96 gaim_prefs_add_bool("/gaim/gtk/sound/enabled/send_im", TRUE); | |
97 gaim_prefs_add_string("/gaim/gtk/sound/file/send_im", ""); | |
98 gaim_prefs_add_bool("/gaim/gtk/sound/enabled/join_chat", FALSE); | |
99 gaim_prefs_add_string("/gaim/gtk/sound/file/join_chat", ""); | |
100 gaim_prefs_add_bool("/gaim/gtk/sound/enabled/left_chat", FALSE); | |
101 gaim_prefs_add_string("/gaim/gtk/sound/file/left_chat", ""); | |
102 gaim_prefs_add_bool("/gaim/gtk/sound/enabled/send_chat_msg", FALSE); | |
103 gaim_prefs_add_string("/gaim/gtk/sound/file/send_chat_msg", ""); | |
104 gaim_prefs_add_bool("/gaim/gtk/sound/enabled/chat_msg_recv", FALSE); | |
105 gaim_prefs_add_string("/gaim/gtk/sound/file/chat_msg_recv", ""); | |
106 gaim_prefs_add_bool("/gaim/gtk/sound/enabled/nick_said", FALSE); | |
107 gaim_prefs_add_string("/gaim/gtk/sound/file/nick_said", ""); | |
7460
3973a09525b3
[gaim-migrate @ 8073]
Christian Hammond <chipx86@chipx86.com>
parents:
7035
diff
changeset
|
108 gaim_prefs_add_bool("/gaim/gtk/sound/enabled/pounce_default", TRUE); |
3973a09525b3
[gaim-migrate @ 8073]
Christian Hammond <chipx86@chipx86.com>
parents:
7035
diff
changeset
|
109 gaim_prefs_add_string("/gaim/gtk/sound/file/pounce_default", ""); |
8633 | 110 gaim_prefs_add_bool("/gaim/gtk/sound/conv_focus", TRUE); |
10074 | 111 gaim_prefs_add_bool("/gaim/gtk/sound/mute", FALSE); |
5684 | 112 gaim_prefs_add_string("/gaim/gtk/sound/command", ""); |
113 gaim_prefs_add_string("/gaim/gtk/sound/method", "automatic"); | |
114 | |
115 #ifdef USE_AO | |
116 gaim_debug(GAIM_DEBUG_INFO, "sound", | |
117 "Initializing sound output drivers.\n"); | |
118 ao_initialize(); | |
119 #endif /* USE_AO */ | |
120 | |
10087 | 121 gaim_prefs_connect_callback(gaim_gtk_sound_get_handle(), "/gaim/gtk/sound/method", |
5794
5e93fc46d1af
[gaim-migrate @ 6219]
Christian Hammond <chipx86@chipx86.com>
parents:
5684
diff
changeset
|
122 _pref_sound_method_changed, NULL); |
5684 | 123 } |
124 | |
125 | |
126 static void gaim_gtk_sound_shutdown(void) | |
127 { | |
128 #ifdef USE_AO | |
129 ao_shutdown(); | |
130 #endif | |
6199 | 131 sound_initialized = FALSE; |
5684 | 132 } |
133 | |
10074 | 134 #if defined(USE_NAS_AUDIO) || defined(USE_AO) |
135 gboolean expire_old_child(gpointer data) | |
136 { | |
137 int ret; | |
138 pid_t pid = GPOINTER_TO_INT(data); | |
139 | |
140 ret = waitpid(pid, NULL, WNOHANG | WUNTRACED); | |
141 | |
142 if(ret == 0) { | |
143 if(kill(pid, SIGKILL) < 0) | |
144 gaim_debug_error("gtksound", "Killing process %d failed (%s)\n", pid, strerror(errno)); | |
145 } | |
146 | |
147 return FALSE; /* do not run again */ | |
148 } | |
149 #endif | |
150 | |
5684 | 151 static void gaim_gtk_sound_play_file(const char *filename) |
152 { | |
153 const char *method; | |
154 #if defined(USE_NAS_AUDIO) || defined(USE_AO) | |
155 pid_t pid; | |
156 #ifdef USE_AO | |
157 AFfilehandle file; | |
158 #endif | |
159 #endif | |
160 | |
6199 | 161 if (!sound_initialized) |
162 gaim_prefs_trigger_callback("/gaim/gtk/sound/method"); | |
163 | |
10074 | 164 if (gaim_prefs_get_bool("/gaim/gtk/sound/mute")) |
5684 | 165 return; |
166 | |
167 method = gaim_prefs_get_string("/gaim/gtk/sound/method"); | |
168 | |
10074 | 169 if (!strcmp(method, "none")) { |
170 return; | |
171 } else if (!strcmp(method, "beep")) { | |
5684 | 172 gdk_beep(); |
173 return; | |
174 } | |
175 | |
176 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) { | |
177 char *tmp = g_strdup_printf(_("Unable to play sound because the chosen file (%s) does not exist."), filename); | |
178 gaim_notify_error(NULL, NULL, tmp, NULL); | |
179 g_free(tmp); | |
180 return; | |
181 } | |
182 | |
183 #ifndef _WIN32 | |
184 if (!strcmp(method, "custom")) { | |
185 const char *sound_cmd; | |
186 char *command; | |
187 GError *error = NULL; | |
188 | |
189 sound_cmd = gaim_prefs_get_string("/gaim/gtk/sound/command"); | |
190 | |
191 if (!sound_cmd || *sound_cmd == '\0') { | |
192 gaim_notify_error(NULL, NULL, | |
193 _("Unable to play sound because the " | |
194 "'Command' sound method has been chosen, " | |
195 "but no command has been set."), NULL); | |
196 return; | |
197 } | |
198 | |
7464 | 199 if(strstr(sound_cmd, "%s")) |
200 command = gaim_strreplace(sound_cmd, "%s", filename); | |
201 else | |
202 command = g_strdup_printf("%s %s", sound_cmd, filename); | |
5684 | 203 |
204 if(!g_spawn_command_line_async(command, &error)) { | |
205 char *tmp = g_strdup_printf(_("Unable to play sound because the configured sound command could not be launched: %s"), error->message); | |
206 gaim_notify_error(NULL, NULL, tmp, NULL); | |
207 g_free(tmp); | |
208 g_error_free(error); | |
209 } | |
210 | |
211 g_free(command); | |
212 return; | |
213 } | |
214 #if defined(USE_NAS_AUDIO) || defined(USE_AO) | |
215 pid = fork(); | |
216 if (pid < 0) | |
217 return; | |
218 else if (pid == 0) { | |
219 #ifdef USE_NAS_AUDIO | |
220 if (!strcmp(method, "nas")) { | |
221 if (play_file_nas(filename)) | |
222 _exit(0); | |
223 } | |
224 #endif /* USE_NAS_AUDIO */ | |
225 | |
226 #ifdef USE_AO | |
227 file = afOpenFile(filename, "rb", NULL); | |
228 if(file) { | |
229 ao_device *device; | |
230 ao_sample_format format; | |
231 int in_fmt; | |
232 int bytes_per_frame; | |
233 | |
234 format.rate = afGetRate(file, AF_DEFAULT_TRACK); | |
235 format.channels = afGetChannels(file, AF_DEFAULT_TRACK); | |
236 afGetSampleFormat(file, AF_DEFAULT_TRACK, &in_fmt, | |
237 &format.bits); | |
238 | |
239 /* XXX: libao doesn't seem to like 8-bit sounds, so we'll | |
240 * let libaudiofile make them a bit better for us */ | |
241 if(format.bits == 8) | |
242 format.bits = 16; | |
243 | |
244 afSetVirtualSampleFormat(file, AF_DEFAULT_TRACK, | |
245 AF_SAMPFMT_TWOSCOMP, format.bits); | |
246 | |
247 #if __BYTE_ORDER == __BIG_ENDIAN | |
248 format.byte_format = AO_FMT_BIG; | |
249 afSetVirtualByteOrder(file, AF_DEFAULT_TRACK, | |
250 AF_BYTEORDER_BIGENDIAN); | |
251 #elif __BYTE_ORDER == __LITTLE_ENDIAN | |
252 format.byte_format = AO_FMT_LITTLE; | |
253 afSetVirtualByteOrder(file, AF_DEFAULT_TRACK, | |
254 AF_BYTEORDER_LITTLEENDIAN); | |
255 #endif | |
256 | |
257 bytes_per_frame = format.bits * format.channels / 8; | |
258 | |
259 device = ao_open_live(ao_driver, &format, NULL); | |
260 | |
261 if(device) { | |
262 int frames_read; | |
263 char buf[4096]; | |
264 int buf_frames = sizeof(buf) / bytes_per_frame; | |
265 | |
266 while((frames_read = afReadFrames(file, AF_DEFAULT_TRACK, | |
267 buf, buf_frames))) { | |
268 if(!ao_play(device, buf, frames_read * bytes_per_frame)) | |
269 break; | |
270 } | |
271 ao_close(device); | |
272 } | |
273 afCloseFile(file); | |
274 } | |
275 ao_shutdown(); | |
276 #endif /* USE_AO */ | |
277 _exit(0); | |
10074 | 278 } else { |
279 gaim_timeout_add(PLAY_SOUND_TIMEOUT, expire_old_child, GINT_TO_POINTER(pid)); | |
5684 | 280 } |
281 #else /* USE_NAS_AUDIO || USE_AO */ | |
282 gdk_beep(); | |
283 return; | |
284 #endif /* USE_NAS_AUDIO || USE_AO */ | |
285 #else /* _WIN32 */ | |
286 gaim_debug(GAIM_DEBUG_INFO, "sound", "Playing %s\n", filename); | |
287 | |
288 if (!PlaySound(filename, 0, SND_ASYNC | SND_FILENAME)) | |
289 gaim_debug(GAIM_DEBUG_ERROR, "sound", "Error playing sound.\n"); | |
290 #endif /* _WIN32 */ | |
291 } | |
292 | |
293 static void gaim_gtk_sound_play_event(GaimSoundEventID event) | |
294 { | |
295 char *enable_pref; | |
296 char *file_pref; | |
297 | |
298 if ((event == GAIM_SOUND_BUDDY_ARRIVE) && mute_login_sounds) | |
299 return; | |
300 | |
301 if (event >= GAIM_NUM_SOUNDS) { | |
302 gaim_debug(GAIM_DEBUG_MISC, "sound", | |
303 "got request for unknown sound: %d\n", event); | |
304 return; | |
305 } | |
306 | |
307 enable_pref = g_strdup_printf("/gaim/gtk/sound/enabled/%s", | |
308 sounds[event].pref); | |
309 file_pref = g_strdup_printf("/gaim/gtk/sound/file/%s", sounds[event].pref); | |
310 | |
311 /* check NULL for sounds that don't have an option, ie buddy pounce */ | |
312 if (gaim_prefs_get_bool(enable_pref)) { | |
313 char *filename = g_strdup(gaim_prefs_get_string(file_pref)); | |
314 if(!filename || !strlen(filename)) { | |
315 if(filename) g_free(filename); | |
316 filename = g_build_filename(DATADIR, "sounds", "gaim", sounds[event].def, NULL); | |
317 } | |
318 | |
319 gaim_sound_play_file(filename); | |
320 g_free(filename); | |
321 } | |
322 | |
323 g_free(enable_pref); | |
324 g_free(file_pref); | |
325 } | |
326 | |
327 static GaimSoundUiOps sound_ui_ops = | |
328 { | |
329 gaim_gtk_sound_init, | |
330 gaim_gtk_sound_shutdown, | |
331 gaim_gtk_sound_play_file, | |
332 gaim_gtk_sound_play_event | |
333 }; | |
334 | |
7035
feb3d21a7794
[gaim-migrate @ 7598]
Christian Hammond <chipx86@chipx86.com>
parents:
6778
diff
changeset
|
335 GaimSoundUiOps * |
feb3d21a7794
[gaim-migrate @ 7598]
Christian Hammond <chipx86@chipx86.com>
parents:
6778
diff
changeset
|
336 gaim_gtk_sound_get_ui_ops(void) |
5684 | 337 { |
338 return &sound_ui_ops; | |
339 } | |
340 | |
341 | |
5794
5e93fc46d1af
[gaim-migrate @ 6219]
Christian Hammond <chipx86@chipx86.com>
parents:
5684
diff
changeset
|
342 static void _pref_sound_method_changed(const char *name, GaimPrefType type, |
5684 | 343 gpointer val, gpointer data) { |
6778 | 344 if(type != GAIM_PREF_STRING || strcmp(name, "/gaim/gtk/sound/method")) |
345 return; | |
346 | |
6199 | 347 sound_initialized = TRUE; |
348 | |
5684 | 349 #ifdef USE_AO |
350 ao_driver = -1; | |
351 | |
352 if(!strcmp(val, "esd")) | |
353 ao_driver = ao_driver_id("esd"); | |
354 else if(!strcmp(val, "arts")) | |
355 ao_driver = ao_driver_id("arts"); | |
356 else if(!strcmp(val, "automatic")) | |
357 ao_driver = ao_default_driver_id(); | |
358 | |
359 if(ao_driver != -1) { | |
360 ao_info *info = ao_driver_info(ao_driver); | |
361 gaim_debug(GAIM_DEBUG_INFO, "sound", | |
362 "Sound output driver loaded: %s\n", info->name); | |
363 } | |
364 #endif /* USE_AO */ | |
365 #ifdef USE_NAS | |
366 if (!strcmp(val, "nas")) | |
367 gaim_debug(GAIM_DEBUG_INFO, "sound", | |
368 "Sound output driver loaded: NAS output\n"); | |
369 #endif /* USE_NAS */ | |
370 } | |
371 | |
372 #ifdef USE_NAS_AUDIO | |
373 static gboolean play_file_nas(const char *filename) | |
374 { | |
375 AuServer *nas_serv; | |
376 gboolean ret = FALSE; | |
377 | |
378 if((nas_serv = AuOpenServer(NULL, 0, NULL, 0, NULL, NULL))) { | |
379 ret = AuSoundPlaySynchronousFromFile(nas_serv, filename, 100); | |
380 AuCloseServer(nas_serv); | |
381 } | |
382 | |
383 return ret; | |
384 } | |
385 | |
386 #endif /* USE_NAS_AUDIO */ | |
387 | |
388 void gaim_gtk_sound_set_login_mute(gboolean mute) | |
389 { | |
390 mute_login_sounds = mute; | |
391 } | |
392 | |
393 const char *gaim_gtk_sound_get_event_option(GaimSoundEventID event) | |
394 { | |
395 if(event >= GAIM_NUM_SOUNDS) | |
396 return 0; | |
397 | |
398 return sounds[event].pref; | |
399 } | |
400 | |
401 char *gaim_gtk_sound_get_event_label(GaimSoundEventID event) | |
402 { | |
403 if(event >= GAIM_NUM_SOUNDS) | |
404 return NULL; | |
405 | |
406 return sounds[event].label; | |
407 } | |
10087 | 408 |
409 void *gaim_gtk_sound_get_handle() | |
410 { | |
411 static int handle; | |
412 | |
413 return &handle; | |
414 } |