/* 
 * Copyright (C) 1994 Mark Boyns (boyns@sdsu.edu) and
 *                    Mark Scott (mscott@mcd.mot.com)
 *
 * FvwmAudio version 1.1
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* ChangeLog

 * Fixed FvwmAudio to reflect the changes made to the module protocol.

 * Szijarto Szabolcs <saby@sch.bme.hu> provided FvwmSound code that used
 $HOSTDISPLAY for the rplay host.  The code has been added to FvwmAudio.

 * Fixed bugs reported by beta testers, thanks!

 * Builtin rplay support has been added to FvwmAudio.  This support is
 enabled when FvwmAudio is compiled with HAVE_RPLAY defined and when
 FvwmAudioPlayCmd is set to builtin-rplay.  I guess it's safe to say
 that FvwmSound is now obsolete. -- Mark Boyns 5/7/94
 
 * FvwmAudio is based heavily on an Fvwm module "FvwmSound" by Mark Boyns
 and the real credit for this really goes to him for the concept.
 I just stripped out the "rplay" library dependencies to allow generic
 audio play command support.

 */

/*
 * This module is based on FvwmModuleDebugger which has the following
 * copyright:
 *
 * This module, and the entire ModuleDebugger program, and the concept for
 * interfacing this module to the Window Manager, are all original work
 * by Robert Nation
 *
 * Copyright 1994, Robert Nation. No guarantees or warantees or anything
 * are provided or implied in any way whatsoever. Use this program at your
 * own risk. Permission to use this program for any purpose is given,
 * as long as the copyright is kept intact.
 */

/*
 * afterstep includes:
 */
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
#include "../../configure.h"
#include "../../afterstep/module.h"
#include "../../version.h"
#include "../../lib/aftersteplib.h"     

/*
 * rplay includes:
 */
#ifdef HAVE_RPLAY
#include <rplay.h>
#endif

#define BUILTIN_STARTUP		MAX_MESSAGES
#define BUILTIN_SHUTDOWN	MAX_MESSAGES+1
#define BUILTIN_UNKNOWN		MAX_MESSAGES+2
#define MAX_BUILTIN		3

#define BUFSIZE			512

/* globals */
char	*MyName;
int	fd_width;
int	fd[2];
char	audio_play_cmd_line[BUFSIZE], audio_play_dir[BUFSIZE];
time_t	audio_delay = 0;	/* seconds */
#ifdef HAVE_RPLAY
int	rplay_fd = -1;
#endif

/* prototypes */
void	Loop(int *);
void	process_message(unsigned long,unsigned long *);
void	DeadPipe(int);
void	config(char *);
void	done(int);
int     audio_play(short); 

/* define the message table */
char	*messages[MAX_MESSAGES+MAX_BUILTIN] =
{
	"toggle_paging",
	"new_page",
	"new_desk",
	"add_window",
	"raise_window",
	"lower_window",
	"configure_window",
	"focus_change",
	"destroy_window",
	"iconify",
	"deiconify",
	"window_name",
	"icon_name",
	"res_class",
	"res_name",
	"end_windowlist",
	"icon_location",
	"map",
/* add builtins here */
	"startup",
	"shutdown",
	"unknown",
};

/* define the sound table  */
char	*sound_table[MAX_MESSAGES+MAX_BUILTIN];

#ifdef HAVE_RPLAY
/* define the rplay table */
RPLAY	*rplay_table[MAX_MESSAGES+MAX_BUILTIN];
#endif

main(int argc, char **argv)
{
	char *temp, *s;

	if ((argc != 6)&&(argc != 7))
	{
		fprintf(stderr,"%s Version %s should only be executed by afterstep!\n",
			MyName, VERSION);
		exit(1);
	}

	/* Save our program  name - for error messages */
	temp = argv[0];
	s=strrchr(argv[0], '/');
	if (s != NULL)
		temp = s + 1;

	MyName = safemalloc(strlen(temp)+2);
	strcpy(MyName,"*");
	strcat(MyName, temp);

	/* Dead pipe == AS died */
	signal (SIGPIPE, DeadPipe);  
  
	fd[0] = atoi(argv[1]);
	fd[1] = atoi(argv[2]);

	audio_play_dir[0] = '\0';
	audio_play_cmd_line[0] = '\0';

	/*
	 * Read the sound configuration.
	 */
	config(argv[3]);

	/*
	 * Play the startup sound.
	 */
	audio_play(BUILTIN_STARTUP);
	SendText(fd,"Nop",0);
	Loop(fd);
}

/***********************************************************************
 *
 *  Procedure:
 *	config - read the sound configuration file.
 *
 ***********************************************************************/
void	config(char *config_file)
{
	FILE	*fp;
	char	buf[BUFSIZE];
	char	*message;
	char	*sound;
	char	*p;
	int	i, found;
#ifdef HAVE_RPLAY
	char	host[128];
	int	volume = RPLAY_DEFAULT_VOLUME;
	int	priority = RPLAY_DEFAULT_PRIORITY;
#endif
	
	fp = fopen(config_file, "r");
	if (fp == NULL)
	{
		done(1);
	}

	/*
	 * Intialize all the sounds.
	 */
	for (i = 0; i < MAX_MESSAGES+MAX_BUILTIN; i++) {
		sound_table[i] = NULL;
#ifdef HAVE_RPLAY
		rplay_table[i] = NULL;
#endif
	}

#ifdef HAVE_RPLAY
	strcpy(host, rplay_default_host());
#endif

	while (fgets(buf, sizeof(buf), fp))
	{
		buf[strlen(buf)-1] = '\0';
		if (buf[0] != '*')
		{
			continue;
		}
		/*
		 * Search for *ASAudio.
		 */
		if (mystrncasecmp(buf, MyName, strlen(MyName)) == 0)
		{
			p = strtok(buf, " \t");
			
			if (mystrcasecmp(p, "*AudioPlayCmd") == 0) {
				p = strtok(NULL, "\n"); /* allow parameters */
				if (p && *p) {
					strcpy(audio_play_cmd_line, p);
				}
			}
			else if (mystrcasecmp(p, "*AudioDir") == 0) {
				p = strtok(NULL, " \t");
				if (p && *p) {
					strcpy(audio_play_dir, p);
				}
			}
			else if (mystrcasecmp(p, "*AudioDelay") == 0) {
				p = strtok(NULL, " \t");
				if (p && *p) {
					audio_delay = atoi(p);
				}
			}
			
#ifdef HAVE_RPLAY
			/*
			 * Check for rplay configuration options.
			 */
			else if (mystrcasecmp(p, "*AudioRplayHost") == 0)
			{
				p = strtok(NULL, " \t");
				if (p && *p)
				{
					/*
					 * Check for environment variables like $HOSTDISPLAY.
					 */
					if (*p == '$')
					{
						char	*c1, *c2;
						c2 = host;
						for (c1 = (char *)getenv(p+1); *c1 && (*c1 != ':'); c1++)
						{
							*c2++ = *c1;
						}
						*c2 = '\0';
					}
					else
					{
						strcpy(host, p);
					}
				}
			}
			else if (mystrcasecmp(p, "*AudioRplayVolume") == 0)
			{
				p = strtok(NULL, " \t");
				if (p && *p) {
					volume = atoi(p);
				}
			}
			else if (mystrcasecmp(p, "*AudioRplayPriority") == 0)
			{
				p = strtok(NULL, " \t");
				if (p && *p) {
					priority = atoi(p);
				}
			}
#endif
			/*
			 * *Audio <message_type> <audio_file>
			 */
			else  {
				message = strtok(NULL, " \t");
				sound = strtok(NULL, " \t");
				
				if (!message || !*message || !sound || !*sound)
				{
					continue;
				}
				
				found = 0;
				
				for (i = 0; !found && i < MAX_MESSAGES+MAX_BUILTIN; i++)
				{
					if (mystrcasecmp(message, messages[i]) == 0) {
#ifdef HAVE_RPLAY
						rplay_table[i] = rplay_create(RPLAY_PLAY);
						rplay_set(rplay_table[i], RPLAY_APPEND,
							RPLAY_SOUND,	sound,
							RPLAY_PRIORITY,	priority,
							RPLAY_VOLUME,	volume,
							NULL);
#endif
						sound_table[i]=safemalloc(strlen(sound)+1);
						strcpy(sound_table[i],sound);
						found++;
					}
				}
			}
		}
	}

	fclose(fp);

#ifdef HAVE_RPLAY
	/*
	 * Builtin rplay support is enabled when FvwmAudioPlayCmd == builtin-rplay.
	 */
	if (mystrcasecmp(audio_play_cmd_line, "builtin-rplay") == 0)
	{
		rplay_fd = rplay_open(host);
		if (rplay_fd < 0)
		{
			rplay_perror("rplay_open");
			done(1);
		}
	}
#endif
}

/***********************************************************************
 *
 *  Procedure:
 *	Loop - wait for data to process
 *
 ***********************************************************************/
void Loop(int *fd)
{
	unsigned long	header[3], *body;
	char		*cbody;
	int		body_length,count,count2=0, total;
	time_t 		now, last_time = 0;
	unsigned long	code;
	
	while (1)
	{
		if ((count = read(fd[1],header,3*sizeof(unsigned long))) > 0)
		{
			/*
			 * Ignore messages that occur during the delay
			 * period.
			 */
			now = time(0);
			if (now < (last_time + audio_delay))
			{
				continue;
			}
			last_time = now;
			
			if(header[0] == START_FLAG)
			{
				body_length = header[2]-3;
				body = (unsigned long *)
					safemalloc(body_length * sizeof(unsigned long));
				cbody = (char *)body;
				total = 0;
				while(total < body_length*sizeof(unsigned long))
				{
					if((count2=read(fd[1],&cbody[total],
						body_length*sizeof(unsigned long)-total)) >0)
						total += count2;
					else if(count2 < 0)
						DeadPipe(0);
				}

				/*
				 * code will equal the number of shifts in the
				 * base-2 header[1] number.  Could use log here
				 * but this should be fast enough.
				 */
				code = -1;
				while (header[1])
				{
					code++;
					header[1] >>= 1;
				}
				
				/*
				 * Play the sound.
				 */
				if (code >= 0 && code < MAX_MESSAGES)
				{
					audio_play(code);
				}
				else
				{
					audio_play(BUILTIN_UNKNOWN);
				}
				
				free(body);
			}
		}
		if(count <= 0)
			DeadPipe(1);
	}
}


/***********************************************************************
 *
 *  Procedure:
 *	SIGPIPE handler - SIGPIPE means afterstep is dying
 *
 ***********************************************************************/
void DeadPipe(int nonsense)
{
	done(0);
}

/***********************************************************************
 *
 *  Procedure:
 *	done - common exit point for FvwmAudio.
 *
 ***********************************************************************/
void	done(int n)
{
	audio_play(BUILTIN_SHUTDOWN);
	exit(n);
}

/***********************************************************************
 *
 * Procedure:
 *
 *    audio_play - actually plays sound from lookup table
 *
 **********************************************************************/
int audio_play(short sound) 
{
	static char buf[BUFSIZE];

#ifdef HAVE_RPLAY
	if (rplay_fd != -1)
	{
		if (rplay_table[sound])
		{
			if (rplay(rplay_fd, rplay_table[sound]) < 0)
			{
				rplay_perror("rplay");
			}
		}
		return 0;
	}
#endif

	if (sound_table[sound])
	{
		memset(buf,NULL,BUFSIZE);

		/*
		 * Don't use audio_play_dir if it's NULL or if the sound file
		 * is an absolute pathname.
		 */
		if (audio_play_dir[0] == '\0' || sound_table[sound][0] == '/')
		{
			sprintf(buf,"%s %s", audio_play_cmd_line, sound_table[sound]);
		}
		else
		{
			sprintf(buf,"%s %s/%s", audio_play_cmd_line, audio_play_dir,
				sound_table[sound]);
		}
		return system(buf);
	}  

	return 1;
}






syntax highlighted by Code2HTML, v. 0.9.1