[comp.sys.atari.st] "startup" desk accessory

dclemans@mntgfx.UUCP (02/22/87)

What follows is a special "startup" desk accessory collection, called
"startupm.c".

It contains the following pieces:

	All "control panel" desktop.inf lines are read on bootup (or on
	request) and the system is initialized appropriately.  If you've
	used the standard Atari desk accessories to set desktop parameters
	(colors, mouse info, etc), serial port info and parallel port info
	and saved that to a desktop.inf file, this desk accessory will
	set that information without having to have the standard desk
	accessories present, taking up memory and accessory slots.

	(Using an algorithm described once that's probably dependent on
	the current OS roms) "startupm" displays the current amount of
	free memory in the upper right hand corner of the screen.  It also
	displays the current time and date adjacent to the free memory
	display.

	"Startupm" puts an entry in the desk accessory menu called
	"Other...".  If that entry is selected, a dialog box is presented
	with three options.  The first, "Reset", rereads the desktop.inf
	file and resets system parameters.  It is typically used after
	some program has clobbered the color map.

	The second, "Idle", is a screen saver pattern that displays a
	bouncing "ST" icon on a background that periodically changes color.

	The final option, "Run Program", provides a way to run a program
	(which can't be a GEM program (it must be a TOS-only program)
	unfortunately) via a file selector dialog box.  It's only really
	useful if you want to run something that's not currently displayed
	in a window, and you don't want to change the windows.

"Startupm.c" should be compiled as a desk accessory (of course).  I have
only tried Megamax C; using other compilers shouldn't be difficult.

dgc

/*********** Start of startupm.c **********/
/*
 * Startup: implement the instructions regarding the ST
 *	"control panel", as specified in the file \desktop.inf.
 *	This allows the control panel desk accessory set to be 
 *	removed from the system, thus saving memory.
 *
 * We also need to look at the keyboard clock.  If that has a "good"
 * value, the system clock should be set to match the keyboard clock.
 *
 * Dave Clemans, 7/86
 *
 * Modified 1/87 by Dave Clemans
 * Displays an information line in the top right corner of menu bar
 * that gives the current time and date, and the amount of free memory.
 * The free memory size algorithm is taken from a Compuserve posting,
 * and WILL ONLY WORK WITH THE CURRENT TOS ROMS!
 *
 * Modified 2/87 by Dave Clemans
 * Implement a "screen saver" idle pattern with a bouncing "ST".
 * Implement a way to execute TOS compatible (i.e. non-GEM) programs
 * (only really useful in cases where you want to look at files not
 * in an existing window, and you don't want to change existing windows).
 *
 * The file "\desktop.inf" is a series of lines, each describing
 * part of the controls available to the user.  All significant
 * lines begin with a '#'.  A character following the '#' tells
 * what kind of line it is.
 *
 * #aNNNNNN			RS232 configuration
 *	These set various attributes such as baud rate.
 * Index:			Description:
 * 0				1 = half duplex, 0 = full duplex
 * 1				0 = 9600, 1 = 4800, 2 = 1200, 3 = 300
 * 2				0 = no parity, 1 = odd, 2 = even
 * 3				0 = 8 bits, 1 = 7 bits, 2 = 6 bits, 3 = 5 bits
 * 4				bit 0 on -> XON/XOFF, bit 1 on -> RTS/CTS
 * 5				0 = no stripping, 1 = strip high bit
 *
 * #bNNNNNN			Printer configuration
 *	These set various printer configuration values, such as draft
 *	vs. final qualify, dot matrix vs. daisy wheel, etc.
 * Index:			Description:
 * 0				1 = daisy wheel, 0 = dot matrix
 * 1				1 = color, 0 = black and white
 * 2				1 = 960 pixels per line, 0 = 1280 pixels per line
 * 3				1 = final quality, 0 = draft quality
 * 4				1 = modem port, 0 = printer port
 * 5				1 = sheet feeder, 0 = continous feed
 *
 * #cNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
 *					The actual "control panel"
 *	The first 48 data characters on this line are actually 16 triplets
 *	of numbers, each number ranging from 0 to 7.  Each number is a
 *	color index; together they describe the system color pallette.
 *	The other characters encode key click, bell, whether
 *	or not keys repeat, time delay before key repeat, and interval
 *	between repeated characters, among others.
 * Index:			Description:
 * 0-2				RGB value for color index 0
 * 3-5				RGB value for color index 1
 * 6-8				RGB value for color index 2
 * 9-11				RGB value for color index 3
 * 12-14			RGB value for color index 4
 * 15-17			RGB value for color index 5
 * 18-20			RGB value for color index 6
 * 21-23			RGB value for color index 7
 * 24-26			RGB value for color index 8
 * 27-29			RGB value for color index 9
 * 30-32			RGB value for color index 10
 * 33-35			RGB value for color index 11
 * 36-38			RGB value for color index 12
 * 39-41			RGB value for color index 13
 * 42-44			RGB value for color index 14
 * 45-47			RGB value for color index 15
 * 48				Mouse double click response time
 * 49				1 = key click enabled, 0 = no key click
 * 50				1 = audible bell, 0 = no bell
 * 51-52			Time for keys to start to repeat
 * 53-54			How fast keys repeat
 *
 * The following are local lines that this program accepts; they are NOT
 * generated by the control panel.
 *
 * #sNN				Seek rate
 *	Set the seek rate value for floppy disks
 * Index:			Description:
 * 0-1				The seek rate
 *
 * #rCCCCC......		Run program
 *	Run a program (basically, an "autorun" facility that happens after
 *	GEM is initialized).  No arguments are passed to the program.
 *	The program is run only a boot/reset, not after a desk accessory call.
 * Index:			Description:
 * 0-end			Full pathname of the program
 */
#include <string.h>
#include <osbind.h>
#define SEEKRATE	0x440
#define	CONTERM		0x484
#define	READCLOCK	0x1C

#include <obdefs.h>
#include <gemdefs.h>

#ifndef	NULL
#define	NULL		0
#endif	NULL

char selector[] = "[1][Select the desired action...            ][Reset|Idle|Run program]";

/*
 * Data buffers
 */
char buffer[512];
int bufSize,bufPtr;
int saveColors[16],usedColors[16];

/*
 * Simple decimal string to int conversion
 */
static int str2int(s,l)
register char *s;
register int l;
{
	register int acc;

	for (acc = 0; l > 0; --l)
	{	/* convert a character at a time */
		acc *= 10;
		acc += ((*s++) - '0');
	}
	return acc;
}	/* end of str2int */

/*
 * Simple hexadecimal string to int conversion
 */
static int str2hex(s,l)
register char *s;
register int l;
{
	register int acc;

	for (acc = 0; l > 0; --l)
	{	/* convert a character at a time */
		acc <<= 4;
		if (*s >= 'A' && *s <= 'F')
			acc += ((*s++) - 'A');
		else if (*s >= 'a' && *s <= 'f')
			acc += ((*s++) - 'a');
		else acc += ((*s++) - '0');
	}
	return acc;
}	/* end of str2hex */

/*
 * Get a character from the input file with buffering
 */
static int readchar(fp)
int fp;
{
	if (bufPtr >= bufSize)
	{	/* fill up our buffer */
		bufSize = (int)Fread(fp,(long)(sizeof buffer),buffer);
		if (bufSize <= 0)
			return bufSize;
		bufPtr = 0;
	}
	return buffer[bufPtr++];
}	/* end of readchar */

/*
 * Read a line of input
 */
static int readline(fp,buf)
int fp;
char *buf;
{
	register int c;
	register char *sbuf;

	sbuf = buf;
	for (;;)
	{	/* until EOF, ERR, or end of line */
		c = readchar(fp);
		if (c <= 0)
			return c;
		if (sbuf == buf && (c == '\r' || c == '\n'))
			continue;
		*sbuf++ = c;
		if (c == '\r' || c == '\n')
			break;
	}
	*sbuf = '\0';
	return (int)(sbuf - buf);
}	/* end of readline */

/*
 * Data for "supervisor" procedures
 */
int bitsOn,bitsOff;
char *cmdName,*cmdTail;

/*
 * Set the console type bits
 */
static setCONTERM()
{
	register char *p;

	p = (char *)CONTERM;
	*p = (*p & ~bitsOff) | bitsOn;
}	/* end of setCONTERM */

/*
 * Set the seek rate bits
 */
static setSEEKRATE()
{
	register char *p;

	p = (char *)SEEKRATE;
	*p = bitsOn;
}	/* end of setSEEKRATE */

/*
 * Read the keyboard clock; if the value looks reasonable set the system
 * clock to match the keyboard clock.
 */
static checkClock()
{
	union
	{
		long longword;
		struct
		{
			int date;
			int time;
		} a;
	} theDate;

	/* Get the keyboard clock */
	theDate.longword = Gettime();

	/* Set the GEMDOS time if everything looks good */
	if (((theDate.a.date >> 9) & 0x3F) != 0)
	{	/* Set the system time */
		Tsetdate(theDate.a.date);
		Tsettime(theDate.a.time);
	}
}	/* end of checkClock */

/*
 * Read in and process the control panel information file to reset the
 * desktop into the modes that the user wants to use.
 */
static resetDesktop(flag)
int flag;
{
	register int infFP;
	register int word;
	register char *p;
	int limit;
	int words[21];
	char line[80];

	if (flag)
		checkClock();

	infFP = Fopen("\\desktop.inf",0);
	if (infFP < 0)
		infFP = Fopen("c:\\desktop.inf",0);
	if (infFP < 0)
		infFP = Fopen("a:\\desktop.inf",0);
	if (infFP < 0)
	{	/* If we can't open the file */
		return;			/* no info to reset from */
	}
	bufSize = bufPtr = 0;

	while (readline(infFP,line) > 0)
	{	/* while something to do */
		if (line[0] != '#')
			continue;
		switch (line[1])
		{	/* see if it is a line we know about */
			case 'r':		/* run program */
				if (!flag)	/* if not boot/reset time */
					break;
				line[0] = '\0';
				line[1] = '\0';
				for (p = &line[2]; *p; p++)
					if (*p == '\n' || *p == '\r')
						*p = '\0';
				evnt_timer(1000,0);
				Pexec(0,&line[2],&line[0],(char *)NULL);
				break;
			case 's':		/* seekrate */
				bitsOn = str2hex(&line[2],2);
				Supexec(setSEEKRATE);
				break;
			case 'a':		/* rs232 conf */
				words[0] = str2int(&line[2],1);
				words[1] = str2int(&line[3],1);
				words[2] = str2int(&line[4],1);
				words[3] = str2int(&line[5],1);
				words[4] = str2int(&line[6],1);
				words[5] = str2int(&line[7],1);
				/* half/full duplex not implemented */
				/* strip bit not implemented */
				word = 0x80 | (words[3] << 5);
				if (words[3] >= 2)
					word |= 0x18;
				else word |= 0x08;
				if (words[2] != 0)
					word |= 0x04;
				if (words[2] == 2)
					word |= 0x02;
				switch (words[1])
				{	/* get speed number */
					case 0:
						words[1] = 1;
						break;
					case 1:
						words[1] = 2;
						break;
					case 2:
						words[1] = 7;
						break;
					case 3:
						words[1] = 9;
						break;
				}
				Rsconf(words[1],words[4],word,-1,-1,-1);
				break;
			case 'b':		/* printer conf */
				words[0] = str2int(&line[2],1);
				words[1] = str2int(&line[3],1);
				words[2] = str2int(&line[4],1);
				words[3] = str2int(&line[5],1);
				words[4] = str2int(&line[6],1);
				words[5] = str2int(&line[7],1);
				word = 0;
				if (words[0])
					word |= 0x01;
				if (words[1])
					word |= 0x02;
				if (words[2])
					word |= 0x04;
				if (words[3])
					word |= 0x08;
				if (words[4])
					word |= 0x10;
				if (words[5])
					word |= 0x20;
				Setprt(word);
				break;
			case 'c':		/* control panel */
				for (word = 0; word < 16; word++)
					words[word] = str2hex(&line[2+(3*word)],3);
				words[16] = str2int(&line[50],1);
				words[17] = str2int(&line[51],1);
				words[18] = str2int(&line[52],1);
				words[19] = str2hex(&line[53],2);
				words[20] = str2hex(&line[55],2);
				limit = Getrez();
				switch (limit)
				{	/* how many colors? */
					case 0:
						limit = 16;
						break;
					case 1:
						limit = 4;
						break;
					case 2:
						limit = 2;
						break;
					default:
						limit = 0;
						break;
				}
				for (word = limit-1; word >= 0; word--)
				{	/* do the funny color mapping */
					if (word == 0)
						Setcolor(0,words[0]);
					else if (word == 1)
						Setcolor(limit-1,words[1]);
					else Setcolor(word-1,words[word]);
				}
				Kbrate(words[19],words[20]);
				bitsOn = bitsOff = 0;
				if (words[18])
					bitsOn |= 0x04;
				else bitsOff |= 0x04;
				if (words[17])
					bitsOn |= 0x01;
				else bitsOff |= 0x01;
				Supexec(setCONTERM);
				evnt_dclick(words[16],1);
				break;
			default:		/* stuff we ignore */
				break;
		}
	}
	Fclose(infFP);
}	/* end of resetDesktop */

/*
 * Data structures and function declarations to talk to GEM.
 */
int	contrl[12];				/* The parameter passing structures */
int	intin[128];
int	intout[128];
int	ptsin[128];
int	ptsout[128];
int	screen_width,screen_height;	/* Screen, etc. info */
int	char_width,char_height;
int	box_width,box_height;
int	desk_x,desk_y,desk_width,desk_height;
int	appl_handle;			/* Handles */
int	vdi_handle;

/*
 * Structures to look at GEM's memory allocator
 */
typedef struct MD
{
	struct MD *m_link;		/* next MD (or NULL) */
	long m_start;			/* startaddr of block */
	long m_length;			/* #bytes in block */
	long *m_own;			/* pointer to owning process */
} MD;

typedef struct MPB
{
	MD *mp_mfl;			/* memory free list */
	MD *mp_mal;			/* memory allocated list */
	MD *mp_rover;			/* roving ptr */
} MPB;

#define	GEMDOS_MPB	0x56EC

/*
 * Find the total amount of free space in the system
 * Must be called in supervisor mode
 */
long memsize;

freemem()
{
	MD *mdp;

	memsize = 0;
	mdp = (MD *)((MPB *)GEMDOS_MPB)->mp_mfl;
	while (mdp != (MD *)NULL)
	{	/* while there are free blocks */
		memsize += mdp->m_length;
		mdp = mdp->m_link;
	}
}	/* end of freemem */

/*
 * Return a printable character version of the free memory info
 */
char *mem_info()
{
	register long ksize;
	static char membuf[16];

	Supexec(freemem);
	ksize = memsize / 1024L;
	if (ksize % 1024L > 512)
		ksize++;
	sprintf(membuf,"%ldK free",ksize);
	return membuf;
}	/* end of mem_info */

/*
 * Return a printable version of the current time
 */
char *time_info()
{
	long datetime;
	int date,time;
	static char timebuf[32];

	datetime = Gettime();
	time = (int)(datetime & (long)0xFFFF);
	date = (int)((datetime >> 16) & (long)0xFFFF);
	sprintf(timebuf,"%d/%d %d:%02d",
		(date & 0x1E0) >> 5,
		date & 0x1F,
		/* (((date & 0xFE00) >> 9) & 0x7F) + 80, */
		((time & 0xF800) >> 11) & 0x1F,
		(time & 0x7E0) >> 5 /* , */
		/* (time & 0x1F) * 2 */);
	return timebuf;
}	/* end of time_info */

/*
 * Save/Restore the menu bar of a screen
 */
static menubar(flag)
int flag;
{
	register int i;
	register int *screen;
	static int savebar[961];

	screen = (int *)Physbase();

	if (flag)
	{	/* if to save the menubar */
		for (i = 0; i <= 960; i++)
			savebar[i] = screen[i];
	}
	else
	{	/* if to restore the menubar */
		for (i = 0; i <= 960; i++)
			screen[i] = savebar[i];
		form_dial(3,0,0,screen_width,screen_height,0,0,screen_width,screen_height);
	}
}	/* end of menubar */

/* GEM Icon Definition: */
#define st_W 0x0040
#define st_H 0x0040
#define stSIZE 0x0100
int st[stSIZE] = 
{ 0x0000, 0x0000, 0x0000, 0x0000,
  0x0000, 0x0000, 0x0000, 0x0000,
  0x00FF, 0xFFFF, 0xFFFF, 0xFF00,
  0x0080, 0x0000, 0x0000, 0x0100,
  0x0080, 0x0000, 0x0000, 0x0100,
  0x009F, 0xFFFF, 0xFFFF, 0xF900,
  0x0090, 0x0000, 0x0000, 0x0900,
  0x0090, 0x0000, 0x0000, 0x0900,
  0x0091, 0xFFC0, 0xFFFF, 0xC900,
  0x0093, 0xFFC0, 0xFFFF, 0xC900,
  0x0093, 0x0000, 0x00C0, 0x0900,
  0x0093, 0x0000, 0x00C0, 0x0900,
  0x0093, 0x0000, 0x00C0, 0x0900,
  0x0093, 0x0000, 0x00C0, 0x0900,
  0x0093, 0x0000, 0x00C0, 0x0900,
  0x0093, 0x0000, 0x00C0, 0x0900,
  0x0093, 0x0000, 0x00C0, 0x0900,
  0x0091, 0xFF80, 0x00C0, 0x0900,
  0x0091, 0xFFC0, 0x00C0, 0x0900,
  0x0090, 0x00C0, 0x00C0, 0x0900,
  0x0090, 0x00C0, 0x00C0, 0x0900,
  0x0090, 0x00C0, 0x00C0, 0x0900,
  0x0090, 0x00C0, 0x00C0, 0x0900,
  0x0090, 0x00C0, 0x00C0, 0x0900,
  0x0090, 0x00C0, 0x00C0, 0x0900,
  0x0090, 0x00C0, 0x00C0, 0x0900,
  0x0093, 0xFFC0, 0x00C0, 0x0900,
  0x0093, 0xFF80, 0x00C0, 0x0900,
  0x0090, 0x0000, 0x0000, 0x0900,
  0x0090, 0x0000, 0x0000, 0x0900,
  0x009F, 0xFFFF, 0xFFFF, 0xF900,
  0x0080, 0x0000, 0x0000, 0x0100,
  0x0080, 0x0000, 0x0000, 0x0100,
  0x00FF, 0xFFFF, 0xFFFF, 0xFF00,
  0x0000, 0x0000, 0x0000, 0x0000,
  0x0000, 0x0000, 0x0000, 0x0000,
  0x0FFF, 0xFFFF, 0xFFFF, 0xFFF0,
  0x0811, 0x1111, 0x1111, 0x1010,
  0x0822, 0x2222, 0x2222, 0x2010,
  0x0844, 0x4444, 0x4444, 0x4010,
  0x0888, 0x8888, 0x8888, 0x8010,
  0x0911, 0x1111, 0x1111, 0x0010,
  0x0FFF, 0xFFFF, 0xFFFF, 0xFFF0,
  0x0800, 0x0000, 0x0000, 0x0010,
  0x0BFF, 0xFFFF, 0xFFCF, 0x9FD0,
  0x0A49, 0x2492, 0x484A, 0x9550,
  0x0A49, 0x2492, 0x484A, 0x9550,
  0x0BFF, 0xFFFF, 0xFFCF, 0x9FD0,
  0x0A49, 0x2492, 0x484A, 0x9550,
  0x0A49, 0x2492, 0x484A, 0x9550,
  0x0BFF, 0xFFFF, 0xFFCF, 0x9FD0,
  0x0A49, 0x2492, 0x484A, 0x9550,
  0x0A49, 0x2492, 0x484A, 0x9550,
  0x0BFF, 0xFFFF, 0xFFCF, 0x9FD0,
  0x0A49, 0x2492, 0x4840, 0x1550,
  0x0A49, 0x2492, 0x4840, 0x1550,
  0x0BFF, 0xFFFF, 0xFFC0, 0x1FD0,
  0x0A40, 0x0000, 0x0440, 0x1150,
  0x0A40, 0x0000, 0x0440, 0x1150,
  0x0BFF, 0xFFFF, 0xFFC0, 0x1FD0,
  0x0800, 0x0000, 0x0000, 0x0010,
  0x0FFF, 0xFFFF, 0xFFFF, 0xFFF0,
  0x0000, 0x0000, 0x0000, 0x0000,
  0x0000, 0x0000, 0x0000, 0x0000
};
FDB	mfdb;

/*
 * Display an idle pattern on the screen until a mouse button
 * is pressed.
 */
static idlePattern()
{
	register int counter,ctr;
	static int notfirst = 0;
 	int x,y;
	int xy_array[8];
	int colors[2];
	FDB screen;

	graf_mouse(M_OFF,(MFORM *)NULL);
	menubar(1);
	for (x = 0; x < 16; x++)
	{	/* get current color pallette */
		usedColors[x] = saveColors[x] = Setcolor(x,-1) & 0x7FFF;
	}
	vsf_interior(vdi_handle,1);
	vsf_style(vdi_handle,0);
	vsf_color(vdi_handle,0);
	xy_array[0] = xy_array[1] = 0;
	xy_array[2] = screen_width - 1;
	xy_array[3] = screen_height - 1;
	vr_recfl(vdi_handle,xy_array);

	if (!notfirst)
	{	/* initialize the icon descriptor block */
		mfdb.fd_addr = (long)st;
		mfdb.fd_w = st_W;
		mfdb.fd_h = st_H;
		mfdb.fd_wdwidth = st_W / 16;
		mfdb.fd_nplanes = 1;
		mfdb.fd_stand = 1;
		mfdb.fd_r1 = mfdb.fd_r2 = mfdb.fd_r3 = 0;
		vr_trnfm(vdi_handle,&mfdb,&mfdb);
		notfirst++;
	}

	screen.fd_addr = 0;
	screen.fd_w = screen_width;
	screen.fd_h = screen_height;
	screen.fd_wdwidth = screen_width / 16;
	screen.fd_stand = 0;
	screen.fd_r1 = screen.fd_r2 = screen.fd_r3 = 0;

	xy_array[0] = 0;
	xy_array[1] = 0;
	xy_array[2] = st_W - 1;
	xy_array[3] = st_H - 1;
	colors[0] = 0;
	colors[1] = 1;
	for (counter = 0; ; counter++)
	{	/* wait for something to do */
		if ((counter % 64) == 0)
		{	/* switch screen colors? */
			ctr = ((counter >> 6) & 0xFF) % 15;
			ctr++;
			usedColors[ctr] = saveColors[ctr];
			usedColors[0] = usedColors[ctr];
			usedColors[ctr] = saveColors[0];
			Setpallete(usedColors);
		}
		xy_array[4] = (int)((long)Random() % (long)(screen_width - st_W));
		xy_array[5] = (int)((long)Random() % (long)(screen_height - st_H));
		xy_array[6] = xy_array[4] + st_W - 1;
		xy_array[7] = xy_array[5] + st_H - 1;
		vrt_cpyfm(vdi_handle,3,xy_array,&mfdb,&screen,colors);

		for (x = 0; x < 7; x++)
		{	/* wait for a while */
			for (y = 0; y < 32000; y++)
				/* do nothing */;
		}
		if (Bconstat(2) != 0)
		{	/* if a key was pressed */
			break;
		}

		vrt_cpyfm(vdi_handle,3,xy_array,&mfdb,&screen,colors);
	}

	Setpallete(saveColors);
	menubar(0);
	graf_mouse(M_ON,(MFORM *)NULL);
}	/* end of idlePattern */

/*
 * Run a program, selected via a file selector box
 */
static runProgram()
{
	register int i;
	register char *cp;
	int action;
	char path[64],file[64];

	menubar(1);

	strcpy(path,"C:\\*.*");
	file[0] = '\0';
	action = 0;
	fsel_input(path,file,&action);
	if (action == 0)
		return;
	cp = rindex(path,'\\');
	if (cp != (char *)NULL)
	{	/* wild card still there? */
		if (index(cp,'*') != (char *)NULL || index(cp,'?') != (char *)NULL)
			*cp = '\0';
	}
	if (file[0] != '\0')
	{	/* if a full or partial filename to add on */
		strcat(path,"\\");
		strcat(path,file);
	}
	file[0] = file[1] = '\0';
	graf_mouse(M_OFF,(MFORM *)NULL);
	Pexec(0,path,file,(char *)NULL);

	menubar(0);
	graf_mouse(M_ON,(MFORM *)NULL);
}	/* end of runProgram */

/*
 * Initialize the GEM interface; load our resource file.
 */
static int Initialize()
{
	int work_in[11],work_out[57];
	register int i;
	extern int gl_apid;

	appl_handle = appl_init();
	if (appl_handle < 0)
	{	/* If it didn't work */
		return appl_handle;
	}
	appl_handle = gl_apid;		/* work around problem in GEM binding */
	vdi_handle = graf_handle(&char_width,&char_height,&box_width,&box_height);
	if (vdi_handle < 0)
	{	/* If it didn't work */
		return vdi_handle;
	}
	wind_get(0,WF_WORKXYWH,&desk_x,&desk_y,&desk_width,&desk_height);
	for (i = 0; i < 10; i++)
		work_in[i] = 1;
	work_in[10] = 2;
	if (v_opnvwk(work_in,&vdi_handle,work_out) == 0)
	{	/* If it didn't work */
		return -1;
	}
	screen_width = work_out[0]+1;
	screen_height = work_out[1]+1;

	return 0;
}	/* end of Initialize */

/*
 * Main routine; read desktop.inf lines and dispatch them
 * off to be processed.
 */
main()
{
	extern int gl_apid;
	int menuid;
	register int rc;
	int mbuf[8];
	register int event;
	int junk;
	int count;
	int keycode,keystate;
 	int x,y,w;
	char buffer[48];

	rc = Initialize();
	if (rc < 0)
		Pterm(rc);
	resetDesktop(1);
	menuid = menu_register(gl_apid,"  Other...");
	vst_height(vdi_handle,6,&char_width,&char_height,&box_width,&box_height);
	w = (screen_width * 2) / 3 + 10;
	sprintf(buffer,"%-9s %-10s",mem_info(),time_info());
	v_gtext(vdi_handle,w,box_height,buffer);
	for (;;)
	{	/* wait for something to do */
		event = evnt_multi(MU_MESAG|MU_TIMER,
			2,1,1,
			0,0,0,0,0,
			0,0,0,0,0,
			mbuf, 5000, 0, &x, &y, &junk, &keystate, &keycode, &count);
		sprintf(buffer,"%-9s %-10s",mem_info(),time_info());
		v_gtext(vdi_handle,w,box_height,buffer);
		if (event & MU_MESAG)
		{	/* a message from the system? */
			switch (mbuf[0])
			{	/* anything that we have to worry about? */
				case AC_OPEN:
					rc = form_alert(1,selector);
					if (rc == 1)
					{	/* redo desktop.inf */
						resetDesktop(0);
					}
					else if (rc == 2)
					{	/* screen saver */
						idlePattern();
					}
					else if (rc == 3)
					{	/* run a program */
						runProgram();
					}
					break;
				case AC_CLOSE:	/* probably can ignore this */
					break;
				default:
					break;
			}
		}
	}
}	/* end of main */