1 /*
2 * Copyright (C) 1994 Mark Boyns (boyns@sdsu.edu) and
3 * Mark Scott (mscott@mcd.mot.com)
4 *
5 * FvwmAudio version 1.1
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., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 /* ChangeLog
23
24 * Fixed FvwmAudio to reflect the changes made to the module protocol.
25
26 * Szijarto Szabolcs <saby@sch.bme.hu> provided FvwmSound code that used
27 $HOSTDISPLAY for the rplay host. The code has been added to FvwmAudio.
28
29 * Fixed bugs reported by beta testers, thanks!
30
31 * Builtin rplay support has been added to FvwmAudio. This support is
32 enabled when FvwmAudio is compiled with HAVE_RPLAY defined and when
33 FvwmAudioPlayCmd is set to builtin-rplay. I guess it's safe to say
34 that FvwmSound is now obsolete. -- Mark Boyns 5/7/94
35
36 * FvwmAudio is based heavily on an Fvwm module "FvwmSound" by Mark Boyns
37 and the real credit for this really goes to him for the concept.
38 I just stripped out the "rplay" library dependencies to allow generic
39 audio play command support.
40
41 */
42
43 /*
44 * This module is based on FvwmModuleDebugger which has the following
45 * copyright:
46 *
47 * This module, and the entire ModuleDebugger program, and the concept for
48 * interfacing this module to the Window Manager, are all original work
49 * by Robert Nation
50 *
51 * Copyright 1994, Robert Nation. No guarantees or warantees or anything
52 * are provided or implied in any way whatsoever. Use this program at your
53 * own risk. Permission to use this program for any purpose is given,
54 * as long as the copyright is kept intact.
55 */
56
57 /*
58 * afterstep includes:
59 */
60 #include <stdio.h>
61 #include <signal.h>
62 #include <fcntl.h>
63 #include <string.h>
64 #include <sys/wait.h>
65 #include <sys/time.h>
66 #include <unistd.h>
67 #include <ctype.h>
68 #include <stdlib.h>
69 #include "../../configure.h"
70 #include "../../afterstep/module.h"
71 #include "../../version.h"
72 #include "../../lib/aftersteplib.h"
73
74 /*
75 * rplay includes:
76 */
77 #ifdef HAVE_RPLAY
78 #include <rplay.h>
79 #endif
80
81 #define BUILTIN_STARTUP MAX_MESSAGES
82 #define BUILTIN_SHUTDOWN MAX_MESSAGES+1
83 #define BUILTIN_UNKNOWN MAX_MESSAGES+2
84 #define MAX_BUILTIN 3
85
86 #define BUFSIZE 512
87
88 /* globals */
89 char *MyName;
90 int fd_width;
91 int fd[2];
92 char audio_play_cmd_line[BUFSIZE], audio_play_dir[BUFSIZE];
93 time_t audio_delay = 0; /* seconds */
94 #ifdef HAVE_RPLAY
95 int rplay_fd = -1;
96 #endif
97
98 /* prototypes */
99 void Loop(int *);
100 void process_message(unsigned long,unsigned long *);
101 void DeadPipe(int);
102 void config(char *);
103 void done(int);
104 int audio_play(short);
105
106 /* define the message table */
107 char *messages[MAX_MESSAGES+MAX_BUILTIN] =
108 {
109 "toggle_paging",
110 "new_page",
111 "new_desk",
112 "add_window",
113 "raise_window",
114 "lower_window",
115 "configure_window",
116 "focus_change",
117 "destroy_window",
118 "iconify",
119 "deiconify",
120 "window_name",
121 "icon_name",
122 "res_class",
123 "res_name",
124 "end_windowlist",
125 "icon_location",
126 "map",
127 /* add builtins here */
128 "startup",
129 "shutdown",
130 "unknown",
131 };
132
133 /* define the sound table */
134 char *sound_table[MAX_MESSAGES+MAX_BUILTIN];
135
136 #ifdef HAVE_RPLAY
137 /* define the rplay table */
138 RPLAY *rplay_table[MAX_MESSAGES+MAX_BUILTIN];
139 #endif
140
main(int argc,char ** argv)141 main(int argc, char **argv)
142 {
143 char *temp, *s;
144
145 if ((argc != 6)&&(argc != 7))
146 {
147 fprintf(stderr,"%s Version %s should only be executed by afterstep!\n",
148 MyName, VERSION);
149 exit(1);
150 }
151
152 /* Save our program name - for error messages */
153 temp = argv[0];
154 s=strrchr(argv[0], '/');
155 if (s != NULL)
156 temp = s + 1;
157
158 MyName = safemalloc(strlen(temp)+2);
159 strcpy(MyName,"*");
160 strcat(MyName, temp);
161
162 /* Dead pipe == AS died */
163 signal (SIGPIPE, DeadPipe);
164
165 fd[0] = atoi(argv[1]);
166 fd[1] = atoi(argv[2]);
167
168 audio_play_dir[0] = '\0';
169 audio_play_cmd_line[0] = '\0';
170
171 /*
172 * Read the sound configuration.
173 */
174 config(argv[3]);
175
176 /*
177 * Play the startup sound.
178 */
179 audio_play(BUILTIN_STARTUP);
180 SendText(fd,"Nop",0);
181 Loop(fd);
182 }
183
184 /***********************************************************************
185 *
186 * Procedure:
187 * config - read the sound configuration file.
188 *
189 ***********************************************************************/
config(char * config_file)190 void config(char *config_file)
191 {
192 FILE *fp;
193 char buf[BUFSIZE];
194 char *message;
195 char *sound;
196 char *p;
197 int i, found;
198 #ifdef HAVE_RPLAY
199 char host[128];
200 int volume = RPLAY_DEFAULT_VOLUME;
201 int priority = RPLAY_DEFAULT_PRIORITY;
202 #endif
203
204 fp = fopen(config_file, "r");
205 if (fp == NULL)
206 {
207 done(1);
208 }
209
210 /*
211 * Intialize all the sounds.
212 */
213 for (i = 0; i < MAX_MESSAGES+MAX_BUILTIN; i++) {
214 sound_table[i] = NULL;
215 #ifdef HAVE_RPLAY
216 rplay_table[i] = NULL;
217 #endif
218 }
219
220 #ifdef HAVE_RPLAY
221 strcpy(host, rplay_default_host());
222 #endif
223
224 while (fgets(buf, sizeof(buf), fp))
225 {
226 buf[strlen(buf)-1] = '\0';
227 if (buf[0] != '*')
228 {
229 continue;
230 }
231 /*
232 * Search for *ASAudio.
233 */
234 if (mystrncasecmp(buf, MyName, strlen(MyName)) == 0)
235 {
236 p = strtok(buf, " \t");
237
238 if (mystrcasecmp(p, "*AudioPlayCmd") == 0) {
239 p = strtok(NULL, "\n"); /* allow parameters */
240 if (p && *p) {
241 strcpy(audio_play_cmd_line, p);
242 }
243 }
244 else if (mystrcasecmp(p, "*AudioDir") == 0) {
245 p = strtok(NULL, " \t");
246 if (p && *p) {
247 strcpy(audio_play_dir, p);
248 }
249 }
250 else if (mystrcasecmp(p, "*AudioDelay") == 0) {
251 p = strtok(NULL, " \t");
252 if (p && *p) {
253 audio_delay = atoi(p);
254 }
255 }
256
257 #ifdef HAVE_RPLAY
258 /*
259 * Check for rplay configuration options.
260 */
261 else if (mystrcasecmp(p, "*AudioRplayHost") == 0)
262 {
263 p = strtok(NULL, " \t");
264 if (p && *p)
265 {
266 /*
267 * Check for environment variables like $HOSTDISPLAY.
268 */
269 if (*p == '$')
270 {
271 char *c1, *c2;
272 c2 = host;
273 for (c1 = (char *)getenv(p+1); *c1 && (*c1 != ':'); c1++)
274 {
275 *c2++ = *c1;
276 }
277 *c2 = '\0';
278 }
279 else
280 {
281 strcpy(host, p);
282 }
283 }
284 }
285 else if (mystrcasecmp(p, "*AudioRplayVolume") == 0)
286 {
287 p = strtok(NULL, " \t");
288 if (p && *p) {
289 volume = atoi(p);
290 }
291 }
292 else if (mystrcasecmp(p, "*AudioRplayPriority") == 0)
293 {
294 p = strtok(NULL, " \t");
295 if (p && *p) {
296 priority = atoi(p);
297 }
298 }
299 #endif
300 /*
301 * *Audio <message_type> <audio_file>
302 */
303 else {
304 message = strtok(NULL, " \t");
305 sound = strtok(NULL, " \t");
306
307 if (!message || !*message || !sound || !*sound)
308 {
309 continue;
310 }
311
312 found = 0;
313
314 for (i = 0; !found && i < MAX_MESSAGES+MAX_BUILTIN; i++)
315 {
316 if (mystrcasecmp(message, messages[i]) == 0) {
317 #ifdef HAVE_RPLAY
318 rplay_table[i] = rplay_create(RPLAY_PLAY);
319 rplay_set(rplay_table[i], RPLAY_APPEND,
320 RPLAY_SOUND, sound,
321 RPLAY_PRIORITY, priority,
322 RPLAY_VOLUME, volume,
323 NULL);
324 #endif
325 sound_table[i]=safemalloc(strlen(sound)+1);
326 strcpy(sound_table[i],sound);
327 found++;
328 }
329 }
330 }
331 }
332 }
333
334 fclose(fp);
335
336 #ifdef HAVE_RPLAY
337 /*
338 * Builtin rplay support is enabled when FvwmAudioPlayCmd == builtin-rplay.
339 */
340 if (mystrcasecmp(audio_play_cmd_line, "builtin-rplay") == 0)
341 {
342 rplay_fd = rplay_open(host);
343 if (rplay_fd < 0)
344 {
345 rplay_perror("rplay_open");
346 done(1);
347 }
348 }
349 #endif
350 }
351
352 /***********************************************************************
353 *
354 * Procedure:
355 * Loop - wait for data to process
356 *
357 ***********************************************************************/
Loop(int * fd)358 void Loop(int *fd)
359 {
360 unsigned long header[3], *body;
361 char *cbody;
362 int body_length,count,count2=0, total;
363 time_t now, last_time = 0;
364 unsigned long code;
365
366 while (1)
367 {
368 if ((count = read(fd[1],header,3*sizeof(unsigned long))) > 0)
369 {
370 /*
371 * Ignore messages that occur during the delay
372 * period.
373 */
374 now = time(0);
375 if (now < (last_time + audio_delay))
376 {
377 continue;
378 }
379 last_time = now;
380
381 if(header[0] == START_FLAG)
382 {
383 body_length = header[2]-3;
384 body = (unsigned long *)
385 safemalloc(body_length * sizeof(unsigned long));
386 cbody = (char *)body;
387 total = 0;
388 while(total < body_length*sizeof(unsigned long))
389 {
390 if((count2=read(fd[1],&cbody[total],
391 body_length*sizeof(unsigned long)-total)) >0)
392 total += count2;
393 else if(count2 < 0)
394 DeadPipe(0);
395 }
396
397 /*
398 * code will equal the number of shifts in the
399 * base-2 header[1] number. Could use log here
400 * but this should be fast enough.
401 */
402 code = -1;
403 while (header[1])
404 {
405 code++;
406 header[1] >>= 1;
407 }
408
409 /*
410 * Play the sound.
411 */
412 if (code >= 0 && code < MAX_MESSAGES)
413 {
414 audio_play(code);
415 }
416 else
417 {
418 audio_play(BUILTIN_UNKNOWN);
419 }
420
421 free(body);
422 }
423 }
424 if(count <= 0)
425 DeadPipe(1);
426 }
427 }
428
429
430 /***********************************************************************
431 *
432 * Procedure:
433 * SIGPIPE handler - SIGPIPE means afterstep is dying
434 *
435 ***********************************************************************/
DeadPipe(int nonsense)436 void DeadPipe(int nonsense)
437 {
438 done(0);
439 }
440
441 /***********************************************************************
442 *
443 * Procedure:
444 * done - common exit point for FvwmAudio.
445 *
446 ***********************************************************************/
done(int n)447 void done(int n)
448 {
449 audio_play(BUILTIN_SHUTDOWN);
450 exit(n);
451 }
452
453 /***********************************************************************
454 *
455 * Procedure:
456 *
457 * audio_play - actually plays sound from lookup table
458 *
459 **********************************************************************/
audio_play(short sound)460 int audio_play(short sound)
461 {
462 static char buf[BUFSIZE];
463
464 #ifdef HAVE_RPLAY
465 if (rplay_fd != -1)
466 {
467 if (rplay_table[sound])
468 {
469 if (rplay(rplay_fd, rplay_table[sound]) < 0)
470 {
471 rplay_perror("rplay");
472 }
473 }
474 return 0;
475 }
476 #endif
477
478 if (sound_table[sound])
479 {
480 memset(buf,0,BUFSIZE);
481
482 /*
483 * Don't use audio_play_dir if it's NULL or if the sound file
484 * is an absolute pathname.
485 */
486 if (audio_play_dir[0] == '\0' || sound_table[sound][0] == '/')
487 {
488 sprintf(buf,"%s %s", audio_play_cmd_line, sound_table[sound]);
489 }
490 else
491 {
492 sprintf(buf,"%s %s/%s", audio_play_cmd_line, audio_play_dir,
493 sound_table[sound]);
494 }
495 return system(buf);
496 }
497
498 return 1;
499 }
500
501
502
503
504