[comp.windows.x] XGHOST: Source code release; here it is!

jim@athsys.uucp (Jim Becker) (02/21/89)

Finally! Here is the XGhost work that I have done. There are only four
files in this shar, their usefulness is as follows:

	README.XGhost 	-- how/what/why XGhost is, release notes.

	casper.c 	-- little program that monitors XGhost status

	changes.patch 	-- `diff -c' format changes, executed from
			   the core.src directory. (Note that this is
			   my first time doing this, and it may need 
			   tweeking. I also started from a virgin X11R2..)

	sunCapture.c	-- main record/playback logic for XGhost. This goes
			   into the ddx/sun directory.

I hope that this works ok; I'm not well versed in the mechanisms for
this type of release. Additional changes to this, for R3 and any other
additions, will have to be done by someone else. My current status is
changing and I will not be able to continue enhancing this code. This
code was created and tested on Sun 3/60 and Sun 3/50 running OS release
3.4.

Good luck, have fun!

-Jim Becker		...!sun!athsys!jim	February 20th, 1989


#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README.XGhost casper.c changes.patch sunCapture.c
# Wrapped by jim@tityus on Mon Feb 20 11:19:09 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README.XGhost' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README.XGhost'\"
else
echo shar: Extracting \"'README.XGhost'\" \(6539 characters\)
sed "s/^X//" >'README.XGhost' <<'END_OF_FILE'
X
X			XGhost.Sun Documentation
X
XThis is the description of the implementation and usage of the XGhost
Xfeature now in the X Server logic for Sun machines. This addition is
Xfor the X11R2 version of the code. The upgrade to R3 is something that
XI don't have the time to do. We are also not yet running R3 here.
X
XThe release of this code to the net is the first time that I have
Xdone such a thing, so I hope that what I am doing is corrct. I did
Xa `diff -c' to create the file, then am sharing everything together.
X
XOverview
X
XThe XGhost functionality affords the user the ability to capture and
Xplayback an arbitrary user session with the X Window System.  This
Xcapture/playback simulates the exact sequence of user input at the
XFirm_event level (i.e. -- device driver) within the Server.
X
XIt is important to note that a sequence that is captured will only
Xreplay in the same method if the machine state is identical at the
Xcommencement of playback to that at commencement of capture. This
Xmeans that this logic doesn't understand anything other than really
Xlow level stuff, so the same low level stuff needs the same starting
Xpoint to work correctly. Note also that the mouse sensitivity during
Xstorage and playback has to be the same.
X
XIf anything goes astray in the playback the software will currently
Xnot recognize this has happened. It will merrily continue along with
Xghosting the user input. Make sure that you take this into account
Xbefore putting "rm -Rf *" type operations into the script! I attempted
Xto provide a shortcut to afford this, but it cause the playback to be
Xinterrupted (it would pause until user input happened). Fixes that
Xcorrect this would be helpful, as one currently has to wait for the
Xduration of the session to gain control of the server once again.
X
X
XUsage
X
XThere are two modes available, saving events and playing events.
XNormally these would be mutually exclusive, but with minor changes to
Xthe record logic they need not be.  The signal to the logic that there
Xis a change in the current state is signified by performing mouse
Xclicks in the upper left hand corner of the screen. The mouse buttons
Xeach stand for a different mode change, as follows:
X
X	Left Button	-- commence capture of events
X
X	Middle Button	-- conclude capture of events
X
X	Right Button	-- perform playback of events
X
XThe events that are stored and played back are saved in specific
Xhardcoded files. These files are either created or opened upon desired
Xaction. The files used are stored in the directory /usr/tmp and are as
Xfollows:
X
X	save.events	-- file created with user events
X
X	play.events	-- file opened and played during playback
X
X	debug.events	-- optional debugging output file
X
XThe play.events file is created initially by a capture as a
Xsave.events file. It needs to be moved or renamed before playback is
Xexpected. The debugging file is useful if the logic isn't working or
Xis being moved to another platform. There is an internal flag in the
XsunCapture.c file that toggles this file creation.
X
XFiles that are active can be identified because the file mask is
Xmodified to enable the execute bit. Once the file is no longer active
Xthe bit is disabled. This can be used programmatically to (uggh) poll
Xthe current state of the XGhost logic. It can also be checked with the
X'-F' option of ls.  Each time there is a capture event session the
Xsave.events (and the possibly the debug.events) file(s) are rewritten.
XThere is a program created, called casper.c, that outputs current
Xstate via the state of an icon in the lower right corner of the
Xdisplay screen.
X
XIt is important to move or rename save files that are desired to be
Xretained. The best method to manage the files is a program that
Xassigns significance to them by naming and moves them around as
Xneeded. I intended to write one for our use, but it would not be
Xavailable outside of my company, as it used proprietary UI technology.
X
XThe format of the files is ASCII, but it is compressed as much as
Xpossible to save space. There is no compression of the mouse events or
Xother events, as that would not function for our need. This is easy to
Xdo, but in our case would not work. To determine the complete format
Xof the files look to the source code.
X
XDuring capture the autorepeat feature of the server is disabled.
XDuring playback the terminal bell is disabled. These are due to funky
Xprogrammatic needs; the call to usleep() upsets the timer interrupt.
XThe call to usleep() could be replaced by a timeout call to select(),
Xbut I do not have the time to make this modification.
X
XFiles Modified
X
XThere was the addition of only one file, sunCapture.c, to implement
Xthe save logic. Other files in the ddx/sun directory that had to be
Xmodified are the following:
X
X	sunInit.c, sunKbd.c, sunMouse.c, Imakefile
X
XThe changes are available in a diff patch file. The other file that
Xhad to be changed (that took me FOREVER to find!!) is the file:
X
X	os/4.2bsd/connection.c
X
XThere is just a single line change in there, that resets the signal
Xhandler and timer for me after it is used in the ReadBuffer() routine.
XMake sure that you put this in, as not having it will cause the
Xplayback to hang you!
X
XThere is a quick hack program, casper.c, that provides iconic feedback
Xof the current state of the XGhost. Build it and run it in the
Xbackground.
X
X
XConclusion
X
XThe logic plays back events at the same speed as the initial input.
XMaking this playback faster requires additional logic to synchronize
Xdifferent types of resource usage completion. This is beyond the scope
Xof the input mechanisms, and is "an exercise for the reader".
X
XThe method used for the timing of the events should probably use a
Xdelta time between events, rather than a delta from the beginning of
Xthe run.  This is useful so one can concatenate different sessions
Xtogether for a longer run. Also the keystrokes should probabily be
Xmapped in the events, so it is easier to read what the user typed if
Xlooking at events. A will leave this completion for others.
X
XI hope that this effort is useful and functions as a sturdy bridge
Xuntil said time when the X Consortium completes and distributes their
Xsolution.
X
XSorry that this took so long to get out, it was somewhat of a battle
Xto have it actually happen. Any fixes or additions that are
Xaccomplished would be of interest to me, please give me feedback as
Xappropriate. I am moving to Sun Microsystems in the next few weeks
Xalso, so mail responses may take a little while after the 23rd. Hope
Xthat this helps !!
X
X
X-Jim Becker	...!sun!athsys!jim		February 20, 1989
X
X
END_OF_FILE
if test 6539 -ne `wc -c <'README.XGhost'`; then
    echo shar: \"'README.XGhost'\" unpacked with wrong size!
fi
# end of 'README.XGhost'
fi
if test -f 'casper.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'casper.c'\"
else
echo shar: Extracting \"'casper.c'\" \(15366 characters\)
sed "s/^X//" >'casper.c' <<'END_OF_FILE'
X/*
X *	casper.c	-- program to watch XGhost status
X *
X *	This program is used to provide a graphical display of the
X *	current state of the XGhost logic in the X Server. The method
X *	used is to watch for the execute bit(s) in the save/playback
X *	hardcoded files that the XGhost server logic modifies. The
X *	output of this program is simply the correct iconic form to
X *	represent the nature of the currect activity of the XGhost.
X *
X * 	This program currently puts the initial icon in the lower-right
X *	corner of the screen. This should be generalized w/ geometry
X *	interpretation. (It's just a `quick hack'...)
X *
X *	Jim Becker	February 14, 1989
X */
X
X#include	<ctype.h>
X#include	<stdio.h>
X#include 	<time.h>
X#include	<sys/types.h>
X#include	<sys/stat.h>
X#include	<sys/dir.h>
X     
X#include 	<X11/Xlib.h>
X#include	<X11/Xatom.h>
X#include	<X11/Xutil.h>
X
X#define		DELAY_TIME	3
X
X#define		WIDTH		64
X#define		HEIGHT		64
X#define		TOP_RECT	46
X#define		BORDER_WIDTH	2
X
X#define		TRUE		1
X#define		FALSE		0
X
X#define		applic_name	"Casper"
X#define		playback_file	"/usr/tmp/play.events"
X#define		save_file	"/usr/tmp/save.events"
X
Xtypedef		enum		{Inactive, Record, Playback}	XGhostState;
X
XXGhostState	CurrentState, PriorState;
XDisplay		*display;
XVisual		visual;
XXSetWindowAttributes
X		attributes;
XWindow		RootWin;
XWindow		window;
XPixmap		inactive, record, playback;
XGC		gc, gcb;
X
X/*
X *	==== These are iconic images already created an bundled herein ====
X */
X#define inactive_width 64
X#define inactive_height 64
Xstatic char inactive_bits[] = {
X   0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x01, 0x00, 0x00, 0x00,
X   0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x80,
X   0x01, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0x80, 0x01, 0xfc, 0x01, 0x08,
X   0x10, 0x00, 0x7f, 0x80, 0x01, 0xff, 0x07, 0x04, 0x20, 0xc0, 0xff, 0x81,
X   0x81, 0xff, 0x0f, 0xfe, 0x7f, 0xe0, 0xff, 0x83, 0xc1, 0xff, 0x1f, 0xfe,
X   0x7f, 0xf0, 0xff, 0x87, 0xe1, 0xff, 0x3f, 0x00, 0x00, 0xf8, 0xff, 0x8f,
X   0xe1, 0xff, 0x3f, 0x00, 0x00, 0xf8, 0xff, 0x8f, 0xf1, 0xff, 0x7f, 0x00,
X   0x00, 0xfc, 0xff, 0x9f, 0xf1, 0xff, 0x7f, 0xd0, 0x7b, 0xfc, 0xff, 0x9f,
X   0xf1, 0xdf, 0x7f, 0x50, 0x48, 0xfc, 0xf7, 0x9f, 0xf1, 0x8f, 0x7f, 0x50,
X   0x38, 0xfc, 0xe3, 0x9f, 0xf1, 0xdf, 0x7f, 0x50, 0x48, 0xfc, 0xf7, 0x9f,
X   0xf1, 0xff, 0x7f, 0xde, 0x7b, 0xfc, 0xff, 0x9f, 0xf1, 0xff, 0x7f, 0x00,
X   0x00, 0xfc, 0xff, 0x9f, 0xe1, 0xff, 0x3f, 0x00, 0x00, 0xf8, 0xff, 0x8f,
X   0xe1, 0xff, 0x3f, 0x80, 0x01, 0xf8, 0xff, 0x8f, 0xc1, 0xff, 0x1f, 0x80,
X   0x01, 0xf0, 0xff, 0x87, 0x81, 0xff, 0x0f, 0xa0, 0x05, 0xe0, 0xff, 0x83,
X   0x01, 0xff, 0x07, 0xc0, 0x03, 0xc0, 0xff, 0x81, 0x01, 0xfc, 0x01, 0x80,
X   0x01, 0x00, 0x7f, 0x80, 0x01, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x80,
X   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00,
X   0x00, 0x00, 0x00, 0x80, 0x61, 0x00, 0x60, 0x00, 0x01, 0x00, 0x00, 0x80,
X   0xe1, 0x00, 0x70, 0x00, 0x01, 0x00, 0x00, 0x80, 0xc1, 0x01, 0x38, 0x00,
X   0x01, 0x00, 0x80, 0x80, 0x81, 0x03, 0x1c, 0x00, 0x01, 0x00, 0x80, 0x80,
X   0x01, 0x07, 0x0e, 0x00, 0x01, 0x00, 0xe0, 0x83, 0x01, 0x0e, 0x07, 0x3e,
X   0x9f, 0x8f, 0x8f, 0x80, 0x01, 0x9c, 0x03, 0x22, 0x91, 0x88, 0x80, 0x80,
X   0x01, 0xf8, 0x01, 0x22, 0x91, 0x88, 0x8f, 0x80, 0x01, 0xf0, 0x00, 0x22,
X   0x91, 0x08, 0x88, 0x80, 0x01, 0xf0, 0x00, 0x3e, 0x91, 0x8f, 0x8f, 0x80,
X   0x01, 0xf8, 0x01, 0x20, 0x00, 0x00, 0x00, 0x80, 0x01, 0x9c, 0x03, 0x20,
X   0x00, 0x00, 0x00, 0x80, 0x01, 0x0e, 0x07, 0x20, 0xff, 0xff, 0xff, 0x80,
X   0x01, 0x07, 0x0e, 0x20, 0x00, 0x00, 0x00, 0x80, 0x81, 0x03, 0x1c, 0x3e,
X   0xfc, 0xff, 0x3f, 0x80, 0xc1, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x80,
X   0xe1, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x80, 0x61, 0x00, 0x60, 0xfe,
X   0xff, 0xff, 0xff, 0x83, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
X   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xf9, 0xff, 0xff, 0xff,
X   0xff, 0xff, 0xff, 0x9f, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf,
X   0x1d, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xfd, 0xfd, 0xff, 0xff,
X   0xff, 0xff, 0xff, 0xbf, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf,
X   0xfd, 0xfd, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xbf, 0xfd, 0xfd, 0xff, 0xff,
X   0x7f, 0xff, 0xff, 0xbf, 0xfd, 0xfd, 0xfd, 0xf7, 0x1f, 0xec, 0xff, 0xbf,
X   0xfd, 0xfd, 0xc1, 0xeb, 0x70, 0x7f, 0x37, 0xbc, 0xfd, 0xfd, 0xdd, 0xdd,
X   0x7e, 0x6f, 0xb7, 0xbd, 0xfd, 0xfd, 0xdd, 0xc1, 0x7e, 0x6f, 0x37, 0xbc,
X   0xfd, 0xfd, 0xdd, 0xdd, 0x7e, 0xef, 0xba, 0xbf, 0x1d, 0xc0, 0xdd, 0xdd,
X   0x70, 0xef, 0x3d, 0xbc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf,
X   0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x01, 0x00, 0x00, 0x00,
X   0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
X   0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
X
X#define record_width 64
X#define record_height 64
Xstatic char record_bits[] = {
X   0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x01, 0x00, 0x00, 0x00,
X   0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x40, 0x06, 0x00, 0x00, 0x80,
X   0x01, 0x00, 0x00, 0x80, 0x0c, 0x00, 0x00, 0x80, 0x01, 0xfc, 0x01, 0x00,
X   0x19, 0x00, 0x7f, 0x80, 0x01, 0xff, 0x07, 0x00, 0x32, 0xc0, 0xff, 0x81,
X   0x81, 0xff, 0x0f, 0xfe, 0x7f, 0xe0, 0xff, 0x83, 0xc1, 0xff, 0x1f, 0xfe,
X   0x7f, 0xf0, 0xff, 0x87, 0xe1, 0xff, 0x3f, 0x00, 0x00, 0xf8, 0xff, 0x8f,
X   0xe1, 0xff, 0x3f, 0x00, 0x00, 0xf8, 0xff, 0x8f, 0xf1, 0xff, 0x7f, 0x00,
X   0x00, 0xfc, 0xff, 0x9f, 0xf1, 0xff, 0x7f, 0xd0, 0x7b, 0xfc, 0xff, 0x9f,
X   0xf1, 0xdf, 0x7f, 0x50, 0x48, 0xfc, 0xf7, 0x9f, 0xf1, 0x8f, 0x7f, 0x50,
X   0x38, 0xfc, 0xe3, 0x9f, 0xf1, 0xdf, 0x7f, 0x50, 0x48, 0xfc, 0xf7, 0x9f,
X   0xf1, 0xff, 0x7f, 0xde, 0x7b, 0xfc, 0xff, 0x9f, 0xf1, 0xff, 0x7f, 0x00,
X   0x00, 0xfc, 0xff, 0x9f, 0xe1, 0xff, 0x3f, 0x00, 0x00, 0xf8, 0xff, 0x8f,
X   0xe1, 0xff, 0x7f, 0x81, 0xa1, 0xf8, 0xff, 0x8f, 0xc1, 0xff, 0x9f, 0x82,
X   0x41, 0xf1, 0xff, 0x87, 0x81, 0xff, 0x0f, 0xa5, 0x85, 0xe2, 0xff, 0x83,
X   0x01, 0xff, 0x07, 0xca, 0x03, 0xc5, 0xff, 0x81, 0x01, 0xfc, 0x01, 0x94,
X   0x01, 0x0a, 0x7f, 0x80, 0x01, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x80,
X   0x01, 0x00, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x80, 0x01, 0x00, 0x00, 0x05,
X   0x80, 0x02, 0x00, 0x80, 0x61, 0x00, 0xe0, 0x02, 0x41, 0x01, 0x00, 0x80,
X   0xe1, 0x00, 0x70, 0x01, 0xa1, 0x00, 0x00, 0x80, 0xc1, 0x01, 0x38, 0x00,
X   0x01, 0x00, 0x80, 0x80, 0x81, 0x03, 0x1c, 0x00, 0x01, 0x00, 0x80, 0x80,
X   0x01, 0x07, 0x0e, 0x00, 0x01, 0x00, 0xe0, 0x83, 0x01, 0x0e, 0x07, 0x3e,
X   0x9f, 0x8f, 0x8f, 0x80, 0x01, 0x9c, 0x03, 0x22, 0x91, 0x88, 0x80, 0x80,
X   0x01, 0xf8, 0x01, 0x22, 0x91, 0x88, 0x8f, 0x80, 0x01, 0xf0, 0x00, 0x22,
X   0x91, 0x08, 0x88, 0x80, 0x01, 0xf0, 0x00, 0x3e, 0x91, 0x8f, 0x8f, 0x80,
X   0x01, 0xf8, 0x01, 0x20, 0x00, 0x00, 0x00, 0x80, 0x01, 0x9c, 0x03, 0x20,
X   0x00, 0x00, 0x00, 0x80, 0x01, 0x0e, 0x07, 0x20, 0xff, 0xff, 0xff, 0x80,
X   0x01, 0x07, 0x0e, 0x20, 0x00, 0x00, 0x00, 0x80, 0x81, 0x03, 0x1c, 0x3e,
X   0xfc, 0xff, 0x3f, 0x80, 0xc1, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x80,
X   0xe1, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x80, 0x61, 0x00, 0x60, 0xfe,
X   0xff, 0xff, 0xff, 0x83, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
X   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xf9, 0xff, 0xff, 0xff,
X   0xff, 0xff, 0xff, 0x9f, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf,
X   0x1d, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xdd, 0xf7, 0xff, 0xff,
X   0xff, 0xff, 0xff, 0xbf, 0xdd, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf,
X   0xdd, 0xf7, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xbf, 0xdd, 0xf7, 0xff, 0xff,
X   0xff, 0xfb, 0xff, 0xbf, 0x1d, 0xf8, 0xff, 0xff, 0xfd, 0xdb, 0xff, 0xbf,
X   0x5d, 0x3e, 0x0c, 0x83, 0xe1, 0xfb, 0xff, 0xbf, 0xdd, 0xbc, 0xed, 0xbb,
X   0x6d, 0xd8, 0x6c, 0xb8, 0xdd, 0x39, 0xec, 0xbb, 0x7d, 0xdb, 0x6a, 0xbb,
X   0xdd, 0xb3, 0xef, 0xbb, 0x7d, 0xdb, 0x66, 0xb8, 0xdd, 0x37, 0x0c, 0x83,
X   0x7d, 0xd8, 0xee, 0xbb, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xb8,
X   0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x01, 0x00, 0x00, 0x00,
X   0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
X   0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
X
X#define playback_width 64
X#define playback_height 64
Xstatic char playback_bits[] = {
X   0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x01, 0x00, 0x00, 0x00,
X   0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x80,
X   0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x80, 0x01, 0xfc, 0x01, 0x00,
X   0x18, 0x00, 0x7f, 0x80, 0x01, 0xff, 0x07, 0x00, 0x30, 0xc0, 0xff, 0x81,
X   0x81, 0xff, 0x0f, 0xfe, 0x7f, 0xe0, 0xff, 0x83, 0xc1, 0xff, 0x1f, 0xfe,
X   0x7f, 0xf0, 0xff, 0x87, 0xe1, 0xff, 0x3f, 0x00, 0x00, 0xf8, 0xff, 0x8f,
X   0xe1, 0xff, 0x3f, 0x00, 0x00, 0xf8, 0xff, 0x8f, 0xf1, 0xff, 0x7f, 0x00,
X   0x00, 0xfc, 0xff, 0x9f, 0xf1, 0xff, 0x7f, 0xd0, 0x7b, 0xfc, 0xff, 0x9f,
X   0xf1, 0xdf, 0x7f, 0x50, 0x48, 0xfc, 0xf7, 0x9f, 0xf1, 0x8f, 0x7f, 0x50,
X   0x38, 0xfc, 0xe3, 0x9f, 0xf1, 0xdf, 0x7f, 0x50, 0x48, 0xfc, 0xf7, 0x9f,
X   0xf1, 0xff, 0x7f, 0xde, 0x7b, 0xfc, 0xff, 0x9f, 0xf1, 0xff, 0x7f, 0x00,
X   0x00, 0xfc, 0xff, 0x9f, 0xe1, 0xff, 0x3f, 0x00, 0x00, 0xf8, 0xff, 0x8f,
X   0xe1, 0xff, 0xff, 0x80, 0x61, 0xf8, 0xff, 0x8f, 0xc1, 0xff, 0x9f, 0x81,
X   0xc1, 0xf0, 0xff, 0x87, 0x81, 0xff, 0x0f, 0xa3, 0x85, 0xe1, 0xff, 0x83,
X   0x01, 0xff, 0x07, 0xc6, 0x03, 0xc3, 0xff, 0x81, 0x01, 0xfc, 0x01, 0x8c,
X   0x01, 0x06, 0x7f, 0x80, 0x01, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x80,
X   0x01, 0x00, 0x00, 0x06, 0x00, 0x03, 0x00, 0x80, 0x01, 0x00, 0x00, 0x03,
X   0x80, 0x01, 0x00, 0x80, 0x61, 0x00, 0xe0, 0x01, 0xc1, 0x00, 0x00, 0x80,
X   0xe1, 0x00, 0xf0, 0x00, 0x61, 0x00, 0x00, 0x80, 0xc1, 0x01, 0x38, 0x00,
X   0x01, 0x00, 0x80, 0x80, 0x81, 0x03, 0x1c, 0x00, 0x01, 0x00, 0x80, 0x80,
X   0x01, 0x07, 0x0e, 0x00, 0x01, 0x00, 0xe0, 0x83, 0x01, 0x0e, 0x07, 0x3e,
X   0x9f, 0x8f, 0x8f, 0x80, 0x01, 0x9c, 0x03, 0x22, 0x91, 0x88, 0x80, 0x80,
X   0x01, 0xf8, 0x01, 0x22, 0x91, 0x88, 0x8f, 0x80, 0x01, 0xf0, 0x00, 0x22,
X   0x91, 0x08, 0x88, 0x80, 0x01, 0xf0, 0x00, 0x3e, 0x91, 0x8f, 0x8f, 0x80,
X   0x01, 0xf8, 0x01, 0x20, 0x00, 0x00, 0x00, 0x80, 0x01, 0x9c, 0x03, 0x20,
X   0x00, 0x00, 0x00, 0x80, 0x01, 0x0e, 0x07, 0x20, 0xff, 0xff, 0xff, 0x80,
X   0x01, 0x07, 0x0e, 0x20, 0x00, 0x00, 0x00, 0x80, 0x81, 0x03, 0x1c, 0x3e,
X   0xfc, 0xff, 0x3f, 0x80, 0xc1, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x80,
X   0xe1, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x80, 0x61, 0x00, 0x60, 0xfe,
X   0xff, 0xff, 0xff, 0x83, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
X   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xf9, 0xff, 0xff, 0xff,
X   0xff, 0xff, 0xff, 0x9f, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf,
X   0x1d, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xdd, 0xf7, 0xff, 0xff,
X   0xff, 0xff, 0xff, 0xb7, 0xdd, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb7,
X   0xdd, 0xb7, 0xff, 0xff, 0xfe, 0xff, 0xef, 0xb7, 0xdd, 0xb7, 0xff, 0xff,
X   0xfe, 0xff, 0xef, 0xb7, 0xdd, 0xb7, 0xef, 0xff, 0xfe, 0xfd, 0x6f, 0xb7,
X   0x1d, 0xb8, 0xd7, 0xdd, 0xfe, 0x3a, 0xac, 0xb7, 0xdd, 0xbf, 0xbb, 0xdd,
X   0x60, 0xb7, 0xcf, 0xb7, 0xdd, 0xbf, 0x83, 0xdd, 0x6e, 0xb0, 0xaf, 0xb7,
X   0xdd, 0xbf, 0xbb, 0xc1, 0x6e, 0xb7, 0x6f, 0xbf, 0xdd, 0x3f, 0xbb, 0xdf,
X   0x60, 0x37, 0xec, 0xb6, 0xfd, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xbf,
X   0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x01, 0x00, 0x00, 0x00,
X   0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
X   0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
X
X
X/*
X *	actual code!
X */
X
XInitializeProgram()
X{
X	XSizeHints		xsize;
X	XSetWindowAttributes	wattr;
X	int			scrno;
X	int			white, black;
X	XGCValues		gcvalues;
X	int			gcvaluemask;
X	int			X,Y;
X
X	display	= XOpenDisplay( NULL );
X
X	if( display == NULL ) {
X		printf("** Can't access the display !! **\n");
X		exit(1);
X	}
X
X	scrno	= DefaultScreen(display);
X	RootWin	= RootWindow(display,scrno);
X
X	white	= WhitePixel(display,DefaultScreen(display));
X	black	= BlackPixel(display,DefaultScreen(display));
X
X	/* hardcode position to lower right edge of screen */
X	X	= DisplayWidth(  display, scrno ) - WIDTH  - BORDER_WIDTH * 2 - 2;
X	Y	= DisplayHeight( display, scrno ) - HEIGHT - BORDER_WIDTH * 2 - 2;
X
X	window	= XCreateSimpleWindow( display, RootWin, 
X			    X, Y, WIDTH, HEIGHT, BORDER_WIDTH,
X			    black, white );
X
X	if( window == NULL ) {
X		printf("** Can't open display window !! **\n");
X		return FALSE;
X	}
X
X 	XChangeProperty( display, window, XA_WM_NAME, XA_STRING, 8, 
X			 PropModeReplace, applic_name, sizeof(applic_name) );
X
X	xsize.x			= X;
X	xsize.y			= Y;
X	xsize.width		= WIDTH;
X	xsize.height		= HEIGHT;
X	xsize.min_width		= WIDTH;
X	xsize.min_height	= HEIGHT;
X	xsize.max_width		= WIDTH;
X	xsize.max_height	= HEIGHT;
X
X	xsize.flags		= USPosition | USSize 	| 
X				  PPosition  | PSize 	|
X				  PMinSize   | PMaxSize;
X
X	XSetNormalHints( display, window, &xsize );
X
X	XSelectInput( display, window, ExposureMask  );
X
X	XMapWindow( display, window );
X
X	/* create normal graphics contexts */
X	gcvaluemask		= GCFunction		| 
X				  GCPlaneMask		|
X				  GCForeground		|
X				  GCBackground		|
X				  GCGraphicsExposures	|
X				  GCSubwindowMode	;
X
X	gcvalues.function	= GXcopy;
X	gcvalues.plane_mask	= AllPlanes;
X	gcvalues.foreground	= black;
X	gcvalues.background	= white;
X	gcvalues.graphics_exposures
X				= FALSE;
X	gcvalues.subwindow_mode	= IncludeInferiors;
X
X	gc		= XCreateGC( display, window, gcvaluemask, &gcvalues );
X
X	/* create inverting graphics context */
X	gcvalues.foreground	= white^black;
X	gcvalues.function	= GXxor;
X
X	gcb		= XCreateGC( display, window, gcvaluemask, &gcvalues );
X
X	inactive	= XCreateBitmapFromData( display, window,
X				inactive_bits, inactive_width, inactive_height );
X
X	record		= XCreateBitmapFromData( display, window,
X				record_bits, record_width, record_height );
X
X	playback	= XCreateBitmapFromData( display, window,
X				playback_bits, playback_width, playback_height );
X
X	CurrentState = PriorState	= Inactive;
X}
X
X
XDetermineImage()
X{
X	struct 	stat	filestatus;
X
X	CurrentState	= Inactive;
X
X        if( stat( playback_file, &filestatus ) == 0 ) {
X		if( filestatus.st_mode & S_IEXEC ) 
X			CurrentState	= Playback;
X		else {
X			if( stat( save_file, &filestatus ) == 0 ) {
X				if( filestatus.st_mode & S_IEXEC )
X					CurrentState	= Record;
X			}
X		}		
X	}
X
X	if( CurrentState != PriorState ) {
X		UpdateImage();
X		PriorState	= CurrentState;
X	}
X}
X
XUpdateImage()
X{
X	Pixmap	pix;
X
X/*	PrintState(); */
X
X	switch( CurrentState ) {
X	case	Inactive:
X		pix	= inactive;
X		break;
X	case	Record:
X		pix	= record;
X		break;
X	case	Playback:
X		pix	= playback;
X		break;
X	}
X
X	XCopyArea( display, pix, window, gc, 0,0,
X		   record_width, record_height, 0,0); /* all sizes are the same */
X
X	XFlush( display );
X}
X
X/*
X *	invert blinks the state while it is active
X */
XBlinkState()
X{
X	XFillRectangle( display, window, gcb, 0, TOP_RECT, WIDTH, HEIGHT-TOP_RECT );
X}
X	
XPrintState()
X{
X	char	*p;
X
X	switch( CurrentState ) {
X	case	Inactive:
X		p	= "Inactive";
X		break;
X	case	Record:
X		p	= "Record";
X		break;
X	case	Playback:
X		p	= "Playback";
X		break;
X	}
X
X	printf("current state is %s\n", p );
X}
X
Xmain()
X{
X	XEvent		xevent;
X
X	if( !InitializeProgram() ) {
X		printf("Can't startup, shutdown.\n");
X		exit(1);
X	}
X
X	while( TRUE ) {
X
X		/* check current status */
X		DetermineImage();
X
X		/* we don't do anything much here */
X		while( XPending(display) ) {
X
X			XNextEvent( display, &xevent );
X
X			switch( xevent.type ) {
X			case	Expose:
X				UpdateImage();
X				break;
X			default:
X				break;
X			}
X
X		}
X
X		sleep(DELAY_TIME);
X
X		if( CurrentState == Record || 
X		    CurrentState == Playback )
X			BlinkState();
X	}
X}
X
X	
X
END_OF_FILE
if test 15366 -ne `wc -c <'casper.c'`; then
    echo shar: \"'casper.c'\" unpacked with wrong size!
fi
# end of 'casper.c'
fi
if test -f 'changes.patch' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'changes.patch'\"
else
echo shar: Extracting \"'changes.patch'\" \(9102 characters\)
sed "s/^X//" >'changes.patch' <<'END_OF_FILE'
XCommon subdirectories: oldserver/ddx and server/ddx
XCommon subdirectories: oldserver/os and server/os
XCommon subdirectories: oldserver/ddx/sun and server/ddx/sun
Xdiff -c -r oldserver/ddx/sun/Imakefile server/ddx/sun/Imakefile
X*** oldserver/ddx/sun/Imakefile	Mon Feb 20 10:06:15 1989
X--- server/ddx/sun/Imakefile	Mon Feb 20 10:07:16 1989
X***************
X*** 2,7 ****
X--- 2,8 ----
X  	sunInit.c \
X  	sunBW2.c \
X  	sunBW2zoid.c \
X+ 	sunCapture.c \
X  	sunCursor.c \
X  	sunIo.c \
X  	sunKbd.c \
XOnly in server/ddx/sun: README.XGhost
XOnly in server/ddx/sun: change.list
XOnly in server/ddx/sun: sunCapture.c
Xdiff -c -r oldserver/ddx/sun/sunInit.c server/ddx/sun/sunInit.c
X*** oldserver/ddx/sun/sunInit.c	Mon Feb 20 10:05:43 1989
X--- server/ddx/sun/sunInit.c	Thu Jan 12 13:39:08 1989
X***************
X*** 87,93 ****
X   *-----------------------------------------------------------------------
X   */
X  /*ARGSUSED*/
X! static void
X  SigIOHandler(sig, code, scp)
X      int		code;
X      int		sig;
X--- 87,93 ----
X   *-----------------------------------------------------------------------
X   */
X  /*ARGSUSED*/
X! /*static  -- jcb */ void
X  SigIOHandler(sig, code, scp)
X      int		code;
X      int		sig;
Xdiff -c -r oldserver/ddx/sun/sunKbd.c server/ddx/sun/sunKbd.c
X*** oldserver/ddx/sun/sunKbd.c	Mon Feb 20 10:05:44 1989
X--- server/ddx/sun/sunKbd.c	Thu Jan 12 13:39:10 1989
X***************
X*** 61,66 ****
X--- 61,67 ----
X  extern CARD8 *sunModMap[];
X  extern KeySymsRec sunKeySyms[];
X  extern CARD16 keyModifiersList[];
X+ extern long	  EnabledDevices;
X  
X  static void 	  sunBell();
X  static void 	  sunKbdCtrl();
X***************
X*** 139,144 ****
X--- 140,149 ----
X  		    return (!Success);
X  		}
X  		sysKbPriv.fd = kbdFd;
X+ 
X+ 	        /* define the device to the capture logic */
X+ 		cap_init( kbdFd, 1 );
X+ 
X  		(void) ioctl (kbdFd, KIOCTYPE, &sysKbPriv.type);
X  		(void) ioctl (kbdFd, KIOCGTRANS, &sunKbPriv.trans);
X  #ifdef KB_SUN4
X***************
X*** 273,278 ****
X--- 278,286 ----
X  		}
X  
X  		RemoveEnabledDevice(kbdFd);
X+ 
X+ 		/* terminate known connection to capture */
X+ 		cap_term( kbdFd );
X  	    }
X  	    pKeyboard->on = FALSE;
X  	    break;
X***************
X*** 302,311 ****
X      KbPrivPtr	  pPriv = (KbPrivPtr) pKeyboard->devicePrivate;
X      int	  	  kbdCmd;   	    /* Command to give keyboard */
X      int	 	  kbdOpenedHere; 
X!  
X!     if (loudness == 0) {
X   	return;
X-     }
X  
X      kbdOpenedHere = ( pPriv->fd < 0 );
X      if ( kbdOpenedHere ) {
X--- 310,319 ----
X      KbPrivPtr	  pPriv = (KbPrivPtr) pKeyboard->devicePrivate;
X      int	  	  kbdCmd;   	    /* Command to give keyboard */
X      int	 	  kbdOpenedHere; 
X! 
X!     /* jcb -- disable use of usleep() if timer signal is being used */
X!     if ( cap_play_is_active() || loudness == 0)
X   	return;
X  
X      kbdOpenedHere = ( pPriv->fd < 0 );
X      if ( kbdOpenedHere ) {
X***************
X*** 326,331 ****
X--- 334,340 ----
X       * Leave the bell on for a while == duration (ms) proportional to
X       * loudness desired with a 10 thrown in to convert from ms to usecs.
X       */
X+ 
X      usleep (pPriv->ctrl->bell_duration * 1000);
X   
X      kbdCmd = KBD_CMD_NOBELL;
X***************
X*** 414,421 ****
X      static Firm_event	evBuf[MAXEVENTS];   /* Buffer for Firm_events */
X  
X      pPriv = (KbPrivPtr) pKeyboard->devicePrivate;
X-     nBytes = read (pPriv->fd, evBuf, sizeof(evBuf));
X  
X      if (nBytes < 0) {
X  	if (errno == EWOULDBLOCK) {
X  	    *pNumEvents = 0;
X--- 423,432 ----
X      static Firm_event	evBuf[MAXEVENTS];   /* Buffer for Firm_events */
X  
X      pPriv = (KbPrivPtr) pKeyboard->devicePrivate;
X  
X+     /* use the capture/playback read routine */
X+     nBytes = cap_read (pPriv->fd, evBuf, sizeof(evBuf));
X+ 
X      if (nBytes < 0) {
X  	if (errno == EWOULDBLOCK) {
X  	    *pNumEvents = 0;
X***************
X*** 427,433 ****
X  	*pNumEvents = nBytes / sizeof (Firm_event);
X      }
X  
X!     if (autoRepeatKeyDown && autoRepeatReady &&
X  	     pPriv->ctrl->autoRepeat == AutoRepeatModeOn && *pNumEvents == 0) {
X  	*pNumEvents = 1;			/* Fake the event */
X  	evBuf[0].id = AUTOREPEAT_EVENTID;	/* Flags autoRepeat event */
X--- 438,445 ----
X  	*pNumEvents = nBytes / sizeof (Firm_event);
X      }
X  
X!     /* jcb -- no auto repeat allowed for this server during capture !! (sorry) */
X!     if ( cap_is_passive() && autoRepeatKeyDown && autoRepeatReady &&
X  	     pPriv->ctrl->autoRepeat == AutoRepeatModeOn && *pNumEvents == 0) {
X  	*pNumEvents = 1;			/* Fake the event */
X  	evBuf[0].id = AUTOREPEAT_EVENTID;	/* Flags autoRepeat event */
X***************
X*** 473,479 ****
X  
X      ptrPriv = (PtrPrivPtr) LookupPointerDevice()->devicePrivate;
X  
X!     if (autoRepeatKeyDown && fe->id == AUTOREPEAT_EVENTID) {
X  	pPriv = (KbPrivPtr) pKeyboard->devicePrivate;
X  	if (pPriv->ctrl->autoRepeat != AutoRepeatModeOn) {
X  		autoRepeatKeyDown = 0;
X--- 485,492 ----
X  
X      ptrPriv = (PtrPrivPtr) LookupPointerDevice()->devicePrivate;
X  
X!     /* jcb -- no autorepeat allowed in this server during capture !! */
X!     if ( cap_is_passive() && autoRepeatKeyDown && fe->id == AUTOREPEAT_EVENTID) {
X  	pPriv = (KbPrivPtr) pKeyboard->devicePrivate;
X  	if (pPriv->ctrl->autoRepeat != AutoRepeatModeOn) {
X  		autoRepeatKeyDown = 0;
X***************
X*** 535,541 ****
X  	    xE.u.u.type = KeyRelease;
X      }
X  
X!     if ((xE.u.u.type == KeyPress) && (keyModifiers == 0)) {
X  	/* initialize new AutoRepeater event & mark AutoRepeater on */
X  	if (autoRepeatDebug)
X              ErrorF("sunKbdProcessEvent: VKEY_DOWN\n");
X--- 548,555 ----
X  	    xE.u.u.type = KeyRelease;
X      }
X  
X!     /* jcb -- no auto repeat allowed for this server during capture !! */
X!     if ( cap_is_passive() && (xE.u.u.type == KeyPress) && (keyModifiers == 0)) {
X  	/* initialize new AutoRepeater event & mark AutoRepeater on */
X  	if (autoRepeatDebug)
X              ErrorF("sunKbdProcessEvent: VKEY_DOWN\n");
X***************
X*** 762,767 ****
X--- 776,787 ----
X  {
X      static struct timeval artv = { 0, 0 };	/* autorepeat timeval */
X  
X+     /* insure there is no time delay in select() if we are waiting for playback */
X+     if( cap_play_is_active() ) {
X+         *pptv = NULL;
X+ 	return;
X+     }
X+ 
X      if (!autoRepeatKeyDown)
X  	return;
X  
X***************
X*** 790,795 ****
X--- 810,819 ----
X      pointer pReadmask;
X  {
X      struct timeval tv;
X+ 
X+     /* don't do anything during playback */
X+     if( cap_play_is_active() ) 
X+         return;
X  
X      if (autoRepeatDebug)
X  	ErrorF("sunWakeupHandler(ar=%d, err=%d):\n", autoRepeatKeyDown, err);
Xdiff -c -r oldserver/ddx/sun/sunMouse.c server/ddx/sun/sunMouse.c
X*** oldserver/ddx/sun/sunMouse.c	Mon Feb 20 10:05:43 1989
X--- server/ddx/sun/sunMouse.c	Thu Jan 12 13:39:12 1989
X***************
X*** 127,132 ****
X--- 127,135 ----
X  			}
X  		    
X  		    sysMousePriv.fd = fd;
X+ 
X+ 		    /* define the device to the capture logic */
X+ 		    cap_init( fd, 0 );
X  		}
X  	    }
X  
X***************
X*** 171,176 ****
X--- 174,182 ----
X  			VUIDSFORMAT, &oformat) < 0) {
X  		    Error ("VUIDSFORMAT");
X  		}
X+ 
X+ 		/* terminate known connection to capture */
X+ 		cap_term( ((PtrPrivPtr)pMouse->devicePrivate)->fd );
X  	    }
X  	    break;
X  
X***************
X*** 251,257 ****
X  
X      pPriv = (PtrPrivPtr) pMouse->devicePrivate;
X  
X!     nBytes = read (pPriv->fd, evBuf, sizeof(evBuf));
X  
X      if (nBytes < 0) {
X  	if (errno == EWOULDBLOCK) {
X--- 257,264 ----
X  
X      pPriv = (PtrPrivPtr) pMouse->devicePrivate;
X  
X!     /* use the capture/playback read routine */
X!     nBytes = cap_read (pPriv->fd, evBuf, sizeof(evBuf));
X  
X      if (nBytes < 0) {
X  	if (errno == EWOULDBLOCK) {
X***************
X*** 330,335 ****
X--- 337,359 ----
X  
X      xE.u.keyButtonPointer.time = TVTOMILLI(fe->time);
X  
X+     /* jcb -- mouse action in upper left signals change in capture/replay status */
X+     if( (pPriv->x < 10 && 		/* upper left corner 	*/
X+ 	 pPriv->y < 10) 	&& 
X+         (fe->value == VKEY_UP)  &&	/* mouse released	*/
X+         (fe->id == MS_LEFT 	|| 	/* one of the buttons	*/
X+ 	 fe->id == MS_MIDDLE 	||
X+ 	 fe->id == MS_RIGHT)	) {
X+ 
X+         /* set to stable base for constant initialization coordinates */
X+         pPriv->x = 0;
X+ 	pPriv->y = 0;
X+ 	pSunPriv->mouseMoved = TRUE;
X+ 
X+ 	/* dispatch the mouse click to do the appropriate action */
X+       	cap_activate( fe->id );
X+     }        
X+ 
X      switch (fe->id) {
X  	case MS_LEFT:
X  	case MS_MIDDLE:
X***************
X*** 456,461 ****
X--- 480,486 ----
X      xE.u.keyButtonPointer.rootY = pPriv->y;
X  
X      (* pMouse->processInputProc) (&xE, pMouse);
X+ 
X  }
X  
X  /*-
XCommon subdirectories: oldserver/os/4.2bsd and server/os/4.2bsd
Xdiff -c -r oldserver/os/4.2bsd/connection.c server/os/4.2bsd/connection.c
X*** oldserver/os/4.2bsd/connection.c	Mon Feb 20 10:53:01 1989
X--- server/os/4.2bsd/connection.c	Thu Jan 12 13:40:40 1989
X***************
X*** 325,330 ****
X--- 325,334 ----
X       * we must have read what we wanted. If we timed out, return FALSE */
X      if(fTimeOut && debug_conns)
X  	ErrorF("Timed out on connection %d\n", conn);
X+ 
X+     /* jim becker -- reset the use of the timer that has been usurped!! */
X+     cap_reset_timer();
X+ 
X      return (!fTimeOut);
X  }
X  
END_OF_FILE
if test 9102 -ne `wc -c <'changes.patch'`; then
    echo shar: \"'changes.patch'\" unpacked with wrong size!
fi
# end of 'changes.patch'
fi
if test -f 'sunCapture.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sunCapture.c'\"
else
echo shar: Extracting \"'sunCapture.c'\" \(14272 characters\)
sed "s/^X//" >'sunCapture.c' <<'END_OF_FILE'
X/*
X *	sunCapture.c	-- code for ddx/sun record and playback 
X *			   of user events at the vuid level.
X *
X *	This is the main module for this software functionality.
X *	This code is used to capture and playback the events that
X *	the user performs. This interfaces to the other software
X *	simply by replacing the read() call that is used by the
X *	keyboard and mouse drivers with a read() that performs
X *	the needed logic. Other support calls are sprinkled in
X *	ddx/sun code to perform initialization and activation 
X *	of the various modes of this record/replay logic.
X *
X *	Jim Becker	-- December 22, 1988
X */
X
X#include	"sun.h"
X#include	<ctype.h>
X#include	<stdio.h>
X#include	<signal.h>
X#include	<sys/time.h>
X
X#define	BADVAL		-1
X#define	MAXBUF		32
X#define	DEBUG_FLAG	FALSE
X
X#define	SAVEFILE	"/usr/tmp/save.events"
X#define	PLAYFILE	"/usr/tmp/play.events"
X#define	DEBUGFILE	"/usr/tmp/debug.events"
X
Xstatic	short		saveact	= FALSE;
Xstatic	short		playact	= FALSE;
Xstatic	short		dbugact	= FALSE;
X
Xstatic	int		keyfd	= BADVAL;
Xstatic	int		moufd	= BADVAL;
Xstatic	FILE		*savefd;
Xstatic	FILE		*playfd;
Xstatic	FILE		*dbugfd;
X
Xstatic	Firm_event	locbuffer[MAXBUF];
Xstatic	int		keyecnt, mouecnt;
Xstatic	int		eventcnt;
Xstatic	int		actmask		= 0766;
Xstatic	int		passmask	= 0666;
Xstatic	int		timeexpired	= TRUE;
Xstatic	int		timestart	= 0;
Xstatic	short		debug_needed	= DEBUG_FLAG;
X
Xstatic	struct timeval	tval;
Xstatic	struct timezone	tzone;
Xstatic	struct itimerval value, ovalue;
X
X/*	hooks to the signal IO processing */
Xextern	int		sunSigIO;
Xextern	void		SigIOHandler();
X
X/*	predefines to local routines	  */
Xstatic	void		cap_init_debug(), 	cap_term_debug(), 	cap_output_debug(),
X			cap_open_play(),	cap_close_play(),	cap_open_save(),
X			cap_close_save(),	cap_output_events(),	cap_printf(),
X			cap_check_abort();
X
X/*
X *	alarm handler routine -- sets flag signalling event is ready.
X */
Xstatic	void
Xalarm_handler(sig, code, scp)
Xint 		sig, code;
Xstruct sigcontext *scp;
X{
X	/* signals that there are events in the wings for process */
X	/* the following two flags are set also by SIGIO signals  */
X	sunSigIO++;
X	isItTimeToYield++;
X	
X	timeexpired	= TRUE;
X}
X	
X
X/*
X *	==== Routines Called From External Sources ====
X */
X
X/*
X *	initialize the file descriptor local copy (for definition)
X */
Xvoid
Xcap_init( fd, bool )
Xint		fd;
Xint		bool;
X{
X	char	*str;
X
X	/* 0 = mouse; 1 = keyboard */
X	if( bool == 0 )
X		moufd	= fd;
X	else
X		keyfd	= fd;
X}
X
X/*
X *	new vuid read routine. returns Firm_events to the user
X *	either from the virtual device or from the playback script.
X *	note that real reads during playback are lost.
X */
Xint
Xcap_read( fd, bufptr, bufsize )
Xint		fd;
Xchar		*bufptr;
Xint		bufsize;
X{
X	int	numbytes;
X
X	if( playact ) 
X		numbytes = cap_play_read( fd, bufptr, bufsize );
X	else
X		numbytes = read( fd, bufptr, bufsize );
X
X	if( saveact && numbytes > 0 )
X		cap_output_events( fd, bufptr, numbytes );
X
X	/* the following call _would_ allow premature abort of the playback  */
X	/* but the use of it causes the system to pause until some user input*/
X	/* (note that I don't believe this _should_ happen, but it does....) */
X/*	cap_check_abort();	*/
X
X	return numbytes;
X}
X
X/*
X *	terminate knowledge of an fd
X */
Xvoid
Xcap_term( fd )
Xint		fd;
X{
X	if( fd == moufd )
X		moufd	= BADVAL;
X	else
X	if( fd == keyfd )
X		keyfd	= BADVAL;
X
X	cap_close_save();
X}
X
X/*
X *	called to change the modes from the mouse button dispatcher
X */
Xvoid
Xcap_activate( button )
Xu_short		button;
X{
X	switch( button ) {
X	case	MS_LEFT:
X		cap_open_save();
X		break;
X	case	MS_MIDDLE:
X		cap_close_save();
X		break;
X	case	MS_RIGHT:
X		cap_open_play();
X		break;
X	}
X}
X
X
X/*
X *	Reset the timer that ReadBuffer() stole (in os/4.2bsd/connection.c) !!
X */
Xvoid
Xcap_reset_timer()
X{
X	if( playact ) {
X		signal( SIGIO,   SIG_IGN );
X		signal( SIGALRM, alarm_handler );
X
X		setitimer( ITIMER_REAL, &value, &ovalue );
X	}
X}
X
X
X/*
X *	state information used by rest of sun ddx
X */
Xint
Xcap_play_is_active()
X{
X	return playact;
X}
X
X
Xint
Xcap_is_passive()
X{
X	return (playact || saveact ? FALSE : TRUE );
X}
X
X
X
X/*
X *	==== Routines Used Internally ====
X */
X
X/*
X *	open the playback file for the user
X */
Xstatic	void
Xcap_open_play()
X{
X	playfd	= fopen( PLAYFILE, "r" );
X
X	playact	= (playfd != NULL);
X
X	if( playact ) {
X
X		/* make the mask appear active  */
X	  	chmod( PLAYFILE, actmask );
X
X		signal( SIGIO,   SIG_IGN );
X		signal( SIGALRM, alarm_handler );
X
X		gettimeofday( &tval, &tzone );
X
X		timestart	= tval.tv_sec;
X
X		eventcnt = 0;
X
X		if( debug_needed == TRUE ) 
X			cap_init_debug(); 
X
X		if( cap_load_next() == FALSE )
X			cap_close_play();
X	}
X}
X
X/*
X *	terminate the playback file reading
X */
Xstatic	void
Xcap_close_play()
X{
X	if( playact ) {
X		fclose(playfd);
X		playfd	= NULL;
X		playact	= FALSE;
X
X		signal( SIGALRM, SIG_IGN );
X		signal( SIGIO,   SigIOHandler);
X
X		/* make the mask appear passive  */
X	  	chmod( PLAYFILE, passmask );
X
X		cap_term_debug();
X	}
X}
X
X
X/*
X *	initiate the save functionality
X */
Xstatic	void
Xcap_open_save()
X{
X	savefd	= fopen( SAVEFILE, "w" );
X
X	chmod( SAVEFILE, actmask );
X
X	saveact	= (savefd != NULL);
X
X	timestart = 0;
X}
X
X/*
X *	terminate the save functionality
X */
Xstatic	void
Xcap_close_save()
X{
X	if( saveact ) {
X		fclose(savefd);
X		savefd	= NULL;
X		saveact	= FALSE;
X
X		chmod( SAVEFILE, passmask );
X	}
X}
X
X/*
X *	this is the main routine that either picks up the
X *	next loaded event from the playback file or simulates
X *	the actual situation during storage by returning
X *	failure status. The success of this routine returning
X *	the next event depends on: 1) The correct type of
X *	event (from matching fds) 2) The expiration of the
X *	event timer. The event timer goes off when the 
X *	delta difference between the last event and the
X *	current event has elapsed.
X */
Xstatic	int
Xcap_play_read( fd, bufptr, bufsize )
Xint		fd;
Xchar		*bufptr;
Xint		bufsize;
X{
X	int		bytesize;
X
X	/* return either next event to the user or simulate EWOULDBLOCK */
X	if( (((fd == moufd) && (mouecnt > 0)) ||
X	     ((fd == keyfd) && (keyecnt > 0))) &&
X	    timeexpired) {
X
X		/* we have valid information, move it to user 	*/
X		/* buffer and return correct size in bytes	*/
X		bytesize	= (sizeof(Firm_event)) * eventcnt;
X
X		bcopy( (char *)locbuffer, bufptr, bytesize );
X
X		/* pre-load the next event and setup timer for it */ 
X		if( cap_load_next() == FALSE )
X			cap_close_play();
X
X		return bytesize;
X	}
X	else {
X		errno	= EWOULDBLOCK;
X		return BADVAL;
X	}
X}
X
X/*
X *	load the next ASCII description of the
X *	events into the buffer. Note that the
X *	logic allows the time values to be generated
X *	in pairs (or better) that are reconstructed
X *	upon demand. (Thus saving space in the event
X *	file).
X */
X
Xstatic	int
Xcap_load_next()
X{
X	Firm_event	*event		= locbuffer;
X	Firm_event	*levent		= NULL;
X	int		locnumevents;
X	int		id, ptype, pair, evalue, sec, usec;
X	char		etype;
X	char		mtype;
X	int		totusec;
X	int		numparsed;
X
X	/* back to zero! */
X	mouecnt = keyecnt = eventcnt = 0;
X
X	numparsed = fscanf(playfd,"%c%d", &etype, &eventcnt);
X
X	if( numparsed != 2 ) 
X		return FALSE;
X
X	if( etype == 'm' )
X		mouecnt	= eventcnt;
X	else
X		keyecnt	= eventcnt;
X
X	locnumevents	= eventcnt;
X
X	while( locnumevents-- > 0 ) {
X
X		id = ptype = pair = evalue = sec = usec	= 0;
X
X		if( etype == 'm' ) {
X
X			fscanf(playfd, "_%c'%d:%x/%x",
X				&mtype, &evalue, &sec, &usec );
X
X			/* these are the only ones I have seen generated.. */
X			switch( mtype ) {
X			case 'X':
X				id	= LOC_X_DELTA;
X				ptype	= FE_PAIR_ABSOLUTE;
X				pair	= 130;	/* (don't know significance!) */
X				break;
X			case 'Y':
X				id	= LOC_Y_DELTA;
X				ptype	= FE_PAIR_ABSOLUTE;
X				pair	= 131;	/* (don't know significance!) */
X				break;
X			case 'L':
X				id	= MS_LEFT;
X				break;
X			case 'M':
X				id	= MS_MIDDLE;
X				break;
X			case 'R':
X				id	= MS_RIGHT;
X				break;
X			}
X
X		}
X		else {
X			fscanf(playfd, "_%d'%d:%x/%x",
X				&id, &evalue, &sec, &usec );
X		}
X
X		/* zero then fill the event structure for user */
X		bzero( (char *)event, sizeof(Firm_event) );
X	
X		event->id	 	= (u_short)id;
X		event->pair_type	= (u_char)ptype;
X		event->pair		= (u_char)pair;
X		event->value		= evalue;
X		event->time.tv_sec 	= sec;
X		event->time.tv_usec	= usec;
X
X		/* fill in same time from last event if needed */
X		if( levent != NULL && TVTOMILLI(event->time) == 0 ) {
X			event->time.tv_sec  = levent->time.tv_sec;
X			event->time.tv_usec = levent->time.tv_usec;
X		}
X		else
X			event->time.tv_sec += timestart;
X
X		/* set the last event to the current and bump ptr */
X		levent	= event++;
X	}
X
X	fscanf(playfd,"\n");
X
X	/* is the debugging mode active ? */
X	if( dbugact )
X		cap_output_debug( (etype == 'm' ? moufd : keyfd ) );
X
X	/* determine delta value to next time event and set it */
X	if( locbuffer[0].time.tv_usec < tval.tv_usec ) {
X		locbuffer[0].time.tv_usec += 1000000;
X		locbuffer[0].time.tv_sec  -= 1;
X	}
X
X	value.it_value.tv_sec	= locbuffer[0].time.tv_sec  - tval.tv_sec;
X	value.it_value.tv_usec	= locbuffer[0].time.tv_usec - tval.tv_usec;
X
X	/* insure that there are no strange numbers occuring... */
X	if( value.it_value.tv_usec > 1000000 ) {
X		value.it_value.tv_sec	+= 1;
X		value.it_value.tv_usec	%= 1000000;
X	}
X	else
X	if( value.it_value.tv_usec < 0 )
X		value.it_value.tv_usec	 = 0;
X
X	/* update current (relative) time of the last event in set */
X	tval	= locbuffer[eventcnt-1].time;
X	totusec	= TVTOMILLI(value.it_value);
X
X	/* insure some delay is there */
X	if( totusec < 10 ) {
X	  	value.it_value.tv_sec = 0;
X		value.it_value.tv_usec= 10000;
X	}
X
X	/* is the debugging logic active? */
X	if( dbugact )
X		cap_printf("timer(%d/%u)(%d)\n", value.it_value.tv_sec,
X				value.it_value.tv_usec, totusec );
X
X	/* clear timer expiration flag */
X	timeexpired = FALSE;
X
X	/* load the timer. This should always work... */
X	setitimer( ITIMER_REAL, &value, &ovalue );
X
X	return TRUE;
X}	
X
X
X/*
X *	output current events to the ASCII text file. The events
X *	output are compressed as much as possible as far as information
X *	content. This includes different fields for mouse and keyboard,
X *	as well as translation of mouse type and removal of redundant
X *	timing information.
X */
X
Xstatic	void
Xcap_output_events( fd, bufptr, bufsize )
Xint		fd;
Xchar		*bufptr;
Xint		bufsize;
X{
X	Firm_event	*event;
X	Firm_event	*levent	= NULL;
X	int		numevents;
X	char		etype;
X	char		mtype;
X	char		*format;
X
X	event		= (Firm_event *)bufptr;
X	numevents	= bufsize / (sizeof(Firm_event));
X
X	if( fd == moufd )
X		etype	= 'm';
X	else
X		etype	= 'k';
X
X	fprintf(savefd,"%c%d", etype, numevents);
X
X	/* check for first time event, set base time */
X	if( timestart == 0 ) 
X		timestart = event->time.tv_sec;
X
X	while( numevents-- > 0 ) {
X
X	  	/* we do different event formats for mouse and keyboard */
X		/* in order to minimize size of output file.. 		*/
X	
X		if( etype == 'm' ) {
X
X			/* these are the only ones I have seen generated.. */
X			switch( event->id ) {
X			case LOC_X_DELTA:
X				mtype	= 'X';
X				break;
X			case LOC_Y_DELTA:
X				mtype	= 'Y';
X				break;
X			case MS_LEFT:
X				mtype	= 'L';
X				break;
X			case MS_MIDDLE:
X				mtype	= 'M';
X				break;
X			case MS_RIGHT:
X				mtype	= 'R';
X				break;
X			}
X
X			/* surpress redundant time values by setting to zero */
X			if( levent != NULL && 
X			    TVTOMILLI(event->time) == TVTOMILLI(levent->time) ) 
X				format	= "_%c'%d:0/0";
X			else
X				format	= "_%c'%d:%x/%x";
X
X			fprintf(savefd, format, 
X				mtype, event->value, 
X				event->time.tv_sec - timestart, 
X				event->time.tv_usec );
X		}
X		else {
X
X			/* surpress redundant time values by setting to zero */
X			if( levent != NULL && 
X			    TVTOMILLI(event->time) == TVTOMILLI(levent->time) ) 
X				format	= "_%d'%d:0/0";
X			else
X				format	= "_%d'%d:%x/%x";
X
X			fprintf(savefd, format, 
X				event->id, event->value, 
X				event->time.tv_sec - timestart, 
X				event->time.tv_usec );
X
X		}
X
X		/* save the last event, and get to the next */
X		levent	= event++;
X	}
X
X	fprintf(savefd,"\n");
X}
X
X
X/*
X *	this routine is used to check to see if the user has clicked
X *	the left mouse button during playback. If this is the case the
X *	playback is terminated. This is understood the next time we read.
X *
X *	This Routine Is Currently Not Utilized Because The Read() Causes The
X *	Playback To Wait For Some User Event To Occur, Which Stops Everything.
X */
Xstatic	void
Xcap_check_abort()
X{
X	Firm_event	event;
X	int		numbytes;
X	short		gotleftclick		= FALSE;
X
X	/* read mouse events as long as we have them, left mouse terminates */
X	numbytes = read( moufd, (char *)&event, sizeof(event) );
X
X	while( numbytes > 0 ) {
X
X		if( event.id == MS_LEFT )
X			gotleftclick	= TRUE;
X
X		numbytes = read( moufd, (char *)&event, sizeof(event) );
X	}
X
X	if( gotleftclick )
X		cap_close_play();
X}
X
X
X/*
X *	==== Routines that are used for debugging ====
X *
X *	These are utilized if the DEBUG_FLAG is TRUE.
X *
X *	Note that I don't care for #ifdefs for code....
X */
Xstatic	void
Xcap_init_debug()
X{
X	dbugfd	= fopen( DEBUGFILE, "w" );
X
X	chmod( DEBUGFILE, actmask );
X
X	dbugact	= (dbugfd != NULL);
X
X	if( dbugact )
X		cap_printf("**Initial debug entry**\n");
X}
X
Xstatic	void
Xcap_term_debug()
X{
X	if( dbugact ) {
X		cap_printf("**Final debug entry**\n");
X		fclose(dbugfd);
X		dbugfd 	= NULL;
X		dbugact	= FALSE;
X
X		chmod( DEBUGFILE, passmask );
X	}
X}
X
Xstatic	void
Xcap_output_debug( fd )
Xint		fd;
X{
X	Firm_event	*event		= locbuffer;
X	int		numevents	= eventcnt;
X	char		etype;
X	char		mtype;
X
X	if( !dbugact )
X	  	return;
X
X	if( fd == moufd )
X		etype	= 'm';
X	else
X		etype	= 'k';
X
X	fprintf(dbugfd,"%c%d", etype, numevents);
X
X	while( numevents-- > 0 ) {
X
X	  	/* we do different event formats for mouse and keyboard */
X		/* in order to minimize size of output file.. 		*/
X	
X		if( etype == 'm' ) {
X
X			/* these are the only ones I have seen generated.. */
X			switch( event->id ) {
X			case LOC_X_DELTA:
X				mtype	= 'X';
X				break;
X			case LOC_Y_DELTA:
X				mtype	= 'Y';
X				break;
X			case MS_LEFT:
X				mtype	= 'L';
X				break;
X			case MS_MIDDLE:
X				mtype	= 'M';
X				break;
X			case MS_RIGHT:
X				mtype	= 'R';
X				break;
X			}
X
X			fprintf(dbugfd, "_%c'%d:%x/%x",
X				mtype, event->value, 
X				event->time.tv_sec - timestart, 
X				event->time.tv_usec );
X		}
X		else {
X
X			fprintf(dbugfd, "_%d'%d:%x/%x",
X				event->id, event->value, 
X				event->time.tv_sec - timestart, 
X				event->time.tv_usec );
X
X		}
X
X		event++;
X	}
X
X	fprintf(dbugfd,"\n");
X
X	fflush(dbugfd);
X}
X
Xstatic	void
Xcap_printf( str, a,b,c,d )
Xchar		*str;
Xint		a,b,c,d;
X{
X	if( dbugact ) {
X		fprintf(dbugfd, str, a,b,c,d );
X		fflush(dbugfd);
X	}
X}
END_OF_FILE
if test 14272 -ne `wc -c <'sunCapture.c'`; then
    echo shar: \"'sunCapture.c'\" unpacked with wrong size!
fi
# end of 'sunCapture.c'
fi
echo shar: End of shell archive.
exit 0