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);
}