/*****************************************************************************/
/*	ascdc - AfterStep CD Changer				             */
/*	Version .3						             */
/*	By Rob Malda							     */		
/*	malda@cs.hope.edu						     */
/*	http://www.cs.hope.edu/~malda/					     */
/*									     */
/*	This is an 'AfterStep Look & Feel' Wharf style applet that can be    */
/*	used to control an IDE CD Changer.				     */
/*									     */
/*	It works wonderfully with my NEC 4x4 Changer			     */
/*									     */
/*	Things to do:							     */
/*	- Detect what CD is currently active.				     */
/*	- Mounting & Unmounting. (Sorta Works)				     */
/*	- Autolaunch CD Player	 (Sorta Works)				     */
/*****************************************************************************/

#include <stdio.h>
#include <stdlib.h>       
#include <string.h>

#include <X11/Xlib.h>
#include <X11/xpm.h>
#include <X11/extensions/shape.h>

#include <errno.h>
#include <unistd.h>
#include <linux/cdrom.h>
#include <linux/ucdrom.h>
/* #include <linux/fs.h>   */                         

#include <sys/mount.h>


  
/* XPM struct and icons ******************************************************/
typedef struct _XpmIcon {
	Pixmap pixmap;
	Pixmap mask;
	XpmAttributes attributes;
} XpmIcon;

XpmIcon cd0XPM, cd1XPM, cd2XPM, cd3XPM, CurrentXPM;
Pixmap	CDMask;


#include "XPM/cd01.xpm"
#include "XPM/cd02.xpm"
#include "XPM/cd03.xpm"
#include "XPM/cd04.xpm"
#include "XPM/cdmask.xbm"

/* 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	swapcd(int newslot);



/* Global stuff **************************************************************/
#define TRUE 1;
#define FALSE 0;
#define MAXSLOT 3;
#define DEFAULTCD 3;
#define DEFAULTDEVICE "/dev/hdd";


Display	*Disp;	 
Window	Root;
Window	Iconwin;
Window	Win;
char	*Geometry = 0;
char    device[128]=DEFAULTDEVICE;

char	cdplayer[128]="/usr/local/bin/xmcd";
int	autoplay=FALSE

char    mountdir[128]="";
int	mounton=FALSE;

int	slot=0;
int	maxslot=MAXSLOT;
int     withdrawn=FALSE;
GC	WinGC;
int     CarrierOn = FALSE;

/*****************************************************************************/
int main(int argc,char *argv[])
{       	
	ParseCmdLine(argc, argv);      
	CreateWindow();
        XSetCommand(Disp,Win,argv,argc); 
	MainLoop();
	return 0;
}







void swapcd(int newslot)
{
        int fd;           /* file descriptor for CD-ROM device */
        int status=0;       /* return status for system calls */
        


	if(newslot == slot)
		return;
	slot=newslot;

        /* open device */
        fd = open (device, 0);
     
        if(mounton) umount(device);

        if (fd < 0) {
                fprintf (stderr, "ascdc: open failed for `%s': %s\n",
                         device, strerror (errno));
        }                       

       /* load */
        status += ioctl (fd, CDROM_SELECT_DISC, slot);
        if (status != 0) {
                fprintf (stderr,
                         "%s: CDROM_SELECT_DISC ioctl failed for ascdc: %s\n",
                         device, strerror (errno));
        }

        /* close device */
        status += close (fd);
        if (status != 0) {
                fprintf (stderr, "ascdc: close failed for `%s': %s\n",
                         device, strerror (errno));
			 }

	if(status==0)
	{
		if(slot==0) CurrentXPM=cd0XPM;
        	if(slot==1) CurrentXPM=cd1XPM;	
        	if(slot==2) CurrentXPM=cd2XPM;
		if(slot==3) CurrentXPM=cd3XPM;
                RedrawWindow(&CurrentXPM); 	
		
		if(mounton) 
                  {  status=mount(device,mountdir,"iso9660", 0xC0ED0001,0); 
                     fprintf(stderr, "errno %i Mounter %s\n",errno, mountdir);
		     if(status!=0) 
			{  
 			   if(autoplay)
			   system(cdplayer);
			}
		      else fprintf(stderr, "errno %i Mounter %s\n",errno, mountdir);
		  } 

	}




}                          







/*****************************************************************************/
void Help()
{       
	fprintf(stderr,"ascdc - Version 0.3\n");
	fprintf(stderr,"by Rob Malda - malda@cs.hope.edu\n");
	fprintf(stderr,"http://www.cs.hope.edu/~malda/\n");
	fprintf(stderr,"usage:  ascdc [-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,"d Device                  /dev/hdd  IDE Device of the Changer\n");
	fprintf(stderr,"c cdplayer                none	    Location of program to launch for audio CDs.\n");
	fprintf(stderr,"n Number to set now	  none	    If set, switches to slot 0 on launch.\n");
	fprintf(stderr,"m Mount/Umount Directory  none      If desired umounts & mounts\n");
	fprintf(stderr,"                                     eg /cdrom or /mnt\n");
        fprintf(stderr,"w Withdrawn               none      Starts window in Withdrawn State (for WindowMaker)");
	fprintf(stderr,"\n\n");
	exit(1); 
}

/****************************************************************************/
void CreateWindow(void)
{
	int i;
	unsigned int borderwidth ;
	char *display_name = NULL; 
	char *wname = "ascdc";
	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;
        XClassHint classHint; 
	
	/* Open display */
	if (!(Disp = XOpenDisplay(display_name)))  
	{ 
		fprintf(stderr,"ascdc: can't open display %s\n", XDisplayName(display_name));    
		exit (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 = cd0XPM.attributes.width;
	SizeHints.height= cd0XPM.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);	     


        classHint.res_name =  "ascdc";
        classHint.res_class = "ASCdc";
        XSetClassHint(Disp, Win, &classHint);    




	XSetWMNormalHints(Disp, Win, &SizeHints);
	XSelectInput(Disp, Win, (ExposureMask | ButtonPressMask | 
				 StructureNotifyMask));
	XSelectInput(Disp, Iconwin, (ExposureMask | ButtonPressMask | 
				     StructureNotifyMask));
	
	if (XStringListToTextProperty(&wname, 1, &name) ==0) 
	{
		fprintf(stderr, "ascdc: can't allocate window name\n");
		exit(-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);  

	 
	CDMask = XCreateBitmapFromData(Disp, Win, 
						cdmask_bits, 
						cdmask_width, 
						cdmask_height);		
		
		XShapeCombineMask(Disp, Win, ShapeBounding, 0, 0, 
				  CDMask, ShapeSet);
		XShapeCombineMask(Disp, Iconwin, ShapeBounding, 0, 0, 
				  CDMask, ShapeSet);
		
	WmHints.initial_state = withdrawn?WithdrawnState:NormalState;
	WmHints.icon_window = Iconwin;
	WmHints.icon_x = SizeHints.x;
	WmHints.icon_y = SizeHints.y;
	WmHints.flags = StateHint | IconWindowHint | IconPositionHint  | WindowGroupHint; ;
        WmHints.window_group = Win; 
	XSetWMHints(Disp, Win, &WmHints); 	
	XMapWindow(Disp,Win);
	RedrawWindow(&CurrentXPM);
}

/****************************************************************************/
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 'w':
                                withdrawn=TRUE;
                                continue;       
			 case 'g': /* Geometry */
				if(++i >= argc) Help();
				Geometry = argv[i];
				continue;
			 case 'd': /* Device */
				if(++i >= argc) Help();
				strcpy(&device[0], argv[i]);
				continue;

			 case 'm' :
				if(i++ >= argc) Help();
				strcpy(mountdir, argv[i]);
				mounton=TRUE;
				continue;
			 case 'c' :
				if(i++ >=argc) Help();
				strcpy(cdplayer, argv[i]);
				strcat(cdplayer, " &");
				fprintf(stdout, "cdplayer: %s\n,", cdplayer);
				autoplay=TRUE;
				continue;
			 case 'n' :
				if(i++ >=argc) Help();
				swapcd(atoi(argv[i]));
				continue;
			case 'h':  /* Help */
				if(++i >= argc) Help();
				continue;
			
			 default:
				Help();
			}
		}
		else
			Help();
	}

}

/****************************************************************************/
void MainLoop()
{
	XEvent Event;            
			      
	/* Main loop */
	while(1)
	{		
		/* Check events */
		while (XPending(Disp))
		{
			XNextEvent(Disp,&Event);
			switch(Event.type)
			{		     
			 case Expose:		/* Redraw window */
				if(Event.xexpose.count == 0)
					RedrawWindow(&CurrentXPM); 
				break;		     
			 case ButtonPress:	/* Mouseclick */
				
				if(Event.xbutton.y < 24)
                                    {
				      if(Event.xbutton.x < 24) swapcd(0);
				      else swapcd(1);		
				    }
				else
				    { if(Event.xbutton.x < 24) swapcd(3);
				      else swapcd(2);
                                    }
				break;			 
			 case DestroyNotify:	/* Destroy window */
				XFreeGC(Disp, WinGC);
				XDestroyWindow(Disp, Win);
				XDestroyWindow(Disp, Iconwin);
				XCloseDisplay(Disp);				
				exit(0); 
				break;			
			}
		}
		XFlush(Disp);
		usleep(5000L);
	}
}

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

	
	XGetWindowAttributes(Disp,Root,&Attributes);		
	cd0XPM.attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);

	Ret = XpmCreatePixmapFromData(Disp, Root, cd01, &cd0XPM.pixmap, 
				      &cd0XPM.mask, &cd0XPM.attributes);

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

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

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



        CurrentXPM=cd0XPM;	
	if(Ret != XpmSuccess)
        {
                fprintf(stderr, "ascdc: not enough free color cells\n");
                exit(1);
        }          
}

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

/****************************************************************************/
void RedrawWindow(XpmIcon *Icon)
{		
	FlushExpose(Win);
	FlushExpose(Iconwin);
	
		XShapeCombineMask(Disp, Win, ShapeBounding, 0, 0, 
				  CDMask, ShapeSet);
		XShapeCombineMask(Disp, Iconwin, ShapeBounding, 0, 0, 
				  CDMask, ShapeSet);
	
	XCopyArea(Disp,Icon->pixmap,Win,WinGC,
		  0,0,Icon->attributes.width, Icon->attributes.height,0,0);	
	XCopyArea(Disp,Icon->pixmap,Iconwin,WinGC,
		  0,0,Icon->attributes.width, Icon->attributes.height,0,0);
}

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