[comp.windows.x] snap: program to save X bitmaps in SUN raster format

jim@athsys.uucp (Jim Becker) (09/01/88)

   


Please excuse the following source posting to comp.windows.x, but 
I my stuff isn't complex enough for comp.sources.x to accept it..

The following is a program that clips arbitrary areas of the X
display and saves them in Sun raster format, which is FrameMaker
and other compatible. See intro comment for further details.


-Jim Becker

--skip or snip--

/*
 *	snap.c	-- Screen snapshot program for SUN raster images (SUN ONLY!)
 *
 *	Snap is utility that allows the user to select (crop) a portion of an
 *	X window display and save the bitmap image in a FrameMaker compatible
 *	picture format. (FrameMaker is probabily trademarked by Frame, Inc.).
 *	
 *	The "snap" program functions from the command line, and is best if run
 *	in a small window located away from the section(s) of the screen to be
 *	snapshot. It has been written to poll for keyboard keys being held
 *	down, and checks for the keys about three times a second. When it
 *	notices a key of import has been depressed, the appropriate action is
 *	taken. This polling behavior has been done for our requirements, as
 *	we need to have snapshots of the current pointer and popups correct.
 *	
 *	Selection of the portion of the screen to snapshot is done by using a
 *	"guide window". This is a heavy bordered empty window that frames the
 *	portion of the screen that is the current subject of the capture
 *	operation.  This window is resized and positioned using the standard
 *	window manager, and it's content is captured in the snapshot. Note
 *	that it must be "behind" the windows of interest when the snapshot is
 *	taken, else it will obscure the image to be captured. The snapshot(s)
 *	taken will contain the entire area within the borders of the guide
 *	window.
 *	
 *	Command line invokation of the program is simple, "snap [ <prefix> ]"
 *	is used to start it. The <prefix> is the optional name that is used
 *	for the stored images. Files that are created by snap are named using
 *	the convention "<prefix>.Snn", where "nn" is the numeric photo number.
 *	Multiple images can be captured during the run of the snap program,
 *	and it appends the photo count to the name. The default <prefix> is
 *	"Snap", yielding names "Snap.S01", "Snap.S02" etc. This makes it easy
 *	to slide through all the names when Importing them into FrameMaker.
 *	
 *	All program selection is via the mechanism of holding down keys, and 
 *	the keys that are held down are fairly obscure ones on the Sun keyboard.
 *	Most programs ignore these keys, but beware of editors and such that may
 *	try to interpret these keys. "It seemed fine in all my tests.. (FLW)".
 *	
 *	The keys of interest are as follows:
 *	
 *		ALTERNATE  -- Snapshot the screen image 
 *	
 *		R13        -- Bring guide window to front
 *	
 *		R15        -- Push guide window to back
 *	
 *		R3         -- Exit the snap program
 *	
 *	
 *	The key depression is not registered immediately, but as soon as
 *	it is noticed a message is printed to the program startup window.
 *	In addition, snapshots ring the terminal bell upon completion. Note
 *	that there is a delay in place after the snapshot before the next,
 *	but the auto-winder is working. Watch out for multiple like shots.
 *	
 *	To build: 	cc -o snap snap.c -lX11 -lm -lpixrect
 *
 *	Created on a Sun -- only for a Sun !!
 *
 *	Right2Copy (R2C) 1988 -- Athena Systems, Inc.
 *
 *	Rev 	Date	Who		Reason
 *	---	-----	---		------
 *	V1.0	08/16	Jim Becker	Initial creation
 *		08/23	Jim Becker	Cleaned up and released to X
 *
 */

#include	<ctype.h>
#include	<stdio.h>
#include	<pixrect/pixrect_hs.h>

#include	<X11/X.h>
#include 	<X11/Xlib.h>
#include 	<X11/Xutil.h>
#include 	<X11/Xatom.h>

#define	ICON_NAME	"*Snapper*"
#define	DEFAULT_NAME	"Snap"
#define	SLEEP_DURATION	300000

#define	PLACE		100
#define	SIZE		400
#define	BW		5

#define	MIN(a,b)	((a) < (b) ? (a) : (b) )
#define	MAX(a,b)	((a) > (b) ? (a) : (b) )

#define	TRUE		1
#define	FALSE		0

Display		*mdisplay;
Window		mroot, oroot;
GC		mdefaultgc;

int		dwidth, dheight;
int		x, y;
int		wwidth, wheight;
int		ww	= BW;

char		name[100];
int		count;

struct	pixrect	*screen;

/*
 *	init program - create guide window and place
 */
init()
{
	XSizeHints	xsize;
	int		screenno;

	if( (mdisplay = XOpenDisplay( NULL ) ) == NULL ) {
		printf("Can't grab display !!\n" );
		exit(1);
	}

	if( (screen = pr_open( "/dev/fb" ) ) == NULL ) {
		printf("Can't grab pixrect screen !!\n");
		exit(1);
	}

	screenno	= DefaultScreen(mdisplay);
	mroot		= RootWindow(mdisplay,screenno);

	dwidth		= DisplayWidth( mdisplay,screenno);
	dheight		= DisplayHeight(mdisplay,screenno);

	mdefaultgc	= DefaultGC(mdisplay,screenno);
	
	XSetSubwindowMode( mdisplay, mdefaultgc, IncludeInferiors );

	oroot	= XCreateSimpleWindow( mdisplay, mroot, PLACE,PLACE,SIZE,SIZE,
				ww,1, WhitePixel(mdisplay,screenno));

	if( oroot == NULL ) {
		printf("Can't open viewing window!\n");
		exit(1);
	}

 	XChangeProperty( mdisplay, oroot, XA_WM_NAME, XA_STRING, 8, 
			 PropModeReplace, ICON_NAME, sizeof(ICON_NAME)-1 );

	xsize.x		= 0;
	xsize.y		= 0;
	xsize.width	= SIZE;
	xsize.height	= SIZE;
	xsize.min_width	= 0;
	xsize.min_height= 0;
	xsize.max_width	= dwidth;
	xsize.max_height= dheight;

	xsize.flags	= USPosition | USSize;

	XSetNormalHints( mdisplay, oroot, &xsize );
	
	x		= PLACE;
	y		= PLACE;
	wheight		= SIZE;
	wwidth		= SIZE;

	XSelectInput( mdisplay, oroot, StructureNotifyMask );

	XMapWindow(   mdisplay, oroot );
}

/*
 *	terminate program
 */
term()
{
	XDestroyWindow( mdisplay, oroot );
	XCloseDisplay( mdisplay );

	pr_close( screen );
}
  

/*
 *	handle resize of guide window
 */
resetsize( cevent )
XConfigureEvent	*cevent;
{
	/* get new location and size */

	x	= MAX(cevent->x,0);
	y	= MAX(cevent->y,0);
	wwidth	= cevent->width;
	wheight	= cevent->height;

	/* crop to size of the physical screen */
	if( (x + wwidth + ww*2) > dwidth )
	  	wwidth	= dwidth - x - ww*2;

	if( (y + wheight + ww*2) > dheight )
		wheight	= dheight - y - ww*2;
}


/*
 *	ugly polling of the keymap for special keys.
 *
 *	(Have checked return code to determine these key combos).
 */
checkkeymap( done )
short		*done;
{
	char		keys[32];
	char		buffer[100];

	XQueryKeymap( mdisplay, keys );

	if( keys[3] & 0x04 ) {	/* check for the "Alternate" key */

		/* determine name of new file */
		sprintf(buffer, "%s.S%02d", name, ++count);

		printf("Save to file '%s'...", buffer );
		fflush(stdout);

		if( savecurrenttodisk( buffer ) )
			printf(".done.\n");
		else
			printf(".Error saving!!\n");

		XBell( mdisplay, 100 );

		return;
	}
	else
	if( keys[14] & 0x80 ) {	/* check for the R13 key */
		printf("Window to front.\n");
		XRaiseWindow( mdisplay, oroot );
	}
	else
	if( keys[15] & 0x02 ) {	/* check for the R15 key */
		printf("Window to back.\n");
		XLowerWindow( mdisplay, oroot );
	}
	else
	if( keys[3]  & 0x40 ) {	/* check for the R3 key */
		printf("Done snapping.\n");
		*done	= TRUE;
	}
	else
	  	return FALSE;

	return TRUE;
}

/*
 *	clip the current location and store the frame buffer to disk
 */
savecurrenttodisk( filename )
char		*filename;
{
	FILE		*out;
struct	pixrect		*clip;
	colormap_t	*colormap	= NULL;
	int		type		= RT_BYTE_ENCODED; /* RLL encoded */
	int		copy_flag	= TRUE;

	out	= fopen( filename, "w" );
	
	if( out == NULL )
		return FALSE;

	clip	= pr_region( screen, x+ww, y+ww, wwidth, wheight );

	pr_dump( clip, out, colormap, type, copy_flag );		

	fclose( out );

	pr_destroy( clip );

	return TRUE;
}

/*
 *	=== MAINLINE ===
 */
main( argc, argv )
int	argc;
char	**argv;
{
	XEvent		xevent;
	XButtonEvent	*bevent	= &xevent.xbutton;
	short		done	= FALSE;

	/* get the name prefix from the user or default */
	if( argc >= 2 )
		strcpy( name, *++argv );
	else
		strcpy( name, DEFAULT_NAME );

	/* initalize program, exits if error */
	init();

	/* loop until exit key is sensed */
	while( !done ) {

		while( XPending( mdisplay ) ) {

			XNextEvent( mdisplay, &xevent );

			switch( xevent.type ) {
			case	ConfigureNotify:
				resetsize( &xevent );
				break;
			}
		}


		/* check (poll) for keys. if found let user up before repeat */
		if( checkkeymap( &done ) && !done )
			sleep(1);
		else
			usleep(SLEEP_DURATION);
	}

	term();

	exit(0);
}