#ifndef lint
static char *RCSid = "$Header: /home/jsellens/src/users/biffer/RCS/x10biffer.c,v 1.5 1991/05/13 15:15:12 jmsellens Exp $";
#endif

#if 0
/*
 * $Log: x10biffer.c,v $
 * Revision 1.5  91/05/13  15:15:12  jmsellens
 * send lines variable to X10biffer()
 * 
 * Revision 1.4  90/08/19  17:53:10  jmsellens
 * Check for X10 define
 * 
 * Revision 1.3  90/02/02  00:43:05  jmsellens
 * Add x11, separate out doprog stuff, etc ...
 * 
 * Revision 1.2  90/01/29  19:12:30  jmsellens
 * Add Beeps thing
 * 
 * Revision 1.1  88/02/04  16:45:12  jmsellens
 * Initial revision
 * 
 */
#endif

#include "biffer.h"

#ifdef X10

#include <X/Xlib.h>
#include <X/Xkeyboard.h>
#include <syslog.h>


#define XNAME		"biffer"	/* for getting defaults */
#define MAX_TIMEOUT	5	/* show window 5 minutes at most */
#define MIN_TIMEOUT	1	/* and for at least 1 minute */


static int msgSplit();
static int msglines, hdrlines;

/* XCreateCursor *must* have a bit mask supplied; NULL won't work.
 * -IAN!
 */
static short cursor[] = {0x0000, 0x7ffe, 0x4fc2, 0x4ffe, 0x7ffe,
		  0x7ffe, 0x781e, 0x7ffe , 0x7ffe, 0x0000};
static short cursor_mask[] = {0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
		  0xffff, 0xffff, 0xffff , 0xffff, 0xffff};
#define CURSORWIDTH   16
#define CURSORHEIGHT  10

xtimeout()
{
    exit(0);
}

errHandler( display, err )
Display *display;
XErrorEvent *err;
{
    syslog( LOG_ERR, "X10 error: %s", XErrDescrip( err->error_code ) );
    exit(0);
}

ioerrHandler( display )
Display *display;
{
    syslog( LOG_ERR, "X10 IO error" );
    exit(0);
}



X10biffer( device, lines )
char *device;
int lines;
{
    char dname[260], hdrbuf[200], *p;
    Display *dpy;
    WindowInfo winfo;
    FontInfo finfo;
    Font font;
    int width, height, minheight, twidth;
    int bwidth = 2;
    int inner = 2;
    int vertical = 2;
    int horizontal = 0;
    Window w;
    XEvent rep;
    int done = FALSE, hoffset, voffset, hdrbuflen, linesleft;
    int minutes = MAX_TIMEOUT;
    int buttonGoaway = TRUE, keyGoaway = TRUE;
    int reverse = 0;
    char *geom = (char *)NULL;
    int volume = 0;
    int beeps = 1;
    int forepix = BlackPixel;
    int backpix = WhitePixel;
    int brdrpix = BlackPixel;
    int mouspix = BlackPixel;
    char *option;
    char *font_name = "8x13";
    char *fore_color = NULL;
    char *back_color = NULL;
    char *brdr_color = NULL;
    char *mous_color = NULL;
    Color cdef;

    /* use the last character in device as the display number on
       this host */
    (void) sprintf( dname, "%s:%c", host, device[strlen(device)-1] );

    if (!XOpenDisplay(dname))
	exit(0);

    if ( fork() != 0 )
	return;

    XErrorHandler( errHandler );
    XIOErrorHandler( ioerrHandler );

    /* make the header line for the window */
    (void) sprintf( hdrbuf, "New mail for %s@%s has arrived",
	recipient, machine );
    hdrbuflen = strlen( hdrbuf );

    if (option = XGetDefaultHome(home, XNAME, "BodyFont"))
	font_name = option;
    geom = XGetDefaultHome(home, XNAME, "Geometry");
    fore_color = XGetDefaultHome(home, XNAME, "Foreground");
    back_color = XGetDefaultHome(home, XNAME, "Background");
    brdr_color = XGetDefaultHome(home, XNAME, "Border");
    mous_color = XGetDefaultHome(home, XNAME, "Mouse");
    if (option = XGetDefaultHome(home, XNAME, "BorderWidth"))
	bwidth = atoi(option);
    if (option = XGetDefaultHome(home, XNAME, "InternalBorder"))
	inner = atoi(option);
    if (option = XGetDefaultHome(home, XNAME, "Lines"))
	lines = atoi(option);
    if (option = XGetDefaultHome(home, XNAME, "Timeout"))
	minutes = atoi(option);
    if (option = XGetDefaultHome(home, XNAME, "Volume")) {
	volume = atoi(option);
	if (volume >  7) volume =  7;
	if (volume < -7) volume = -7;
    }
    if (option = XGetDefaultHome(home, XNAME, "Beeps")) {
	beeps = atoi(option);
	if (beeps < 0) beeps = 0;
	if (beeps > 10) beeps = 10;
    }
    if (option = XGetDefaultHome(home, XNAME, "ReverseVideo")) 
	if ( strcmp(option, "on") == 0 )
	    reverse = 1;
    if (option = XGetDefaultHome(home, XNAME, "ButtonGoAway")) 
	if ( strcmp(option, "off") == 0 )
	    buttonGoaway = FALSE;
    if (option = XGetDefaultHome(home, XNAME, "KeyGoAway")) 
	if ( strcmp(option, "off") == 0 )
	    keyGoaway = FALSE;
    if (reverse) {
	brdrpix = backpix;
	backpix = forepix;
	forepix = brdrpix;
	mouspix = forepix;
    }

    if (DisplayCells() > 2) {
	if (back_color && XParseColor(back_color, &cdef) &&
	    XGetHardwareColor(&cdef))
		backpix = cdef.pixel;
	if (fore_color && XParseColor(fore_color, &cdef) &&
	    XGetHardwareColor(&cdef))
		forepix = cdef.pixel;
	if (brdr_color && XParseColor(brdr_color, &cdef) &&
	    XGetHardwareColor(&cdef))
		brdrpix = cdef.pixel;
	if (mous_color && XParseColor(mous_color, &cdef) &&
	    XGetHardwareColor(&cdef))
		mouspix = cdef.pixel;
    }

    if ((font = XGetFont(font_name)) == NULL)
	if ((font = XGetFont("8x13")) == NULL)
	    exit(0);
    XQueryFont(font, &finfo);
    XQueryWindow(RootWindow, &winfo);

    width = XStringWidth( hdrbuf, &finfo, 0, 0 );
    width = msgSplit(&finfo,width) + inner*2;

    minheight = bwidth + 2*finfo.height + 4*inner;
    height = (hdrlines+(lines<msglines?lines:msglines))*finfo.height + inner*2;
    height += finfo.height + bwidth + inner*2; /* space for header */

    horizontal = (winfo.width - width - (bwidth << 1)) / 2;

    if ( geom != (char *)NULL ) {
	int mask;
	mask = XParseGeometry( geom, &horizontal, &vertical, &width, &height );
	if ( height < minheight ) height = minheight;
	if ( (mask&WidthValue) && width < 100 ) width = 100;
	if ( (mask&XValue) && (mask&XNegative) )
	    horizontal = DisplayWidth()+horizontal-width-2*(inner+bwidth);
	if ( (mask&YValue) && (mask&YNegative) )
	    vertical = DisplayHeight()+vertical-height-2*(inner+bwidth);
    }
    if ( horizontal > DisplayWidth()-10 ) horizontal = DisplayWidth()-10;
    if ( horizontal < 10-width ) horizontal = 10-width;
    if ( vertical > DisplayHeight()-10 ) vertical = DisplayHeight()-10;
    if ( vertical < 10-height ) vertical = 10-height;
    w = XCreateWindow(RootWindow, horizontal, vertical, width, height, bwidth,
	XMakeTile(brdrpix), XMakeTile(backpix));
    if ( ! w )	/* oops - no window - oh well */
	exit(0);
    /* it seems that the window will be at least min+increment big */
    XSetResizeHint( w, 100, minheight-finfo.height, 1, finfo.height );
    XStoreName(w, XNAME);
    { int rwidth; int rheight;
      XQueryCursorShape(CURSORWIDTH,CURSORHEIGHT,&rwidth,&rheight);
      if( CURSORWIDTH <= rwidth && CURSORHEIGHT <= rheight ){
	  XDefineCursor(w, XCreateCursor(CURSORWIDTH, CURSORHEIGHT, cursor,
	      cursor_mask, CURSORWIDTH/2, CURSORHEIGHT/2,
	      mouspix, backpix, GXcopy));
      }
    }
    XSelectInput(w, KeyPressed|ButtonPressed|ExposeWindow);
    XMapWindow(w);
    {
	int i;
	for ( i=0; i<beeps; i++ )
	    XFeep(volume);
    }
    /* force this window to go away in at most MAX_TIMEOUT minutes */
    if (minutes > MAX_TIMEOUT) minutes = MAX_TIMEOUT;
    if (minutes < MIN_TIMEOUT) minutes = MIN_TIMEOUT;
    signal(SIGALRM, xtimeout);
    alarm(minutes * 60);

    do {
	XNextEvent(&rep);
	switch ( rep.type ) {
	    case ExposeWindow:
		height = ((XExposeEvent *)(&rep))->height;
		width = ((XExposeEvent *)(&rep))->width;
		hoffset = voffset = inner;
		twidth = XStringWidth( hdrbuf, &finfo, 0, 0 );
		if ( twidth < width-inner*2 )
		    hoffset = (width - twidth - inner*2) / 2;
		XText( w, hoffset, voffset, hdrbuf, hdrbuflen,
		    font, forepix, backpix );
		voffset += finfo.height;
		XLine( w, 0, voffset, width-1, voffset,
		    1, bwidth, brdrpix, GXcopy, AllPlanes );
		voffset += inner + bwidth + inner;
		p = msgbuf;
		inheader = TRUE;
		linesleft = hdrlines+msglines;
		while ( *p ) {
		    if ( voffset > height )
			break;	/* past bottom of window */
		    /* no more than 1 full line left visible */
		    if ( voffset+finfo.height*2 > height
			&& linesleft > 1 ) {
			XText(w, inner, voffset, "...more...", 10,
			    font, forepix, backpix);
			break;
		    }
		    if ( !inheader || !skipHeader( p ) ) {
			if ( *p != '\n' ) /* something on line */
			    XText(w, inner, voffset, p,
				strlen(p), font, forepix, backpix);
			voffset += finfo.height;
			linesleft--;
		    }
		    while ( *p && *p!='\n' )
			p++;
		    p++;	/* hop the \0 or \n */
		}
		break;
	    case KeyPressed:
		if ( ! IsShiftKey( ((XKeyEvent *)(&rep))->detail & 0xFF ) ) {
		    /* not just a modifier key press */
		    int n;
		    char *cp;
		    done = keyGoaway;
		    if ( ! done ) {
			cp = XLookupMapping( (XKeyPressedEvent *)(&rep), &n );
			/* check to see if it's q, Q, or ^D */
			if ( n == 1 && ( *cp=='q' || *cp=='Q' || *cp=='\004' ) )
			    done = TRUE;
		    }
		}
		break;
	    case ButtonPressed:
		done = buttonGoaway;
		break;
	}
    } while ( !done );
    exit(0);
}


/* since we've forked, we can wreck our copy of msgbuf */
/* replace the \n's with \0's so we can easily deal with a line at a time
   Note that this leaves the last two characters in msgbuf as \0's.
   And msgbuf is *guaranteed* to end in \n\0.  But if we find a line that
   is just a \n, leave it there or else the \n\n becomes \0\0 which looks
   like the end of the message. */
static int
msgSplit( finfo, maxwidth )
FontInfo *finfo;
int maxwidth;
{
    register char *p = msgbuf;
    char *start;	/* so we can find width of lines */
    int printable, width;
    inheader = TRUE;
    hdrlines = msglines = 0;
    while ( *p ) {
	printable = TRUE;
	if ( inheader ) {
	    if ( !skipHeader( p ) )
		hdrlines++;	/* this counts blank line after header */
	    else
		printable = FALSE;
	} else
	    msglines++;
	if ( *p == '\n' ) {
	    p++;	/* just leave it there */
	} else {
	    start = p;
	    while ( *p != '\n' )
		p++;
	    *p++ = '\0';
	    if ( printable ) {
		width = XStringWidth( start, finfo, 0, 0 );
		if ( width > maxwidth )
		    maxwidth = width;
	    }
	}
    }
    return( maxwidth );
}

#endif /* X10 */
