/*
 * See the README file for info on using this source.
 * 
 * @(#)superxbell.c 1.11 93/05/18
 * 
 * brian.warkentine@Eng.Sun.COM
 */
#include <X11/Xlib.h>
#include <X11/Xresource.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <sys/file.h>
#include <sys/fcntl.h>
#include <sys/mman.h>
#include <dlfcn.h>


#define	AU_MAGIC	0x2e736e64
#define U_LAW		1
#define	INFO_SIZE	(au_head.hdr_size - 24)

typedef struct {
	unsigned long   magic;	/* should be 0x2e736e64 = ".snd" */
	unsigned long   hdr_size;	/* this struct's 24 bytes + info text */
	unsigned long   data_size;	/* number of bytes of sound data */
	unsigned long   encoding;	/* usually (always?) U_LAW = 1 */
	unsigned long   sample_rate;	/* usually (always?) 8000 Hz */
	unsigned long   channels;	/* usually (always?) 1 */
}               Audio_header;

#define AUDIO_UNKNOWN_SIZE  ((unsigned)(~0))

#define NSOUNDS 10

XrmDatabase     defaults_rdb;	/* Overload for libxview. */

XBell(dpy, percent)
	Display        *dpy;
	int             percent;/* ignored - use gaintool instead */
{
	static char    *xbell_audio_files[NSOUNDS];
	static int      n_files = 0;
	static int      initialize = 1;
	static int      fileindex = 0;
	int             sound_fd = -1;
	int             audio_dev = -1;
	caddr_t         mapaddr = (caddr_t) - 1;
	struct stat     st;
	Audio_header   *audio_header;
	int             size;
	static void	(*_real_XBell) ();

	if (initialize) {
		char           *tmp;
		static char     filenames[200];
		void           *dlhandle;

		initialize--;

		dlhandle = dlopen(XLIB, RTLD_LAZY);
		if (dlhandle)
			_real_XBell = (void (*) ()) dlsym(dlhandle, "XBell");

		if (defaults_rdb) {
			char           *type;
			XrmValue        value;

			if (XrmGetResource(defaults_rdb,
				   "SuperXBell.sounds", "SuperXBell.sounds",
					   &type, &value))
				tmp = value.addr;
			else
				tmp = XGetDefault(dpy, "SuperXBell", "sounds");
		} else
			tmp = XGetDefault(dpy, "SuperXBell", "sounds");

		if (!tmp || !*tmp)
			goto default_bell;
		else {
			while(*tmp && (*tmp == ' ' || *tmp == '\t'))
				tmp++;
			if (!tmp || !*tmp)
				goto default_bell;
		}

		(void) strcpy(filenames, tmp);

		tmp = filenames;
		xbell_audio_files[n_files] = tmp;
		n_files++;

		while (*tmp && n_files < NSOUNDS) {

			if (*tmp == ' ' || *tmp == '\t') {
				*tmp = 0;
				tmp++;
				if (*tmp) {
					while(*tmp == ' ' || *tmp == '\t')
						tmp++;
					xbell_audio_files[n_files] = tmp;
					n_files++;
				}
				continue;
			}
			tmp++;
		}
	}

	if (strcmp(XDisplayString(dpy), ":0.0"))
		goto default_bell;
	
	if (n_files == 0)
		goto default_bell;

	if (fileindex >= n_files)
		fileindex = 0;

	if (stat(xbell_audio_files[fileindex], &st) == -1 ||
	    st.st_size == 0) {
		fileindex++;
		goto default_bell;
	}

	sound_fd = open(xbell_audio_files[fileindex], O_RDONLY);
	fileindex++;
	if (sound_fd == -1)
		goto default_bell;

	mapaddr = mmap(0, st.st_size, PROT_READ, MAP_SHARED, sound_fd, 0);
	if (mapaddr == (caddr_t) - 1)
		goto default_bell;

	audio_header = (Audio_header *) mapaddr;
	if (audio_header->magic != AU_MAGIC)
		goto default_bell;

	audio_dev = open("/dev/audio", O_WRONLY | O_NDELAY);
	if (audio_dev == -1)
		goto default_bell;

	if (audio_header->data_size == AUDIO_UNKNOWN_SIZE ||
	    audio_header->hdr_size + audio_header->data_size > st.st_size)
		write(audio_dev, mapaddr + audio_header->hdr_size,
		      st.st_size - audio_header->hdr_size);
	else
		write(audio_dev, mapaddr + audio_header->hdr_size,
		      audio_header->data_size);
	goto cleanup;

default_bell:
	if(_real_XBell)
		(_real_XBell)(dpy, percent);

cleanup:

	if (mapaddr != (caddr_t) - 1)
		munmap(mapaddr, st.st_size);
	if (sound_fd != -1)
		close(sound_fd);
	if (audio_dev != -1)
		close(audio_dev);
	return;
}

#ifdef MAIN
main()
{
	Display        *dpy;

	dpy = XOpenDisplay(NULL);
	XBell(dpy, 0);
	XFlush(dpy);
	XBell(dpy, 0);
	XFlush(dpy);
	XBell(dpy, 0);
	XFlush(dpy);
	XBell(dpy, 0);
	XFlush(dpy);
	XBell(dpy, 0);
	XCloseDisplay(dpy);
}
#endif
