[comp.sources.misc] A sample bitmapped window manager

ganek@apollo.UUCP (11/09/87)

The following C code is from the SIGGRAPH '87 course #23
notes. Course #23 was titled "Introduction to Window Management". 
The code is set of routines that can be used to build a 
simple window system and some simple applications. They 
were used to demonstrate some basic windowing principles. 

I had promised the student that I would distribute them
electronically. (Better late than never! :-)

Any suggestions, improvements or bugs reports would be 
welcomed and may be applied to future courses.  

   Daniel E. Ganek  CHM-02-RD
   Apollo Computer Co.
   220 Mill Rd               
   Chelmsford, MA 01824

   (617)-256-6600 x8543
   ganek@apollo.UUCP

/****start sws.h********/
/*	sws.h - 
 * Include file for the Simple Window System
 * This is used by clients (applications)
 *
 * Author: Daniel E. Ganek	
 * SIGGRAPH '87	
 */

#include <sys/types.h>
#include <sys/time.h>

/* window system error codes*/

#define	sws_no_such_window	-1	/* bad wid */
#define	sws_not_connected	-2	/* window_system_connect not called */
#define	sws_too_many_displays	-3	/* can pen another dislay */
#define	sws_bad_display_name	-4	/* display name unknown */
#define	sws_bad_size		-5	/* zero or negative size window */
#define	sws_bad_background_op	-6	/* invalid background operation */
#define	sws_bad_queue		-7	/* bad qid id */

/* define background and foreground "colors" */

#define	BACKGROUND	0
#define	FOREGROUND	1

/*	
 * Define some window related types: point, area, window_info	
 */

typedef	struct	{
	short	int	x;	/* a point = (x,y) */
	short	int	y;
} point;	

typedef	struct	{
	point	origin;		/* an area = origin, size */
	point	size;
} area;

/* visibility information block */

typedef	struct	{
	area	warea;		/* window area */
	area	*vis_list;	/* ptr to visiblity list */
} vis_info;	

/*	
 * Input Types
 * This is very simple event record.  In the real world we'd have a device ID and quite an extensive
 * set of data types - here everything is encapsulated into an single 8-bit byte	
 */

typedef	struct	{
	struct	timeval	tmstamp;	/* event time */
	long		window;		/* window id */
	point		position;	/* position of locator */
	unsigned char	code;		/* event data/code */
					/* codes 0-127 are ASCII */
					/* codes 128-255 are other */
} input_event;

/* 
 * Event Codes 
 * Codes 0-127 are the standard ASCII events 
 * use codes 128-255 for our special stuff	
 */

#define	LAST_ASCII	0x7F	/* last valid ascii code */
#define	REFRESH		0x80	/* refresh window needed */
#define	ENTER		0x82	/* locator entered window */
#define	EXIT		0x83	/* locator left window */
#define	LOCATOR		0x91	/* locator event */

/* mouse buttons down and up*/

#define	LEFT		0xA0	/* left mouse button down */
#define	LEFT_UP		0xA1	/* left mouse button up */
#define	MIDDLE		0xA2	/* middle mouse button down*/
#define	MIDDLE_UP	0xA3	/* middle mouse button up */
#define	RIGHT		0xA4	/* right mouse button down */
#define	RIGHT_UP	0xA5	/* right mouse button up */

/* 10 function keys f0-f9 down and up */

#define	F0	0xB0	
#define	F0_UP	0xB1	
#define	F1	0xB2	
#define	F1_UP	0xB3	
#define	F2	0xB4	
#define	F2_UP	0xB5	
#define	F3	0xB6	
#define	F3_UP	0xB7	
#define	F4	0xB8	
#define	F4_UP	0xB9	
#define	F5	0xBA	
#define	F5_UP	0xBB	
#define	F6	0xBC	
#define	F6_UP	0xBD	
#define	F7	0xBE	
#define	F7_UP	0xBF	
#define	F8	0xC0	
#define	F8_UP	0xC1	
#define	F9	0xC2	
#define	F9_UP	0xC3	


/*	
 * Input Selection Mask
 * The input selection mask is also greatly simplified. We've	
 * collapsed everything into a small number of bits. Again in	
 * the real world we have to select on a per device and per	
 * code basis, e.g. we'd be able to select any specific ASCII	
 * character. 
 */

typedef	long	event_mask;	

/* system events */

#define	REFRESH_MASK	1<<0	/* window refresh needed */
#define	ENTER_MASK	1<<2	/* locator entered window */
#define	EXIT_MASK	1<<3	/* locator left window */
#define	LOCATOR_MASK	1<<4	/* locator event */
#define	KBD_MASK	1<<5	/* kbd (ASCII) events */
#define	LEFT_MASK	1<<6	/* left mouse button down */
#define	LEFT_UP_MASK	1<<7	/* left mouse button up */
#define	MIDDLE_MASK	1<<8	/* middle mouse button down*/
#define	MIDDLE_UP_MASK	1<<9	/* middle mouse button up */
#define	RIGHT_MASK	1<<10	/* right mouse button down */
#define	RIGHT_UP_MASK	1<<11	/* right mouse button up */

/* 10 Functions f0-f9 down and up */

#define	F0_MASK		1<<12	
#define	F0_UP_MASK	1<<13	
#define	F1_MASK		1<<14	
#define	F1_UP_MASK	1<<15	
#define	F2_MASK		1<<16	
#define	F2_UP_MASK	1<<17	
#define	F3_MASK		1<<18	
#define	F3_UP_MASK	1<<19	
#define	F4_MASK		1<<20	
#define	F4_UP_MASK	1<<21	
#define	F5_MASK		1<<22	
#define	F5_UP_MASK	1<<23	
#define	F6_MASK		1<<24	
#define	F6_UP_MASK	1<<25	
#define	F7_MASK		1<<26	
#define	F7_UP_MASK	1<<27	
#define	F8_MASK		1<<28	
#define	F8_UP_MASK	1<<29	
#define	F9_MASK		1<<30	
#define	F9_UP_MASK	1<<31	
#define	ALL_EVENTS	-1

/* window system functions*/

extern	long	window_system_connect();	/* connect to a display system */
extern	long	window_create();		/* create a new window */
extern	long	window_close();			/* close a window */
extern	long	window_pop();			/* pop(raise) a window */
extern	long	window_change();		/* change origin or size */
extern	long	window_wid();			/* return wid of containing point */
extern	long	window_information();		/* return window info */

/* input functions */

extern	long	input_connect();		/* connect to the input system */
extern	long	input_wait();			/* wait for an input event */
extern	long	input_check();			/* check for input */
extern	long	input_select();			/* declare event interests */
extern	long	input_process();		/* process an input event */
/****end   sws.h********/

/****start sws_env.h****/
/* sws_env.h
 * System dependent include file 
 * for the Simple Window System
 *
 * Author: Daniel E. Ganek	
 * SIGGRAPH '87	
 */

#define	TRUE	1	
#define	FALSE	0

#include "sws.h"	

/* 
 * Internal Definitions
 * These are used by the SWS system independent module (sws.c)
 * and the system dependent module.
 */

/* 
 * Input event queue - a simple ring buffer with and additional system 
 * dependent piece to handle synchronization.	
 */

#define	BUFFER_SIZE	16	/* size of input buffer */	
#define	Q_SYS_SIZE	 2	/* size of system specific stuff */

typedef	struct	{
	int		in;			/* ring buffer in index */
	int		out;			/* wout index */
	input_event	buffer[BUFFER_SIZE];	/*buffer */
	long		system[Q_SYS_SIZE];	/*system specific data */
} event_queue;

/* system routine to handle synchronization */

extern	void	system_init_queue();	/* init system specific stuff in queue structure */
extern	void	system_adv_queue();	/* wake up any waiters on the queue */
extern	void	system_wait_queue();	/* wait for an event to appear on the queue */


/* 
 * Event Routing Block	
 * A list of these hangs off each window 
 * They are used to determine to which queue events are to be routed.	
 */

typedef	struct	route_desc	{
	struct	route_desc	*next;	/* forward */
	struct	route_desc	*prev;	/* back ptrs */
	event_mask		mask;	/* mask definig which event to route */
	event_queue		*queue;	/* queue to route them t  */
} route_block;

/*
 * Window Input Information	
 * This record contains info relating to input.	
 */

typedef	struct	{
	route_block	*erb;	/* event routing block */
} input_info;

/*
 *	Window Information	
 * This record contains all info relating to a window, output and input.	
 */

typedef	struct	win_rec	{
	struct	win_rec	*next;		/* forward ptr */
	struct	win_rec	*prev;		/* back ptrs */
	vis_info	visinfo;	/* area and visiblity info*/
	input_info	inputinfo;	/* input info */
} window_rec;	

/*
 * This record contains all the info relating to a display
 * A display consists of all a screen with windows and input devices.
 */
	
typedef	struct	{
	area		screen;		/* origin and size of screen */
	window_rec	*windows;	/* window list */
	long		back_window;	/* wid of background window */
	route_block	*grab_list;	/* grab list */
	long		locator_wid;	/* window containing locator */
	point		locator_pos;	/* abs position of locator */
	long		*system;	/* system info */
} display_rec;


/* 
 * Procedure to get display record from system 
 */

extern	display_rec	*system_get_display();
	

/*
 * System specific versions of malloc, realloc, and free.
 * In a shared memory environment these work out of shared memory
 */

extern	char	*sws_malloc();
extern	char	*sws_realloc();
extern	void	sws_free();
/**** end  sws_env.h****/

/****start clock.c******/
/*
 * clock.c
 *
 * Author: Daniel E. Ganek	
 * SIGGRAPH '87	
 *
 * Clock program for use with the Simple Window Manager
 * Draws a clock face once every n seconds.
 *
 */

#include <stdio.h>		/* some standard stuff */
#include <math.h>
#include "sws.h"		/* window system definitions */

#define	N_seconds	2	/* redisplay clock every N_seconds */
#define	TRUE		1	
#define	FALSE		0
#define	TWOPI		6.28318

void DispClock();		/* routine to actually draw the clock */

main()
{	
	long		bid;	/* background ID */
	long		wid;	/* our window ID */
	vis_info	w_info;	/* size of window, etc. */
	point		center;	/* where to center clock */
	short		radius;	/* radius of clock proper */
	long		status;	/* internal status - if false, exit */


	bid = window_system_connect(0L);	/* connect to the wid system */

	if (bid <= NULL)	/* oops problems */
		return(bid);

	/* First create a window for the clock */

	w_info.origin.x = 25;
	w_info.origin.y = 25;
	w_info.size.x	= 50;
	w_info.size.y	= 50;

	wid = window_create(w_info.origin,w_info.size);

	status = (wid > 0);	/* set status */

	/*
	 * Once every N_second, redraw the clock.	
	 *	Get window size,	
	 *	Clear window,	
	 *	Calculate clock position	
	 *	center it in the window, use the smaller	
	 *	of x and y dimensions to determine radius	
	 *	Draw it	
	 */

	/* To exit program delete the clock window */	

	while (status == TRUE) {
		if ((status = (draw_lock(wid)) > 0)) {
			if ((status = (window_information(wid, &w_info)) > 0) ) {
				draw_clear(wid,BACKGROUND);
				center.x = (w_info.size.x - 1)/2;
				center.y = (w_info.size.y - 1)/2;
				radius = w_info.size.x < w_info.size.y ? (.9*w_info.size.x/2) : (.9*w_info.size.y/2); 
				DispClock(wid, center, radius);	
			}

			draw_unlock(wid);
		}	

		sleep(N_seconds);
	}

	return (status);
}

/* 
 * This routine displays the image of a clock.	
 * The image consists of a large circle with small	
 * circles at each hour. The time is indicated by	
 * hour and minute hands which are simple lines.	
 */

void 
DispClock(wid, center, radius)
short	radius;
long	wid;
point	center;
{
	int		i;
	point		c;
	long		tim;
	struct tm	t;
	float		f_hour;

	/* Cursory error check*/

	if (radius == 0)	
		return;

	/* Draw the outlining circle */

	draw_circle(wid, center, radius);

	/* Draw the hour tics */

	for (i = 0; i < 12; i++) {
		c.x = center.x + .9*radius*sin(i*TWOPI/12) + .5;
		c.y = center.y + .9*radius*cos(i*TWOPI/12) + .5;
		draw_circle(wid,c,(short int)(.05*radius));
	}

	/* Get the time and draw hands */

	tim = time(NULL);
	t = *localtime(&tim);

	/* Draw minute hand */

	c.x = center.x + .7*radius*sin(t.tm_min*TWOPI/60) + .5;
	c.y = center.y - .7*radius*cos(t.tm_min*TWOPI/60) + .5;
	draw_line(wid, center, c);

	/* Draw hour hand */

	f_hour = (t.tm_hour + t.tm_min/60.)*TWOPI/12;
	c.x = center.x + .5*radius*sin(f_hour) + .5;
	c.y = center.y - .5*radius*cos(f_hour) + .5;
	draw_line(wid, center, c);

	return;
}	
/**** end  clock.c******/

/****start swm.c********/
/*
 * swm.c 
 *
 * Author: Daniel E. Ganek	
 * SIGGRAPH '87	
 *
 * Simple Window Manger for the Simple Window System
 *
 * This is what we refer to as a "minimal" window manager
 * It does the following:
 *	grow window
 *	move window
 *	pop window
 *	close window 
 *	refresh background window
 *
 * It demostrates some of the basic functions
 *	input
 *	refresh(exposure events)
 *	grabbing events
 *
 */

#include <stdio.h>
#include "sws.h"

#define	TRUE	1	
#define	FALSE	0

void	process_event();

static	long		bid;	/* background wid */
static	long		qid;	/* our queue id */
static	event_mask	grab;	/* default grab mask */
static	input_event	event;	/* current input event */

main()
{
	vis_info	w_info;
	long		wid;			/* window to draw in */

	/* Connect us to SWS and get the background ID */

	if ((bid = window_system_connect(0L)) <= NULL) 
		return (bid);

	qid = input_connect(bid);

	/* 
	 * We want to grab some function keys for window manager stuff,
	 * i.e. POP, CLOSE, MOVE AND GROW
	 *
	 * We also want refresh events for the backgorund	
	 */

	grab = F0_MASK | F0_UP_MASK | F1_MASK | F1_UP_MASK |
	 F2_MASK | F2_UP_MASK | F3_MASK | F3_UP_MASK;

	input_select(0, qid, grab);
	input_select(bid, qid, REFRESH_MASK);

	/* just loop waiting for an event we asked for */
	/* and then process it. */

	while (TRUE) {
		input_wait(qid,&event);
		process_event(&event);
	}

	exit(0);
}
	
/*
 * a simple routine to xor a box
 */

void
box(wid, barea)
long	wid;				/* window to draw in */
area	barea;				/* box area */
{
	point	p0;
	point	p1;

	draw_lock(wid);
	draw_clip(bid,FALSE);	

	p0 = barea.origin;
	p1.x = barea.origin.x + barea.size.x - 1;
	p1.y = p0.y;
	draw_xor(wid, p0, p1);		/* top */

	p0.x = p1.x;
	p0.y = p1.y + 1;
	p1.y = barea.origin.y + barea.size.y - 1;
	draw_xor(wid, p0, p1);		/* right */

	p0.x = p1.x - 1;
	p0.y = p1.y;
	p1.x = barea.origin.x;
	draw_xor(wid, p0, p1);		/* bottom */

	p0.x = p1.x;
	p0.y = p1.y - 1;
	p1.y = barea.origin.y + 1;
	draw_xor(wid, p0, p1);		/* left */

	draw_clip(bid, TRUE);		/* turn clipping back on*/
	draw_unlock(wid);	

	return;
}

/*
 * move_window
 *
 * This routine moves a window. It grabs the locator
 * and follows it with a box. It stops when F2 is let up.
 *
 */ 

void
move_window(event)
input_event	*event;
{
	long		wid;		/* window to move */
	vis_info	winfo;

	wid = window_wid(event->position);

	if (wid == bid)			/* can't move background */
		return;

	input_select(0, qid, ALL_EVENTS);	/* grab everything */
	window_information(wid, &winfo);	/* get current origin and size */
	winfo.origin = event->position;		/* start postion */
	box(bid, winfo);			/* draw a box */

	while (TRUE) {				/* look for locs and F2 up */
		input_wait(qid, event);		/* wait for next event */

		if (event->code == F2_UP) {	/* F2_up */
			box(bid, winfo);	/* erase old box */
			break;			/* all done! */
		}

		if (event->code == LOCATOR) {
			box(bid, winfo);		/* erase old box */
			winfo.origin = event->position;	/* move the box */
			box(bid, winfo);		/* redraw box */
		}	
	}

	draw_lock(wid);

	window_change(wid, winfo);		/* move it */
	window_pop(wid);			/* pop it */

	draw_unlock(wid);

	input_select(0, qid, grab);		/* back to old grab */

	return;
}

/*
 * grow_window
 *
 * This routine grows a window. It grabs the locator
 * and follows it with the lower right hand corner of a box.
 * It stops when F3 is let up.
 *
 */ 

long
grow_window(event)
input_event	*event;
{
	long		wid;			/* window to grow */
	vis_info	winfo;
	point		new_size;
	char		box_drawn;

	wid = window_wid(event->position);

	if (wid == bid)				/* can't grow background */
		return;

	input_select(0, qid, ALL_EVENTS);	/* grab everything */
	window_information(wid, &winfo);	/* get current origin and size */
	box_drawn = FALSE;			/* no box yet */

	while (TRUE) {				/* look for locs and F3 up */

		if ((event->code == LOCATOR) || (event->code == F3)) {

			/* calc new size */

			new_size.x = event->position.x - winfo.origin.x;
			new_size.y = event->position.y - winfo.origin.y;

			/* ignore "negative" and small boxes */

			if ((new_size.x > 5) && (new_size.y > 5)) {
				if (box_drawn)
					box(bid, winfo);		/* erase old box */

				winfo.size = new_size;			/* new box size */
				box(bid, winfo);			/* draw it */
				box_drawn = TRUE;
			}
		}

		input_wait(qid, event);			/* wait for next event */

		if (event->code == F3_UP) {		/* F3_up */
			if (box_drawn)
				box(bid, winfo);	/* erase old box */

			break;	
		}
	}

	if ((new_size.x > 5) && (new_size.y > 5)) {	/* ignore "negative" boxes */
		draw_lock(wid);
		window_change(wid, winfo);		/* new size */
		window_pop(wid);			/* pop it */
		draw_unlock(wid);
	}
	else
		box_drawn = FALSE;

	input_select(0, qid, grab);			/* back to old grab */

	return (box_drawn);
}

/*
 * process_event
 * 
 * This routine processes the events we receive.
 * by calling the appropriate routine.
 * Since most routines modify someone elses
 * window, i.e. move it or grow, etc. we 
 * fill it in with the background value until
 * the other guy gets around to fixing it up.
 * 
 * We "own" the background; so we're looking for
 * refresh events for it.	
 *
 */ 

void
process_event(event)
input_event	*event;
{
	long	wid;				/* window */

	wid = 0;

	switch (event->code) {
	case F0:
		wid = window_wid(event->position);

		if (window_pop(wid) <= 0)
			wid = 0;

		break;

	case F1:
		wid = window_wid(event->position);
		window_close(wid);
		wid = 0;

		break;

	case F2:
		wid = window_wid(event->position);
		move_window(event);	

		break;

	case F3:
		wid = window_wid(event->position);

		if (grow_window(event) <= 0)
			wid = 0;	
		
		break;

	case REFRESH:
		if (event->window == bid)
			wid = bid;		/* refresh background */
	}

	if (wid) {
		draw_lock(wid);

		if (wid != bid) 
			draw_clear(wid, BACKGROUND);	
		else
			draw_clear(wid, FOREGROUND);	/* The "background" color for the bid */

		/* is the writing color of other windows */	

		draw_unlock(wid);
	}

	return;
}
/**** end  swm.c********/

/****start sws.c********/
/* 
 * sws.c 
 * Simple Window System
 * 
 * Author: Daniel E. Ganek
 * SIGGRAPH '87
 *
 * This is a simple (almost trivial) window system 
 * There is no window hiearchy; but it does support occluding windows.
 * Input is limited to ASCII keystroke, mouse motion, 
 * mouse buttons, and 10 function keys.
 * 
 * Though simple, this system demostrates many of the functions
 * that must exists in a window system and problems associated
 * with them.  The methods and algorithms included here are 
 * not necessarily the most efficient or desirable. As a matter 
 * of fact, things have been kept simple so as not to confuse
 * the beginner.  NUMEROUS optimizations would be made in a real system.
 */

#include <stdio.h>
#include "sws_env.h"

#define	max(a, b)	((a) < (b) ? (b) : (a))	/* std max and min */
#define	min(a, b)	((a) > (b) ? (b) : (a))

/* 
 * static declarations 
 */

static	display_rec	*display = NULL;		/* pointer to the display record */
static	int		is_locked;			/* did I already lock the db? */
static	area		null_area = { 0, 0, 0, 0 };	/* handy for zeroing things */

/* 
 * forward declarations 
 */

window_rec	*check_window();
void		calc_vis_list();

/*****************************************************************************************/

/* 
 * window_lock 
 * window_unlock 
 *
 * These routines are used to set and release the mutex lock on the 
 * window database. This is necessary in shared memory environment 
 * since we don't want two or more client simultaneously modifying the
 * database!  We rely on system_lock() to do the actual locking.
 *
 * This is not necessary in a server environment, if only the server is
 * accessing the database and display.
 *
 */

void
window_lock()
{
    system_lock(display);
}

void
window_unlock()
{

    system_unlock(display);
}




/*****************************************************************************************/

/* 
 * window_system_connect 
 *
 * This must be the first routine called by a client, since
 * it connects the client to the display system specified
 * by "display_name".  It returns the wid of the background
 * window. By convention, NULL specifies the default display.
 */

long 
window_system_connect(display_name)
char	*display_name;
{
	/* If this is this first time we've been called, do the initialization */
	if (display <= NULL) {
		display = system_get_display(display_name);	/* get screen info pointer */

		if(display <= NULL)				/* some sort of error */
			return ((long)display);

		if (display->windows == NULL) {			/* if no windows, create */
			/* background same size as screen */
			window_lock();
			display->back_window = window_create(display->screen.origin, display->screen.size); 
			display->locator_wid = display->back_window; /*init locator database */
			window_unlock();
		}
		return ((long)display->back_window);
	}

	else					/* already inited - just return background id */
		return ((long)display->back_window);

}

/*****************************************************************************************/

/* 
 * window_create 
 *
 * This routine creates a new window at the specified origin and specified
 * size.  It places this window on top of all others.  Most windows systems
 * would make this window of null size and not display it.  This would give
 * the application time to fix it up and make it look right.  A window can be
 * larger than the size of the display, the visibility lists will not include
 * any areas outside of the display.
 *
 * After creating the window, this routine recalculates the
 * visibilty areas of all other windows.
 */

long
window_create(origin, size)
point	origin;					/* origin of new window */
point	size;					/* size of new widnow */
{
	window_rec	*new_window;

	/* some checks first */

	if (display <= NULL)			/* not inited */
		return (sws_not_connected);

	if ((size.x < 0) | (size.y < 0)) 
		return (sws_bad_size);		/* negative size is illegal */

	/* alloc memory for new window descriptor */ 

	new_window = (window_rec*) sws_malloc(sizeof *new_window);

	/* init window descriptor */

	new_window->visinfo.warea.origin = origin;
	new_window->visinfo.warea.size = size;
	new_window->visinfo.vis_list = NULL;

	/* input stuff */

	new_window->inputinfo.erb = NULL;			/* no input selected */

	window_lock();				/* make sure no one else is around */

	/* insert this window on top of the window list*/

	new_window->next = display->windows;
	display->windows = new_window;
	new_window->prev = NULL;

	if (new_window->next != NULL) 
		new_window->next->prev = new_window;
 
	/* new window is on top, so recalculate vis lists */
	/* no exposure events are possible. */

	calc_vis_list(new_window, FALSE);

	window_unlock();			/* all done with db */

	return ((long)new_window);
}

/*****************************************************************************************/

/*
 * window_change 
 *
 * This routine changes the size and origin of a window.
 * Some systems get fancy and have two routines.
 *
 * This routine recalculates the visibility lists; refresh
 * events can be generated.
 *
 */

long
window_change(wid, origin, size)
long	wid;					/*window to change*/
point	origin;					/*new origin */
point	size;					/*new size */
{
	window_rec	*window;

	/* some checks first */

	if (display <= NULL)				/* not inited */
		return (sws_not_connected);

	if ((window = check_window(wid)) == NULL)	/* bad ID */
		return (sws_no_such_window);

	if (window->next == NULL)			/* can't change background */
		return (sws_bad_background_op);

	if ((size.x < 0) || (size.y < 0))		/* no negative size */
		return (sws_bad_size);

	window_lock();					/* make sure no one else is around */

	/* change parameters and recalc vis lists */

	window->visinfo.warea.size = size;
	window->visinfo.warea.origin = origin;

	/* recalc vis lists, refresh possible */

	calc_vis_list(window, TRUE);
	window_unlock();				/* all done with db */

	return (TRUE);
}

/*****************************************************************************************/

/* 
 * window_close 
 *
 * This routine closes, i.e. deletes, a window. All traces of the
 * window are removed from the system.
 * 
 * This routine recalculates the visibility lists; refresh
 * events can be generated.
 *
 */

long
window_close(wid)
long	wid;
{
	window_rec	*window;
	route_block	*erb;

	/* some check first */

	if (display <= NULL)				/* not inited */
		return (sws_not_connected);

	if ((window = check_window(wid)) == NULL)	/* bad ID */
		return (sws_no_such_window);

	if (window->next == NULL)			/* don't touch background */
		return (sws_bad_background_op);

	window_lock();					/* make sure no one else is around */

	/* first remove window from linked list*/

	if (window->next != NULL)
		window->next->prev = window->prev;

	if (window->prev != NULL)
		window->prev->next = window->next;
	else
		display->windows = window->next;

	/* recalc vis lists, refresh events possible */

	calc_vis_list(window->next, TRUE);
	window_unlock();				/* all done with db */

	/* free up memory */
	/* This is usually the source of memory leaks */
	/* In this case we must delete the vis list, and */
	/* event routing blocks */

	erb = window->inputinfo.erb;			/* erb's */

	while (erb != NULL) {
		window->inputinfo.erb = erb->next;
		sws_free(erb);
		erb = window->inputinfo.erb;
	}

	sws_free(window->visinfo.vis_list);		/* vis list */
	sws_free(window);				/* window record */

	return (TRUE); 
}

/*****************************************************************************************/

/* 
 * window_pop 
 * 
 * This routine pops (raises) a window to the top of
 * of the window list. 
 *
 * Refresh events are only possible for window being popped.
 *
 */

long
window_pop(wid)
long	wid;
{
	window_rec	*window;

	/* some checks first */

	if (display <= NULL)				/* not inited */
		return (sws_not_connected);

	if ((window = check_window(wid)) == NULL)	/* bad ID */
		return (sws_no_such_window);

	window_lock();					/* make sure no one else is around */

	/* if we were on top, don't bother */

	if (window->prev == NULL) {
		window_unlock();
		return (TRUE); 
	}

	/* can't pop, background */

	if (window->next == NULL) {
		window_unlock();
		return (sws_bad_background_op);
	}

	/* first remove window from linked list */

	window->next->prev = window->prev;
	window->prev->next = window->next;

	/* now insert on top of the list */

	window->next = display->windows;		/* we point to old top */
	display->windows = window;			/* screen points to us */
	window->prev = NULL;				/* we're on top */
	window->next->prev = window;			/* next guy points back to us */

	/* (can't be null) */

	/* recalc vis lists, refresh events are only possible for window */

	calc_vis_list(window, FALSE);
	input_process(window, REFRESH);			/* might get unnecessary REFRESH events */
	window_unlock();				/* all done with db */

	return (TRUE);

}

/*****************************************************************************************/

/* 
 * window_information 
 * This routine returns interesting info 
 * about a window: 
 *	origin
 *	size
 *	ptr to vis list
 *
 */

long 
window_information(wid, winfo)
long		wid;
vis_info	*winfo;
{
	window_rec	*window;

	/* some checks first */

	if (display <= NULL)				/* not inited */
		return (sws_not_connected);

	if ((window = check_window(wid)) == NULL)	/* bad ID */
		return (sws_no_such_window);

	/* Just copy internal info - this is a bit dangerous */
	/* since we're giving the app our ptr to the vis lists */
	/* Some people may consider this a feature! */

	window_lock();					/* make sure no one else is around */
	*winfo = window->visinfo;
	window_unlock();				/* all done with db */

	return (TRUE);
}

/*****************************************************************************************/

/* 
 * window_wid 
 * This routine return the wid of the topmost 
 * window containing the point (x, y). This is
 * used by clients to figure out which window
 * the locator is pointing to. 
 *
 */

long 
window_wid(position)
point	position;
{
	window_rec	*window;

	/* some check first */

	if (display <= NULL)				/* not inited */
		return (sws_not_connected);

	window_lock();					/* make sure no one else is around */

	/* Start at the top and look for the first */
	/* window that contains the point */

	for (window = display->windows; window != NULL; window = window->next) {
		if (((position.x > window->visinfo.warea.origin.x)
		 && (position.x <= (window->visinfo.warea.origin.x + window->visinfo.warea.size.x - 1)))
		 && ((position.y > window->visinfo.warea.origin.y)
		 && (position.y <= (window->visinfo.warea.origin.y + window->visinfo.warea.size.y - 1)))) {
			window_unlock();
			return ((long)window);
		}
	}

	window_unlock();				/* all done with db */

	/* if outside just return background */
	/* We could return NULL */

	return (display->back_window);
}

/*****************************************************************************************/

/* 
 * check_window
 * validates a wid and	returns pointer to the window record 
 * in our case wid IS the pointer, this routine simply 
 * checks to see that it is in the current list
 * this prevents random wid from being used, but is not
 * so robust as to detect re-use of the same memory
 *
 */

window_rec *
check_window(wid)
long	wid;
{
	window_rec	*window;

	/* search the window list */

	for (window = display->windows; window != NULL; window = window->next)
		if ((long)window == wid)
			return (window);			/* found it */

	/* didn't find it */

	return (NULL);
}

/*****************************************************************************************/

/*
 * overlap
 * little routine to determine if two rectangles overlap
 * returns TRUE if they do, FALSE otherwise. Doesn't figure
 * out who's on top.
 *
 */

int
overlap(a, b)
area	a;
area	b;
{
	if (a.origin.x >= b.origin.x+b.size.x)	/* a to the right? */
		return (FALSE); 

	if (a.origin.y >= b.origin.y+b.size.y)	/* a below? */
		return (FALSE);

	if (a.origin.x+a.size.x <= b.origin.x)	/* a above? */
		return (FALSE);

	if (a.origin.y+a.size.y <= b.origin.y)	/* a to the left? */
		return (FALSE);

	/* If the above tests fail areas overlap */

	return (TRUE);
}

/*****************************************************************************************/

/*
 * exclude
 *
 * This routine excludes a rectangle(top) from a list of rectangles
 * note that this routine does not produce an irreducible set; but
 * does gaurantee that no two rectangles overlap.
 *
 * (WARNING: I think there's a bug in here!)
 *
 */


area	*
exclude (top, list)
area	top;					/* area to be exclude */
area	*list;					/* list of areas to operate on */
{
	area	*new_list;
	int	i;
	int	j;
	int	len;

	struct	{
		int	xs;
		int	xe;
		int	ys;
		int	ye
	} rem, top_limits;

	/* it's a lot easier using start and end point */

	top_limits.xs = top.origin.x;
	top_limits.xe = top.origin.x + top.size.x;
	top_limits.ys = top.origin.y;
	top_limits.ye = top.origin.y + top.size.y;

	j = 0;
	for (len = 0;list[len].size.x != 0; len++)		/* get list len */
		;

	new_list = (area *)sws_malloc((len+1)*sizeof (*new_list));	/* make new list */

	/* look for the four exposed strips at top, sides and bottom */

	for (i = 0; i < len; i++) {
		if (overlap(top, list[i]) == FALSE)	/*if no overlap, just copy */
			new_list[j++] = list[i];
		else {					/* calc 4 fragments */
							/* make enough room for 4 new entries */
			new_list = (area *)sws_realloc(new_list, max(len+4, j+4)*sizeof (*new_list));

			rem.xs = list[i].origin.x;	/* start with full area */
			rem.xe = list[i].origin.x + list[i].size.x;
			rem.ys = list[i].origin.y;
			rem.ye = list[i].origin.y + list[i].size.y;

			if (top_limits.xs > rem.xs) {	/* left side strip */
				new_list[j].origin.x = rem.xs;
				new_list[j].origin.y = rem.ys;
				new_list[j].size.x = top_limits.xs - rem.xs;
				new_list[j++].size.y = rem.ye - rem.ys;
				rem.xs = top_limits.xs;
			}

			if (top_limits.xe < rem.xe) {	/* right side strip */
				new_list[j].origin.x = top_limits.xe;
				new_list[j].origin.y = rem.ys;
				new_list[j].size.x = rem.xe - top_limits.xe;
				new_list[j++].size.y = rem.ye - rem.ys;
				rem.xe = top_limits.xe;
			}

			if (top_limits.ye < rem.ye) {	/* bottom strip */
				new_list[j].origin.x = rem.xs;
				new_list[j].origin.y = top_limits.ye;
				new_list[j].size.x = rem.xe - rem.xs;
				new_list[j++].size.y = rem.ye - top_limits.ye;
				rem.ye = top_limits.ye;
			}

			if (top_limits.ys > rem.ys) {	/* top strip */
				new_list[j].origin.x = rem.xs;
				new_list[j].origin.y = rem.ys;
				new_list[j].size.x = rem.xe - rem.xs;
				new_list[j++].size.y = top_limits.ys - rem.ys;
			}
		}
	}

	/* free the old list and return new */

	new_list[j] = null_area;	/* insert terminator */
	sws_free(list);

	return (new_list);
}

/*****************************************************************************************/

/* calc_vis_list
 * This routine calculates the visibility list for a set of windows
 * starting with "start_window" and going down to the bottom.
 * The resultant vis lists are not irreducible - we could add 
 * a coalescing routine at the end.
 *
 * When we expose an area of a window, we generate an refresh event.
 * If we don't expose an area we shouldn't generate an refresh event.
 * That calculation is a bit tedious. Our simple exposure algorithm 
 * can't tell the difference between exposure and damage. So, we ask
 * for help from the caller in the form of the "notify" parameter.
 * If TRUE, we generate refresh events; if FALSE we don't.
 *
 * (Doing it right would be a nice excerise for the student) 
 *
 */

void 
calc_vis_list(start_window, notify)
window_rec	*start_window;		/* where in the window list to start */
int		notify;
{
	window_rec	*window;
	window_rec	*super_window;
	int		i;
	int		max_len;
	int		gen_exposure;
	area		*new_list;
	point		end;

	for (window = start_window; window != NULL; window = window->next) {
		max_len = 10;
		new_list = (area *)sws_malloc(max_len*sizeof (*new_list));

		/* Since the physical display can be smaller than the window, 
		 * start with physical display limits and intersect them with
		 * the window limits, if this were hierarchical system we'd
		 * use the parent limits instead of the display limits.
		 */

		/* first, adjust the origin if necessary */

		new_list->origin.x = max(display->screen.origin.x, window->visinfo.warea.origin.x);
		new_list->origin.y = max(display->screen.origin.y, window->visinfo.warea.origin.y);

		/* now calc the size */

		end.x = min(display->screen.origin.x + display->size.x, window->visinfo.warea.origin.x + window->visinfo.warea.size.x);
		end.y = min(display->screen.origin.y + display->size.y, window->visinfo.warea.origin.y + window->visinfo.warea.size.y);
		new_list->size.x = max(end.x - new_list->origin.x, 0);
		new_list->size.y = max(end.y - new_list->origin.y, 0);

		/* if either dim is zero, set the other to zero */

		if (new_list->size.x != 0 && new_list->size.y != 0) {
			new_list[1].size.x =0;
			new_list[1].size.y =0;

			/* we have a no-zero visibility list, exclude area
			 * covered by a superior window
			 */

			for (super_window = display->windows;
			 super_window != window; super_window = super_window->next) {

				/* check for no overlap first */

				if (overlap(super_window->visinfo.warea, window->visinfo.warea))
					new_list = exclude(super_window->visinfo.warea, new_list);
			}
		}
 
		/* all done with this window check to see if */
		/* we should generate an exposure event */

		if (window->visinfo.vis_list != NULL) {
			if (notify) {
				/* Compare the old list to the new and if they are different */
				/* generate an exposure event. NOTE THAT THIS METHOD WILL */
				/* CAUSE SPURIOUS EXPOSURE EVENTS.	(nice exercise for the student!) */

				gen_exposure = FALSE;

				for (i = 0; new_list[i].size.x + window->visinfo.vis_list[i].size.x != 0; i++) {
					/* compare element */

					if (!same_area(&window->visinfo.vis_list[i]), &new_list[i]) {
						gen_exposure = TRUE;	/* not equal */
						break;			/* get out */ 
					}
				}

				if (gen_exposure)
					(void)input_process(window, REFRESH); 
			}
		}

		sws_free(window->visinfo.vis_list);	/* free up the old list, and get new one */
		window->visinfo.vis_list = new_list;
	}

	return;
}

static
same_area(a, b)
area	a, b;
{
	if (a.origin.x != b.origin.x || a.origin.y != b.origin.y)
		return FALSE;
	if (a.size.x != b.size.x || a.size.y != b.size.y)
		return FALSE;
	return TRUE;
}

/*****************************************************************************************/
/* INPUT ROUTINES */
/*****************************************************************************************/

/*
 * input_connect 
 *
 * Creates an input connection between the window system
 * and a client. This conection consists of an event queue 
 * used to queue input events. Queue is standard ring buffer. 
 */

long 
input_connect()
{
	event_queue	*queue;
 
	/* some checks first */

	if (display <= NULL)				/* not inited */
		return (sws_not_connected);

	/* create the event queue */

	queue =	(event_queue *)sws_malloc(sizeof (*queue));
	queue->in = 0;					/* set in and out equal - i.e. queue empty */
	queue->out = 0;

	/* do system specific init */

	system_init_queue(queue);

	return ((long)queue);

}

/*****************************************************************************************/

/*
 * input_checks 
 * This routine returns the next event from the input queue 
 * if none is available it return null.
 *
 */

long
input_check(queue, event)
event_queue	*queue;
input_event	*event;
{
	/* some checks first */

	if (display <= NULL)				/* not inited */
		return (sws_not_connected);

	if (queue == NULL)				/* null qid */
		return (sws_bad_queue);

	/* if something in queue, get it */

	if (queue->in != queue->out) {
		*event = queue->buffer[queue->out];
		queue->out = (queue->out + 1) % BUFFER_SIZE;
		return (TRUE);
	}
	else 
		return (FALSE);
}

/*****************************************************************************************/

/*
 * input_wait returns the next event from the input queue 
 * if none is available this routine blocks until one is available
 *
 */

long 
input_wait(queue, event)
event_queue	*queue;
input_event	*event;
{
	long	result;
 
	/* if something in queue or error return it */
	/* else wait for something */

	while (TRUE) {
		if (result = input_check(queue, event))
			return (result);

		else
			system_wait(queue);
	}

	/* NOTREACHED */
}


/*****************************************************************************************/

/*
 * input select 
 * This routine is used by caller to specify which type
 * events the caller is interested in. They are is specified 
 * by an event mask. No events are ever given to a client
 * unless the client has asked for them.
 *
 * There two type of selects:
 * 
 *	Normal - caller specifies a wid, if the event occurs
 *	in that window, all clients who requested it 
 *	for that window get it - multiple routes!
 *
 *	Grab - caller specifies a NULL qid, the caller wants
 *	all such events whether or not the event occured
 *	in the window. 
 *
 * An event occurs in the window if the locator is in the window
 * or the event is window related, i.e. EXIT, ENTER, REFRESH.
 *
 */

long 
input_select(wid, queue, mask)
long		wid;
event_queue	*queue;
event_mask	mask;
{
	window_rec	*window;	/* window of interest */
	route_block	*route_rec;
	route_block	**last_rec;

	/* some checks first */

	if (display <= NULL)					/* not inited */
		return (sws_not_connected);

	if (wid != 0) {
		if ((window = check_window(wid)) == NULL)	/* bad ID */
			return (sws_no_such_window);

		route_rec = window->inputinfo.erb;
		last_rec = &(window->inputinfo.erb);
	}

	else {
		/* no wid, it's a grab request */

		route_rec = display->grab_list;
		last_rec = &display->grab_list;
	}

	/* get ptr to first routing block and
	 * see if we already have a mask for it 
	 * if mask = 0, delete the entry
	 */

	while (route_rec != NULL) {
		if (route_rec->queue = queue) {			/* found it */
			if (mask != 0)				/* store new mask */
				route_rec->mask = mask;

			else {					/* if NULL mask, delete entry */
				if (route_rec->next != NULL)
					route_rec->next->prev = route_rec->prev;

				if (route_rec->prev != NULL)
					route_rec->prev->next = route_rec->next;

				sws_free(route_rec);
			}

			return (TRUE);
		}

		else {						/* keep looking */
			last_rec = &route_rec;
			route_rec = route_rec->next;
		}
	}

	/* no entry for this queue, create one (unless mask == 0) */

	if (mask != 0) {

		/* get some memory */

		route_rec = (route_block *)sws_malloc(sizeof (*route_rec));
		route_rec->queue = queue;			/* store qid */
		route_rec->mask = mask;				/* current mask */
		route_rec->prev = *last_rec;			/* fix-up pointers */
		route_rec->next = NULL;				/* we are the end of list */

		if (route_rec->prev != NULL) 
			route_rec->prev->next = route_rec;
		else
			*last_rec = route_rec;
	}

	return (TRUE);
}

/*****************************************************************************************/

/*
 * input_process 
 * This routine takes an event from a client and processes it, i.e,
 * tries to route it. It is primarily used internally and by an
 * "input server process". It can be called by any client. 
 * 
 * If a wid is specified the event is routed directly to that
 * window, it cannot be "grabbed". If no wid is specified, 
 * the event is routed to the window containing the locator.
 *
 */

long 
input_process(wid, code, data)
long		wid;				/* route event to this window */
unsigned char	code;				/* event code */
point		data;				/* device specific data */
{
	void	position_locator();		/* some forward declarations */
	long	route_event();

	input_event	event;		/* event to be generated and routed */
	long		success;	/* boolean as to whether or not we've routed the event */
	window_rec	*window;	/* window of interest */
	event_mask	mask;		/* routing mask */

	if (display <= NULL)			/* not inited */
		return (sws_not_connected);

	/* check for device specific processing */
	/* in our case that's the locator */

	if (code == LOCATOR) 
		position_locator(data);		/* this routine will generate enter/exit events */

	/* check the wid and validate locator postion */
	/* if wid route to that window, if 0 use wid that locator is in */

	if (wid != 0) {
		if ((window = check_window(wid)) == NULL)	/* bad ID */
			return (sws_no_such_window);

		event.window = wid;				/* store the window ID */
	}

	else {							/* no wid, use locator wid */

		/* bad locator wid */

		if ((window = check_window(display->locator_wid)) == NULL)
			position_locator(display->locator_pos);		/* fix it */

		event.window = display->locator_wid;
	}

	/* now generate an event record and route it to all who want it */

	gettimeofday(&event.tmstamp, NULL);		/* get timestamp */
	event.code = code;				/* store code */
	success = FALSE;				/* we haven't routed yet */
	mask = 0;					/* force mask generation */

	/* if no wid initially specified, check grab list */

	if (wid == 0) {
		event.window = display->back_window;		/* for grabs use backgropund wid */
		event.position = display->locator_pos;		/* and abs position */
		success = route_event(display->grab_list, &event, &mask, FALSE);
	}

	/* if no grab, try window routing list */

	if (success == FALSE ) {
		window = check_window(event.window);		/* get window ptr */

		/* calculate relative position of locator in window */

		event.position.x = display->locator_pos.x - window->visinfo.warea.origin.x;
		event.position.y = display->locator_pos.y - window->visinfo.warea.origin.y;
		success = route_event(window->inputinfo.erb, &event, &mask, TRUE);
	}

	return (success);
}

/*****************************************************************************************/

/*
 * route_event 
 * This routine routes an event, using the specified routing list
 * It calculates and returns a mask for the event.
 * If multi_route is TRUE, the event is routed to multiple clients.
 *
 */

long 
route_event(list, event, mask, multi_route)
route_block	*list;
input_event	*event;
event_mask	*mask;
long		multi_route;
{
	route_block *route_rec;
	long		in;
	long		success;

	/* Generate a mask if necessary.
	 * This is really cumbersome since we crammed 
	 * all events into a single 32 bit mask. In the
	 * real world we would have set up a cleaner relation.
	 */

	if (*mask == 0)
		switch (event->code) {
		case LOCATOR:	*mask = LOCATOR_MASK;	break;
		case REFRESH:	*mask = REFRESH_MASK;	break;
		case ENTER:	*mask = ENTER_MASK;	break;
		case EXIT:	*mask = EXIT_MASK;	break;
		case LEFT:	*mask = LEFT_MASK;	break;
		case LEFT_UP:	*mask = LEFT_UP_MASK;	break;
		case MIDDLE:	*mask = MIDDLE_MASK;	break;
		case MIDDLE_UP:	*mask = MIDDLE_UP_MASK;	break;
		case RIGHT:	*mask = RIGHT_MASK;	break;
		case RIGHT_UP:	*mask = RIGHT_UP_MASK;	break;
		case F0:	*mask = F0_MASK;	break;
		case F0_UP:	*mask = F0_UP_MASK;	break;
		case F1:	*mask = F1_MASK;	break;
		case F1_UP:	*mask = F1_UP_MASK;	break;
		case F2:	*mask = F2_MASK;	break;
		case F2_UP:	*mask = F2_UP_MASK;	break;
		case F3:	*mask = F3_MASK;	break;
		case F3_UP:	*mask = F3_UP_MASK;	break;
		case F4:	*mask = F4_MASK;	break;
		case F4_UP:	*mask = F4_UP_MASK;	break;
		case F5:	*mask = F5_MASK;	break;
		case F5_UP:	*mask = F5_UP_MASK;	break;
		case F6:	*mask = F6_MASK;	break;
		case F6_UP:	*mask = F6_UP_MASK;	break;
		case F7:	*mask = F7_MASK;	break;
		case F7_UP:	*mask = F7_UP_MASK;	break;
		case F8:	*mask = F8_MASK;	break;
		case F8_UP:	*mask = F8_UP_MASK;	break;
		case F9:	*mask = F9_MASK;	break;
		case F9_UP:	*mask = F9_UP_MASK;	break;
		default:	*mask = KBD_MASK;	break;	/* event->code <= LAST_ASCII */
		}

	/* Try to route the event, look at each mask on the routing list. */
	/* Keep going until the list is exhausted unless no multiple routes */
	/* were specified. In that case exit on the first route */

	success = FALSE;
	route_rec = list;

	while (route_rec != NULL) {			/* look for a match */
		if (route_rec->mask & *mask) {		/* found one */
			in = (route_rec->queue->in + 1) % BUFFER_SIZE;	/* calc new in */
			if (in != route_rec->queue->out) {	/* check for overflow */
				route_rec->queue->buffer[route_rec->queue->in] = *event;	/* there's room */
				route_rec->queue->in = in;	/* advance the in */
				system_adv(route_rec->queue);	/* wake up waiters */
				success = TRUE;	/* successful route */
			}
		}

		if (success && (multi_route == FALSE)) 
			break;

		route_rec = route_rec->next;	/* go thru all routing blocks */
	}

	return (success);
}

/*****************************************************************************************/

/*
 * position_locator
 * This routine keeps track of the position of the locator.  It will generate
 * EXIT and ENTER EVENTS as the locator moves from window to window.
 *
 * It also moves the cursor pattern.
 */

void
position_locator(position)
point	position;
{
	long	new_wid;

	/* get wid of new position */

	new_wid = window_wid(position);

	if (display->locator_wid != new_wid) {		/* it's different */
		if (display->locator_wid != 0) 
			/* generate an exit event */
			input_process(display->locator_wid, EXIT);

		display->locator_pos = position;	/* new position */
		display->locator_wid = new_wid;	/* new wid */ 

		/* generate an enter event */
		input_process(display->locator_wid, ENTER);
	}

	else						/* same wid, just new position */
		display->locator_pos = position;


	draw_cursor(position);				/* fix up the cursor */

	return;
}
/**** end  sws.c********/

/****start sis.c********/
/*
 * sis.c 
 * Simple Input Server 
 * for the Simple Window System
 *
 * Author: Daniel E. Ganek	
 * SIGGRAPH '87	
 *
 */

#include <stdio.h>
#include "sws_env.h"

main()
{
	long	bid;		/* background wid */
	char	code;		/* input code */
	point	data;		/* input data */

	/* Connect us to the SWS - we're probably the first ones */

	if ((bid = window_system_connect(0L)) <= NULL) 
		return (bid);

	/*
	 * Just loop getting an input event from the system and	
	 * processing it.  To convert this to a general purpose	
	 * server replace "get_system_input" with a "get_event"	
	 * routine and replace "input_process" with a switch	
	 * statement to process either input events or client	
	 * requests.
	 */

	while (TRUE) {
		get_system_input(&code, &data);	/* get a system event */
		input_process(0, code, data);	/* give it to the SWS */
	}					/* and let it route it */

	exit(0);
}
/**** end  sis.c********/