/*****************************************************************************/
/*	asmodem - AfterStep Modem					     */
/*	Version .32 						             */
/*	By Rob Malda							     */	
/*	malda@cs.hope.edu						     */
/*	http://www.cs.hope.edu/~malda/					     */
/*      based Almost Entirely on AfterStep Mail				     */
/*									     */
/*	asmail - AfterStep Mail		                                     */
/*	Version 0.31				                             */
/*	By Per Liden			                                     */
/*	pt95pli@student.hk-r.se		                                     */
/*	http://www.rby.hk-r.se/~pt95pli	                                     */
/*					                                     */
/*****************************************************************************/

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

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

XpmIcon OffModem, OnModem,  Current;
Pixmap	OffMask,OnMask,  CurrentMask;


#include "onmodem.xpm"
#include "offmodem.xpm"
#include "onmodem_s.xpm"
#include "offmodem_s.xpm"
#include "onmodem_s_m.xbm"
#include "offmodem_s_m.xbm"

/* Functions *****************************************************************/
int	CheckMailBox(char *Mbox);
int	IsNewMail(char *Mbox);
int	IsMail(char *Mbox);
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);

/* Global stuff **************************************************************/
#define DEFINTERVAL 5		/* Default interval 5 sec */
#define TRUE 1;
#define FALSE 0;
#define DEFAULTMODEM "/var/lock/LCK..modem"

Display	*Disp;	 
Window	Root;
Window	Iconwin;
Window	Win;
char	*Geometry = 0;
char    OnExecute[256] ="";
char    OffExecute[256] ="";
char	DisconnectExecute[256]="";
char    DefaultModem[256]=DEFAULTMODEM;
char	*MailBox = 0;
int	ExecuteFlag = 0;
int     OffExecuteFlag = 0;
int     OnExecuteFlag= 0;
int	DisconnectExecuteFlag=0;
int	Shape = 0;
int	Interval = DEFINTERVAL;
GC	WinGC;
int     CarrierOn = FALSE;

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

/*****************************************************************************/
void Help()
{       
	fprintf(stderr,"asmodem - Version 0.32\n");
	fprintf(stderr,"usage:  asmodem [-options ...] \n");
	fprintf(stderr,"options:\n");
	fprintf(stderr,"        -s               transparent edge mode\n");
	fprintf(stderr,"        -m <file>        modem lockfile (ie /var/lock/LCK..cua1) \n");
        fprintf(stderr,"			 default is /var/lock/LCK..modem (/dev/modem) \n");
	fprintf(stderr,"        -u <sec>         update frequency (default 5 sec)\n");	
	fprintf(stderr,"        -p [+|-]x[+|-]y  position of asmodem\n");
	fprintf(stderr,"        -d <program>     program to execute on click while disconnected (dial?)\n");
	fprintf(stderr,"        -r <program>     program to execute on disconnect (redial?  beep?)\n");
        fprintf(stderr,"	-h <program>	 program to excute on click while connected (hangup?)");
	fprintf(stderr,"\n");       
	exit(1);
}

/****************************************************************************/
void CreateWindow(void)
{
	int i;
	unsigned int borderwidth ;
	char *display_name = NULL; 
	char *wname = "asmodem";
	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,"asmodem: 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 = OffModem.attributes.width;
	SizeHints.height= OffModem.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, "asmodem: 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);  

	if(Shape) 
	{ 
		OnMask = XCreateBitmapFromData(Disp, Win, 
						onmodem_s_m_bits, 
						onmodem_s_m_width, 
						onmodem_s_m_height);		
		OffMask = XCreateBitmapFromData(Disp, Win, 
						offmodem_s_m_bits, 
						offmodem_s_m_width, 
						offmodem_s_m_height);		
		CurrentMask = OffMask;
		
		XShapeCombineMask(Disp, Win, ShapeBounding, 0, 0, 
				  CurrentMask, ShapeSet);
		XShapeCombineMask(Disp, Iconwin, ShapeBounding, 0, 0, 
				  CurrentMask, 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(&Current);
}

/****************************************************************************/
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 's':
				Shape = 1;
				continue;
			 case 'm':
				if(++i >= argc) Help();
				MailBox = argv[i];
				continue;
			 case 'u':
				if(++i >= argc) Help();				
				Interval = atoi(argv[i]);
				if(!Interval)
				{
					fprintf(stderr, "asmodem: invalid update frequency specified, using default\n");
					Interval = DEFINTERVAL;  /* Use default */
				}
				continue;
			 case 'p':
				if(++i >= argc) Help();
				Geometry = argv[i];
				continue;
			 case 'd':
				if(++i >= argc) Help();
				strcpy(&OffExecute[0], argv[i]);
				strcat(&OffExecute[0], " &");
			        OffExecuteFlag = 1;
				continue;
			 case 'r':
				if(++i >= argc) Help();
				strcpy(&DisconnectExecute[0], argv[i]);
				strcat(&DisconnectExecute[0], " &");
				DisconnectExecuteFlag = 1;
				continue;
			case 'h':
				if(++i >= argc) Help();
				strcpy(&OnExecute[0], argv[i]);
				strcat(&OnExecute[0], " &");
				OnExecuteFlag = 1;
				continue;
			
			 default:
				Help();
			}
		}
		else
			Help();
	}

	if(!MailBox)
	{	
		/* Default Modem Device /dev/modem */       
		/* fprintf(stderr, "asmodem: no modem specified\n"); */
                MailBox = DefaultModem;
		/* exit(1); */
	}       
}

/****************************************************************************/
void MainLoop()
{
	XEvent Event;            
			      
	/* Main loop */
	while(1)
	{		
		/* Check mailbox */
		if(CheckMailBox(MailBox))	       
			RedrawWindow(&Current);				
		
		/* Check events */
		while (XPending(Disp))
		{
			XNextEvent(Disp,&Event);
			switch(Event.type)
			{		     
			 case Expose:		/* Redraw window */
				if(Event.xexpose.count == 0)
					RedrawWindow(&Current); 
				break;		     
			 case ButtonPress:	/* Mouseclick */
				if (CarrierOn==1) system(OnExecute);
				else system(OffExecute);
				break;			 
			 case DestroyNotify:	/* Destroy window */
				XFreeGC(Disp, WinGC);
				XDestroyWindow(Disp, Win);
				XDestroyWindow(Disp, Iconwin);
				XCloseDisplay(Disp);				
				exit(0); 
				break;			
			}
		}
		XFlush(Disp);
		sleep(Interval);
	}
}

/****************************************************************************/
int CheckMailBox(char *Mbox)
{
	XpmIcon Prev = Current;

	if (IsMail(Mbox))
	{  /* We have a modem! */
		if(!IsNewMail(Mbox))
		{      
		if(!CarrierOn)
			{ 
			/* fprintf(stderr, "New Modem.  I should beep."); */
			if(ExecuteFlag == 2)
				ExecuteFlag = 1;
			Current = OnModem;	  
			CurrentMask = OnMask;
			CarrierOn=TRUE;	
			}		
		}
	}	  
	else 
	{
		if(CarrierOn)
		{
		CarrierOn=FALSE;
		/* fprintf(stderr, "I have lost modem.  I should beep."); */
                system(DisconnectExecute);
		if(ExecuteFlag == 2)
			ExecuteFlag = 1;
		Current = OffModem;	  
		CurrentMask = OffMask;
		}
	}
	
	return (Prev.pixmap != Current.pixmap);
}	

/****************************************************************************/
int IsNewMail(char *Mbox)
{
	FILE* MailFile;
	int Ret = 0;
	char Buffer[30] = "";
	int WasStatus = 1;
	int NewMailn = 0;

	MailFile = fopen(Mbox, "r");

	while(!feof(MailFile))
	{
		fgets(Buffer, 29, MailFile);
		if(!strncmp(Buffer, "From ", 5))
		{
			if(!WasStatus) NewMailn++;
			WasStatus=0;
		}		
		else if(!strncmp(Buffer, "Status: RO", 10))
			WasStatus = 1;
	}
	fclose(MailFile);

	return (NewMailn||!WasStatus);
}

/****************************************************************************/
int IsMail(char *Mbox)
{
	FILE* MailFile = 0;
	int Ret = 0;            

	MailFile = fopen(Mbox, "r");	
	if(MailFile == 0)
		Ret = 0;
	else
	{
		fgetc(MailFile);
		if(feof(MailFile))
			Ret = 0;
		else			
			Ret = 1;
	}
	fclose(MailFile);

	return Ret;    
}

/****************************************************************************/
void GetXPM(void)
{
	char **offmodem_xpm, **onmodem_xpm;
	XWindowAttributes Attributes;
	int Ret;

	if(Shape)
	{
		offmodem_xpm = offmodem_s;
		onmodem_xpm = onmodem_s;
	}
	else
	{
		offmodem_xpm = offmodem;
		onmodem_xpm = onmodem;
	}
	
	XGetWindowAttributes(Disp,Root,&Attributes);		
	OffModem.attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);

	Ret = XpmCreatePixmapFromData(Disp, Root, offmodem_xpm, &OffModem.pixmap, 
				      &OffModem.mask, &OffModem.attributes);

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

	OnModem.attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
	Ret = XpmCreatePixmapFromData(Disp, Root, onmodem_xpm, &OnModem.pixmap, 
				      &OnModem.mask, &OnModem.attributes);
	Current = OffModem;	  
	CurrentMask = OffMask;
}

/****************************************************************************/
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);
	
	if(Shape) 
	{ 				
		XShapeCombineMask(Disp, Win, ShapeBounding, 0, 0, 
				  CurrentMask, ShapeSet);
		XShapeCombineMask(Disp, Iconwin, ShapeBounding, 0, 0, 
				  CurrentMask, 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,"asmodem: can't parse %s\n", ColorName);
	else if(!XAllocColor (Disp, Attributes.colormap, &Color)) 
		fprintf(stderr,"asmodem: can't allocate %s\n", ColorName);       
	
	return Color.pixel;
}












