[comp.windows.x] The PanHandler code

chuck@Morgan.COM (Chuck Ocheret) (05/16/91)

There has been some discussion on the motif-talk mailing list about
scrolled windows and how to accomplish scrolling.  A few people have
suggested that the code I describe below should be presented to the net
since it is of general utility.

>   From my experience(my text widget with smooth scrolling in both directions)
>   I believe it is possible to write a set of convenience routines that would 
>   be a great assistance to "Application Defined Scroll". 
>   In fact, this type of scrolling can be built into XmScrolled window....
		:
		:
>   almost readable... However, the computations of the copied/exposed areas
>   are never pretty - therefore a Motif support here would help a lot.

I wrote something which I call the PanHandler which does the
calculations you want.  In addition it takes care of a problem which
burns a lot of people who don't think the scrolling problem through.

This problem is the result of the asynchronous nature of X.  If you
have an occluding window over the rectangle you are trying to pan, the
your XCopyArea() calls can result in GraphicsExpose events since some
areas couldn't get copied.  However, you might not see those
GraphicsExpose events until after you have done a bunch of pans.

When the GraphicsExpose events eventually get to your client, you have
already scrolled the damaged areas to new locations, so that the x, y
members of the GraphicsExpose events need to be adjusted.

This is what the PanHandler fixes for you.  It maintains a queue of
your pan requests and keeps it up to date with respect to incoming
Expose, GraphicsExpose and NoExpose events.  It adjusts the Expose and
GraphicsExpose events accordingly so that you repair the right
portions of your window.

I have seen very few programs which scroll properly when there is an
occluding window.

Below is a shar file which contains source for the PanHandler (with a
simple Imakefile) and a brief description of the functionality (in
README).  The (ugly) Xlib program PanHandlerUT displays a
quarter-infinite grid which you can pan around by dragging it with
btn1motion.

The PanHandler is a public component of the Widget set I am writing
for Morgan Stanley to replace Motif.  PanHandlerUT is an Xlib program
because I didn't want to be bound to any particular widget set for
unit test programs for general utilites.

By the way, there is something very silly in this implementation...can
anyone find it?

Let me know if you find this useful.

+--------------------+             Chuck Ocheret             +---------------+
|chuck@fid.Morgan.COM|       Morgan Stanley & Co., Inc.      |(212) 703-4474 |
|    Duty now ...    |19th Floor, 1251 Avenue of the Americas|for the future.|
+--------------------+      New York, N.Y.  10020 USA        +---------------+

#! /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
#	Imakefile
#	Node.c
#	Node.h
#	PanHandler.c
#	PanHandler.h
#	PanHandlerI.h
#	PanHandlerUT.c
# This archive created: Tue May 14 19:21:59 1991
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(2973 characters)'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
sed 's/^	X//' << \SHAR_EOF > 'README'
	XXcPanHandler XcCreatePanHandler() - allocates a PanHandler
	X
	Xvoid XcPanRectangle(panHandler, display, window, gc, x, y, width, height,
	X		    src_x, src_y, dest_x, dest_y, panExposeCB, client)
	X  XcPanHandler	panHandler;		/* it */
	X  Display	*display;		/* duh? */
	X  Window	window;			/* dubudoy? */
	X  GC		gc;			/* gee. see. */
	X  int		x, y;			/* origin of rectangle */
	X  unsigned int	width, height;		/* extent of rectangle */
	X  int		src_x, src_y;		/* pan start */
	X  int		dest_x, dest_y;		/* pan end */
	X  void		(*panExposeCB)();	/* see below */
	X  XtPointer	client;			/* client data for panExposeCB */
	X
	XWhen panning a rectangle you end up doing 1 copy and up to 2 exposes.
	XSince you can compute which regions get exposed, it is more efficient
	Xto just draw them rather than generating an expose event via a round
	Xtrip to the server.  The panExposeCB, if non-NULL, gets called with an
	XXcPanAccomplish structure which describes the regions to draw (see
	Xbelow).  If panExposeCB is NULL, XClearArea calls are made as needed
	Xto generate expose events.
	X
	Xvoid XcPanEvent(panHandler, event)
	X  XcPanHandler	panHandler;	/* it */
	X  XEvent	*event;		/* Expose, GraphicsExpose, or NoExpose event */
	X
	XWhen you get such events for the window you are panning, pass the
	Xevents to XcPanEvent and they will get their x, y members adjusted.
	XIt is important to pass all such events so that the PanHandler can
	Xkeep track of what pans are still pending.
	X
	XThere are two other utility routines:
	X
	Xvoid XcPanCompute(x, y, width, height, delta_x, delta_y, how)
	X  int			x, y;
	X  unsigned int		width, height;
	X  int			delta_x, delta_y;
	X  XcPanAccomplish	*how;
	X
	XThis fills in the XcPanAccomplish structure with information about the
	Xcopy and exposes needed to accomplish the pan.  XcPanRectangle() uses
	XXcPanCompute().
	X
	Xtypedef struct xcPanAccomplish {
	X  int		copy_src_x, copy_src_y;			/* copy info */
	X  unsigned int	copy_width, copy_height;
	X  int		copy_dst_x, copy_dst_y;
	X  int		expose_1_x, expose_1_y;			/* 1st expose info */
	X  unsigned int	expose_1_width, expose_1_height;
	X  int		expose_2_x, expose_2_y;			/* 2nd exopse info */
	X  unsigned int	expose_2_width, expose_2_height;
	X} XcPanAccomplish;
	X
	XIf copy_width, expose_1_width, or expose_2_width are 0 then the
	Xcorresponding copy or expose should not be performed.
	X
	Xvoid XcPanAccrue(panHandler, delta_x, delta_y)
	X  XcPanHandler	panHandler;
	X  int		delta_x, delta_y;
	X
	XPretty obvious.
	X
	XThe unit test program (Xlib) included, PanHandlerUT, lets you click
	Xthe left mouse on a grid and drag it around.  Initially you can't drag
	Xto the right or down (I didn't feel like dealing with negative numbers
	Xfor a test program).  Put up some occluding windows and try to make it
	Xbreak.  You can make it fall behind and look ugly, but it will
	Xeventually catch up.  Note that the MIT X server does some really
	Xdifferent things when an occluding window is a shaped one like oclock.
	XIt still works but it gets much slower and the exposed regions are
	Xmuch larger than I would expect.
SHAR_EOF
if test 2973 -ne "`wc -c < 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 2973 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'Imakefile'" '(285 characters)'
if test -f 'Imakefile'
then
	echo shar: will not over-write existing file "'Imakefile'"
else
sed 's/^	X//' << \SHAR_EOF > 'Imakefile'
	XHEADERS = Node.h PanHandler.h PanHandlerP.h
	X   SRCS = Node.c PanHandler.c
	X   OBJS = ${SRCS:%.c=%.o}
	X
	XNormalLibraryObjectRule()
	XNormalLibraryTarget(chuck,$(OBJS))
	X
	XDependTarget()
	X
	XAllTarget(PanHandlerUT)
	XNormalProgramTarget(PanHandlerUT,PanHandlerUT.o,libchuck.a,libchuck.a,-lXt -lX11)
SHAR_EOF
if test 285 -ne "`wc -c < 'Imakefile'`"
then
	echo shar: error transmitting "'Imakefile'" '(should have been 285 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'Node.c'" '(1703 characters)'
if test -f 'Node.c'
then
	echo shar: will not over-write existing file "'Node.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'Node.c'
	X#ifndef lint
	Xstatic char rcsid[] = "@(#) $Id$";
	X#endif
	X/*
	X * Copyright 1991 Gigadactyl, Inc.
	X *
	X * Permission to use, copy, modify, and distribute this software and its
	X * documentation for any purpose and without fee is hereby granted, provided
	X * that the above copyright notice appear in all copies and that both that
	X * copyright notice and this permission notice appear in supporting
	X * documentation, and that the name of Gigadactyl, Inc. not be used in
	X * advertising or publicity pertaining to distribution of the software
	X * without specific, written prior permission.  Gigadactyl, Inc. makes no
	X * representations about the suitability of this software for any purpose.
	X * It is provided "as is" without express or implied warranty.
	X *
	X * Author: Charles A. Ocheret <chuck@fid.morgan.com>
	X */
	X
	X#include "Node.h"
	X
	XXcNode *XcNodeAlloc()
	X{
	X	register XcNode *p;
	X
	X	p = (XcNode *)XtMalloc(sizeof(*p));
	X	p->f = p->b = p;
	X	p->d = (XtPointer)(0);
	X	return p;
	X}
	X
	X/* cuts head circular list before h and inserts new circular list,
	X * making large circular list.  This operation is semetrical with
	X * respect to interchange of h and p.
	X */
	X
	Xvoid XcNodeInsert(p, h)
	X	register XcNode *p;
	X	register XcNode *h;
	X{
	X	if ((p != (XcNode *)(0)) && (h != (XcNode *)(0))) {
	X		register XcNode *t = p->b;
	X
	X		p->b->f = h;
	X		p->b = h->b;
	X		h->b->f = p;
	X		h->b = t;
	X	}
	X}
	X
	Xvoid XcNodeRemove(p)
	X	register XcNode *p;
	X{
	X	if (p != (XcNode*)(0)) {
	X		p->b->f = p->f;
	X		p->f->b = p->b;
	X		p->b = p;
	X		p->f = p;
	X	}
	X	return;
	X}
	X
	Xvoid XcNodeToad(p)
	X	register XcNode *p;
	X{
	X	if (p != (XcNode*)(0)) {
	X		XcNode *o = p->b;
	X		XcNode *q = p->f;
	X		XcNode *r;
	X
	X		o->f = q;
	X		q->b = o;
	X		p->f = r = q->f;
	X		p->b = q;
	X		q->f = p;
	X		r->b = p;
	X	}
	X}
SHAR_EOF
if test 1703 -ne "`wc -c < 'Node.c'`"
then
	echo shar: error transmitting "'Node.c'" '(should have been 1703 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'Node.h'" '(1093 characters)'
if test -f 'Node.h'
then
	echo shar: will not over-write existing file "'Node.h'"
else
sed 's/^	X//' << \SHAR_EOF > 'Node.h'
	X/*
	X *	@(#) $Id$
	X */
	X/*
	X * Copyright 1991 Gigadactyl, Inc.
	X *
	X * Permission to use, copy, modify, and distribute this software and its
	X * documentation for any purpose and without fee is hereby granted, provided
	X * that the above copyright notice appear in all copies and that both that
	X * copyright notice and this permission notice appear in supporting
	X * documentation, and that the name of Gigadactyl, Inc. not be used in
	X * advertising or publicity pertaining to distribution of the software
	X * without specific, written prior permission.  Gigadactyl, Inc. makes no
	X * representations about the suitability of this software for any purpose.
	X * It is provided "as is" without express or implied warranty.
	X *
	X * Author: Charles A. Ocheret <chuck@fid.morgan.com>
	X */
	X
	X#ifndef _Node_h_
	X#define _Node_h_
	X
	X#include <X11/Intrinsic.h>
	X
	X#define XcNodeAt(np) ((Node *)((np)->d))
	X
	Xtypedef struct xcNode XcNode;
	Xstruct xcNode {
	X	XcNode		*f;
	X	XcNode		*b;
	X	XtPointer	d;
	X};
	X
	Xextern XcNode *XcNodeAlloc();
	Xextern void XcNodeInsert();
	Xextern void XcNodeRemove();
	Xextern void XcNodeToad();
	X
	X#endif _Node_h_
SHAR_EOF
if test 1093 -ne "`wc -c < 'Node.h'`"
then
	echo shar: error transmitting "'Node.h'" '(should have been 1093 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'PanHandler.c'" '(7144 characters)'
if test -f 'PanHandler.c'
then
	echo shar: will not over-write existing file "'PanHandler.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'PanHandler.c'
	X#ifndef lint
	Xstatic char rcsid[] = "@(#) $Id$";
	X#endif
	X/*
	X * Copyright 1991 Gigadactyl, Inc.
	X *
	X * Permission to use, copy, modify, and distribute this software and its
	X * documentation for any purpose and without fee is hereby granted, provided
	X * that the above copyright notice appear in all copies and that both that
	X * copyright notice and this permission notice appear in supporting
	X * documentation, and that the name of Gigadactyl, Inc. not be used in
	X * advertising or publicity pertaining to distribution of the software
	X * without specific, written prior permission.  Gigadactyl, Inc. makes no
	X * representations about the suitability of this software for any purpose.
	X * It is provided "as is" without express or implied warranty.
	X *
	X * Author: Charles A. Ocheret <chuck@fid.morgan.com>
	X */
	X
	X/*
	X * A scroll manager solves an edge condition problem relating to Expose
	X * and GraphicsExpose events occurring among a series of XCopy(Area|Plane)
	X * requests.  When an area is copied, maybe to accomplish scrolling or
	X * panning, occluding windows will cause GraphicsExpose events for the
	X * regions which couldn't be copied.  If there were no occluding windows
	X * then a NoExpose event is generated.  Also the removal of an occluding
	X * window will cause an Expose event.  If a number of XCopyArea requests
	X * are performed in rapid succession, perhaps in response to scrollBar
	X * activity, these expose events contain obsolete x and y information
	X * since the virtual coordinates have been scrolled to a new location.
	X * The scroll manager is an object which keeps track of the scrolls
	X * performed and and the offsets required to relocate expose events to
	X * the current coordinate system.
	X */
	X
	X#include <X11/Intrinsic.h>
	X#include "PanHandlerI.h"
	X
	XXcPanHandler XcCreatePanHandler()
	X{
	X	XcPanHandler	panHandler;
	X
	X	panHandler = XtNew(struct xcPanHandler);
	X	panHandler->offset_x = 0;
	X	panHandler->offset_y = 0;
	X	panHandler->pan_count = 0;
	X	panHandler->pan_queue = XcNodeAlloc();
	X	panHandler->pan_handling = FALSE;
	X	return panHandler;
	X}
	X
	Xstatic void RemovePan(panHandler)
	X	XcPanHandler	panHandler;
	X{
	X	XcPanAmount	*panAmount;
	X	XcNode		*node;
	X
	X	if (panHandler->pan_count != 0) {
	X		/* Remove a pan from the queue */
	X		node = panHandler->pan_queue->f;
	X		panAmount = (XcPanAmount *)node->d;
	X		panHandler->offset_x -= panAmount->x;
	X		panHandler->offset_y -= panAmount->y;
	X		XcNodeRemove(node);
	X		XtFree((char *)panAmount);
	X		XtFree((char *)node);
	X		panHandler->pan_count--;
	X	}
	X}
	X
	Xvoid XcPanEvent(panHandler, event)
	X	XcPanHandler	panHandler;
	X	XEvent		*event;
	X{
	X	int  		x, y;
	X
	X	switch (event->type) {
	X	case Expose:
	X		event->xexpose.x += panHandler->offset_x;
	X		event->xexpose.y += panHandler->offset_y;
	X		break;
	X	case GraphicsExpose:
	X		if (panHandler->pan_handling == FALSE) {
	X			RemovePan(panHandler);
	X			if (event->xgraphicsexpose.count != 0) {
	X				panHandler->pan_handling = TRUE;
	X			}
	X		} else if (event->xgraphicsexpose.count == 0) {
	X			panHandler->pan_handling = FALSE;
	X		}
	X		event->xgraphicsexpose.x += panHandler->offset_x;
	X		event->xgraphicsexpose.y += panHandler->offset_y;
	X		break;
	X	case NoExpose:
	X		RemovePan(panHandler);
	X		panHandler->pan_handling = FALSE;
	X	default:
	X		break;
	X	}
	X}
	X
	X/*
	X * Records a new pan request in a pan handler
	X */
	Xvoid XcPanAccrue(panHandler, delta_x, delta_y)
	X	XcPanHandler	panHandler;
	X	int		delta_x, delta_y;
	X{
	X	XcNode		*node;
	X	XcPanAmount	*panAmount;
	X
	X	/* Place new pan onto the queue */
	X	panAmount = XtNew(XcPanAmount);
	X	panAmount->x = delta_x;
	X	panAmount->y = delta_y;
	X	panHandler->offset_x += delta_x;
	X	panHandler->offset_y += delta_y;
	X	panHandler->pan_count++;
	X	node = XcNodeAlloc();
	X	node->d = (XtPointer)panAmount;
	X	XcNodeInsert(panHandler->pan_queue, node);
	X}
	X
	Xvoid XcPanCompute(x, y, width, height, delta_x, delta_y, how)
	X	int		x, y;
	X	unsigned int	width, height;
	X	int		delta_x, delta_y;
	X	XcPanAccomplish	*how;
	X{
	X	int	tx, ty;
	X	int	code;
	X
	X	if (delta_x == 0 && delta_y == 0) {
	X		/* No real pan was made so don't do anything */
	X		how->copy_width=how->expose_1_width = how->expose_2_width = 0;
	X	} else if (ABS(delta_x) > width || ABS(delta_y) > height) {
	X		/* A delta is large enough that the entire
	X		   rectangle must be repainted */
	X		how->copy_width = how->expose_2_width = 0;
	X		how->expose_1_x = x;
	X		how->expose_1_y = y;
	X		how->expose_1_width = width;
	X		how->expose_1_height = height;
	X	} else {
	X		/* Since we've taken care of the above 2
	X		   cases we can do the following */
	X		if (delta_x < 0) {
	X			how->copy_src_x = x - delta_x;
	X			how->copy_width = width + delta_x;
	X			how->copy_dst_x = x;
	X			how->expose_2_x = x + width + delta_x;
	X			how->expose_2_width = -delta_x;
	X		} else if (delta_x == 0) {
	X			how->copy_src_x = x;
	X			how->copy_width = width;
	X			how->copy_dst_x = x;
	X			how->expose_2_width = 0;
	X		} else {	/* delta_x > 0 */
	X			how->copy_src_x = x;
	X			how->copy_width = width - delta_x;
	X			how->copy_dst_x = x + delta_x;
	X			how->expose_2_x = x;
	X			how->expose_2_width = delta_x;
	X		}
	X		if (delta_y < 0) {
	X			how->copy_src_y = y - delta_y;
	X			how->copy_height = height + delta_y;
	X			how->copy_dst_y = y;
	X			how->expose_1_x = x;
	X			how->expose_1_y = y + height + delta_y;
	X			how->expose_1_width = width;
	X			how->expose_1_height = -delta_y;
	X			how->expose_2_y = y;
	X			how->expose_2_height = height + delta_y;
	X		} else if (delta_y == 0) {
	X			how->copy_src_y = y;
	X			how->copy_height = height;
	X			how->copy_dst_y = y;
	X			how->expose_1_width = 0;
	X			how->expose_2_y = y;
	X			how->expose_2_height = height;
	X		} else {	/* delta_y > 0 */
	X			how->copy_src_y = y;
	X			how->copy_height = height - delta_y;
	X			how->copy_dst_y = y + delta_y;
	X			how->expose_1_x = x;
	X			how->expose_1_y = y;
	X			how->expose_1_width = width;
	X			how->expose_1_height = delta_y;
	X			how->expose_2_y = y + delta_y;
	X			how->expose_2_height = height - delta_y;
	X		}
	X	}
	X}
	X
	X/*
	X * Convenience function to pan a rectangle and update its pan handler
	X */
	Xvoid XcPanRectangle(panHandler, display, window, gc, x, y, width, height,
	X		    src_x, src_y, dest_x, dest_y, panExposeCB, client)
	X	XcPanHandler	panHandler;
	X	Display		*display;
	X	Window		window;
	X	GC		gc;
	X	int		x, y;
	X	unsigned int	width, height;
	X	int		src_x, src_y;
	X	int		dest_x, dest_y;
	X	void		(*panExposeCB)();
	X	XtPointer	client;
	X{
	X	int		delta_x, delta_y;
	X	XcPanAccomplish	how;
	X
	X	delta_x = dest_x - src_x;
	X	delta_y = dest_y - src_y;
	X	XcPanCompute(x, y, width, height, delta_x, delta_y, &how);
	X
	X	if (how.copy_width != 0) {
	X		/* We shouldn't accrue pan info if we are not doing a copy */
	X		/* XXX - Is this right?  Can we get out of sync here? */
	X		XcPanAccrue(panHandler, delta_x, delta_y);
	X		XCopyArea(display, window, window, gc,
	X			  how.copy_src_x, how.copy_src_y,
	X			  how.copy_width, how.copy_height,
	X			  how.copy_dst_x, how.copy_dst_y);
	X	}
	X	if (panExposeCB != (void(*)())0) {
	X		(*panExposeCB)(display, window, gc, &how, client);
	X	} else {
	X		if (how.expose_1_width != 0) {
	X			XClearArea(display, window,
	X				   how.expose_1_x, how.expose_1_y,
	X				   how.expose_1_width, how.expose_1_height,
	X				   TRUE);
	X		}
	X		if (how.expose_2_width != 0) {
	X			XClearArea(display, window,
	X				   how.expose_2_x, how.expose_2_y,
	X				   how.expose_2_width, how.expose_2_height,
	X				   TRUE);
	X		}
	X	}
	X}
SHAR_EOF
if test 7144 -ne "`wc -c < 'PanHandler.c'`"
then
	echo shar: error transmitting "'PanHandler.c'" '(should have been 7144 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'PanHandler.h'" '(1823 characters)'
if test -f 'PanHandler.h'
then
	echo shar: will not over-write existing file "'PanHandler.h'"
else
sed 's/^	X//' << \SHAR_EOF > 'PanHandler.h'
	X/*
	X *	@(#) $Id$
	X */
	X/*
	X * Copyright 1991 Gigadactyl, Inc.
	X *
	X * Permission to use, copy, modify, and distribute this software and its
	X * documentation for any purpose and without fee is hereby granted, provided
	X * that the above copyright notice appear in all copies and that both that
	X * copyright notice and this permission notice appear in supporting
	X * documentation, and that the name of Gigadactyl, Inc. not be used in
	X * advertising or publicity pertaining to distribution of the software
	X * without specific, written prior permission.  Gigadactyl, Inc. makes no
	X * representations about the suitability of this software for any purpose.
	X * It is provided "as is" without express or implied warranty.
	X *
	X * Author: Charles A. Ocheret <chuck@fid.morgan.com>
	X */
	X
	X#ifndef _PanHandler_h_
	X#define _PanHandler_h_
	X
	X/*
	X * This structure describes how to accomplish a pan of a certain rectangular
	X * area.  The normal semantics mean to perform a copy of an area followed by
	X * the exposure of up to 2 regions.  The routine XcPanCompute() fills in this
	X * structure.  The convenience function XcPanRectangle() uses XcPanCompute().
	X */
	Xtypedef struct xcPanAccomplish {
	X	int		copy_src_x, copy_src_y;
	X	unsigned int	copy_width, copy_height;
	X	int		copy_dst_x, copy_dst_y;
	X	int		expose_1_x, expose_1_y;
	X	unsigned int	expose_1_width, expose_1_height;
	X	int		expose_2_x, expose_2_y;
	X	unsigned int	expose_2_width, expose_2_height;
	X} XcPanAccomplish;
	X
	Xtypedef struct xcPanAmount {
	X	int		x;	/* distance in x moved by pan */
	X	int		y;	/* distance in y moved by pan */
	X} XcPanAmount;
	X
	X/* Opaque type XcPanHandler */
	Xtypedef struct xcPanHandler *XcPanHandler;
	X
	Xextern XcPanHandler	XcCreatePanHandler();
	Xextern void		XcPanEvent();
	Xextern void		XcPanAccrue();
	Xextern void		XcPanCompute();
	Xextern void		XcPanRectangle();
	X
	X#endif _PanHandler_h_
SHAR_EOF
if test 1823 -ne "`wc -c < 'PanHandler.h'`"
then
	echo shar: error transmitting "'PanHandler.h'" '(should have been 1823 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'PanHandlerI.h'" '(541 characters)'
if test -f 'PanHandlerI.h'
then
	echo shar: will not over-write existing file "'PanHandlerI.h'"
else
sed 's/^	X//' << \SHAR_EOF > 'PanHandlerI.h'
	X/*
	X *	@(#) $Id$
	X */
	X
	X#ifndef _PanHandlerI_h_
	X#define _PanHandlerI_h_
	X
	X#include "Node.h"
	X#include "PanHandler.h"
	X
	X#ifndef ABS
	X#define ABS(x) (((x) < 0) ? (-(x)) : (x))
	X#endif ABS
	X
	Xstruct xcPanHandler {
	X  int		offset_x;	/* Amount to correct expose event in x */
	X  int		offset_y;	/* Amount to correct expose event in y */
	X  Cardinal	pan_count;	/* Number of pans still outstanding */
	X  XcNode	*pan_queue;	/* Linked list representing pans done */
	X  Boolean	pan_handling;	/* Has 1st GraphicsExpose for pan been seen? */
	X};
	X
	X#endif _PanHandlerI_h_
SHAR_EOF
if test 541 -ne "`wc -c < 'PanHandlerI.h'`"
then
	echo shar: error transmitting "'PanHandlerI.h'" '(should have been 541 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'PanHandlerUT.c'" '(5744 characters)'
if test -f 'PanHandlerUT.c'
then
	echo shar: will not over-write existing file "'PanHandlerUT.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'PanHandlerUT.c'
	X#ifndef lint
	Xstatic char rcsid[] = "@(#) $Id$";
	X#endif
	X/*
	X * Copyright 1991 Gigadactyl, Inc.
	X *
	X * Permission to use, copy, modify, and distribute this software and its
	X * documentation for any purpose and without fee is hereby granted, provided
	X * that the above copyright notice appear in all copies and that both that
	X * copyright notice and this permission notice appear in supporting
	X * documentation, and that the name of Gigadactyl, Inc. not be used in
	X * advertising or publicity pertaining to distribution of the software
	X * without specific, written prior permission.  Gigadactyl, Inc. makes no
	X * representations about the suitability of this software for any purpose.
	X * It is provided "as is" without express or implied warranty.
	X *
	X * Author: Charles A. Ocheret <chuck@fid.morgan.com>
	X */
	X
	X#include <stdio.h>
	X#include <X11/Intrinsic.h>
	X#include "PanHandler.h"
	X
	X#define INCREMENT 50
	X
	Xvoid patternRectangle(display, window, gc, origin, x, y, width, height)
	X	Display		*display;
	X	Window		window;
	X	GC		gc;
	X	XcPanAmount	*origin;
	X	int		x, y;
	X	unsigned int	width, height;
	X{
	X	XSegment	*segments, *sp;
	X	int		i, xstart, xend, ystart, yend, count;
	X	XClearArea(display, window, x, y, width, height, FALSE);
	X	x += origin->x;
	X	y += origin->y;
	X	xstart = (((x+INCREMENT-1)/INCREMENT) * INCREMENT) - origin->x;
	X	ystart = (((y+INCREMENT-1)/INCREMENT) * INCREMENT) - origin->y;
	X	xend = (((x + width - 1)/INCREMENT) * INCREMENT) - origin->x;
	X	yend = (((y + height - 1)/INCREMENT) * INCREMENT) - origin->y;
	X	x -= origin->x;
	X	y -= origin->y;
	X	if (xstart <= xend)
	X		count = 1 + ((xend - xstart)/INCREMENT);
	X	else
	X		count = 0;
	X	if (ystart <= yend)
	X		count += 1 + ((yend - ystart)/INCREMENT);
	X	if (count == 0)
	X		return;
	X	sp = segments = (XSegment *)XtMalloc(count * sizeof(*segments));
	X	for (i = xstart; i <= xend; i += INCREMENT) {
	X		sp->x1 = sp->x2 = i;
	X		sp->y1 = y;
	X		sp->y2 = y + height;
	X		sp++;
	X	}
	X	for (i = ystart; i <= yend; i += INCREMENT) {
	X		sp->y1 = sp->y2 = i;
	X		sp->x1 = x;
	X		sp->x2 = x + width;
	X		sp++;
	X	}
	X	XDrawSegments(display, window, gc, segments, count, CoordModeOrigin);
	X	XtFree((char *)segments);
	X}
	X
	Xvoid panExposeCB(display, window, gc, how, client)
	X	Display		*display;
	X	Window		window;
	X	GC		gc;
	X	XcPanAccomplish	*how;
	X	XtPointer	client;
	X{
	X	if (how->expose_1_width != 0) {
	X		patternRectangle(display, window, gc, client,
	X				 how->expose_1_x, how->expose_1_y,
	X				 how->expose_1_width, how->expose_1_height);
	X	}
	X	if (how->expose_2_width != 0) {
	X		patternRectangle(display, window, gc, client,
	X				 how->expose_2_x, how->expose_2_y,
	X				 how->expose_2_width, how->expose_2_height);
	X	}
	X}
	X
	Xmain(argc, argv)
	X	int	argc;
	X	char	*argv[];
	X{
	X	Display			*display;
	X	int			screen;
	X	XSetWindowAttributes	winAtts;
	X	unsigned long		MrMask;
	X	Window			win, root;
	X	XGCValues		gcval;
	X	GC			white_gc;
	X	char			cp[10];
	X	KeySym			ksym;
	X	XEvent			e;
	X	XcPanHandler		panHandler;
	X	int			x, y, i, k;
	X	unsigned int		width, height;
	X	XcPanAmount		origin;
	X
	X	XtToolkitInitialize();
	X	if ((display = XOpenDisplay(NULL)) == NULL) {
	X		(void)fprintf(stderr, "Couldn't open display '%s'\n",
	X			      XDisplayName(NULL));
	X		exit(1);
	X	}
	X	screen = DefaultScreen(display);
	X
	X	MrMask = CWBackPixel | CWBorderPixel;
	X	winAtts.border_pixel = WhitePixel(display, screen);
	X	winAtts.background_pixel = BlackPixel(display, screen);
	X	win = XCreateSimpleWindow(display,
	X				  RootWindow(display, screen),
	X				  0, 0,
	X				  500, 500,
	X				  2,
	X				  DefaultDepth(display, screen),
	X				  InputOutput,
	X				  CopyFromParent,
	X				  MrMask,
	X				  winAtts);
	X
	X	MrMask = GCForeground |GCBackground | GCGraphicsExposures | GCFunction;
	X	gcval.foreground = WhitePixel(display, screen);
	X	gcval.background = BlackPixel(display, screen);
	X	gcval.graphics_exposures = True;
	X	gcval.function = GXcopy;
	X	white_gc = XCreateGC(display, win, MrMask, gcval);
	X
	X	XSelectInput(display, win,
	X		     Button1MotionMask |
	X		     KeyPressMask |
	X		     ButtonPressMask |
	X		     StructureNotifyMask |
	X		     ExposureMask);
	X	XMapWindow(display, win);
	X	XGetGeometry(display, win, &root, &i, &i, &width, &height, &i, &i);
	X
	X	panHandler = XcCreatePanHandler();
	X	origin.x = origin.y = 0;
	X
	X	for (;;) {
	X		XFlush(display);
	X		XNextEvent(display, &e);
	X		switch (e.type) {
	X		case ButtonPress:
	X			x = e.xbutton.x;
	X			y = e.xbutton.y;
	X			break;
	X		case MotionNotify:
	X			origin.x += x - e.xmotion.x;
	X			if (origin.x < 0) {
	X				x -= origin.x;
	X				origin.x = 0;
	X			}
	X			origin.y += y - e.xmotion.y;
	X			if (origin.y < 0) {
	X				y -= origin.y;
	X				origin.y = 0;
	X			}
	X			XcPanRectangle(panHandler, display, win, white_gc,
	X				       0, 0, width, height,
	X				       x, y, e.xmotion.x, e.xmotion.y,
	X				       panExposeCB, &origin);
	X			x = e.xmotion.x;
	X			y = e.xmotion.y;
	X			break;
	X		case Expose:
	X			XcPanEvent(panHandler, &e);
	X			patternRectangle(display, win, white_gc, &origin,
	X					 e.xexpose.x,     e.xexpose.y,
	X					 e.xexpose.width, e.xexpose.height);
	X			break;
	X		case GraphicsExpose:
	X			XcPanEvent(panHandler, &e);
	X			patternRectangle(display, win, white_gc, &origin,
	X					 e.xgraphicsexpose.x,
	X					 e.xgraphicsexpose.y,
	X					 e.xgraphicsexpose.width,
	X					 e.xgraphicsexpose.height);
	X			break;
	X		case NoExpose:
	X			XcPanEvent(panHandler, &e);
	X			break;
	X		case ConfigureNotify:
	X			width = e.xconfigure.width;
	X			height = e.xconfigure.height;
	X			break;
	X		case KeyPress:
	X			k = XLookupString(&e, cp, sizeof(cp)-1, &ksym, NULL);
	X			cp[k] = '\0';
	X			if (!strcmp(cp, "q")) {
	X				exit(0);
	X			} else if (!strcmp(cp, "l")) {
	X				XClearArea(display, win, 0, 0,
	X					   width, height, TRUE);
	X			} else if (!strcmp(cp, "r")) {
	X				origin.x = origin.y = 0;
	X				XClearArea(display, win, 0, 0,
	X					   width, height, FALSE);
	X				patternRectangle(display, win, white_gc,
	X						 &origin, 0, 0, width, height);
	X			}
	X			break;
	X		default:
	X			break;
	X		}
	X	}
	X}
SHAR_EOF
if test 5744 -ne "`wc -c < 'PanHandlerUT.c'`"
then
	echo shar: error transmitting "'PanHandlerUT.c'" '(should have been 5744 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0


-- 
+--------------------+             Chuck Ocheret             +---------------+
|chuck@fid.Morgan.COM|       Morgan Stanley & Co., Inc.      |(212) 703-4474 |
|    Duty now ...    |19th Floor, 1251 Avenue of the Americas|for the future.|
+--------------------+      New York, N.Y.  10020 USA        +---------------+