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