[comp.windows.x] WindWalker: program to graphically display window hierarchy

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

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

The following program graphically displays the current window hier-
archy of the X system. It is useful if you use such paradigms as
InterViews, DECwindows or the X toolkit for your interface. It lets
one watch window resource allocation and lineage. My first X program,
so not very flexible or totally efficient!


-Jim Becker

--skip or snip--

/*
 *	windw.c	-- X window information display program, graphical!
 *
 *	Continuous collection and visual graphing of current 
 *	window hierarchy. Also allows the user to toggle buttons
 *	and change the "sleep" time using the mouse.
 *
 *	This program is written to talk directly to the Xlib, and
 *	is linked standalone to toolkits. It was done as a quick
 *	X exercise and to help solve the question:
 *
 *		 "Why is InterViews so slow ??"
 *
 *	It may prove useful to those following the Toolkit pathway
 *	in determining and pinpointing window creation overhead.
 *
 *	Mouse input is understood, with the three buttons meaning:
 *
 *		Button1		- decrease snapshot delay time (1sec)
 *
 *		Button2		- snapshot the system NOW!
 *
 *		Button3		- increase snapshot delay time (1sec)
 *
 *	Note that there are some things that should be changed,
 *	specifically the sleeping/wakeup algorithm should be a
 *	signal timer for consistency and window resizing isn't
 *	recognized. I now know better... this was just a test!
 *
 *	To build: 	cc -o windw windw.c -lX11 -lm
 *
 *	Created on a Sun, but should work on anything!
 *
 *	Right2Copy (R2C) 1988 -- Athena Systems, Inc.
 *
 *	Rev 	Date	Who		Reason
 *	---	-----	---		------
 *	V1.0	05/12	Jim Becker	Initial creation
 *		05/18	Jim Becker	Upgrade to X11 server calls
 *		08/23	Jim Becker	Cleaned up and released to X
 *
 */

#include	<ctype.h>
#include	<stdio.h>
#include 	<X11/Xlib.h>
#include	<X11/Xatom.h>
#include	<X11/Xutil.h>

#define		applic_name	"Windwalker"

#define		MAX_SEGMENTS	700
#define		PLACE_X		20
#define		PLACE_Y		800
#define		WIDTH		645
#define		HEIGHT		100
#define		ONOFF_X		(WIDTH-40)
#define		ONOFF_Y		10
#define		CLOSE_X		(WIDTH-40)
#define		CLOSE_Y		50
#define		MSG_X		590
#define		TRUE		1
#define		FALSE		0

#define		DEFAULT_FONT	"6x10"
#define		DEFAULT_SLEEP	5

#define		Snap		"**snap**"
#define		Sleep		" <sleep>"
#define		Quiet		" -quiet-"
#define		PRT_STR		\
    "Windwalker: mapped(%3d) unmapped(%3d) unviewable(%3d) total(%3d) max(%3d) delay(%2d)"

#define		ArePendingEvents()	(XPending(display) != 0)
#define		NoPendingEvents()	(XPending(display) == 0)
#define		CLICK(t)		(bevent->button) == (t)

#define		EventMask	(ButtonPressMask | ExposureMask | StructureNotifyMask)
#define		ulong		unsigned long



Display		*display;
Visual		visual;
XSetWindowAttributes
		attributes;
Window		RootWin;
Window		window;
Font		fid;
Drawable	draw;
GC		gc;
XGCValues	gcvalues;
int		gcvaluemask;
XImage		on_image;
XImage		off_image;
XImage		close_image;
ulong		white, black;
int		totalnum,  totalmapped,  totalunmapped, totalinvisible;
int		maxnum;
int		ototalnum;
char		buffer[255];
short		enabled		= TRUE;
int		secdelay	= DEFAULT_SLEEP;
int		x,y,w,h;

/*	buffered line segments	*/
XSegment	segments[MAX_SEGMENTS];
int		sindex;

/*
 *	display bitmaps, compiled into the code.
 */

/* on button */
#define on_width  32
#define on_height 32
static short on_bits[] = {
	 0xffff, 0xffff, 0x0001, 0x8000, 0x0001, 0x8000, 0x0001, 0x8000, 0x0001,
	 0x8000, 0x0001, 0x8000, 0x0001, 0x8000, 0x0001, 0x8000, 0xc001, 0x8007,
	 0xf001, 0x801f, 0xf801, 0x803f, 0xfc01, 0x807f, 0xfc01, 0x807f, 0xfe01,
	 0x80ff, 0xfe01, 0x80ff, 0xfe01, 0x80ff, 0xfe01, 0x80ff, 0xfe01, 0x80ff,
	 0xfc01, 0x807f, 0xfc01, 0x807f, 0xf801, 0x803f, 0xf001, 0x801f, 0xc001,
	 0x8007, 0x0001, 0x8000, 0x0fc1, 0xa008, 0x0841, 0x9014, 0x0841, 0x8822,
	 0x0841, 0x8441, 0x8841, 0x8280, 0x4fc1, 0x8100, 0x0001, 0x8000, 0xffff,
	 0xffff,};

/* off button */
#define off_width  32
#define off_height 32
static short off_bits[] = {
	 0xffff, 0xffff, 0x0001, 0x8000, 0x0001, 0x8000, 0x0001, 0x8000, 0x0001,
	 0x8000, 0x0001, 0x8000, 0xe001, 0x800f, 0x1801, 0x8030, 0x0401, 0x8040,
	 0x0201, 0x8080, 0x0101, 0x8100, 0x0101, 0x8100, 0x0081, 0x8200, 0x0081,
	 0x8201, 0x8081, 0x8203, 0xc081, 0x8207, 0x8081, 0x8203, 0x0081, 0x8201,
	 0x0081, 0x8200, 0x0101, 0x8100, 0x0101, 0x8100, 0x0201, 0x8080, 0x0401,
	 0x8040, 0x1801, 0x8030, 0xefc1, 0x8fcf, 0x8841, 0x8080, 0x8841, 0x8383,
	 0x8841, 0x8080, 0x8841, 0x8080, 0x8fc1, 0x8080, 0x0001, 0x8000, 0xffff,
	 0xffff,};

/* done/close button */
#define close_width  32
#define close_height 32
static short close_bits[] = {
	 0xe000, 0x000f, 0x1c00, 0x0070, 0xe300, 0x018f, 0x1c80, 0x0270, 0x0240,
	 0x0480, 0x01a0, 0x0b00, 0x0050, 0x1400, 0x0048, 0x2400, 0x0028, 0x2800,
	 0x0014, 0x5000, 0xff94, 0x57ff, 0x0014, 0x5000, 0x038a, 0xa000, 0x048a,
	 0xa000, 0x088a, 0xa000, 0x088a, 0xa000, 0xe88a, 0xa7a5, 0x288a, 0xa7ad,
	 0x248a, 0xa0b5, 0xe394, 0x57a5, 0x0014, 0x5000, 0xff94, 0x57ff, 0x0028,
	 0x2800, 0x0048, 0x2400, 0x0050, 0x1400, 0x01a0, 0x0b00, 0x0240, 0x0480,
	 0x1c80, 0x0270, 0xe300, 0x018f, 0x1c00, 0x0070, 0xe000, 0x000f, 0x0000,
	 0x0000,};

/* windwalker icon and mask (fancy-smancey) */
#define icon_width 64
#define icon_height 64
static char icon_bits[] = {
   0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc,
   0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x01, 0x00, 0x00,
   0x00, 0x00, 0xf0, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff,
   0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00,
   0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff,
   0xff, 0xff, 0x01, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00,
   0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0xf8, 0xff, 0xed,
   0xfc, 0xff, 0x0f, 0x00, 0x00, 0xfc, 0xdf, 0xff, 0xf7, 0xff, 0x1f, 0x00,
   0x00, 0xfe, 0xff, 0xff, 0xef, 0xff, 0x3f, 0x00, 0x00, 0xff, 0xfb, 0xff,
   0xff, 0xfe, 0x7f, 0x00, 0x00, 0xff, 0x7e, 0xb6, 0xfb, 0xaf, 0x71, 0x00,
   0x80, 0xff, 0xcf, 0xff, 0xcf, 0xff, 0xff, 0x00, 0x80, 0x5f, 0xf3, 0xff,
   0xdf, 0xff, 0xff, 0x00, 0xc0, 0xbf, 0xfd, 0xcf, 0x7f, 0xff, 0xff, 0x01,
   0xc0, 0xff, 0xfe, 0x3c, 0xff, 0xfa, 0xf3, 0x01, 0xe0, 0x6f, 0xaf, 0xff,
   0xfe, 0x67, 0xfa, 0x03, 0xe0, 0x3f, 0xf9, 0xff, 0xf9, 0xff, 0xf7, 0x03,
   0xe0, 0xaf, 0xfe, 0xff, 0xe7, 0x7f, 0xf9, 0x03, 0x60, 0x5f, 0xfb, 0xff,
   0xbf, 0xda, 0xff, 0x03, 0x30, 0xb7, 0xf3, 0xff, 0xff, 0xff, 0xff, 0x07,
   0x30, 0x9f, 0xf3, 0xff, 0xff, 0xff, 0xff, 0x07, 0x30, 0xd7, 0xf3, 0xff,
   0xff, 0xff, 0xff, 0x07, 0x30, 0xe7, 0xf3, 0xff, 0xff, 0xff, 0xff, 0x07,
   0x30, 0xe7, 0xf3, 0xff, 0xff, 0xff, 0xff, 0x07, 0x30, 0xff, 0xf3, 0x7f,
   0xff, 0xf7, 0xff, 0x07, 0x30, 0xff, 0xd3, 0x7f, 0xff, 0xb7, 0xff, 0x07,
   0x30, 0xcf, 0xf3, 0x7f, 0xff, 0xb7, 0xff, 0x07, 0x30, 0x87, 0xf3, 0x7f,
   0xff, 0xb7, 0xfe, 0x07, 0x30, 0x33, 0x53, 0x0b, 0x2d, 0x34, 0x43, 0x04,
   0x30, 0x79, 0x52, 0x6a, 0xad, 0x35, 0x43, 0x07, 0x20, 0xfc, 0x50, 0x69,
   0x21, 0xb4, 0x7a, 0x03, 0x60, 0xfe, 0x59, 0x0b, 0xad, 0xa5, 0x42, 0x03,
   0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0xff,
   0xff, 0xff, 0xff, 0x03, 0xc0, 0xff, 0x03, 0x14, 0x11, 0xc2, 0xff, 0x01,
   0xc0, 0xff, 0xfd, 0xff, 0xff, 0x5f, 0xfd, 0x01, 0x80, 0xff, 0xfd, 0xff,
   0xff, 0xff, 0xff, 0x00, 0x80, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x00,
   0x00, 0xff, 0x11, 0x96, 0x2a, 0xe4, 0x7f, 0x00, 0x00, 0xff, 0xff, 0xff,
   0xff, 0xcf, 0x7f, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xe3, 0x3f, 0x00,
   0x00, 0xfc, 0xff, 0xff, 0x3f, 0xf4, 0x1f, 0x00, 0x00, 0xf8, 0xbf, 0xda,
   0xc1, 0xff, 0x0f, 0x00, 0x00, 0xf0, 0xbf, 0xa5, 0xfe, 0xff, 0x07, 0x00,
   0x00, 0xe0, 0x7f, 0xfc, 0xff, 0xff, 0x03, 0x00, 0x00, 0xc0, 0xff, 0xff,
   0xff, 0xff, 0x01, 0x00, 0x00, 0x80, 0xff, 0x2f, 0xe2, 0xff, 0x00, 0x00,
   0x00, 0x00, 0xff, 0xaf, 0xea, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xaf,
   0xf3, 0x1f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xaf, 0xea, 0x07, 0x00, 0x00,
   0x00, 0x00, 0xc0, 0x23, 0xe2, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc,
   0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
   0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0};

extern	GC	XCreateGC();

/*
 *	line buffering algorithims. rendering of line segments in one packet.
 */
InitBuf()
{
	int		i, j;

	for( i = 0, j = 20; i < MAX_SEGMENTS; i++, j++ ) {

		segments[i].x1	= j;
		segments[i].y1	= 80;

		segments[i].x2	= j;
		segments[i].y2	= 0;
	}

	sindex	= 1;

	x	= 5;
	y	= 5;
	w	= WIDTH-60;
	h	= HEIGHT-10;
}

/*
 *	append next segment of specific depth/length
 */
AddBuf( depth )
int		depth;
{
	if( sindex < MAX_SEGMENTS ) {
		segments[sindex].y2	 = 80 - (depth<<3);
		sindex++;
	}
}


/*
 *	this is a recursive routine that scans the depth of the
 *	X window hierarchy. All the windows are found and looked
 *	at for child relationships. Counts of the window map status
 *	are accumulated for later output.
 */
void
ScanTree( depth, window )
int		depth;
Window		window;
{
	Window 			*children, root, par;
	XWindowAttributes 	winfo;
	int			num, i, j;

	/* return children list */
	XQueryTree( display, window, &root, &par, &children, &num);

	/* are there children of this guy ? */
	if( num == 0 )
		return;

	/* process all the kids */
	for (i = 0, j = 0; i < num; i++) {

		XGetWindowAttributes( display, children[i], &winfo );

		/* update state counter */
		if (winfo.map_state == IsViewable   )
			totalmapped++;
		else
		if (winfo.map_state == IsUnmapped   )
			totalunmapped++;
		else
		if (winfo.map_state == IsUnviewable )
			totalinvisible++;

		/* add to output queue and recurse on it */
		AddBuf( depth );

		ScanTree( depth+1, children[i] );
	}

	/* cumulative total of all windows */
	totalnum	+= num;
}


/*
 *	determine if mouse click was in area where on/off toggle is rendered.
 *	(simple still works... w/o toolkits!)
 */

InOnOff( x, y )
int		x, y;
{
	if( ONOFF_X <= x && x <= (ONOFF_X + on_width) &&
	    ONOFF_Y <= y && y <= (ONOFF_Y + on_height) )
		return TRUE;
	else
		return FALSE;
}

/*
 *	determine if mouse click was in area where done button is rendered.
 */
InClose( x, y )
int		x, y;
{
	if( CLOSE_X <= x && x <= (CLOSE_X + close_width) &&
	    CLOSE_Y <= y && y <= (CLOSE_Y + close_height) )
		return TRUE;
	else
		return FALSE;
}

/*
 *	repaint the bitmap images 
 */
RenderBits()
{
	if( enabled )
		XPutImage( display, draw, gc, &on_image , 0,0, 
			   ONOFF_X, ONOFF_Y, on_width,  on_height  );
	else
		XPutImage( display, draw, gc, &off_image, 0,0,
			   ONOFF_X, ONOFF_Y, off_width, off_height );

	XPutImage( display, draw, gc, &close_image, 0,0,
	           CLOSE_X, CLOSE_Y, close_width, close_height );
}	

/*
 *	draw the line segments and the totals
 */
RenderDisplay()
{
	/* draw line segments in one shot */
	if( sindex > 1 ) {
		XSetState( display, gc, white, black, GXcopy, AllPlanes );
		XFillRectangle( display, draw, gc, 0,0, WIDTH-40, HEIGHT-15 );
		XSetState( display, gc, black, white, GXcopy, AllPlanes );
		XDrawSegments( display, draw, gc, segments, --sindex );
	}

	if( totalnum > maxnum )
		maxnum	= totalnum;

	RenderTotals();
}

/*
 *	render the total numbers 
 */
RenderTotals()
{
	/* create and render the totals to window */
	sprintf( buffer, PRT_STR,
		totalmapped, totalunmapped,
		totalinvisible, totalnum, maxnum, secdelay );

	ototalnum	= totalnum;

	XDrawImageString( display, draw, gc, 10, HEIGHT-5, 
			  buffer, strlen(buffer) );
}

/*
 *	toggle enabled status and display bitmap
 */
ToggleOnOff()
{
	enabled	= (enabled ? FALSE : TRUE);

	RenderBits();
}

InitializeProgram()
{
	XSizeHints		xsize;
	XWindowAttributes	wattr;
	XWMHints		whints;

	display	= XOpenDisplay( NULL );

	if( display == NULL ) {
		printf("** Can't access the display !! **\n");
		exit(1);
	}

	RootWin	= RootWindow(display,DefaultScreen(display));

	visual.visualid			= CopyFromParent;

	white				= WhitePixel(display,DefaultScreen(display));
	black				= BlackPixel(display,DefaultScreen(display));
	attributes.background_pixel	= white;
	attributes.border_pixel 	= black;
	attributes.backing_store 	= NotUseful;

	window	= XCreateWindow( display, RootWin, 
			    PLACE_X, PLACE_Y, WIDTH, HEIGHT, 2,
			    1, CopyFromParent, &visual, 
			    CWBackPixel | CWBorderPixel,
			    &attributes );

	if( window == NULL ) {
		printf("** Can't open display window !! **\n");
		return FALSE;
	}

	XGetWindowAttributes( display, RootWin, &wattr );

 	XChangeProperty( display, window, XA_WM_NAME, XA_STRING, 8, 
			 PropModeReplace, applic_name, sizeof(applic_name) );

	xsize.x			= PLACE_X;
	xsize.y			= PLACE_Y;
	xsize.width		= WIDTH;
	xsize.height		= HEIGHT;
	xsize.min_width		= WIDTH;
	xsize.min_height	= HEIGHT;
	xsize.max_width		= WIDTH;
	xsize.max_height	= HEIGHT;

	xsize.flags		= USPosition | USSize;

	XSetNormalHints( display, window, &xsize );

	whints.flags		= IconPixmapHint | IconMaskHint;
	whints.icon_pixmap	= XCreateBitmapFromData( display, window,
					icon_bits, icon_width, icon_height );
	whints.icon_mask	= whints.icon_pixmap;

	XSetWMHints( display, window, &whints );

	draw	= window;	/* one and the same */

	XSelectInput( display, window, EventMask  );

	fid	= XLoadFont( display, DEFAULT_FONT );
	
	if( fid == NULL ) {
		printf("** Can't open specified font '%s' !! **\n", DEFAULT_FONT );
		return FALSE;
	}

	XMapWindow( display, window );

	gc	= XCreateGC( display, window, gcvaluemask, &gcvalues );

	XSetForeground( display, gc, black );
	XSetBackground( display, gc, white );
	XSetFont(       display, gc, fid   );
	XSetFunction(   display, gc, GXcopy    );
	XSetPlaneMask(  display, gc, AllPlanes );

	/* setup the images (just a little corporate!) */
	on_image.width		= on_width;
	on_image.height		= on_height;
	on_image.xoffset	= 0;
	on_image.format		= XYBitmap;
	on_image.byte_order	= MSBFirst;
	on_image.bitmap_unit	= 16;
	on_image.bitmap_pad	= 16;
	on_image.bytes_per_line	= 4;
	on_image.depth		= 1;

	off_image		= on_image;
	close_image		= on_image;
	
	on_image.data		= (char *)on_bits;
	off_image.data		= (char *)off_bits;
	close_image.data	= (char *)close_bits;

	InitBuf();

	return TRUE;
}

TerminateProgram()
{
	XUnmapWindow(   display, window  );
	XDestroyWindow( display, window  );
	XCloseDisplay(  display );
}

/*
 *	this is called when we iconify. makes the program dormant.
 */
WaitForRemap()
{
	XEvent			xevent;

	do {
		XNextEvent( display, &xevent );
	}
	while( xevent.type != MapNotify );
}

/*
 *	main event processing loop 
 */

ProcessEvents( done )
short		*done;
{
	XEvent			xevent;
	XButtonEvent		*bevent	= &xevent.xbutton;
	XExposeEvent		*eevent	= &xevent.xexpose;
	
	while( ArePendingEvents() && !*done ) {

		XNextEvent( display, &xevent );

		switch( xevent.type ) {
		case	ButtonPress:
			if( InOnOff( bevent->x, bevent->y ) )
				ToggleOnOff();
			else
			if( InClose( bevent->x, bevent->y ) )
				*done	= TRUE;
			else { /* buttons !! */
				if( CLICK(Button1) ) /* decrease delay */
					secdelay--;
				else
				if( CLICK(Button2) ) /* snapshot NOW!! */
					SnapshotSystem();
				else
				if( CLICK(Button3) ) /* increase delay */
					secdelay++;

				/* can't go faster than father time ! */
				if( secdelay <= 0 )
					secdelay = 1;
				else
					RenderTotals();
			}
			break;
		case	Expose:
			RenderBits();
			RenderDisplay();
			XFlush( display );
			break;
		case	UnmapNotify:
			WaitForRemap();
			break;
		case	MapNotify:
			break;
		}
	}
}


/*
 *	dispatcher used to output messages and call scanner for window hierarchy
 */
SnapshotSystem()
{
	if( !enabled )
		XDrawImageString( display, draw, gc, MSG_X, HEIGHT-5, 
				  Quiet, sizeof(Quiet)-1 );
	else {
	
		XDrawImageString( display, draw, gc, MSG_X, HEIGHT-5, 
				  Snap,  sizeof(Snap)-1  );

		XFlush( display );


		/* reset the index to start */
		sindex	= 0;

		/* reset the totals */
		totalnum	= 0;
		totalmapped	= 0;
		totalunmapped	= 0;
		totalinvisible	= 0;

		/* have to do this while the system is static, else problems */
		XGrabServer( display );

		ScanTree( 1, RootWin );

		/* free at last ! */
		XUngrabServer( display );

		/* assume that we are looking for changes in total */
		/* (this is the interest that I had, although not always correct) */
		if( totalnum != ototalnum )
			RenderDisplay();

		XDrawImageString( display, draw, gc, MSG_X, HEIGHT-5, 
				  Sleep, sizeof(Sleep)-1 );

	}
}

/*
 *	pauses between snapshots. This is when the user processes can run !!
 */
PauseForBreather()
{
	int		count;

	for( count = secdelay; count > 0; count-- ) {
		sleep(1);

		if( ArePendingEvents() )
			break;
	}

	return (count == 0);
}

/*
 *	=== MAINLINE ===
 *
 */
main()
{
	short		done	= FALSE;
	
	if( !InitializeProgram() ) {
		printf("Can't startup Windwalker, sorry !\n");
		exit(1);
	}

	/* MAIN LOOP */
	do {
		ProcessEvents( &done );

		if( !done && PauseForBreather() )
			SnapshotSystem(); 
	}
	while( !done );
	
	TerminateProgram();

	exit(0);
}