[net.sources] Magnifying glass for Sun3

schwartz@swatsun.UUCP (04/13/87)

Here is a fun toy for your Sun workstation.  It is the latest (last?)
revision of a magnifying glass program that displays a blown up image
of the area near the cursor in a window. 

To unpack, cd to a clean directory and feed the following to /bin/sh
: ------------- Snip Here ------------------------------------------:
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	Makefile
#	glass.c
#	glass.icon
#	patchlevel.h
# This archive created: Sun Apr 12 21:12:02 1987
export PATH; PATH=/bin:$PATH
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'

This is about the magnifying glass for Sun3s.

The program, glass, takes one argument, which quantifies the magnification it
is to do. If no argument is supplied, glass assumes you mean no magnification.
Glass opens it's own window to display the magnified image.  The image
is a region to the upper left of the cursor, the direction the cursor points,
with the lower right corner of the window corresponding to the tip of the
cursor.

To compile, use the makefile provided.

Glass has several shortcomings right now.

 o		There should be a way to dynamically set the magnification
		factor.  This is easy to do, but who has time?

 o		The program is too slow for small magnification.  Unary
		magnification is fast since a single bitblit is all that
		is needed, and large magnification is fast, since there
		aren't many pixels to do.  I'd be happy if Sun would
		add a "pr_mag" to the sunview library, and thereby get the
		job done without going through all the indirection that users
		have to.

 o		Glass eats up all your spare cycles.  Maybe this isn't too
		bad, especially if you don't use it all the time.  Also
		fixing it (via nice or sleep) might make it slower than
		you like.			

 o		Lint complains about all the sunview library calls that are
		defined in header files and not used.  Too bad.

If anyone decides to fix these, please mail me a copy of the changes.
I, of course, will post any changes I make.

			
			-- Scott Schwartz
				USmail: Swarthmore College
					Swarthmore PA, 19081
				UUCP: ...seismo!bpa!swatsun!schwartz

SHAR_EOF
fi # end of overwriting check
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
# Makefile to compile magnifying glass
# by Scott Schwartz, Dec 1986

# flags.  normally just -O
CFLAGS=-O
LIBS= -lsuntool -lsunwindow -lpixrect

# dependencies for glass
glass: glass.o 
	cc $(CFLAGS) -o glass glass.o $(LIBS)

glass.o: glass.c patchlevel.h Makefile
	cc $(CFLAGS) -c glass.c

# utility stuff
clean:
	rm -f glass glass.o
lint:
	lint -hx glass.c $(LIBS)
shar:
	shar README Makefile glass.c glass.icon patchlevel.h >glass.shar
SHAR_EOF
chmod +x 'Makefile'
fi # end of overwriting check
if test -f 'glass.c'
then
	echo shar: will not over-write existing file "'glass.c'"
else
cat << \SHAR_EOF > 'glass.c'
/*
 *  Magnifying glass		(for Sun-3 workstations)
 *
 *  Copyright 1986, 1987 by Scott Schwartz
 *  This program may be copied with the provision that this copyright 
 *  message remains, and that further distribution of this code and it's 
 *  derivatives is not subject to additional restrictions.
 * 
 *  compile with -lsuntool -lsunwindow -lpixrect
 *
 *  vi: set ts=8
 *
 *  revision history:
 *  10 Nov 86	initial coding		 		Scott Schwartz 
 *
 *  version 1.0		25 Nov 86
 *	modified to draw big pixels with raster-ops		ses
 *
 *  version 1.1		27 Jan 87
 *	does magnification in memory, 				ses
 *	to avoid screen access
 *
 *  version 1.2		27 Jan 87
 *	flood destination with white, 				ses/af
 *      then draw only black spots
 *
 */

#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <suntool/sunview.h>
#include <suntool/canvas.h>
#include <pixrect/pixrect_hs.h>

/* useful macros */
#define EVER ;;
#define break_when(condition)	if (condition) break
#define error(msg)  { fprintf(stderr, "%s: %s\n", argv[0], msg); exit(1); }

/* forward declarations */
extern char *getenv(), *sprintf();
extern Notify_error notify_dispatch();
static Notify_value notice_destroy_event();
extern void pw_mag();
extern void view();

/* constants */
static char frame_label[] = "Magnifying Glass 1.2";
static short icon_image[] = {
#include "glass.icon"
};
DEFINE_ICON_FROM_IMAGE(icon, icon_image);
#define NILPR ((Pixrect *)0)
#define screen_max_x  1152
#define screen_max_y  900
#define max_mag 100

/* global vars */
static Pixrect	*tmpsrc; /* working space, to avoid screen access */ 
static Pixrect	*tmpdst;
static int done = 0;

/* 
 *  main routine
 */
main(argc,argv)
int argc;
char **argv;
{
	/* externals */
	extern Pixrect *tmpsrc, *tmpdst;

	/* things to magnify */
	char *parent; /* name of the parent window, usually win0 */
	Pixrect *srcpr; /* the source pixrect, i.e. the whole screen */
	int rootfd;  /* file descriptor for root window, for cursor location */

	/* things to draw on */
	Frame frame;	/* the actual window frame */
	Canvas canvas;	/* our particular canvas, filling the frame */

	/* misc */
	int mag = 1;			/* magnification in output window */
	char *magstr = "x99999";	/* allocate space for actual string */
	int retcode;

	/* dissociate from parent process */
	if (fork()) 
		exit(0);

	/* open rootwindow */
	parent = getenv("WINDOW_PARENT");
	if (parent==NULL) 
		error("can't get WINDOW_PARENT from environment.")
	rootfd = open(parent, O_RDONLY, 0);
	if (rootfd<0)
		perror(argv[0]); 

	/* open frame buffer */
	srcpr = pr_open("/dev/fb");
	if (srcpr==NILPR)
		perror(argv[0]);

	/* create output window */
	frame = window_create(NULL, FRAME, 
			FRAME_LABEL, frame_label,
			FRAME_ICON, &icon,
			WIN_HEIGHT, 200,
			WIN_WIDTH, 200,
			FRAME_ARGC_PTR_ARGV, &argc, argv,
			0);

	/* handle arguments */		
	if (argc > 1) {
		retcode = sscanf(argv[1], "%d", &mag);
		if (retcode <= 0) 
			error("problem evaluating arguments\nUsage: glass magnification [suntools-options]");
		mag = (mag > max_mag) ? max_mag : mag;
	}

	/* allocate static pixrects */
	tmpsrc = mem_create(screen_max_x, screen_max_y, 1);
	tmpdst = mem_create(screen_max_x, screen_max_y, 1);

	/* gather data on output pixwin */
	canvas = window_create(frame, CANVAS, 0);

	/* set up frame label, and activate window */
	(void)sprintf(magstr, "%s x%d", frame_label, mag);
	window_set(frame, FRAME_LABEL, magstr, WIN_SHOW, TRUE, 0);

	/* set up an interposed event handler so we know when to quit */
	(void)notify_interpose_destroy_func(frame, notice_destroy_event);
	
	/* copy input to output forever */
	for (EVER) {
		(void) notify_dispatch();
		break_when(done);
		if (window_get(frame, FRAME_CLOSED))
			sleep(1);
		else
			view(rootfd, srcpr, canvas, mag);
	}
}

/*
 * view does the work of displaying the (possibly) magnified image
 * at the location indicated by rootfd (mouse). view copies srcpr to dstpw.
 */
void view(rootfd, srcpr, canvas, mag)
	int rootfd;		/* root window, for mouse data */	
	Pixrect *srcpr;		/* screen source pixrect */
	Canvas canvas;
	int mag;
{
	/* constants */
	int maxy = (srcpr->pr_size.y);
	int maxx = (srcpr->pr_size.x);
	Pixwin *dstpw = canvas_pixwin(canvas);

	/* local vars */	
	int x,y;
	int w, h;

	/* 
	 * read mouse coords from vuid register.  sadly, the sunview
	 * programmers guide is not clear on this.  appendix A tells some,
	 * but you have to look at <sundev/vuid_event.h> to see what
 	 * virtual events you can find out about.  luckily, the mouse is
	 * one of them.
	 */
	x = win_get_vuid_value(rootfd, LOC_X_ABSOLUTE);
	y = win_get_vuid_value(rootfd, LOC_Y_ABSOLUTE);

	/* 
	 * find out how big our drawing surface is.
	 */
	w = (int) window_get(canvas, CANVAS_WIDTH);
	h = (int) window_get(canvas, CANVAS_HEIGHT);

	/*
	 * draw on it.
	 */
	if (mag<=1) {
		x = min(maxx-w, max(x-w, 0));
		y = min(maxy-h, max(y-h, 0));
		pw_rop(dstpw, 0, 0, w, h, PIX_SRC, srcpr, x, y);
	}
	else {
		x = min(maxx-w/mag, max(x-w/mag, 0));
		y = min(maxy-h/mag, max(y-h/mag, 0));
		pw_mag(dstpw, 0, 0, w, h, mag, srcpr, x, y);
	}
}

/*
 * pw_mag copies a magnified view of spr to dpw using pixel replication.
 * the arguments are the same as those to the pw_rop library call, except
 * that magnification is passed instead of raster-op.
 */
void pw_mag(dpw, dx, dy, w, h, mag, spr, sx, sy)
	Pixwin *dpw;	/* destination pixwin */
	int dx, dy;  	/* destination x,y */
	int w, h;	/* width and height of block to copy */
	int mag; 	/* magnification */
	Pixrect *spr;	/* source pixrect */
	int sx,sy;	/* location in source to copy from */
{
	/* locals */
	register short si, sj;
	register short di, dj;
	register short jmax = h/mag, imax = w/mag;

	Pixrect r;	/* holds the size of the drawing region when */
			/* gaining access to the screen */
	r.pr_size.x = w;
	r.pr_size.y = h;

	/* make off screen copy of source */
	pr_rop(tmpsrc, 0, 0, w, h, PIX_SRC|PIX_DONTCLIP, spr, sx, sy);

	/* 
	 * mangify....
	 * flood destination with white (why not?)
	 * for each black pixel in the source
	 *   draw a large square on the destination
	 */
	pr_rop(tmpdst, 0, 0, w, h, PIX_CLR|PIX_DONTCLIP, tmpdst, 0, 0);
	for (sj=0, dj=0; sj<=jmax; ++sj, dj+=mag) {
	   for (si=0, di=0; si<=imax; ++si, di+=mag) {
	     if (pr_get(tmpsrc, si, sj)!=0)
	     	pr_rop(tmpdst, di, dj, mag, mag, PIX_SET|PIX_DONTCLIP,
			tmpdst, 0, 0);
	   }
	}		

	/* draw */
	pw_lock(dpw, &r);
	pw_rop(dpw, dx, dy, w, h, PIX_SRC, tmpdst, 0, 0);
   	pw_unlock(dpw);
}

/* 
 * a service routine that gets called when the frame is destroyed
 * by someone selecting 'quit'.  this is basically right out of the 
 * manual.
 */
static Notify_value
notice_destroy_event(frame, status)
	Frame *frame;
	Destroy_status status;
{
	if (status != DESTROY_CHECKING) {
		done = 1;
		(void) notify_stop();
	}
	return (notify_next_destroy_func(frame,status));
}

SHAR_EOF
fi # end of overwriting check
if test -f 'glass.icon'
then
	echo shar: will not over-write existing file "'glass.icon'"
else
cat << \SHAR_EOF > 'glass.icon'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,
	0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,
	0x8888,0x8888,0x8888,0x8888,0x8888,0x8BF8,0x8888,0x8888,
	0x2222,0x1CC7,0x2222,0x2222,0x2222,0x70C1,0xE222,0x2222,
	0x8888,0xC0C0,0xE888,0x8888,0x888B,0xC0C0,0xF888,0x8888,
	0x2226,0x0C0C,0x0E22,0x2222,0x2224,0x0C0C,0x0C22,0x2222,
	0x888C,0x0C0C,0x0E88,0x8888,0x889C,0x0C0C,0x0F08,0x8888,
	0x2230,0xC0C0,0xC122,0x2222,0x2230,0xC0C0,0xC1A2,0x2222,
	0x88A0,0xC0C0,0xC088,0x8888,0x88A0,0xC0C0,0xC088,0x8888,
	0x2240,0x0000,0x0062,0x2222,0x2246,0x0C0C,0x0C62,0x2222,
	0x88C6,0x0C0C,0x0C48,0x8888,0x88C6,0x0C0C,0x0C48,0x8888,
	0x2246,0x0C0C,0x0C62,0x2222,0x2240,0x0000,0x0062,0x2222,
	0x88C0,0xC0C0,0xC1C8,0x8888,0x88A0,0xC0C0,0xC188,0x8888,
	0x2220,0xC0C0,0xC1A2,0x2222,0x2230,0xC0C0,0xC1A2,0x2222,
	0x8890,0x0000,0x0188,0x8888,0x8896,0x0C0C,0x0F88,0x8888,
	0x222E,0x0C0C,0x0F22,0x2222,0x2226,0x0C0C,0x0FA2,0x2222,
	0x888E,0x0C0C,0x0FC8,0x8888,0x888B,0xC0C0,0xFFC8,0x8888,
	0x2222,0xC0C0,0xE7F2,0x2222,0x2222,0x70C1,0xE3F2,0x2222,
	0x8888,0x9CCF,0x09F8,0x8888,0x8888,0x8BF8,0x88FC,0x8888,
	0x2222,0x2222,0x227E,0x2222,0x2222,0x2222,0x223F,0x2222,
	0x8888,0x8888,0x889F,0x8888,0x8888,0x8888,0x888F,0xC888,
	0x2222,0x2222,0x2227,0xE222,0x2222,0x2222,0x2223,0xF222,
	0x8888,0x8888,0x8889,0xF888,0x8888,0x8888,0x8888,0xF888,
	0x2222,0x2222,0x2222,0x7A22,0x2222,0x2222,0x2222,0x3222,
	0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,
	0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,
	0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,
	0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,
	0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,
	0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,
	0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,
	0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222
SHAR_EOF
fi # end of overwriting check
if test -f 'patchlevel.h'
then
	echo shar: will not over-write existing file "'patchlevel.h'"
else
cat << \SHAR_EOF > 'patchlevel.h'
#define PATCHLEVEL 2
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
Scott Schwartz  @  Swarthmore College Computer Science Program
UUCP: ...{{seismo,ihnp4}!bpa, cbmvax!vu-vlsi, sun!liberty}!swatsun!schwartz
AT&T: (215)-328-8610	/* lab phone */

mark@mimsy.UUCP (04/15/87)

Here is an improved version of the magnifying glass program for
the Sun-3.  I have changed it to use the classic rasterop algorithm for
magnifiying, which speed up magnifying quite a bit.  I also added
panels to dynamically change the frequency of updating (so it doesn't eat all
the cpu cycles) and the amount of magnification.

Thanks to Scott Schwartz, who wrote the original, for making something to change!
-mark
Spoken: Mark Weiser 	ARPA:	mark@mimsy.umd.edu	Phone: +1-301-454-7817
After May 15, 1987: weiser@parcvax.xerox.com
-------cut here------------------------------
PATH=/bin:/usr/bin:/usr/ucb:/etc:$PATH
export PATH
all=FALSE
if [ x$1 = x-a ]; then
	all=TRUE
fi
echo Extracting README
sed 's/^X//' <<'//go.sysin dd *' >README

This is about the magnifying glass for Sun3s.

The program, glass, takes one argument, which quantifies the magnification it
is to do. If no argument is supplied, glass assumes you mean no magnification.
Glass opens it's own window to display the magnified image.  The image
is a region to the upper left of the cursor, the direction the cursor points,
with the lower right corner of the window corresponding to the tip of the
cursor.

To compile, use the makefile provided.

If anyone decides to fix these, please mail me a copy of the changes.
I, of course, will post any changes I make.

			
			-- Scott Schwartz
				USmail: Swarthmore College
					Swarthmore PA, 19081
				UUCP: ...seismo!bpa!swatsun!schwartz

//go.sysin dd *
if [ `wc -c < README` != 716 ]; then
	made=FALSE
	echo error transmitting README --
	echo length should be 716, not `wc -c < README`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 644 README
	echo -n '	'; ls -ld README
fi
echo Extracting Makefile
sed 's/^X//' <<'//go.sysin dd *' >Makefile
# Makefile to compile magnifying glass
# by Scott Schwartz, Dec 1986

# flags.  normally just -O
CFLAGS=-g
LIBS= -lsuntool -lsunwindow -lpixrect

# dependencies for glass
glass: glass.o 
	cc $(CFLAGS) -o glass glass.o $(LIBS)

glass.o: glass.c patchlevel.h Makefile
	cc $(CFLAGS) -c glass.c

# utility stuff
clean:
	rm -f glass glass.o
lint:
	lint -hx glass.c $(LIBS)
shar:
	makescript -o glass.shar README Makefile glass.c glass.icon patchlevel.h
//go.sysin dd *
if [ `wc -c < Makefile` != 448 ]; then
	made=FALSE
	echo error transmitting Makefile --
	echo length should be 448, not `wc -c < Makefile`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 755 Makefile
	echo -n '	'; ls -ld Makefile
fi
echo Extracting glass.c
sed 's/^X//' <<'//go.sysin dd *' >glass.c
X/*
 *  Magnifying glass		(for Sun-3 workstations)
 *
 *  Copyright 1986, 1987 by Scott Schwartz
 *  Lots of changes by, but not Copyrighted by, Mark Weiser.
 *  This program may be copied with the provision that this copyright 
 *  message remains, and that further distribution of this code and it's 
 *  derivatives is not subject to additional restrictions.
 * 
 *  compile with -lsuntool -lsunwindow -lpixrect
 *
 *  vi: set ts=8
 *
 *  revision history:
 *  10 Nov 86	initial coding		 		Scott Schwartz 
 *
 *  version 1.0		25 Nov 86
 *	modified to draw big pixels with raster-ops		ses
 *
 *  version 1.1		27 Jan 87
 *	does magnification in memory, 				ses
 *	to avoid screen access
 *
 *  version 1.2		27 Jan 87
 *	flood destination with white, 				ses/af
 *      then draw only black spots
 *
 *  version 2.0		15 Apr 87				mark weiser
 *	vastly faster magnification algorithm using only 2k blits
 *	instead of k**2 (where k is the height or width of the final).
 *	Also added a panel for friendliness, and stopped using *all* the cpu!
 */

#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <suntool/sunview.h>
#include <suntool/canvas.h>
#include <suntool/panel.h>
#include <pixrect/pixrect_hs.h>

X/* useful macros */
#define error(msg)  { fprintf(stderr, "%s: %s\n", argv[0], msg); exit(1); }

X/* forward declarations */
extern char *getenv(), *sprintf();
extern Notify_error notify_dispatch();
static Notify_value notice_destroy_event();
extern void pw_mag();
extern void view();
void update();
void glass_time_proc();
Notify_value update_value_proc();

X/* constants */
static char frame_label[] = "Magnifying Glass 2.0";
static short icon_image[] = {
#include "glass.icon"
};
DEFINE_ICON_FROM_IMAGE(icon, icon_image);
#define NILPR ((Pixrect *)0)
#define screen_max_x  1152
#define screen_max_y  900
#define max_mag 100

X/* global vars */
typedef struct pixrect Pixrect;	/* for 3.2 compatibility. */
static Pixrect	*tmpsrc; /* working space, to avoid screen access */ 
static Pixrect	*tmpdst;
static int done = 0;
Pixrect *srcpr; /* the source pixrect, i.e. the whole screen */
int rootfd;  /* file descriptor for root window, for cursor location */
	Frame frame;	/* the actual window frame */
Canvas canvas;	/* our particular canvas, filling the frame */
int mag = 1;			/* magnification in output window */
int delay = 1000;

X/* 
 *  main routine
 */
main(argc,argv)
int argc;
char **argv;
{
	/* externals */
	extern Pixrect *tmpsrc, *tmpdst;

	/* things to magnify */
	char *parent; /* name of the parent window, usually win0 */

	/* things to draw on */
	Panel panel;

	/* misc */
	char *magstr = "x99999";	/* allocate space for actual string */
	int retcode;

	/* open rootwindow */
	parent = getenv("WINDOW_PARENT");
	if (parent==NULL) 
		error("can't get WINDOW_PARENT from environment.")
	rootfd = open(parent, O_RDONLY, 0);
	if (rootfd<0)
		perror(argv[0]); 

	/* open frame buffer */
	srcpr = pr_open("/dev/fb");
	if (srcpr==NILPR)
		perror(argv[0]);

	/* create output window */
	frame = window_create(NULL, FRAME, 
			FRAME_LABEL, frame_label,
			FRAME_ICON, &icon,
			FRAME_ARGC_PTR_ARGV, &argc, argv,
			0);

	panel = window_create(frame, PANEL, 0);
	(void) panel_create_item(panel, PANEL_SLIDER,
		PANEL_LABEL_STRING, "Mag:",
		PANEL_VALUE, 1,
		PANEL_MIN_VALUE, 1,
		PANEL_MAX_VALUE, 8,
		PANEL_SHOW_RANGE, FALSE,
		PANEL_SHOW_VALUE, TRUE,
		PANEL_SLIDER_WIDTH, 100,
		PANEL_NOTIFY_PROC, update_value_proc,
		PANEL_CLIENT_DATA, &mag,
		0);
	(void) panel_create_item(panel, PANEL_SLIDER,
		PANEL_LABEL_STRING, "Delay:",
		PANEL_VALUE, 10000,
		PANEL_MIN_VALUE, 1000,
		PANEL_MAX_VALUE, 999999,
		PANEL_SHOW_RANGE, FALSE,
		PANEL_SHOW_VALUE, FALSE,
		PANEL_SLIDER_WIDTH, 100,
		PANEL_NOTIFY_PROC, update_value_proc,
		PANEL_CLIENT_DATA, &delay,
		PANEL_ITEM_X, ATTR_COL(0),
		PANEL_ITEM_Y, ATTR_ROW(1),
		0);

	window_fit_height(panel);

	/* handle arguments */		
	if (argc > 1) {
		retcode = sscanf(argv[1], "%d", &mag);
		if (retcode <= 0) 
			error("problem evaluating arguments\nUsage: glass magnification [suntools-options]");
		mag = (mag > max_mag) ? max_mag : mag;
	}

	/* allocate static pixrects */
	tmpsrc = mem_create(screen_max_x, screen_max_y, 1);
	tmpdst = mem_create(screen_max_x, screen_max_y, 1);

	/* gather data on output pixwin */
	canvas = window_create(frame, CANVAS,
			WIN_HEIGHT, 200,
			WIN_WIDTH, 200,
			0);

	window_fit(frame);

	/* set up an interposed event handler so we know when to quit */
	(void)notify_interpose_destroy_func(frame, notice_destroy_event);
	
	/* start us in a second */
	do_with_delay(glass_time_proc, 1, 0);

	/* copy input to output forever */
	window_main_loop(frame);
	exit(0);
}

X/*
 * Thing to do at intervals.
 */
void
glass_time_proc()
{
	if (done) return;
	view(rootfd, srcpr, canvas, mag);
	if (window_get(frame, FRAME_CLOSED)) {
		do_with_delay(glass_time_proc, 2, 0);
	} else {
		do_with_delay(glass_time_proc, 0, delay);
	}
}

X/*
 * view does the work of displaying the (possibly) magnified image
 * at the location indicated by rootfd (mouse). view copies srcpr to dstpw.
 */
void view(rootfd, srcpr, canvas, mag)
	int rootfd;		/* root window, for mouse data */	
	Pixrect *srcpr;		/* screen source pixrect */
	Canvas canvas;
	int mag;
{
	/* constants */
	int maxy = (srcpr->pr_size.y);
	int maxx = (srcpr->pr_size.x);
	Pixwin *dstpw = canvas_pixwin(canvas);

	/* local vars */	
	int x,y;
	int w, h;

	/* 
	 * read mouse coords from vuid register.  sadly, the sunview
	 * programmers guide is not clear on this.  appendix A tells some,
	 * but you have to look at <sundev/vuid_event.h> to see what
 	 * virtual events you can find out about.  luckily, the mouse is
	 * one of them.
	 */
	x = win_get_vuid_value(rootfd, LOC_X_ABSOLUTE);
	y = win_get_vuid_value(rootfd, LOC_Y_ABSOLUTE);

	/* 
	 * find out how big our drawing surface is.
	 */
	w = (int) window_get(canvas, CANVAS_WIDTH);
	h = (int) window_get(canvas, CANVAS_HEIGHT);

	/*
	 * draw on it.
	 */
	if (mag<=1) {
		x = min(maxx-w, max(x-w, 0));
		y = min(maxy-h, max(y-h, 0));
		update(dstpw, 0, 0, w, h, PIX_SRC, srcpr, x, y);
	}
	else {
		x = min(maxx-w/mag, max(x-w/mag, 0));
		y = min(maxy-h/mag, max(y-h/mag, 0));
		pw_mag(dstpw, 0, 0, w, h, mag, srcpr, x, y);
	}
}

X/*
 * pw_mag copies a magnified view of spr to dpw using pixel replication.
 * the arguments are the same as those to the pw_rop library call, except
 * that magnification is passed instead of raster-op.
 */
void pw_mag(dpw, dx, dy, w, h, mag, spr, sx, sy)
	Pixwin *dpw;	/* destination pixwin */
	int dx, dy;  	/* destination x,y */
	int w, h;	/* width and height of block to copy */
	int mag; 	/* magnification */
	Pixrect *spr;	/* source pixrect */
	int sx,sy;	/* location in source to copy from */
{
	/* locals */
	register short si, sj;
	register short di, dj;
	register short jmax = h/mag + 1, imax = w/mag + 1;
	register short x, y, delta;

	Pixrect r;	/* holds the size of the drawing region when */
			/* gaining access to the screen */
	r.pr_size.x = w;
	r.pr_size.y = h;

	/* make off screen copy of source */
	pr_rop(tmpsrc, 0, 0, w, h, PIX_SRC|PIX_DONTCLIP, spr, sx, sy);

	for (x = 0; x < imax; x += 1) {
		for (delta = 0; delta < mag; delta += 1) {
			pr_rop(tmpdst, x*mag+delta, 0, 1, jmax, PIX_SRC|PIX_DONTCLIP, tmpsrc, x, 0);
		}
	}
	for (y = jmax; y >= 0; y -= 1) {
		for (delta = 0; delta < mag; delta += 1) {
			pr_rop(tmpdst, 0, y*mag+delta, w, 1, PIX_SRC|PIX_DONTCLIP, tmpdst, 0, y);
		}
	}


	/* draw */
	update(dpw, dx, dy, w, h, PIX_SRC, tmpdst, 0, 0);
}

X/* for debugging purposes, mostly */
void update(dpw, dx, dy, w, h, mag, spr, sx, sy)
	Pixwin *dpw;	/* destination pixwin */
	int dx, dy;  	/* destination x,y */
	int w, h;	/* width and height of block to copy */
	int mag; 	/* magnification */
	Pixrect *spr;
	/* source pixrect */
	int sx,sy;	/* location in source to copy from */
{
pw_rop(dpw, dx, dy, w, h, mag, spr, sx, sy);
}


X/* 
 * a service routine that gets called when the frame is destroyed
 * by someone selecting 'quit'.  this is basically right out of the 
 * manual.
 */
static Notify_value
notice_destroy_event(frame, status)
	Frame *frame;
	Destroy_status status;
{
	if (status != DESTROY_CHECKING) {
		done = 1;
	}
	return (notify_next_destroy_func(frame,status));
}

X/*
 * The routines below I have found enormously handy when dispatching
 * things via the notifier.  Use them in good health, or bad.  I do.
 *		-mark weiser
 */

X/*
 * Call procedure f in a little while.
 */
do_with_delay(f, secs, usecs)
void (*f)();
int secs, usecs;
{
	Notify_value do_the_call();
	struct itimerval timer;

	/* Sigh, so much work just to wait a bit before starting up. */
	timer.it_interval.tv_usec = 0;
	timer.it_interval.tv_sec = 0;
	timer.it_value.tv_usec = usecs;
	timer.it_value.tv_sec = secs;
	notify_set_itimer_func(f, do_the_call,
		ITIMER_REAL, &timer, NULL);
}

X/*
 * Wrapper to make sure procedures from do_with_delay return good values
 * to the notifier.
 */
Notify_value
do_the_call(f, which)
void (*f)();
{
	(*f)();
	return NOTIFY_DONE;
}

Notify_value
update_value_proc(item, value)
Panel_item item;
{
	int *ptr;
	ptr = (int *)panel_get(item, PANEL_CLIENT_DATA);
	*ptr = value;
	return NOTIFY_DONE;
}
//go.sysin dd *
if [ `wc -c < glass.c` != 9192 ]; then
	made=FALSE
	echo error transmitting glass.c --
	echo length should be 9192, not `wc -c < glass.c`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 644 glass.c
	echo -n '	'; ls -ld glass.c
fi
echo Extracting glass.icon
sed 's/^X//' <<'//go.sysin dd *' >glass.icon
X/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,
	0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,
	0x8888,0x8888,0x8888,0x8888,0x8888,0x8BF8,0x8888,0x8888,
	0x2222,0x1CC7,0x2222,0x2222,0x2222,0x70C1,0xE222,0x2222,
	0x8888,0xC0C0,0xE888,0x8888,0x888B,0xC0C0,0xF888,0x8888,
	0x2226,0x0C0C,0x0E22,0x2222,0x2224,0x0C0C,0x0C22,0x2222,
	0x888C,0x0C0C,0x0E88,0x8888,0x889C,0x0C0C,0x0F08,0x8888,
	0x2230,0xC0C0,0xC122,0x2222,0x2230,0xC0C0,0xC1A2,0x2222,
	0x88A0,0xC0C0,0xC088,0x8888,0x88A0,0xC0C0,0xC088,0x8888,
	0x2240,0x0000,0x0062,0x2222,0x2246,0x0C0C,0x0C62,0x2222,
	0x88C6,0x0C0C,0x0C48,0x8888,0x88C6,0x0C0C,0x0C48,0x8888,
	0x2246,0x0C0C,0x0C62,0x2222,0x2240,0x0000,0x0062,0x2222,
	0x88C0,0xC0C0,0xC1C8,0x8888,0x88A0,0xC0C0,0xC188,0x8888,
	0x2220,0xC0C0,0xC1A2,0x2222,0x2230,0xC0C0,0xC1A2,0x2222,
	0x8890,0x0000,0x0188,0x8888,0x8896,0x0C0C,0x0F88,0x8888,
	0x222E,0x0C0C,0x0F22,0x2222,0x2226,0x0C0C,0x0FA2,0x2222,
	0x888E,0x0C0C,0x0FC8,0x8888,0x888B,0xC0C0,0xFFC8,0x8888,
	0x2222,0xC0C0,0xE7F2,0x2222,0x2222,0x70C1,0xE3F2,0x2222,
	0x8888,0x9CCF,0x09F8,0x8888,0x8888,0x8BF8,0x88FC,0x8888,
	0x2222,0x2222,0x227E,0x2222,0x2222,0x2222,0x223F,0x2222,
	0x8888,0x8888,0x889F,0x8888,0x8888,0x8888,0x888F,0xC888,
	0x2222,0x2222,0x2227,0xE222,0x2222,0x2222,0x2223,0xF222,
	0x8888,0x8888,0x8889,0xF888,0x8888,0x8888,0x8888,0xF888,
	0x2222,0x2222,0x2222,0x7A22,0x2222,0x2222,0x2222,0x3222,
	0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,
	0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,
	0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,
	0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,
	0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,
	0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,
	0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,
	0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222
//go.sysin dd *
if [ `wc -c < glass.icon` != 1933 ]; then
	made=FALSE
	echo error transmitting glass.icon --
	echo length should be 1933, not `wc -c < glass.icon`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 644 glass.icon
	echo -n '	'; ls -ld glass.icon
fi
echo Extracting patchlevel.h
sed 's/^X//' <<'//go.sysin dd *' >patchlevel.h
#define PATCHLEVEL 2
//go.sysin dd *
if [ `wc -c < patchlevel.h` != 21 ]; then
	made=FALSE
	echo error transmitting patchlevel.h --
	echo length should be 21, not `wc -c < patchlevel.h`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 644 patchlevel.h
	echo -n '	'; ls -ld patchlevel.h
fi
-- 
Spoken: Mark Weiser 	ARPA:	mark@mimsy.umd.edu	Phone: +1-301-454-7817
After May 15, 1987: weiser@parcvax.xerox.com

mark@mimsy.UUCP (Mark Weiser) (04/21/87)

Sigh.  Here is yet another improved version.  This one has the ability
to 'lock' the glass to a particular screen location, instead of always
following the cursor, and also works for color.  For some reason
apparently the colors are not always the same in the original and magnified
images, however.  Enjoy again.
------------------------------
: Run this shell script with "sh" not "csh"
PATH=/bin:/usr/bin:/usr/ucb:/etc:$PATH
export PATH
all=FALSE
if [ x$1 = x-a ]; then
	all=TRUE
fi
echo Extracting README
sed 's/^X//' <<'//go.sysin dd *' >README

This is about the magnifying glass for Sun3s.

The program, glass, takes one argument, which quantifies the magnification it
is to do. If no argument is supplied, glass assumes you mean no magnification.
Glass opens it's own window to display the magnified image.  The image
is a region to the upper left of the cursor, the direction the cursor points,
with the lower right corner of the window corresponding to the tip of the
cursor.

To compile, use the makefile provided.

			-- Scott Schwartz
				USmail: Swarthmore College
					Swarthmore PA, 19081
				UUCP: ...seismo!bpa!swatsun!schwartz

Magnification can be changed, and the screen update time changed, with
sliders in the glass tool.  Also, the magnifier can be 'locked' onto
a given screen position.  Also, color now works.

			-mark weiser
			ARPA:	mark@mimsy.umd.edu
			After May 15, 1987: weiser@parcvax.xerox.com
//go.sysin dd *
if [ `wc -c < README` != 879 ]; then
	made=FALSE
	echo error transmitting README --
	echo length should be 879, not `wc -c < README`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 644 README
	echo -n '	'; ls -ld README
fi
echo Extracting glass.man
sed 's/^X//' <<'//go.sysin dd *' >glass.man
X.TH glass 1 "April 15, 1987"
X.AT 3
X.SH NAME
glass \- a magnifying window
X.SH SYNOPSIS
X.B glass
[[options] ]...
X.SH DESCRIPTION
Try it.
X.SH AUTHOR
Scott Schwartz did the first version.
Mark Weiser did a lot of fiddling, including the sliders, locking, and a better
magnification algorithm.

//go.sysin dd *
if [ `wc -c < glass.man` != 290 ]; then
	made=FALSE
	echo error transmitting glass.man --
	echo length should be 290, not `wc -c < glass.man`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 644 glass.man
	echo -n '	'; ls -ld glass.man
fi
echo Extracting Makefile
sed 's/^X//' <<'//go.sysin dd *' >Makefile
# Makefile to compile magnifying glass
# by Scott Schwartz, Dec 1986

# flags.  normally just -O
CFLAGS=-g
LIBS= -lsuntool -lsunwindow -lpixrect

# dependencies for glass
glass: glass.o 
	cc $(CFLAGS) -o glass glass.o $(LIBS)

glass.o: glass.c patchlevel.h Makefile
	cc $(CFLAGS) -c glass.c

# utility stuff
clean:
	rm -f glass glass.o
lint:
	lint -hx glass.c $(LIBS)
shar:
	makescript -o glass.shar README glass.man Makefile glass.c glass.icon patchlevel.h
//go.sysin dd *
if [ `wc -c < Makefile` != 458 ]; then
	made=FALSE
	echo error transmitting Makefile --
	echo length should be 458, not `wc -c < Makefile`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 755 Makefile
	echo -n '	'; ls -ld Makefile
fi
echo Extracting glass.c
sed 's/^X//' <<'//go.sysin dd *' >glass.c
X/*
 *  Magnifying glass		(for Sun-3 workstations)
 *
 *  Copyright 1986, 1987 by Scott Schwartz
 *  This program may be copied with the provision that this copyright 
 *  message remains, and that further distribution of this code and it's 
 *  derivatives is not subject to additional restrictions.
 * 
 *  Lots of changes, but no Copyright, by Mark Weiser.
 * 
 *  compile with -lsuntool -lsunwindow -lpixrect
 *
 *  vi: set ts=8
 *
 *  revision history:
 *  10 Nov 86	initial coding		 		Scott Schwartz 
 *
 *  version 1.0		25 Nov 86
 *	modified to draw big pixels with raster-ops		ses
 *
 *  version 1.1		27 Jan 87
 *	does magnification in memory, 				ses
 *	to avoid screen access
 *
 *  version 1.2		27 Jan 87
 *	flood destination with white, 				ses/af
 *      then draw only black spots
 *
 *  version 2.0		15 Apr 87				mark weiser
 *	vastly faster magnification algorithm using only 2k blits
 *	instead of k**2 (where k is the height or width of the final).
 *	Also added a panel for friendliness, and stopped using *all* the cpu!
 *
 *  version 2.1		17 Apr 87				mark weiser
 *	added locking.
 *
 *  version 2.2		20 Apr 87				mark weiser
 *	added changes for color suns (not tested here).
 */

#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <suntool/sunview.h>
#include <suntool/canvas.h>
#include <suntool/panel.h>
#include <suntool/textsw.h>
#include <pixrect/pixrect_hs.h>

X/* I HATE these macros	-mdw */
#undef min
#undef max


X/* useful macros */
#define ERROR(msg)  { fprintf(stderr, "%s: %s\n", argv[0], msg); exit(1); }

X/* forward declarations */
extern char *getenv(), *sprintf();
extern Notify_error notify_dispatch();
static Notify_value notice_destroy_event();
extern void pw_mag();
extern void view();
void update();
void glass_time_proc();
void lock_proc(), unlock_proc();
Notify_value update_value_proc();

X/* constants */
static char frame_label[] = "Magnifying Glass 2.2";
static short icon_image[] = {
#include "glass.icon"
};
DEFINE_ICON_FROM_IMAGE(icon, icon_image);
#define NILPR ((struct pixrect *)0)
#define SCREEN_MAX_X  1152
#define SCREEN_MAX_Y  900
#define MAX_MAG 32

X/* global vars */
static struct pixrect	*tmpsrc; /* working space, to avoid screen access */ 
static struct pixrect	*tmpdst;
static int done = 0;
struct pixrect *srcpr; /* the source pixrect, i.e. the whole screen */
int rootfd;  /* file descriptor for root window, for cursor location */

X/* things to draw on */
Panel panel;
XFrame frame;	/* the actual window frame */
Canvas canvas;	/* our particular canvas, filling the frame */

int mag = 1;			/* magnification in output window */
int delay = 1000;
int locked = 0;
int locked_x, locked_y;
Panel_item time_left_item;

X/* 
 *  main routine
 */
main(argc,argv)
int argc;
char **argv;
{
	/* externals */
	extern struct pixrect *tmpsrc, *tmpdst;

	/* things to magnify */
	char *parent; /* name of the parent window, usually win0 */

	/* misc */
	char *magstr = "x99999";	/* allocate space for actual string */
	int retcode;

	/* open rootwindow */
	parent = getenv("WINDOW_PARENT");
	if (parent==NULL) 
		ERROR("can't get WINDOW_PARENT from environment.")
	rootfd = open(parent, O_RDONLY, 0);
	if (rootfd<0)
		perror(argv[0]); 

	/* open frame buffer */
	srcpr = pr_open("/dev/fb");
	if (srcpr==NILPR)
		perror(argv[0]);

	/* create output window */
	frame = window_create(NULL, FRAME, 
			FRAME_LABEL, frame_label,
			FRAME_ICON, &icon,
			FRAME_ARGC_PTR_ARGV, &argc, argv,
			0);

	/* handle arguments */		
	if (argc > 1) {
		retcode = sscanf(argv[1], "%d", &mag);
		if (retcode <= 0) 
			ERROR("problem evaluating arguments\nUsage: glass magnification [suntools-options]");
		mag = (mag > MAX_MAG) ? MAX_MAG : mag;
	}

	panel = window_create(frame, PANEL, 0);
	(void) panel_create_item(panel, PANEL_SLIDER,
		PANEL_LABEL_STRING, "Mag:",
		PANEL_VALUE, mag,
		PANEL_MIN_VALUE, 1,
		PANEL_MAX_VALUE, MAX_MAG,
		PANEL_SHOW_RANGE, FALSE,
		PANEL_SHOW_VALUE, TRUE,
		PANEL_SLIDER_WIDTH, 100,
		PANEL_NOTIFY_PROC, update_value_proc,
		PANEL_CLIENT_DATA, &mag,
		0);
	(void) panel_create_item(panel, PANEL_SLIDER,
		PANEL_LABEL_STRING, "Delay:",
		PANEL_VALUE, 10000,
		PANEL_MIN_VALUE, 1000,
		PANEL_MAX_VALUE, 999999,
		PANEL_SHOW_RANGE, FALSE,
		PANEL_SHOW_VALUE, FALSE,
		PANEL_SLIDER_WIDTH, 100,
		PANEL_NOTIFY_PROC, update_value_proc,
		PANEL_CLIENT_DATA, &delay,
		PANEL_ITEM_X, ATTR_COL(0),
		PANEL_ITEM_Y, ATTR_ROW(1),
		0);

	(void) panel_create_item(panel, PANEL_BUTTON,
		PANEL_LABEL_IMAGE, panel_button_image(panel, "Lock", 6, 0),
		PANEL_NOTIFY_PROC, lock_proc,
		PANEL_ITEM_X, ATTR_COL(0),
		PANEL_ITEM_Y, ATTR_ROW(2),
		0);

	(void) panel_create_item(panel, PANEL_BUTTON,
		PANEL_LABEL_IMAGE, panel_button_image(panel, "Unlock", 6, 0),
		PANEL_NOTIFY_PROC, unlock_proc,
		0);

	time_left_item = panel_create_item(panel, PANEL_SLIDER,
		PANEL_LABEL_STRING, "Time Left:",
		PANEL_VALUE, 10,
		PANEL_MIN_VALUE, 0,
		PANEL_MAX_VALUE, 10,
		PANEL_SHOW_RANGE, FALSE,
		PANEL_SHOW_VALUE, FALSE,
		PANEL_SLIDER_WIDTH, 100,
		PANEL_ITEM_X, ATTR_COL(0),
		PANEL_ITEM_Y, ATTR_ROW(2),
		PANEL_SHOW_ITEM, FALSE,
		0);
	window_fit_height(panel);

	/* allocate static pixrects */
	/* (If the last parameter is change from 1 to 8, this might work for color -mdw */
	tmpsrc = mem_create(SCREEN_MAX_X, SCREEN_MAX_Y, srcpr->pr_depth);
	tmpdst = mem_create(SCREEN_MAX_X, SCREEN_MAX_Y, srcpr->pr_depth);

	/* gather data on output pixwin */
	canvas = window_create(frame, CANVAS,
			WIN_HEIGHT, 200,
			WIN_WIDTH, 200,
			0);

	window_fit(frame);

	/* set up an interposed event handler so we know when to quit */
	(void)notify_interpose_destroy_func(frame, notice_destroy_event);
	
	/* start us in a second */
	do_with_delay(glass_time_proc, 1, 0);

	/* copy input to output forever */
	window_main_loop(frame);
	exit(0);
}

X/*
 * Thing to do at intervals.
 */
void
glass_time_proc()
{
	if (done) return;
	if (window_get(frame, FRAME_CLOSED)) {
		do_with_delay(glass_time_proc, 2, 0);
	} else {
		view(rootfd, srcpr, canvas, mag);
		do_with_delay(glass_time_proc, 0, delay);
	}
}

X/*
 * view does the work of displaying the (possibly) magnified image
 * at the location indicated by rootfd (mouse). view copies srcpr to dstpw.
 */
void view(rootfd, srcpr, canvas, mag)
	int rootfd;		/* root window, for mouse data */	
	struct pixrect *srcpr;		/* screen source pixrect */
	Canvas canvas;
	int mag;
{
	/* constants */
	int maxy = (srcpr->pr_size.y);
	int maxx = (srcpr->pr_size.x);
	Pixwin *dstpw = canvas_pixwin(canvas);

	/* local vars */	
	int x,y;
	int w, h;

	/* 
	 * read mouse coords from vuid register.  sadly, the sunview
	 * programmers guide is not clear on this.  appendix A tells some,
	 * but you have to look at <sundev/vuid_event.h> to see what
 	 * virtual events you can find out about.  luckily, the mouse is
	 * one of them.
	 */
	if (locked) {
		x = locked_x;
		y = locked_y;
	} else {
		x = win_get_vuid_value(rootfd, LOC_X_ABSOLUTE);
		y = win_get_vuid_value(rootfd, LOC_Y_ABSOLUTE);
	}

	/* 
	 * find out how big our drawing surface is.
	 */
	w = (int) window_get(canvas, CANVAS_WIDTH);
	h = (int) window_get(canvas, CANVAS_HEIGHT);

	/*
	 * draw on it.
	 */
	if (mag<=1) {
		x = min(maxx-w, max(x-w, 0));
		y = min(maxy-h, max(y-h, 0));
		update(dstpw, 0, 0, w, h, PIX_SRC, srcpr, x, y);
	}
	else {
		x = min(maxx-w/mag, max(x-w/mag, 0));
		y = min(maxy-h/mag, max(y-h/mag, 0));
		pw_mag(dstpw, 0, 0, w, h, mag, srcpr, x, y);
	}
}

X/*
 * pw_mag copies a magnified view of spr to dpw using pixel replication.
 * the arguments are the same as those to the pw_rop library call, except
 * that magnification is passed instead of raster-op.
 */
void pw_mag(dpw, dx, dy, w, h, mag, spr, sx, sy)
	Pixwin *dpw;	/* destination pixwin */
	int dx, dy;  	/* destination x,y */
	int w, h;	/* width and height of block to copy */
	int mag; 	/* magnification */
	struct pixrect *spr;	/* source pixrect */
	int sx,sy;	/* location in source to copy from */
{
	/* locals */
	register short si, sj;
	register short di, dj;
	register short jmax = h/mag + 1, imax = w/mag + 1;
	register short x, y, delta;

	struct pixrect r;	/* holds the size of the drawing region when */
			/* gaining access to the screen */
	r.pr_size.x = w;
	r.pr_size.y = h;

	/* make off screen copy of source */
	pr_rop(tmpsrc, 0, 0, w, h, PIX_SRC|PIX_DONTCLIP, spr, sx, sy);

	for (x = 0; x < imax; x += 1) {
		for (delta = 0; delta < mag; delta += 1) {
			pr_rop(tmpdst, x*mag+delta, 0, 1, jmax, PIX_SRC|PIX_DONTCLIP, tmpsrc, x, 0);
		}
	}
	for (y = jmax; y >= 0; y -= 1) {
		for (delta = 0; delta < mag; delta += 1) {
			pr_rop(tmpdst, 0, y*mag+delta, w, 1, PIX_SRC|PIX_DONTCLIP, tmpdst, 0, y);
		}
	}


	/* draw */
	update(dpw, dx, dy, w, h, PIX_SRC, tmpdst, 0, 0);
}

X/* for debugging purposes, mostly */
void update(dpw, dx, dy, w, h, mag, spr, sx, sy)
	Pixwin *dpw;	/* destination pixwin */
	int dx, dy;  	/* destination x,y */
	int w, h;	/* width and height of block to copy */
	int mag; 	/* magnification */
	struct pixrect *spr;
	/* source pixrect */
	int sx,sy;	/* location in source to copy from */
{
pw_rop(dpw, dx, dy, w, h, mag, spr, sx, sy);
}


X/* 
 * a service routine that gets called when the frame is destroyed
 * by someone selecting 'quit'.  this is basically right out of the 
 * manual.
 */
static Notify_value
notice_destroy_event(frame, status)
	Frame *frame;
	Destroy_status status;
{
	if (status != DESTROY_CHECKING) {
		done = 1;
	}
	return (notify_next_destroy_func(frame,status));
}

X/*
 * The routines below I have found enormously handy when dispatching
 * things via the notifier.  Use them in good health, or bad.  I do.
 *		-mark weiser
 */

X/*
 * Call procedure f in a little while.
 */
do_with_delay(f, secs, usecs)
void (*f)();
int secs, usecs;
{
	Notify_value do_the_call();
	struct itimerval timer;

	/* Sigh, so much work just to wait a bit before starting up. */
	timer.it_interval.tv_usec = 0;
	timer.it_interval.tv_sec = 0;
	timer.it_value.tv_usec = usecs;
	timer.it_value.tv_sec = secs;
	notify_set_itimer_func(f, do_the_call,
		ITIMER_REAL, &timer, NULL);
}

X/*
 * Wrapper to make sure procedures from do_with_delay return good values
 * to the notifier.
 */
Notify_value
do_the_call(f, which)
void (*f)();
{
	(*f)();
	return NOTIFY_DONE;
}

Notify_value
update_value_proc(item, value)
Panel_item item;
{
	int *ptr;
	ptr = (int *)panel_get(item, PANEL_CLIENT_DATA);
	*ptr = value;
	return NOTIFY_DONE;
}

void
lock_proc(item, event)
Panel_item;
Event *event;
{
	extern void do_the_lock();
	popup_msg(frame, event, 
		"After buttoning 'Done' in this window,\n\
you will have ten (10) seconds to put the\n\
cursor someplace.  At the end of 10 seconds,\n\
glass will be locked into looking at that position.");
	locked = 0;
}

void
unlock_proc()
{
	locked = 0;
}

void
do_the_lock()
{
	static void popup_textsw_done();
	locked_x = win_get_vuid_value(rootfd, LOC_X_ABSOLUTE);
	locked_y = win_get_vuid_value(rootfd, LOC_Y_ABSOLUTE);
	locked = 1;
}

X/*
 * The stuff below is some standard hacks I have started using,
 * especially in the 'sdi' game.  I have inserted them here for convenience.
 *		-mark weiser
 */

static Frame popup_frame = NULL;
static Textsw popup_text;
static Panel popup_panel;
static Panel_item popup_msg_item;
static void popup_yes_proc(), popup_no_proc(), popup_textsw_done();

X/*
 * Fake an event, so anyone can popup a message. 
 */
easy_pop(msg)
char *msg;
{
	Event event;
	event_x(&event) = (int)window_get(frame, WIN_X); 
	event_y(&event) = (int)window_get(frame, WIN_Y);
	popup_msg(frame, &event, msg);
}

X/*
 * Pop up a message inside a textsw.  Since textsw's don't really
 * popup (in SunOS 3.2), just display it and put up a 'done' button
 * to kill it when the user is done.
 * Frame should be the frame in which Event occured.  Msg can
 * contain imbedded newlines.
 */
popup_msg(frame, event, msg)
XFrame *frame;
Event *event;
char *msg;
{
	int lines = count_lines(msg);
	if (popup_frame != NULL) {
		/* Can only do one of these at a time. */
		return;
	}
	init_popup_msg(frame, msg, lines);
	textsw_insert(popup_text, msg, strlen(msg));
	window_set(popup_frame, WIN_X, event_x(event),
		WIN_Y, event_y(event),
		WIN_SHOW, TRUE,
		0);
}

X/*
 * A helper proc to do all the work of window creation for message popups
 */
init_popup_msg(baseframe, msg, lines)
XFrame baseframe;
char *msg;
{
	popup_frame = window_create(baseframe, FRAME,
		WIN_ERROR_MSG, "Can't create window.",
		0);
	popup_panel = window_create(popup_frame, PANEL,
		/* WIN_BELOW, popup_text, */
		WIN_X, ATTR_COL(0),		/* bug workaround, should not be necessary */
		0);
	panel_create_item(popup_panel, PANEL_BUTTON,
		PANEL_LABEL_IMAGE, panel_button_image(popup_panel, "Done", 4, NULL),
		PANEL_NOTIFY_PROC, popup_textsw_done,
		0);
	window_fit(popup_panel);
	popup_text = window_create(popup_frame, TEXTSW,
		WIN_ERROR_MSG, "Can't create window.",
		WIN_ROWS, min(30, lines),
		WIN_COLUMNS, max_line(msg)+3,
		TEXTSW_IGNORE_LIMIT, TEXTSW_INFINITY,
		TEXTSW_CONTENTS, msg,
		TEXTSW_BROWSING, TRUE,
		TEXTSW_DISABLE_LOAD, TRUE,
		TEXTSW_DISABLE_CD, TRUE,
		0);
	window_fit(popup_frame);
}

void
timeout_proc()
{
	int val = (int)panel_get_value(time_left_item);
	if (val > 0) {
		panel_set(time_left_item, PANEL_VALUE, val-1, 0);
		do_with_delay(timeout_proc, 1, 0);
	} else {
		panel_set(time_left_item, PANEL_SHOW_ITEM, FALSE, 0);
		panel_paint(panel, PANEL_CLEAR);
		do_the_lock();
	}
}

X/* A helper for killing message popups. */
static void
popup_textsw_done()
{
	window_set(popup_frame, FRAME_NO_CONFIRM, TRUE, 0);
	window_destroy(popup_frame);
	popup_frame = NULL;
	panel_set(time_left_item, PANEL_SHOW_ITEM, TRUE, PANEL_VALUE, 10, 0);
	do_with_delay(timeout_proc, 1, 0);
}

X/*
 * Find the size of the longest line in a string of lines separated by newlines
 */
max_line(s)
char *s;
{
	int max = 0, count = 0;
	while (*s) {
		if (*s++ == '\n') {
			if (count > max)
				max = count;
			count = 0;
			continue;
		}
		count += 1;
	}
	if (count > max)
		max = count;
	return max;
}

X/*
 * Count the number of lines in a string of lines separated by newlines.
 */
count_lines(s)
char *s;
{
	int count = 0;
	while (*s) {
		if (*s++ == '\n')
			count += 1;
	}
	return count+1;
}

X/* need functions, not macros, because of non-idempotent funcall arguments */
max(x,y)
{
	return x<y ? y : x;
}

min(x,y)
{
	return x<y ? x : y;
}
//go.sysin dd *
if [ `wc -c < glass.c` != 14320 ]; then
	made=FALSE
	echo error transmitting glass.c --
	echo length should be 14320, not `wc -c < glass.c`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 644 glass.c
	echo -n '	'; ls -ld glass.c
fi
echo Extracting glass.icon
sed 's/^X//' <<'//go.sysin dd *' >glass.icon
X/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,
	0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,
	0x8888,0x8888,0x8888,0x8888,0x8888,0x8BF8,0x8888,0x8888,
	0x2222,0x1CC7,0x2222,0x2222,0x2222,0x70C1,0xE222,0x2222,
	0x8888,0xC0C0,0xE888,0x8888,0x888B,0xC0C0,0xF888,0x8888,
	0x2226,0x0C0C,0x0E22,0x2222,0x2224,0x0C0C,0x0C22,0x2222,
	0x888C,0x0C0C,0x0E88,0x8888,0x889C,0x0C0C,0x0F08,0x8888,
	0x2230,0xC0C0,0xC122,0x2222,0x2230,0xC0C0,0xC1A2,0x2222,
	0x88A0,0xC0C0,0xC088,0x8888,0x88A0,0xC0C0,0xC088,0x8888,
	0x2240,0x0000,0x0062,0x2222,0x2246,0x0C0C,0x0C62,0x2222,
	0x88C6,0x0C0C,0x0C48,0x8888,0x88C6,0x0C0C,0x0C48,0x8888,
	0x2246,0x0C0C,0x0C62,0x2222,0x2240,0x0000,0x0062,0x2222,
	0x88C0,0xC0C0,0xC1C8,0x8888,0x88A0,0xC0C0,0xC188,0x8888,
	0x2220,0xC0C0,0xC1A2,0x2222,0x2230,0xC0C0,0xC1A2,0x2222,
	0x8890,0x0000,0x0188,0x8888,0x8896,0x0C0C,0x0F88,0x8888,
	0x222E,0x0C0C,0x0F22,0x2222,0x2226,0x0C0C,0x0FA2,0x2222,
	0x888E,0x0C0C,0x0FC8,0x8888,0x888B,0xC0C0,0xFFC8,0x8888,
	0x2222,0xC0C0,0xE7F2,0x2222,0x2222,0x70C1,0xE3F2,0x2222,
	0x8888,0x9CCF,0x09F8,0x8888,0x8888,0x8BF8,0x88FC,0x8888,
	0x2222,0x2222,0x227E,0x2222,0x2222,0x2222,0x223F,0x2222,
	0x8888,0x8888,0x889F,0x8888,0x8888,0x8888,0x888F,0xC888,
	0x2222,0x2222,0x2227,0xE222,0x2222,0x2222,0x2223,0xF222,
	0x8888,0x8888,0x8889,0xF888,0x8888,0x8888,0x8888,0xF888,
	0x2222,0x2222,0x2222,0x7A22,0x2222,0x2222,0x2222,0x3222,
	0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,
	0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,
	0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,
	0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,
	0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,
	0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,
	0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,0x8888,
	0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222
//go.sysin dd *
if [ `wc -c < glass.icon` != 1933 ]; then
	made=FALSE
	echo error transmitting glass.icon --
	echo length should be 1933, not `wc -c < glass.icon`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 644 glass.icon
	echo -n '	'; ls -ld glass.icon
fi
echo Extracting patchlevel.h
sed 's/^X//' <<'//go.sysin dd *' >patchlevel.h
#define PATCHLEVEL 2
//go.sysin dd *
if [ `wc -c < patchlevel.h` != 21 ]; then
	made=FALSE
	echo error transmitting patchlevel.h --
	echo length should be 21, not `wc -c < patchlevel.h`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 644 patchlevel.h
	echo -n '	'; ls -ld patchlevel.h
fi
-- 
Spoken: Mark Weiser 	ARPA:	mark@mimsy.umd.edu	Phone: +1-301-454-7817
After May 15, 1987: weiser@parcvax.xerox.com