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