/*
 *	astuner - AfterStep FM radio control.
 *	Version 3.1
 *	By David Muench.
 *	cc@spork.neonexus.com
 *	<http://www.neonexus.com/cc/>
 *	Modified by Keith Dart <kdart@techie.com>
 *
 *  Based on ascd by Rob Malda and radiotrack-1.1 by Gideon J. le Grange.
 */
 
/*	This is an 'AfterStep Look & Feel' Wharf style applet that can be used
 *	to control an FM radio tuner card.  Note that this program uses the
 *	new radio device driver from Matthew Kirkwood, which is currently only
 *	in Linux kernel 2.1.65 and up. Sorry, you'll have to run the
 *	development kernel, or wait until Linux 2.2 comes out, to use this
 *	tuner.
 */

/* $Id: astuner.c,v 1.2 1998/04/12 07:05:31 kdart Exp kdart $ */

#include "astuner.h"

/* XPM struct and icons ******************************************************/

XpmIcon radiobg_onXPM, radiobg_offXPM, numbersXPM, radiobgXPM, numberstunedXPM, numbersuntunedXPM, lettersXPM, letterstunedXPM, lettersuntunedXPM;

#include "numbersuntuned.xpm"
#include "radiobg_on.xpm"
#include "radiobg_off.xpm"
#include "numberstuned.xpm"
#include "letterstuned.xpm"
#include "lettersuntuned.xpm"

/* Functions *****************************************************************/
void    Help(void);
void    CreateWindow(void);
void    ParseCmdLine(int argc, char *argv[]);
void    MainLoop();
void    GetXPM(void);
int     FlushExpose(Window w);
void    RedrawWindow( XpmIcon *v);
Pixel   GetColor(char *name);      
void	cleanup(int);
void	cleanup_noexit(void);
void	abort(void);
void	processipc(void);
int		checkstation(int);
int		freqtoint(char *);
void	tunetopreset(void);
void	tunetocurrent(void);
void	tuneup(void);
void	tunedown(void);
void	volup(void);
void	voldown(void);
void	toggleonoff(void);
void	turnoff(void);
void	turnon(void);

/* Globals *******************************************************************/
int     lasttime=-1;
Display *Disp;
Window  Root;
Window  Iconwin;
Window  Win;
char    *Geometry = 0;

GC      WinGC;

uid_t	uid = 65535;
char	*rcfile = 0;

int 	fd;  /* fd for radio device */
int 	fifofd = -1;  /* fd for astuner input FIFO */
int		currstation = 8810;
char    radioon = 0;
int		used = 0;
int		presetindex = 0;
int		intune = FALSE;
int		volume = 0;
int		oldvolume = 0;
struct	radio_ctl radiocontrol;
struct	radio_cap radiofeatures;
struct	radio_band radioFMband;

char	letterson = FALSE;
char	onapreset = FALSE;

fd_set	rfds;
struct	timezone tz;
struct	timeval tv;
long	oldtime = 0;

/*****************************************************************************/
int main(int argc,char *argv[])
{
	int count;
	int	devnum = 0;
	int	bandnum = 0;
	int	numradios = 0;
	int ret = 0;
	int lfd, pid;
	struct passwd *pw;
	char lockbuf[12];

	signal (SIGINT, (void *) abort);
	signal (SIGQUIT, (void *) abort);
	signal (SIGTERM, (void *) abort);

	/* Open the radio device. */
	myname = argv[0];
	if((fd = open(RADIODEVICE, O_RDWR)) == -1) {
		perror(argv[0]);
		abort();
	}
	/* Lock the device file. Only one tuner app should control it. */
	flock(fd, LOCK_EX);
	/* Open the FIFO for ipc from cmdtuner. */
	if((mkfifo(FIFOFILE, 0777)) == -1) 
		perror(myname);
	/* must be opened O_RDWR so that the select works properly. */
	if((fifofd = open(FIFOFILE, O_RDWR | O_NONBLOCK)) == -1) {
		perror(myname);
		abort();
	}

	/* set up the select array */
	FD_ZERO(&rfds);
	FD_SET(fifofd, &rfds);

	
	/* This is an FM tuner user interface. Let's search for and use the
	 * first FM tuner we find.
	 */

	if (ioctl(fd, RADIO_NUMDEVS, &numradios)) {
		perror(argv[0]);
		cleanup(-1);
	}
	if (numradios == 0) {
		fprintf(stderr, "%s : no radios found.", argv[0]);
		flock(fd, LOCK_UN);
		close(fd);
		abort();
	}
	for (devnum = 0; devnum < numradios; devnum++)  {
		radiofeatures.dev_num = devnum;
		ioctl(fd, RADIO_GETCAPS, &radiofeatures);
		if (radiofeatures.num_bwidths > 0) {
			radioFMband.dev_num = devnum;
			for( bandnum = 0; bandnum < radiofeatures.num_bwidths; bandnum++) {
				radioFMband.index = bandnum;
				ioctl(fd, RADIO_GETBNDCAP, &radioFMband);
				if (radioFMband.types = RADIO_BAND_FM_STD) 
						goto foundFMtuner;
			}
		}
		fprintf(stderr, "%s : No FM tuner found.\n", argv[0]);
		flock(fd, LOCK_UN);
		close (fd);
		cleanup(-1);
	}
foundFMtuner:
	/* Now, we know we have an FM tuner, and its capabilities are now
	 * stored in the radiofeatures and radioFMband structures.
	 */
	radiocontrol.dev_num = devnum;
	volume = oldvolume = radiocontrol.value = radiofeatures.volmin;
	ioctl(fd, RADIO_SETVOL, &radiocontrol);
	
	uid = getuid();
	pw = getpwuid(uid); 
	rcfile = malloc(strlen(pw->pw_dir) + strlen(RCFILE) + 1); 
	strcpy(rcfile, pw->pw_dir); 
	strcat(rcfile, RCFILE);

	gettimeofday(&tv, &tz);
	oldtime = tv.tv_sec;

	ParseCmdLine(argc, argv); 

	/* Open and read optional config file. */
	yyin=fopen(rcfile,"r");
	if (yyin != NULL)
	{
		yylex();                         
		fclose(yyin);
	}

	onapreset = checkstation(currstation);
    CreateWindow();
	MainLoop();

	flock(fd, LOCK_UN);
	close(fd);
	free(rcfile);
	return 0;
}

/*****************************************************************************/
void Help()
{       
	fprintf(stderr,"astuner - Version 3.1\n");
	fprintf(stderr,"Written by David Muench - cc@spork.neonexus.com\n");
	fprintf(stderr,"Modified by Keith Dart <kdart@techie.com>\n");
	fprintf(stderr,"http://www.neonexus.com/cc/\n");
	fprintf(stderr,"http://www.employees.org/~kdart/astuner.html\n");
	fprintf(stderr,"usage:  astuner [-options ...] \n");
	fprintf(stderr,"options:\n");
	fprintf(stderr,"\n");       
	fprintf(stderr,"  Command                 Default   Why\n");
	fprintf(stderr,"g Geometry                none      Standard X Location\n");
	fprintf(stderr,"h Help                              This message 8-).\n");
	fprintf(stderr,"\nUse the cmdtuner utility to control this applet.\n");
	cleanup(1);
}

/****************************************************************************/
void CreateWindow(void)
{
	int i;
	unsigned int borderwidth ;
	char *display_name = NULL; 
	char *wname = "astuner";
	XGCValues gcv;
	unsigned long gcm;
	XTextProperty name;
	Pixel back_pix, fore_pix;
	Pixmap pixmask;
	int screen;	
	int x_fd;
	int d_depth;
	int ScreenWidth, ScreenHeight;
	XSizeHints SizeHints;
	XWMHints WmHints;
	
	/* Open display */
	if (!(Disp = XOpenDisplay(display_name)))  
	{ 
		fprintf(stderr,"astuner: can't open display %s\n", 
						XDisplayName(display_name));    
		cleanup (1); 
	} 
	
	screen = DefaultScreen(Disp);
	Root = RootWindow(Disp, screen);
	d_depth = DefaultDepth(Disp, screen);
	x_fd = XConnectionNumber(Disp);	
	ScreenHeight = DisplayHeight(Disp,screen);
	ScreenWidth = DisplayWidth(Disp,screen);
	       
	GetXPM();
		
	SizeHints.flags= USSize|USPosition;
	SizeHints.x = 0;
	SizeHints.y = 0;	
	back_pix = GetColor("white");
	fore_pix = GetColor("black");
	
	XWMGeometry(Disp, screen, Geometry, NULL, (borderwidth =1), &SizeHints,
		    &SizeHints.x,&SizeHints.y,&SizeHints.width,
		    &SizeHints.height, &i); 	
	
	SizeHints.width = radiobgXPM.attributes.width;
	SizeHints.height= radiobgXPM.attributes.height;	
	Win = XCreateSimpleWindow(Disp,Root,SizeHints.x,SizeHints.y,
				  SizeHints.width,SizeHints.height,
				  borderwidth,fore_pix,back_pix);
	Iconwin = XCreateSimpleWindow(Disp,Win,SizeHints.x,SizeHints.y,
				      SizeHints.width,SizeHints.height,
				      borderwidth,fore_pix,back_pix);	     
	XSetWMNormalHints(Disp, Win, &SizeHints);
	XSelectInput(Disp, Win, (ExposureMask | ButtonPressMask | 
				 StructureNotifyMask));
	XSelectInput(Disp, Iconwin, (ExposureMask | ButtonPressMask | 
				     StructureNotifyMask));
	
	if (XStringListToTextProperty(&wname, 1, &name) ==0) 
	{
		fprintf(stderr, "%s : can't allocate window name\n", myname);
		cleanup(-1);
	}
	XSetWMName(Disp, Win, &name);
	
	/* Create WinGC */
	gcm = GCForeground|GCBackground|GCGraphicsExposures;
	gcv.foreground = fore_pix;
	gcv.background = back_pix;
	gcv.graphics_exposures = False;
	WinGC = XCreateGC(Disp, Root, gcm, &gcv);  

	 
		
		XShapeCombineMask(Disp, Win, ShapeBounding, 0, 0, 
				  radiobgXPM.mask, ShapeSet);
		XShapeCombineMask(Disp, Iconwin, ShapeBounding, 0, 0, 
				  radiobgXPM.mask, ShapeSet);
		
	WmHints.initial_state = NormalState;
	WmHints.icon_window = Iconwin;
	WmHints.icon_x = SizeHints.x;
	WmHints.icon_y = SizeHints.y;
	WmHints.flags = StateHint | IconWindowHint | IconPositionHint;
	XSetWMHints(Disp, Win, &WmHints); 	
	XMapWindow(Disp,Win);
	RedrawWindow(&radiobgXPM);
}

/****************************************************************************/
void ParseCmdLine(int argc, char *argv[])
{

	char *Argument;
	int i;       
	
	for(i = 1; i < argc; i++) 
	{
		Argument = argv[i];
		
		if (Argument[0] == '-') 
		{
			switch(Argument[1]) 
			{
			 case 'g': /* Geometry */
				if(++i >= argc) Help();
				Geometry = argv[i];
				continue;
			case 'h':  /* Help */
				if(++i >= argc) Help();
				continue;
			
			 default:
				Help();
			}
		}
		else
			Help();
	}

}

/****************************************************************************/
void abort(void)
{
	if (fifofd > 0) {
			flock(fd, LOCK_UN);		
			close(fifofd);
	}
	unlink (FIFOFILE);
	exit(-1);
}

/****************************************************************************/
void cleanup(int exitcode)
{
	turnoff();
	if (fifofd > 0) {
			flock(fd, LOCK_UN);		
			close(fifofd);
	}
	unlink (FIFOFILE);
	exit(exitcode);
}

/****************************************************************************/
void cleanup_noexit(void)
{
	turnoff();
	if (fifofd > 0) {
			flock(fd, LOCK_UN);		
			close(fifofd);
	}
	unlink (FIFOFILE);
}

/****************************************************************************/
void tunetocurrent()
{
	if (radioon == 1) {
		radiocontrol.value = currstation;
		if(ioctl(fd, RADIO_SETFREQ, &radiocontrol))
				perror(myname);
		if(ioctl(fd, RADIO_GETSIGSTR, &radiocontrol))
				perror(myname);
		intune = (radiocontrol.value > radioFMband.strmin) ? 1 : 0;
		onapreset = checkstation(currstation);
	}
}

/****************************************************************************/
void tunetopreset()
{
	if (radioon == 1) {
		if ((presetindex >= 0) && (presetindex < used)) {
			currstation = stations[presetindex].freq;
			tunetocurrent();
		}
	}
}

/****************************************************************************/
void tuneup()
{
	if (radioon == 1) {
		if(!(currstation >= radioFMband.freqmax)) {
			currstation += 10;
			tunetocurrent();
		}
	}

}

/****************************************************************************/
void tunedown()
{
	if (radioon == 1) {
		if(!(currstation <= radioFMband.freqmin)) {
			currstation -= 10;
			tunetocurrent();
		}
	}
}

/****************************************************************************/
void volup()
{
	if (radioon == 1) {
		if(volume < radiofeatures.volmax) {
			radiocontrol.value = ++volume;
			if(ioctl(fd, RADIO_SETVOL, &radiocontrol))
					perror(myname);
		}
	}
}

/****************************************************************************/
void voldown()
{
	if (radioon == 1) {
		if(volume > radiofeatures.volmin) {
			radiocontrol.value = --volume;
			if(ioctl(fd, RADIO_SETVOL, &radiocontrol))
					perror(myname);
		}
	}
}

/****************************************************************************/
void turnon()
{
	if (radioon == 0) {
		radioon = 1;
		volume = radiocontrol.value = oldvolume;
		if(ioctl(fd, RADIO_SETVOL, &radiocontrol))
			perror(myname);
		tunetocurrent();
	}
}

/****************************************************************************/
void turnoff()
{
	if (radioon == 1) {
		/* ioctl(fd, RADIO_OFF); */
		/* What? no OFF command? */
		oldvolume = volume;
		volume = radiocontrol.value = radiofeatures.volmin;
		if(ioctl(fd, RADIO_SETVOL, &radiocontrol))
				perror(myname);
		radioon = 0;
		intune = FALSE;
	}
}

/****************************************************************************/
void toggleonoff()
{

	if(radioon == 0)
		turnon();
	else
		turnoff();
}

/****************************************************************************/
/* sends the current status of the tuner devices to the client control program
 * via a FIFO.
 */ 
void printstatus()
{
	int i;
	char buf[256];
	int ofifofd = -1;

	if((ofifofd = open(OFIFOFILE, O_WRONLY)) == -1) {
		perror(myname);
		return;
	}

	snprintf(buf, 256, "Astuner status.\n");
	write(ofifofd, buf, strlen(buf));
	snprintf(buf, 256, "Radio is %s, tuned to %d (%s tune), volume is %d (%d%%).\n",
					radioon ? "ON" : "OFF",
					currstation,
					intune ? "in" : "out of",
					volume,
					(volume * 100) / radiofeatures.volmax);
	write(ofifofd, buf, strlen(buf));
	snprintf(buf, 256, "Station preset %d (out of %d) is set.\n",
					presetindex, used);
	write(ofifofd, buf, strlen(buf));
	snprintf(buf, 256, "Preset stations:\n");
	write(ofifofd, buf, strlen(buf));
	for(i = 0; i < used; i++) {
		snprintf(buf, 100, "%d: %s %d\n", i, stations[i].name, stations[i].freq);
		write(ofifofd, buf, strlen(buf));
	}
	snprintf(buf, 256, "Radio features:\n  bands: %d\n  min freq: %d\n  max freq: %d\n",
					radiofeatures.num_bwidths, radioFMband.freqmin, 
					radioFMband.freqmax);
	write(ofifofd, buf, strlen(buf));
	snprintf(buf, 256, "  vol min: %d\n  vol max: %d\n  str min: %d\n  str max: %d\n\n",
					radiofeatures.volmin, radiofeatures.volmax,
					radioFMband.strmin, radioFMband.strmax);
	write(ofifofd, buf, strlen(buf));

	close(ofifofd);
}

/****************************************************************************/
/* convert a string of the form xx.x to a fixed point integer. Return -1 on
 * error. This also allows you to specify the "old style" (no decimal) 
 * frequency.
 */
int freqtoint(char *str)
{
	char buf[32];
	int i;

	i = 0;
	while (isspace(*str)) str++;
	if (! isdigit(*str) ) return -1;
	while(*str) {
		if (*str == '.') {
			buf[i++] = *++str;
			buf[i++] = '0';
			buf[i++] = '\0';
			break;
		}
		else if (! isdigit(*str) ) return -1;
		else buf[i++] = *str++;

		if (i>29) return -1; /* play it safe. */
	}
	return atoi(buf);
}

/****************************************************************************/
void MainLoop()
{
	XEvent Event;
	struct timeval timeout;
			      
	/* Main loop */
	while(1)
	{		
		/* Check events */
		while (XPending(Disp))
		{
			XNextEvent(Disp,&Event);
			switch(Event.type)
			{		     
			 case Expose:		/* Redraw window */
				if(Event.xexpose.count == 0)
				{
					lasttime=01;
					XFlush(Disp);
					RedrawWindow(&radiobgXPM);
				} 
				break;		     
			 case ButtonPress:	/* Mouseclick */
				if((Event.xbutton.y >= 17) && (Event.xbutton.y <= 32) && 
								(Event.xbutton.x <= 14))
				{
					tunedown();
				}
				else if((Event.xbutton.y >= 17) && (Event.xbutton.y <= 32) && 
								(Event.xbutton.x <= 31) && 
								(Event.xbutton.x >= 18))
				{
					tuneup();
				}
				else if((Event.xbutton.y >= 34) && (Event.xbutton.y <= 48) && 
								(Event.xbutton.x <= 22))
				{
					toggleonoff();
				}
				else if((used > 0) && (Event.xbutton.y >= 34) && 
								(Event.xbutton.y <= 48) && 
								(Event.xbutton.x >= 27))
				{
					if(Event.xbutton.button == Button1)
					{
						if (presetindex >= used - 1)
							presetindex = 0;
						else
							presetindex++;
					}
					else if(Event.xbutton.button == Button2)
						presetindex = 0;
					else if(Event.xbutton.button == Button3)
					{
						if (presetindex == 0)
							presetindex = used - 1;
						else
							presetindex--;
					}
					tunetopreset();
				}
				else if((Event.xbutton.y >= 17) && (Event.xbutton.y <= 24) && 
								(Event.xbutton.x >= 36))
				{
					volup();
				}
				else if((Event.xbutton.y >= 26) && (Event.xbutton.y <= 32) && 
								(Event.xbutton.x >= 36))
				{
					voldown();
				}

				XFlush(Disp);
				RedrawWindow(&radiobgXPM);
				break;			 
			 case DestroyNotify:	/* Destroy window */
/*				cleanup_noexit(); */
				puts("ASTUNER: got DestroyNotify");
				if (fifofd > 0)
						close(fifofd);
				unlink (FIFOFILE);
				XFreeGC(Disp, WinGC);
				XDestroyWindow(Disp, Win);
				XDestroyWindow(Disp, Iconwin);
				XCloseDisplay(Disp);				
				break;			
			}
		
		}
		if(onapreset == TRUE) {
			gettimeofday(&tv, &tz);
			if(oldtime + 4 <= tv.tv_sec)
			{
				oldtime = tv.tv_sec;
				if(letterson == TRUE)
					letterson = FALSE;
				else
					letterson = TRUE;
				XFlush(Disp);
				RedrawWindow(&radiobgXPM);
			} 
		}
		timeout.tv_sec = 0;
		timeout.tv_usec = 100000;
		FD_ZERO(&rfds);
		FD_SET(fifofd, &rfds);
		switch(select(fifofd+1, &rfds, NULL, NULL, &timeout) ) {
			case 0:
					break;
			case -1:
					perror("select");
					break;
			default:
					if (FD_ISSET(fifofd, &rfds) ) { 
						processipc();
					} 
					break;
		}
	}
}


/****************************************************************************/
void GetXPM(void)
{
	XWindowAttributes Attributes;
	int Ret;

	
	XGetWindowAttributes(Disp,Root,&Attributes);		
	radiobg_onXPM.attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
	Ret = XpmCreatePixmapFromData(Disp, Root, radiobg_on, &radiobg_onXPM.pixmap, 
		&radiobg_onXPM.mask, &radiobg_onXPM.attributes);

	radiobg_offXPM.attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
	Ret = XpmCreatePixmapFromData(Disp, Root, radiobg_off, &radiobg_offXPM.pixmap, 
		&radiobg_offXPM.mask, &radiobg_offXPM.attributes);

	numberstunedXPM.attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
	Ret = XpmCreatePixmapFromData(Disp, Root, numberstuned, &numberstunedXPM.pixmap,
		&numberstunedXPM.mask, &numberstunedXPM.attributes);

	numbersuntunedXPM.attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
	Ret = XpmCreatePixmapFromData(Disp, Root, numbersuntuned, &numbersuntunedXPM.pixmap,
		&numbersuntunedXPM.mask, &numbersuntunedXPM.attributes);

	lettersuntunedXPM.attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
	Ret = XpmCreatePixmapFromData(Disp, Root, lettersuntuned, &lettersuntunedXPM.pixmap,
		&lettersuntunedXPM.mask, &lettersuntunedXPM.attributes);

	letterstunedXPM.attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
	Ret = XpmCreatePixmapFromData(Disp, Root, letterstuned, &letterstunedXPM.pixmap,
		&letterstunedXPM.mask, &letterstunedXPM.attributes);

	radiobgXPM = radiobg_offXPM;
	numbersXPM = numbersuntunedXPM;
	lettersXPM = lettersuntunedXPM;
	if(Ret != XpmSuccess)
        {
                fprintf(stderr, "astuner: not enough free color cells\n");
                cleanup(1);
        }          
}

/****************************************************************************/
int FlushExpose(Window w)
{
	XEvent dummy;
	int i=0;
	
	while (XCheckTypedWindowEvent (Disp, w, Expose, &dummy))i++;
	return i;
}

/****************************************************************************/
void RedrawWindow(XpmIcon *Icon)
{	
	int station[4];
	char ts[5];

	sprintf(ts, "%d", currstation);
	if(currstation >= 10000)
	{
		station[0] = 1;
		station[1] = ts[1] - 48;
		station[2] = ts[2] - 48;
		station[3] = ts[3] - 48;
	}
	else
	{
		station[0] = 0;
		station[1] = ts[0] - 48;
		station[2] = ts[1] - 48;
		station[3] = ts[2] - 48;

	}

	FlushExpose(Win);

	if(radioon == 0) radiobgXPM = radiobg_offXPM;
	else radiobgXPM = radiobg_onXPM;
	
	if(intune == TRUE)
	{
		numbersXPM = numberstunedXPM;
		lettersXPM = letterstunedXPM;
	}
	else
	{
		numbersXPM = numbersuntunedXPM;
		lettersXPM = lettersuntunedXPM;
	}
	
	XShapeCombineMask(Disp, Win, ShapeBounding, 0, 0, 
			  radiobgXPM.mask, ShapeSet);

	XCopyArea(Disp,radiobgXPM.pixmap,Win,WinGC,
		  0,0,radiobgXPM.attributes.width, radiobgXPM.attributes.height,0,0);	

	if(letterson == FALSE)
	{
		if(currstation >= 10000)
		{
			XCopyArea(Disp, numbersXPM.pixmap, Win, WinGC,
				9*10+4,0, 6,11, 1,2);
		}
		else
		{
			XCopyArea(Disp, numbersXPM.pixmap, Win, WinGC,
				9*10+10,0, 6,11, 1,2);
		}
	
        	XCopyArea(Disp, numbersXPM.pixmap, Win, WinGC,
               		9*station[1],0, 9,11, 7,2);

	        XCopyArea(Disp, numbersXPM.pixmap, Win, WinGC,
        		9*station[2],0, 9,11, 16,2);

		XCopyArea(Disp, numbersXPM.pixmap, Win, WinGC,
			9*10,0, 4,11, 25,2);

	        XCopyArea(Disp, numbersXPM.pixmap, Win, WinGC,
        	        9*station[3],0, 9,11, 28,2);
	}
	else
	{
		XCopyArea(Disp, lettersXPM.pixmap, Win, WinGC,
			9 * (stations[presetindex].name[0] - 65),0, 9,11, 2,2);
		
		XCopyArea(Disp, lettersXPM.pixmap, Win, WinGC,
			9 * (stations[presetindex].name[1] - 65),0, 9,11, 11,2);
			
		XCopyArea(Disp, lettersXPM.pixmap, Win, WinGC,
			9 * (stations[presetindex].name[2] - 65),0, 9,11, 20,2);
		
		XCopyArea(Disp, lettersXPM.pixmap, Win, WinGC,
			9 * (stations[presetindex].name[3] - 65),0, 9,11, 29,2);
	}
}

/****************************************************************************/
Pixel GetColor(char *ColorName)
{
	XColor Color;
	XWindowAttributes Attributes;
	
	XGetWindowAttributes(Disp,Root,&Attributes);
	Color.pixel = 0;
	
	if (!XParseColor (Disp, Attributes.colormap, ColorName, &Color)) 
		fprintf(stderr,"astuner: can't parse %s\n", ColorName);
	else if(!XAllocColor (Disp, Attributes.colormap, &Color)) 
		fprintf(stderr,"astuner: can't allocate %s\n", ColorName);       
	
	return Color.pixel;
}

/****************************************************************************/
void processipc(void)
{
	char inbuf[PIPE_BUF];
	char *ptr;
	int charin;
	int	intfreq;
	
	bzero(inbuf, PIPE_BUF);
	if ((fifofd > 2) && ((charin = read(fifofd, inbuf, PIPE_BUF)) > 0)) {
			if(strncasecmp(inbuf, "on", charin) == 0)
				turnon();
			else if(strncasecmp(inbuf, "status", charin) == 0)
				printstatus();
			else if(radioon == 0)  /* Don't do anything else if we're "off". */
				return;
			else if(strncasecmp(inbuf, "off", charin) == 0)
				turnoff();
			else if(strncasecmp(inbuf, "volup", charin) == 0)
				volup();
			else if(strncasecmp(inbuf, "voldown", charin) == 0)
				voldown();
			else if(strncasecmp(inbuf, "tuneup", charin) == 0)
				tuneup();
			else if(strncasecmp(inbuf, "tunedown", charin) == 0)
				tunedown();
			else if(strncasecmp(inbuf, "tuneto", 6) == 0) {
				ptr = inbuf + 6; 
				intfreq = freqtoint(ptr);
				if ((intfreq >= 0) && (intfreq < used )) {
					presetindex = intfreq;
					tunetopreset();
				}
				else if ((intfreq > radioFMband.freqmin) && 
								(intfreq < radioFMband.freqmax)) {
					currstation = intfreq;
					tunetocurrent();
				}
			}
			XFlush(Disp);
			RedrawWindow(&radiobgXPM);
	}
}

int checkstation(int curr)
{
	int count;
	
	for(count = 0; count < used; count++)
	{
		if(curr == stations[count].freq) {
			presetindex = count;
			return TRUE;
		}
	}
	letterson = FALSE;
	return FALSE;
}
