[comp.windows.x] sources for X11 xbiff: toolkit and raw Xlib versions

jim@dandelion.CI.COM (Jim Fulton) (11/10/87)

Okay, following in David Rosenthal footsteps, I'll volunteer a useful little
application as fodder for discussions on how not to write X11 programs :-).

The shar file at the end of this message (whose contents are listed below)
contains the source for two versions of yet another "xbiff" for X11.  One
version uses raw Xlib calls, the other is built on top of the toolkit using the
"Mailbox" widget supplied below. 

	xlib-xbiff.c		raw Xlib version, for example only
	flagup.bit		bitmap for mailbox with flag up
	flagdown.bit		bitmap for mailbox with flag down
	Mailbox.h		Mailbox widget external definitions
	MailboxP.h		Mailbox widget internal definitions
	Mailbox.c		Mailbox widget methods
	xbiff.c			real version, uses Mailbox widget
	Makefile		trival makefile for above

The Mailbox code should just drop into your regular bag of widgets (it would be
handy to put XtNupdate into Atoms.h since I suspect that it will become a
commonly used resource).  It's pretty heavily patterned after the Clock widget.
The only real differences are that the Mailbox does graphics differently and it
accepts ButtonPresses.  It probably would have been better to implement as a
composite of some sort of bitmap label, but I had to start somewhere. 

Both versions of xbiff put up a little window with a picture of a mailbox in
them.  Every "update" seconds the programs go look at the mail file and raise
the flag if new mail has come in.  If the mail file goes away (or is zeroed),
or if the user presses on the mailbox window, the flag is lowered. 

I've run both versions against uwm and wm, on both a QVSS and a QDSS.  My
favorite usage is shown below (there is enough room on a 1024 pixel wide screen
for two 80 column 6x10 xterms and a column of 48x48 utilities or icons).  Note
that in order to get the -1+50 placement to work properly, you have to fix the
sign bugs in the toolkit handling of Negative values returned from
XParseGeometry().  [A bug report for this has been submitted to xbugs; for
reference, look in lib/Xtk/{Initialize,Conversion}.c.]

	(xclock -analog -reverse -bw 0 -padding 2 =48x48-1+1 &)
	(xbiff -rv =48x48-1+50 -bw 0 &)

For the record, this is an original program.  It is derived neither from the
X10 version that I wrote nor the one written at Berkeley. 

Have fun.  I strongly encourage the more artistic people in the audience to
redesign the mailbox bitmaps.  Feel free to send bug fixes, enhancements, etc.
back to me.  Like David, I strongly urge people to use the toolkit.


						Jim Fulton
						Cognition Inc.

uucp:    ...!{mit-eddie,talcott,necntc}!dandelion!jim
domain:  jim@dandelion.ci.com, jim@athena.mit.edu, fulton@eddie.mit.edu


                              - cut here -

#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -s ./Makefile`
then
echo "writting ./Makefile"
cat > ./Makefile << '\End\Of\File\'
CDEBUG = -O
CFLAGS = $(CDEBUG)
BINDIR = /usr/bin/X11/
SHAR = xbiff.shar

all:  xbiff

clean:
	-rm -f *.o *~ a.out xbiff xlib-xbiff $(SHAR).?

install:  xbiff
	install -c -s xbiff $(BINDIR)

shar:
	shar -f $(SHAR) Makefile flagup.bit flagdown.bit \
		Mailbox.h MailboxP.h Mailbox.c xbiff.c xlib-xbiff.c


#
# Toolkit version
#

xbiff:  xbiff.o Mailbox.o
	$(CC) -o xbiff $(CDEBUG) xbiff.o Mailbox.o -lXtk -loldXrm -lX11

Mailbox.o:  Mailbox.h MailboxP.h flagup.bit flagdown.bit


#
# Raw Xlib version
#

xlib-xbiff:  xlib-xbiff.o
	$(CC) -o xlib-xbiff $(CFLAGS) xlib-xbiff.o -lX11


\End\Of\File\
else
  echo "will not over write ./Makefile"
fi
if `test ! -s ./flagup.bit`
then
echo "writting ./flagup.bit"
cat > ./flagup.bit << '\End\Of\File\'
#define flagup_width 48
#define flagup_height 48
static char flagup_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x7f, 0x00,
   0x00, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xef, 0x6a, 0x00,
   0x00, 0x00, 0xc0, 0x7b, 0x75, 0x00, 0x00, 0x00, 0xe0, 0xe0, 0x6a, 0x00,
   0x00, 0x00, 0x30, 0x60, 0x75, 0x00, 0x00, 0x00, 0x18, 0xe0, 0x7f, 0x00,
   0x00, 0x00, 0x0c, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x06, 0xe0, 0x04, 0x00,
   0x00, 0x00, 0x03, 0xe0, 0x04, 0x00, 0x00, 0x80, 0x01, 0xe0, 0x06, 0x00,
   0x00, 0xc0, 0x1f, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x7f, 0xe0, 0x07, 0x00,
   0x00, 0x70, 0xe0, 0xe0, 0x05, 0x00, 0x00, 0x38, 0x80, 0xe1, 0x04, 0x00,
   0x00, 0x18, 0x80, 0xf1, 0x04, 0x00, 0x00, 0x0c, 0x00, 0xfb, 0x04, 0x00,
   0x00, 0x0c, 0x00, 0xff, 0x04, 0x00, 0x00, 0x86, 0x1f, 0xee, 0x04, 0x00,
   0x00, 0x06, 0x06, 0xe6, 0x04, 0x00, 0x00, 0x06, 0x00, 0xe6, 0x04, 0x00,
   0x00, 0x06, 0x00, 0xe6, 0x04, 0x00, 0x00, 0x06, 0x00, 0x66, 0x04, 0x00,
   0x00, 0x56, 0x52, 0x06, 0x04, 0x00, 0x00, 0x76, 0x55, 0x06, 0x04, 0x00,
   0xfc, 0x56, 0x57, 0x06, 0xc4, 0x3f, 0x00, 0x56, 0x55, 0x06, 0x06, 0x00,
   0x00, 0x56, 0xd5, 0x06, 0x03, 0x00, 0x00, 0x06, 0x00, 0x86, 0x01, 0x00,
   0x54, 0x06, 0x00, 0xc6, 0x54, 0x55, 0xaa, 0x06, 0x00, 0x66, 0xaa, 0x2a,
   0x54, 0x06, 0x00, 0x36, 0x55, 0x55, 0xaa, 0x06, 0x00, 0xbe, 0xaa, 0x2a,
   0x54, 0xfe, 0xff, 0x6f, 0x55, 0x55, 0xaa, 0xfc, 0xff, 0xa7, 0xaa, 0x2a,
   0x54, 0x01, 0x88, 0x60, 0x55, 0x55, 0xaa, 0xaa, 0x8a, 0xa0, 0xaa, 0x2a,
   0x54, 0x55, 0x8d, 0x60, 0x55, 0x55, 0xaa, 0xaa, 0x8a, 0xa0, 0xaa, 0x2a,
   0x54, 0x55, 0x8d, 0x60, 0x55, 0x55, 0xaa, 0xaa, 0x8a, 0xa0, 0xaa, 0x2a,
   0x54, 0x55, 0x8d, 0x50, 0x55, 0x55, 0xaa, 0xaa, 0x8a, 0xa8, 0xaa, 0x2a,
   0x54, 0x55, 0x95, 0x54, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x2a,
   0x54, 0x55, 0x55, 0x55, 0x55, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
\End\Of\File\
else
  echo "will not over write ./flagup.bit"
fi
if `test ! -s ./flagdown.bit`
then
echo "writting ./flagdown.bit"
cat > ./flagdown.bit << '\End\Of\File\'
#define flagdown_width 48
#define flagdown_height 48
static char flagdown_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00,
   0x00, 0x00, 0xc0, 0xfb, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xc0, 0x01, 0x00,
   0x00, 0x00, 0x30, 0x00, 0x03, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00,
   0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x04, 0x04,
   0x00, 0x00, 0x03, 0x00, 0x04, 0x06, 0x00, 0x80, 0x01, 0x00, 0x06, 0x07,
   0x00, 0xc0, 0x1f, 0x00, 0x87, 0x07, 0x00, 0xe0, 0x7f, 0x80, 0xc7, 0x07,
   0x00, 0x70, 0xe0, 0xc0, 0xe5, 0x07, 0x00, 0x38, 0x80, 0xe1, 0x74, 0x07,
   0x00, 0x18, 0x80, 0x71, 0x3c, 0x07, 0x00, 0x0c, 0x00, 0x3b, 0x1e, 0x03,
   0x00, 0x0c, 0x00, 0x1f, 0x0f, 0x00, 0x00, 0x86, 0x1f, 0x8e, 0x07, 0x00,
   0x00, 0x06, 0x06, 0xc6, 0x05, 0x00, 0x00, 0x06, 0x00, 0xc6, 0x05, 0x00,
   0x00, 0x06, 0x00, 0xc6, 0x04, 0x00, 0x00, 0x06, 0x00, 0x06, 0x04, 0x00,
   0x00, 0x06, 0x00, 0x06, 0x04, 0x00, 0x00, 0x06, 0x00, 0x06, 0x04, 0x00,
   0xfc, 0x06, 0x00, 0x06, 0xc4, 0x3f, 0x00, 0x06, 0x00, 0x06, 0x06, 0x00,
   0x00, 0x06, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x00, 0x86, 0x01, 0x00,
   0x00, 0x06, 0x00, 0xc6, 0x00, 0x00, 0x00, 0x06, 0x00, 0x66, 0x08, 0x00,
   0x00, 0x06, 0x00, 0x36, 0x08, 0x00, 0x00, 0x06, 0x00, 0xbe, 0x0d, 0xc0,
   0x00, 0xfe, 0xff, 0x1f, 0xce, 0xc0, 0x00, 0xfc, 0xff, 0x17, 0xe7, 0x40,
   0x04, 0x00, 0x30, 0x11, 0x25, 0x78, 0x66, 0x00, 0x33, 0x11, 0x3c, 0x38,
   0x42, 0x03, 0x32, 0x79, 0x18, 0x20, 0x43, 0x0d, 0x36, 0x5b, 0x0e, 0x20,
   0xf0, 0x87, 0x37, 0x1f, 0x1a, 0x20, 0xc0, 0x83, 0x3e, 0xf5, 0x38, 0xd8,
   0x80, 0x01, 0x3e, 0xd7, 0x30, 0xa5, 0x04, 0x01, 0x34, 0x9d, 0xfa, 0x2a,
   0xf7, 0xbf, 0x3e, 0xdd, 0x5f, 0x88, 0x9d, 0xeb, 0x3b, 0x77, 0x52, 0x62,
   0x46, 0x30, 0xe2, 0x0f, 0xe1, 0x47, 0x55, 0x84, 0xc8, 0x11, 0x84, 0x19};
\End\Of\File\
else
  echo "will not over write ./flagdown.bit"
fi
if `test ! -s ./Mailbox.h`
then
echo "writting ./Mailbox.h"
cat > ./Mailbox.h << '\End\Of\File\'
/*
 * $Header: Mailbox.h,v 1.4 87/11/09 10:45:23 jim Exp $
 *
 * Copyright 1987 by Cognition Inc.  All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Cognition Inc. not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Cognition Inc. makes no representations
 * about the suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 */

#ifndef _XtMailbox_h
#define _XtMailbox_h

/*
 * Mailbox widget; looks a lot like the clock widget, don't it...
 */

/* resource names used by mailbox widget that aren't defined in Atoms.h */

#define XtNupdate "update"		/* Int: how often to check mail */


/* structures */

typedef struct _MailboxRec *MailboxWidget;  /* see MailboxP.h */
typedef struct _MailboxClassRec *MailboxWidgetClass;  /* see MailboxP.h */


extern WidgetClass mailboxWidgetClass;

#endif _XtMailbox_h
/* DON'T ADD STUFF AFTER THIS #endif */
\End\Of\File\
else
  echo "will not over write ./Mailbox.h"
fi
if `test ! -s ./MailboxP.h`
then
echo "writting ./MailboxP.h"
cat > ./MailboxP.h << '\End\Of\File\'
/*
 * $Header: MailboxP.h,v 1.4 87/11/09 10:45:30 jim Exp $
 *
 * Copyright 1987 by Cognition Inc.  All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Cognition Inc. not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Cognition Inc. makes no representations
 * about the suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 */

#ifndef _XtMailboxPrivate_h
#define _XtMailboxPrivate_h

#define MAILBOX_DIRECTORY "/usr/spool/mail/\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
#define MAILBOX_VOLUME 33		/* percentage */

typedef struct {			/* new fields for mailbox widget */
    Pixel foreground_pixel;		/* color index of normal state fg */
    GC gc;				/* normal GC to use */
    int update;				/* seconds between updates */
    char filename[sizeof MAILBOX_DIRECTORY];	/* filename to watch */
    long last_size;			/* size in bytes of mailboxname */
    Boolean reverseVideo;		/* do reverse video? */
    XtIntervalId interval_id;		/* time between checks */
    Boolean flag_up;			/* is the flag up? */
    Pixmap flagup_pixmap;		/* for when there is mail */
    Pixmap flagdown_pixmap;		/* for when there isn't mail */
} MailboxPart;

typedef struct _MailboxRec {		/* full instance record */
    CorePart core;
    MailboxPart mailbox;
} MailboxRec;


typedef struct {			/* new fields for mailbox class */
    int dummy;				/* stupid C compiler */
} MailboxClassPart;

typedef struct _MailboxClassRec {	/* full class record declaration */
    CoreClassPart core_class;
    MailboxClassPart mailbox_class;
} MailboxClassRec;

extern MailboxClassRec mailboxClassRec;	 /* class pointer */

#endif _XtMailboxPrivate_h
\End\Of\File\
else
  echo "will not over write ./MailboxP.h"
fi
if `test ! -s ./Mailbox.c`
then
echo "writting ./Mailbox.c"
cat > ./Mailbox.c << '\End\Of\File\'
/*
 * $Header: Mailbox.c,v 1.6 87/11/09 11:50:05 jim Exp $
 *
 * Copyright 1987 by Cognition Inc.  All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Cognition Inc. not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Cognition Inc. makes no representations
 * about the suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 */

#include <stdio.h>			/* for printf */
#include <sys/time.h>			/* for select() */
#include <pwd.h>			/* for getting username */
#include <sys/types.h>			/* for stat() */
#include <sys/stat.h>			/* for stat() */

#include <X11/Xlib.h>			/* for Xlib definitions */
#include <X11/Xutil.h>			/* for Hints */
#include <X11/Intrinsic.h>		/* for toolkit stuff */
#include <X11/Atoms.h>			/* for useful atom names */
#include <X11/cursorfont.h>		/* for cursor constants */
#include "Mailbox.h"			/* for generic mailbox stuff */
#include "MailboxP.h"			/* for implementation mailbox stuff */
#include "flagup.bit"			/* for flag up (mail present) bits */
#include "flagdown.bit"			/* for flag down (mail not here) */

#define PictureWidth flagdown_width	/* better be same as flagup_width */
#define PictureHeight flagdown_height	/* better be same as flagup_height */

#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))


/* Initialization of defaults */

#define offset(field) XtOffset(MailboxWidget,mailbox.field)
#define goffset(field) XtOffset(Widget,core.field)

static XtResource resources[] = {
    { XtNwidth, XtCWidth, XrmRInt, sizeof (int), 
	goffset (width), XrmRString, "48" },
    { XtNheight, XtCHeight, XrmRInt, sizeof (int),
	goffset (height), XrmRString, "48" },
    { XtNupdate, XtCInterval, XrmRInt, sizeof (int),
	offset (update), XrmRString, "30" },
    { XtNforeground, XtCForeground, XrmRPixel, sizeof (Pixel),
	offset (foreground_pixel), XrmRString, "black" },
    { XtNbackground, XtCBackground, XrmRPixel, sizeof (Pixel),
	goffset (background_pixel), XrmRString, "white" },
    { XtNreverseVideo, XtCBoolean, XrmRBoolean, sizeof (Boolean),
	offset (reverseVideo), XrmRString, "FALSE" },
};

#undef offset
#undef goffset

static void GetUserInfo(), CloseDown();
static void check_mailbox(), redraw_mailbox(), beep();
static void Initialize(), Realize(), Destroy(), Redisplay();
static Boolean SetValues();

MailboxClassRec mailboxClassRec = {
    {					/* core fields */
	&widgetClassRec,		/* superclass */
	"Mailbox",			/* class_name */
	sizeof (MailboxRec),		/* size */
	NULL,				/* class_initialize */
	FALSE,				/* class_inited */
	Initialize,			/* initialize */
	Realize,			/* realize */
	NULL,				/* actions */
	0,				/* num_actions */
	resources,			/* resources */
	XtNumber (resources),		/* resource_count */
	NULL,				/* xrm_class */
	TRUE,				/* compress_motion */
	TRUE,				/* compress_exposure */
	FALSE,				/* visible_interest */
	Destroy,			/* destroy */
	NULL,				/* resize */
	Redisplay,			/* expose */
	SetValues,			/* set_values */
	NULL,				/* accept_focus */
    }
};

WidgetClass mailboxWidgetClass = (WidgetClass) &mailboxClassRec;


/*
 * private procedures
 */

static void EventHandler (gw, closure, event)
    Widget gw;
    char *closure;
    XEvent *event;
{
    MailboxWidget w = (MailboxWidget) gw;

    switch (event->type) {
      case ClientMessage:
	if (event->xclient.message_type == XtTimerExpired)
	  check_mailbox (w, FALSE, FALSE);
	break;

      case Expose:
	check_mailbox (w, TRUE, FALSE);
	break;

      case ButtonPress:
	check_mailbox (w, TRUE, TRUE);
	break;
    }
    return;
}


static GC get_mailbox_gc (widget, w)
    Widget widget;
    MailboxWidget w;
{
    XtGCMask valuemask;
    XGCValues xgcv;

    valuemask = GCForeground | GCBackground | GCFunction | GCGraphicsExposures;
    xgcv.foreground = w->mailbox.foreground_pixel;
    xgcv.background = w->core.background_pixel;
    xgcv.function = GXcopy;
    xgcv.graphics_exposures = False;	/* this is Bool, not Boolean */
    return (XtGetGC (widget, valuemask, &xgcv));
}


static void Initialize (request, new)
    Widget request, new;
{
    MailboxWidget w = (MailboxWidget) new;
    XtGCMask valuemask;
    XGCValues xgcv;

    GetUserInfo (w);

    if (w->core.width <= 0) w->core.width = PictureWidth;
    if (w->core.height <= 0) w->core.height = PictureHeight;

    if (w->mailbox.reverseVideo) {
	Pixel tmp;

	tmp = w->mailbox.foreground_pixel;
	w->mailbox.foreground_pixel = w->core.background_pixel;
	w->core.background_pixel = tmp;
    }

    w->mailbox.gc = get_mailbox_gc (new, w);

    XtAddEventHandler (w, ButtonPressMask, TRUE, EventHandler, NULL);

    w->mailbox.interval_id = XtAddTimeOut (w, w->mailbox.update * 1000);
    return;
}


static Pixmap make_picture (dpy, win, bits, width, height, fg, bg)
    Display *dpy;
    Window win;
    char *bits;
    int width, height;
    Pixel fg, bg;
{
    Pixmap bitmap = XCreateBitmapFromData (dpy, win, bits, width, height);
    Pixmap pixmap;

    if (!bitmap) return (0);

    /* the following is Sam Black's (masscomp!black) XMakePixmapFromBitmap() */
    pixmap = XCreatePixmap (dpy, win, width, height,
			    DefaultDepth (dpy, DefaultScreen (dpy)));
    if (pixmap) {
	GC tmpgc;
	XGCValues gcv;
	gcv.function = GXcopy;
	gcv.foreground = fg;
	gcv.background = bg;
	gcv.graphics_exposures = False;
	tmpgc = XCreateGC (dpy, pixmap,
			   GCFunction | GCForeground | GCBackground |
			   GCGraphicsExposures,
			   &gcv);
	XCopyPlane (dpy, bitmap, pixmap, tmpgc, 0, 0, width, height, 0, 0, 1);
	XFreeGC (dpy, tmpgc);
    }
    return (pixmap);
}


static void Realize (gw, valuemask, attr)
    Widget gw;
    XtValueMask valuemask;
    XSetWindowAttributes *attr;
{
    MailboxWidget w = (MailboxWidget) gw;
    register Display *dpy = XtDisplay (w);
    Window win;

    valuemask |= (CWBitGravity | CWCursor);
    attr->bit_gravity = ForgetGravity;
    attr->cursor = XCreateFontCursor (dpy, XC_top_left_arrow);

    win = XCreateWindow (dpy,
			 gw->core.parent->core.window,
			 gw->core.x, gw->core.y,
			 gw->core.width, gw->core.height,
			 gw->core.border_width,
			 gw->core.depth, InputOutput,
			 CopyFromParent, valuemask, attr);
    gw->core.window = win;

    /*
     * build up the pixmaps that we'll put into the image
     */

    w->mailbox.flagup_pixmap = make_picture (dpy, win, flagup_bits,
					     flagup_width, flagup_height,
					     w->core.background_pixel, 
					     w->mailbox.foreground_pixel);
    w->mailbox.flagdown_pixmap = make_picture (dpy, win, flagdown_bits,
					       flagdown_width, flagdown_height,
					       w->mailbox.foreground_pixel,
					       w->core.background_pixel);
    return;
}


static void Destroy (gw)
    Widget gw;
{
    MailboxWidget w = (MailboxWidget) gw;

    XtRemoveTimeOut (w->mailbox.interval_id);
    XtDestroyGC (w->mailbox.gc);
    return;
}


static void Redisplay (gw)
    Widget gw;
{
    MailboxWidget w = (MailboxWidget) gw;

    check_mailbox ((MailboxWidget) gw, TRUE, FALSE);
}


static void check_mailbox (w, force_redraw, reset)
    MailboxWidget w;
    Boolean force_redraw, reset;
{
    struct stat st;
    long mailboxsize = 0;

    if (stat (w->mailbox.filename, &st) == 0) {
	mailboxsize = st.st_size;
    }

    /*
     * Now check for changes.  If reset is set then we want to pretent that
     * there is no mail.  If the mailbox is empty then we want to turn off
     * the flag.  Otherwise if the mailbox has changed size then we want to
     * put the flag up.
     *
     * The cases are:
     *    o  forced reset by user                        DOWN
     *    o  no mailbox or empty (zero-sized) mailbox    DOWN
     *    o  same size as last time                      no change
     *    o  bigger than last time                       UP
     *    o  smaller than last time but non-zero         UP
     *
     * The last two cases can be expressed as different from last
     * time and non-zero.
     */

    if (reset) {			/* forced reset */
	w->mailbox.flag_up = FALSE;
	force_redraw = TRUE;
    } else if (mailboxsize == 0) {	/* no mailbox or empty */
	w->mailbox.flag_up = FALSE;
	if (w->mailbox.last_size > 0) force_redraw = TRUE;  /* if change */
    } else if (mailboxsize != w->mailbox.last_size) {  /* different size */
	w->mailbox.flag_up = TRUE;
	force_redraw = TRUE;
	beep (w);
    } 

    w->mailbox.last_size = mailboxsize;
    if (force_redraw) redraw_mailbox (w);
    return;
}

/*
 * get user name for building mailbox
 */

static void GetUserInfo (w)
    MailboxWidget w;
{
    char *getlogin();
    char *username;
    int len;

    username = getlogin ();
    if (!username) {
	struct passwd *pw = getpwuid (getuid ());

	if (!pw) {
	    fprintf (stderr, "%s:  unable to find a username for you.\n",
		     "Mailbox widget");
	    CloseDown (w, 1);
	}
	username = pw->pw_name;
    }
    strcpy (w->mailbox.filename, MAILBOX_DIRECTORY);
    strcat (w->mailbox.filename, username);
    return;
}

static void CloseDown (w, status)
    MailboxWidget w;
    int status;
{
    Display *dpy = XtDisplay (w);

    XtDestroyWidget (w);
    XCloseDisplay (dpy);
    exit (status);
}


static Boolean SetValues (gcurrent, grequest, gnew, last)
    Widget gcurrent, grequest, gnew;
    Boolean last;
{
    MailboxWidget current = (MailboxWidget) gcurrent;
    MailboxWidget new = (MailboxWidget) gnew;
    Boolean redisplay = FALSE;

    if (current->mailbox.update != new->mailbox.update) {
	XtRemoveTimeOut (current->mailbox.interval_id);
	new->mailbox.interval_id = XtAddTimeOut (gcurrent,
						 new->mailbox.update * 1000);
    }

    if (current->mailbox.foreground_pixel != new->mailbox.foreground_pixel ||
	current->core.background_pixel != new->core.background_pixel) {
	XtDestroyGC (current->mailbox.gc);
	new->mailbox.gc = get_mailbox_gc (gcurrent, new);
	redisplay = TRUE;
    }

    return (redisplay);
}


/*
 * drawing code
 */

static void redraw_mailbox (w)
    MailboxWidget w;
{
    register Display *dpy = XtDisplay (w);
    register Window win = XtWindow (w);
    register int x, y;
    GC gc = w->mailbox.gc;
    Pixmap picture;
    Pixel back;

    /* center the picture in the window */

    x = (w->core.width - PictureWidth) / 2;
    y = (w->core.height - PictureHeight) / 2;

    if (w->mailbox.flag_up) {		/* paint the "up" position */
	back = w->mailbox.foreground_pixel;
	picture = w->mailbox.flagup_pixmap;
    } else {				/* paint the "down" position */
	back = w->core.background_pixel;
	picture = w->mailbox.flagdown_pixmap;
    }

    XSetWindowBackground (dpy, win, back);
    XClearWindow (dpy, win);
    XCopyArea (dpy, picture, win, gc, 0, 0, PictureWidth, PictureHeight, x, y);
    return;
}


static void beep (w)
    MailboxWidget w;
{
    register Display *dpy = XtDisplay (w);
    register Window win = XtWindow (w);

    XBell (dpy, MAILBOX_VOLUME);
    return;
}
\End\Of\File\
else
  echo "will not over write ./Mailbox.c"
fi
if `test ! -s ./xbiff.c`
then
echo "writting ./xbiff.c"
cat > ./xbiff.c << '\End\Of\File\'
/*
 * $Header: xbiff.c,v 1.5 87/11/09 10:45:11 jim Exp $
 *
 * Copyright 1987 by Cognition Inc.  All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Cognition Inc. not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Cognition Inc. makes no representations
 * about the suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 */

#include <stdio.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Intrinsic.h>
#include <X11/Atoms.h>
#include <X11/TopLevel.h>
#include "Mailbox.h"

extern void exit();

char *ProgramName;

static XrmOptionDescRec options[] = {
{ "-u",		XtNupdate,	XrmoptionSepArg,	(caddr_t) NULL },
{ "-update",	XtNupdate,	XrmoptionSepArg,	(caddr_t) NULL },
};


void main (argc, argv)
    int argc;
    char **argv;
{
    Widget toplevel, w;

    ProgramName = argv[0];
    toplevel = XtInitialize ("XBiff", "XBiff", options, XtNumber (options),
			     &argc, argv);
    if (argc != 1) {
	fprintf (stderr,
		 "usage:\n        %s [-options] [=geometry] [host:dpy]\n",
		 ProgramName);
	fprintf (stderr, "where:\n");
	fprintf (stderr, "    -fg color        foreground color\n");
	fprintf (stderr, "    -bg color        background color\n");
	fprintf (stderr, "    -rv              reverse video\n");
	fprintf (stderr, "    -u seconds       update interval\n");
	exit (1);
    }

    w = XtCreateWidget (ProgramName, mailboxWidgetClass, toplevel, NULL, 0);
    XtRealizeWidget (toplevel);
    XtMainLoop ();
}
\End\Of\File\
else
  echo "will not over write ./xbiff.c"
fi
if `test ! -s ./xlib-xbiff.c`
then
echo "writting ./xlib-xbiff.c"
cat > ./xlib-xbiff.c << '\End\Of\File\'
/*
 * $Header: xlib-xbiff.c,v 1.11 87/11/09 10:49:05 jim Exp $
 *
 * Copyright 1987 by Cognition Inc.  All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Cognition Inc. not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Cognition Inc. makes no representations
 * about the suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 */

/*
 * xbiff - raw Xlib version of xbiff for X.V11R1.  For comparison with
 * toolkit version.  Compile with:
 *
 *                 cc -o xlib-xbiff -O xlib-xbiff.c -lX11
 */

#include <errno.h>			/* for system call errors */
#include <stdio.h>			/* general printing and NULL */
#include <X11/Xlib.h>			/* X11 definitions */
#include <X11/Xutil.h>			/* for XParseGeometry() */
#include <X11/cursorfont.h>		/* for cursor constants */
#include <sys/time.h>			/* for select() */
#include <pwd.h>			/* for getting username */
#include <sys/types.h>			/* for stat() */
#include <sys/stat.h>			/* for stat() */

#include "flagup.bit"			/* get icon definitions */
#include "flagdown.bit"			/* get icon definitions */

typedef int Pixel;

/*
 * Macros
 */

#define PictureWidth flagdown_width
#define PictureHeight flagdown_height

#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))



/*
 * External definitions 
 */

extern int errno;
extern int sys_nerr;
extern char *sys_errlist[];


/*
 * Global definitions
 */

char *ProgramName;			/* argv[0] */
int Argc;				/* argc, for toolkits */
char **Argv;				/* argv, for toolkits */
Display *dpy = NULL;			/* the X connection */
int screen;				/* the X screen to use */
Colormap colormap;			/* the colormap to use */
Window w;				/* where to scribble */
GC gc;					/* how to scribble */
Pixmap flagup_pixmap, flagdown_pixmap;	/* for drawing */
int maxcon, connection_mask;		/* for select */
int window_width, window_height;	/* for drawing */
Pixel background_pixel, foreground_pixel, border_pixel;	 /* for drawing */

Bool bool_reverse_video = False;	/* t:reverse video */
Bool bool_flagup = False;		/* t:put flag up */
int seconds_to_pause = 30;		/* how often to check mailbox */
char *mailbox_filename = "/usr/spool/mail/\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
int volume = 33;			/* how loud to ring bell */


/*
 * Main routine
 */

main (argc, argv)
    int argc;
    char **argv;
{
    char *displayname = NULL;

    ProgramName = argv[0];
    Argv = argv;
    Argc = argc;

    Setup (argc, argv);
    GetUserInfo ();
    Doit ();
    CloseDown (0);
}


/* 
 * Utility routines
 */

char *SysError ()
{
    return (errno > 0 && errno < sys_nerr ? sys_errlist [errno] :
	    "unknown error");
}


/*
 * make_picture - returns a pixmap made from the given bitmap and pixels;
 */

Pixmap make_picture (bits, width, height, fg, bg)
    char *bits;
    int width, height;
    Pixel fg, bg;
{
    Pixmap bitmap = XCreateBitmapFromData (dpy, w, bits, width, height);
    Pixmap pixmap;

    if (!bitmap) return (0);

    /* the following is Sam Black's (masscomp!black) XMakePixmapFromBitmap() */
    pixmap = XCreatePixmap (dpy, w, width, height, DefaultDepth (dpy, screen));
    if (pixmap) {
	GC tmpgc;
	XGCValues gcv;
	gcv.function = GXcopy;
	gcv.foreground = fg;
	gcv.background = bg;
	gcv.graphics_exposures = False;
	tmpgc = XCreateGC (dpy, pixmap,
			   GCFunction | GCForeground | GCBackground |
			   GCGraphicsExposures,
			   &gcv);
	XCopyPlane (dpy, bitmap, pixmap, tmpgc, 0, 0, width, height, 0, 0, 1);
	XFreeGC (dpy, tmpgc);
    }
    return (pixmap);
}
	

static Pixel get_a_color (name, monodefault)
    register char *name;
    register Pixel monodefault;
{
    XColor hard_col, exact_col;

    if (!name) return (monodefault);
    if (strcmp (name, "black") == 0) return (BlackPixel (dpy, screen));
    if (strcmp (name, "white") == 0) return (WhitePixel (dpy, screen));
    if (DisplayCells (dpy, screen) <= 2) return (monodefault);
    if (XAllocNamedColor (dpy, colormap, name, &hard_col, &exact_col))
	  return (hard_col.pixel);
    return (monodefault);
}


Setup (argc, argv)
    int argc;
    char **argv;
{
    char *displayname = NULL;
    int i;
    char *geom = NULL;
    int border_width = 0;
    XSizeHints hints;
    XSetWindowAttributes attrs;
    XClassHint class_hints;
    int display_width, display_height;
    int geom_result;
    XGCValues gcv;
    char *fg_name = NULL, *bg_name = NULL, *bd_name = NULL;
    Bool borderWidth_given = False, update_given = False;
    Bool reverseVideo_given = False, volume_given = False;
    Pixel black, white;

    for (i = 1; i < argc; i++) {
	char *arg = argv[i];

	if (arg[0] == '=') {
	    geom = arg;
	} else if (arg[0] == '-' || arg[0] == '+') {
	    int negated = (arg[0] == '=');
	    switch (arg[1]) {
	      case 'r':			/* -rv or -reverse */
		bool_reverse_video = !negated;
		reverseVideo_given = True;
		break;
	      case 'u':			/* -update */
		if (++i >= argc) goto usage;
		seconds_to_pause = max (1, atoi (argv[i]));
		update_given = True;
		break;
	      case 'b':
		switch (arg[2]) {
		  case 'w':		/* -bw pixels */
		    if (++i >= argc) goto usage;
		    border_width = atoi (argv[i]);
		    borderWidth_given = True;
		    break;
		  case 'd':		/* -bd color */
		    if (++i >= argc) goto usage;
		    bd_name = argv[i];
		    break;
		  case 'g':		/* -bg color */
		    if (++i >= argc) goto usage;
		    bg_name = argv[i];
		    break;
		  default:
		    goto usage;
		}
		break;
	      case 'v':			/* -volume percent */
		if (++i >= argc) goto usage;
		volume = atoi (argv[i]);
		volume_given = True;
		break;
	      case 'f':			/* -fg color */
		if (++i >= argc) goto usage;
		fg_name = argv[i];
		break;
	      default:
	      usage:
		fprintf (stderr, "usage:  %s [options] [=geom] [host:dpy]\n",
			 ProgramName);
		fprintf (stderr, "where options are:\n");
		fprintf (stderr, "    -rv             reverse video\n");
		fprintf (stderr, "    -update secs    time between checks\n");
		fprintf (stderr, "    -bw pixels      border width\n");
		fprintf (stderr, "    -fg color       foreground color\n");
		fprintf (stderr, "    -bg color       background color\n");
		fprintf (stderr, "    -bd color       border color\n");
		exit (1);
	    }
	} else {
	    displayname = arg;
	}
    }

    /*
     * open a connection to the display
     */

    dpy = XOpenDisplay (displayname);
    if (!dpy) {
	fprintf (stderr, "%s:  unable to open display \"%s\".\n",
		 ProgramName, XDisplayName (displayname));
	exit (1);
    }
    maxcon = ConnectionNumber (dpy);
    connection_mask = (1L << maxcon);
    screen = DefaultScreen (dpy);
    colormap = DefaultColormap (dpy, screen);

    display_width = DisplayWidth (dpy, screen);
    display_height = DisplayHeight (dpy, screen);


    /*
     * get the resources, SHOULD USE RESOURCE MANAGER!  Actually, should use
     * toolkit.
     */

    if (!fg_name) fg_name = XGetDefault (dpy, ProgramName, "foreground");
    if (!bg_name) bg_name = XGetDefault (dpy, ProgramName, "background");
    if (!bd_name) bd_name = XGetDefault (dpy, ProgramName, "border");
    if (!borderWidth_given) {
	char *cp = XGetDefault (dpy, ProgramName, "borderWidth");
	if (cp) border_width = atoi (cp);
    }
    if (!update_given) {
	char *cp = XGetDefault (dpy, ProgramName, "update");
	if (cp) seconds_to_pause = atoi (cp);
    }
    if (!reverseVideo_given) {
	char *cp = XGetDefault (dpy, ProgramName, "reverseVideo");
	if (cp && (strcmp (cp, "on") == 0 || strcmp (cp, "ON") == 0))
	  bool_reverse_video = True;
    }
    if (!volume_given) {
	char *cp = XGetDefault (dpy, ProgramName, "volume");
	if (cp) volume = atoi (cp);
    }

    /*
     * get the necessary colors
     */

    black = BlackPixel (dpy, screen);
    white = WhitePixel (dpy, screen);
    foreground_pixel = get_a_color (fg_name, black);
    background_pixel = get_a_color (bg_name, white);
    border_pixel = get_a_color (bd_name, black);

    if (bool_reverse_video) {
	Pixel tmp;

	tmp = background_pixel;
	background_pixel = foreground_pixel;
	foreground_pixel = tmp;
    }

    /*
     * parse the command line geometry flag; style taken from 
     * clients/bitmap/bitmap.c and other applications
     */

    geom_result = NoValue;
    hints.min_width = PictureWidth;
    hints.min_height = PictureHeight;
    hints.flags = PMinSize;

    if (geom) geom_result = XParseGeometry (geom, &hints.x, &hints.y,
					    &hints.width, &hints.height);
    if ((geom_result & WidthValue) && (geom_result & HeightValue)) {
	hints.width = max (hints.width, hints.min_width);
	hints.height = max (hints.height, hints.min_height);
	hints.flags |= USSize;
    }

    if ((geom_result & XValue) && (geom_result & YValue)) {
	hints.flags |= USPosition;
    }

    /*
     * supply program defaults in case user didn't provide info; some window
     * managers (like uwm) will ignore our information and force the user to
     * fully specify the position.  sigh.
     */

    if (!(hints.flags & USSize)) {
	hints.width = PictureWidth;
	hints.height = PictureHeight;
	hints.flags |= PSize;
    }

    if (!(hints.flags & USPosition)) {
	hints.x = hints.y = 0;
	hints.flags |= PPosition;
    }

    if (geom_result & XNegative) {
	/* unlike X10, in X11 you give hints without borders */
	hints.x = display_width + hints.x - hints.width;
    }
    if (geom_result & YNegative) {
	/* unlike X10, in X11 you give hints without borders */
	hints.y = display_height + hints.y - hints.height;
    }

    /*
     * build the window
     */

    attrs.background_pixel = background_pixel;
    attrs.border_pixel = border_pixel;
    attrs.event_mask = ExposureMask | ButtonPressMask | StructureNotifyMask;
    attrs.bit_gravity = ForgetGravity;		/* use exposures instead */
    attrs.cursor = XCreateFontCursor (dpy, XC_top_left_arrow);

    w = XCreateWindow (dpy, RootWindow (dpy, screen),
		       hints.x, hints.y, hints.width, hints.height, 
		       border_width,
		       CopyFromParent, CopyFromParent, CopyFromParent, 
		       CWBackPixel | CWBorderPixel | CWEventMask | CWCursor |
		       CWBitGravity,
		       &attrs);
    if (!w) {
	fprintf (stderr, "%s:  unable to create window.\n", ProgramName);
	CloseDown (1);
    }

    /*
     * set the hints in the right order
     */

    class_hints.res_name = "xbiff_mailbox_window";
    class_hints.res_class = "xbiff";
    XSetClassHint (dpy, w, &class_hints);

    window_width = hints.width;		/* save for making pretty */
    window_height = hints.height;
    XSetStandardProperties (dpy, w, "Mail Snoop", "mailbox", None,
			    Argv, Argc, &hints);

    gcv.function = GXcopy;
    gcv.graphics_exposures = False;	/* don't want {Graphics,No}Expose */
    gc = XCreateGC (dpy, w, GCFunction | GCGraphicsExposures, &gcv);

    /*
     * build up the pixmaps that we'll put into the image
     */

    flagup_pixmap = make_picture (flagup_bits,
				  flagup_width, flagup_height,
				  background_pixel, foreground_pixel);
    flagdown_pixmap = make_picture (flagdown_bits,
				    flagdown_width, flagdown_height,
				    foreground_pixel, background_pixel);
    if (!flagup_pixmap || !flagdown_pixmap) {
	fprintf (stderr, "%s:  unable to make flag pixmaps.\n", ProgramName);
	CloseDown (1);
    }

    /* 
     * and let it rip
     */

    XMapWindow (dpy, w);
    XFlush (dpy);

    /*
     * we should now have ConfigureNotify, MapNotify, and Expose events
     * on their way.
     */

    return;
}


CloseDown (status)
    int status;
{
    if (dpy) XCloseDisplay (dpy);
    exit (status);
}


GetUserInfo ()
{
    char *getlogin();
    char *username;

    username = getlogin ();
    if (!username) {
	struct passwd *pw = getpwuid (getuid ());

	if (!pw) {
	    fprintf (stderr, "%s:  unable to find a username for you.\n",
		     ProgramName);
	    CloseDown (1);
	}
	username = pw->pw_name;
    }
    strcat (mailbox_filename, username);
    return;
}


check_mailbox (force_redraw, reset)
    Bool force_redraw, reset;
{
    struct stat st;
    long mailboxsize = 0;
    static long last_mailbox_size = 0;

    if (stat (mailbox_filename, &st) == 0) {
	mailboxsize = st.st_size;
    }

    /*
     * Now check for changes.  If reset is set then we want to pretent that
     * there is no mail.  If the mailbox is empty then we want to turn off
     * the flag.  Otherwise if the mailbox has changed size then we want to
     * put the flag up.
     *
     * The cases are:
     *    o  forced reset by user                        DOWN
     *    o  no mailbox or empty (zero-sized) mailbox    DOWN
     *    o  same size as last time                      no change
     *    o  bigger than last time                       UP
     *    o  smaller than last time but non-zero         UP
     *
     * The last two cases can be expressed as different from last
     * time and non-zero.
     */

    if (reset) {			/* forced reset */
	bool_flagup = False;
	force_redraw = True;
    } else if (mailboxsize == 0) {	/* no mailbox or empty */
	bool_flagup = False;
	if (last_mailbox_size > 0) force_redraw = True;	 /* if change */
    } else if (mailboxsize != last_mailbox_size) {  /* different size */
	bool_flagup = True;
	force_redraw = True;
	beep ();
    } 

    last_mailbox_size = mailboxsize;
    if (force_redraw) redraw ();
    return;
}


Doit ()
{
    XEvent event;
    XConfigureEvent *configure_event_p = (XConfigureEvent *) &event;

    while (1) {
	if (XPending (dpy) == 0) {
	    wait_for_input ();
	    XSync (dpy, 0);		/* force an error if no server */
	    if (XPending (dpy) == 0) {	/* nope, nothing came in */
		check_mailbox (False, False);
		continue;
	    }
	}
	XNextEvent (dpy, &event);
	switch (event.type) {
	  case ConfigureNotify:
	    window_width = configure_event_p->width;  /* for making pretty */
	    window_height = configure_event_p->height;
	    break;

	  case CirculateNotify:
	  case DestroyNotify:
	  case GravityNotify:
	  case MapNotify:
	  case ReparentNotify:
	  case UnmapNotify:
	    /* ignore */
	    break;

	  case Expose:
	    check_mailbox (True, False);
	    break;

	  case ButtonPress:
	    check_mailbox (True, True);
	    break;

	  default:
	    fprintf (stderr,
		     "%s:  unexpected event type %d from display \"%s\".\n",
		     ProgramName, event.type, DisplayString (dpy));
	    break;
	}
    }
}


int wait_for_input ()
{
    struct timeval tv;
    int readmask;
    int nfound;

    tv.tv_sec = seconds_to_pause;
    tv.tv_usec = 0;

    readmask = connection_mask;

    nfound = select (maxcon + 1, &readmask, NULL, NULL, &tv);
    if (nfound < 0) {
	fprintf (stderr, "%s:  error %d (%s) in select (%d second pause).\n",
		 ProgramName, errno, SysError(), seconds_to_pause);
	CloseDown (1);
    }
    return (nfound);
}


redraw ()
{
    int x, y;
    Pixmap picture;
    Pixel back, border;

    /* center the picture in the window */

    x = (window_width - PictureWidth) / 2;
    x = max (x, 0);
    y = (window_height - PictureHeight) / 2;
    y = max (y, 0);

    if (bool_flagup) {			/* paint the "up" position */
	back = foreground_pixel;
	border = background_pixel;
	picture = flagup_pixmap;
    } else {				/* paint the "down" position */
	back = background_pixel;
	border = foreground_pixel;
	picture = flagdown_pixmap;
    }

    XSetWindowBackground (dpy, w, back);
    XSetWindowBorder (dpy, w, border);
    XClearWindow (dpy, w);
    XCopyArea (dpy, picture, w, gc, 0, 0, PictureWidth, PictureHeight, x, y);
    return;
}


beep ()
{
    XBell (dpy, volume);
    return;
}


\End\Of\File\
else
  echo "will not over write ./xlib-xbiff.c"
fi
echo "Finished archive 1 of 1"
exit