argv@island.uu.net (Dan Heller) (03/31/89)
Submitted-by: George Drapeau <drapeau@jessica.stanford.edu> Posting-number: Volume 3, Issue 62 Archive-name: xwatchwin/part01 [I generated a README from the man page. As sent, there were *lots* of lines that went over 80 chars (many over two or three lines), so I reformatted much of the code by moving comments above the lines they commented. I tried to preserve as much of the original style of the author as possible. Please be aware of this when sending articles to be posted by any newsgroup. --argv] #! /bin/sh # This is a shar archive. Extract with sh, not csh. #George D. Drapeau Internet: drapeau@jessica.stanford.edu #Academic Information Resources #Stanford University echo x - Imakefile cat > Imakefile << '25787!Funky!Stuff!' #ifdef SunSharedLibs SYS_LIBRARIES = -lX11 #else LOCAL_LIBRARIES = $(XLIB) #endif SimpleProgramTarget(xwatchwin) 25787!Funky!Stuff! echo x - Makefile cat > Makefile << '25787!Funky!Stuff!' # Makefile generated by imake - do not edit! # $XConsortium: imake.c,v 1.37 88/10/08 20:08:30 jim Exp $ # # The cpp used on this machine replaces all newlines and multiple tabs and # spaces in a macro expansion with a single space. Imake tries to compensate # for this, but is not always successful. # ########################################################################### # X Window System Makefile generated from template file Imake.tmpl # $XConsortium: Imake.tmpl,v 1.91 88/10/23 22:37:10 jim Exp $ # # Do not change the body of the imake template file. Server-specific # parameters may be set in the appropriate .macros file; site-specific # parameters (but shared by all servers) may be set in site.def. If you # make any changes, you'll need to rebuild the makefiles using # "make World" (at best) or "make Makefile; make Makefiles" (at least) in # the top level directory. # # If your C preprocessor doesn't define any unique symbols, you'll need # to set BOOTSTRAPCFLAGS when rebuilding imake (usually when doing # "make Makefile", "make Makefiles", or "make World"). # # If you absolutely can't get imake to work, you'll need to set the # variables at the top of each Makefile as well as the dependencies at the # bottom (makedepend will do this automatically). # ########################################################################### # platform-specific configuration parameters - edit Sun.macros to change # platform: $XConsortium: Sun.macros,v 1.52 88/10/23 11:00:55 jim Exp $ # operating system: SunOS 4.0 BOOTSTRAPCFLAGS = AS = as CC = cc CPP = /lib/cpp LD = ld LINT = lint INSTALL = install TAGS = ctags RM = rm -f MV = mv LN = ln -s RANLIB = ranlib RANLIBINSTFLAGS = -t AR = ar clq LS = ls LINTOPTS = -axz LINTLIBFLAG = -C MAKE = make STD_CPP_DEFINES = STD_DEFINES = ########################################################################### # site-specific configuration parameters - edit site.def to change # site: $XConsortium: site.def,v 1.16 88/10/12 10:30:24 jim Exp $ ########################################################################### # definitions common to all Makefiles - do not edit SHELL = /bin/sh DESTDIR = /usr/local USRLIBDIR = $(DESTDIR)/X11R3/lib BINDIR = $(DESTDIR)/X11R3/bin INCDIR = $(DESTDIR)/include/X11R3 ADMDIR = $(DESTDIR)/usr/adm LIBDIR = $(USRLIBDIR) LINTLIBDIR = $(USRLIBDIR)/lint FONTDIR = $(LIBDIR)/fonts XINITDIR = $(LIBDIR)/xinit XDMDIR = $(LIBDIR)/xdm UWMDIR = $(LIBDIR)/uwm AWMDIR = $(LIBDIR)/awm TWMDIR = $(LIBDIR)/twm MANPATH = $(DESTDIR)/man MANSOURCEPATH = $(MANPATH)/man MANDIR = $(MANSOURCEPATH)1 LIBMANDIR = $(MANSOURCEPATH)3 XAPPLOADDIR = $(LIBDIR)/app-defaults INSTBINFLAGS = -sm 0755 INSTUIDFLAGS = -m 4755 INSTLIBFLAGS = -m 0664 INSTINCFLAGS = -m 0444 INSTMANFLAGS = -m 0444 INSTAPPFLAGS = -m 0444 INSTKMEMFLAGS = -g kmem -m 2755 FCFLAGS = -t CDEBUGFLAGS = PATHSEP = / DEPEND = $(DEPENDSRC)/makedepend IMAKE = $(IMAKESRC)/imake RGB = $(RGBSRC)/rgb FC = $(BDFTOSNFSRC)/bdftosnf MKFONTDIR = $(MKFONTDIRSRC)/mkfontdir MKDIRHIER = $(SCRIPTSSRC)/mkdirhier.sh CFLAGS = $(CDEBUGFLAGS) $(INCLUDES) $(STD_DEFINES) $(DEFINES) LINTFLAGS = $(LINTOPTS) $(INCLUDES) $(STD_DEFINES) $(DEFINES) -DLINT LDFLAGS = $(CDEBUGFLAGS) $(SYS_LIBRARIES) $(SYSAUX_LIBRARIES) TOP = /usr/local/X CLIENTSRC = $(TOP)/clients DEMOSRC = $(TOP)/demos LIBSRC = $(TOP)/lib FONTSRC = $(TOP)/fonts INCLUDESRC = $(TOP)/X11 SERVERSRC = $(TOP)/server UTILSRC = $(TOP)/util SCRIPTSSRC = $(UTILSRC)/scripts EXAMPLESRC = $(TOP)/examples CONTRIBSRC = $(TOP)/contrib DOCSRC = $(TOP)/doc RGBSRC = $(TOP)/rgb DEPENDSRC = $(UTILSRC)/makedepend IMAKESRC = $(UTILSRC)/imake IRULESRC = $(UTILSRC)/imake.includes XLIBSRC = $(LIBSRC)/X XMUSRC = $(LIBSRC)/Xmu TOOLKITSRC = $(LIBSRC)/Xt AWIDGETSRC = $(LIBSRC)/Xaw OLDXLIBSRC = $(LIBSRC)/oldX BDFTOSNFSRC = $(FONTSRC)/bdftosnf MKFONTDIRSRC = $(FONTSRC)/mkfontdir EXTENSIONSRC = $(TOP)/extensions EXTENSIONLIB = $(EXTENSIONSRC)/lib/libXext.a XLIB = $(XLIBSRC)/libX11.a XMULIB = $(XMUSRC)/libXmu.a OLDXLIB = $(OLDXLIBSRC)/liboldX.a XTOOLLIB = $(TOOLKITSRC)/libXt.a XAWLIB = $(AWIDGETSRC)/libXaw.a LINTXLIB = $(XLIBSRC)/llib-lX11.ln LINTXMU = $(XMUSRC)/llib-lXmu.ln LINTXTOOL = $(TOOLKITSRC)/llib-lXt.ln LINTXAW = $(AWIDGETSRC)/llib-lXaw.ln INCLUDES = -I$(TOP) MACROFILE = Sun.macros ICONFIGFILES = $(IRULESRC)/Imake.tmpl \ $(IRULESRC)/$(MACROFILE) $(IRULESRC)/site.def IMAKE_DEFINES = IMAKE_CMD = $(NEWTOP)$(IMAKE) -TImake.tmpl -I$(NEWTOP)$(IRULESRC) \ -s Makefile $(IMAKE_DEFINES) RM_CMD = $(RM) *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a \ .emacs_* tags TAGS make.log MakeOut ########################################################################### # rules: $XConsortium: Imake.rules,v 1.71 88/10/23 22:46:34 jim Exp $ ########################################################################### # start of Imakefile SYS_LIBRARIES = -lX11 OBJS = xwatchwin.o SRCS = xwatchwin.c PROGRAM = xwatchwin all:: xwatchwin xwatchwin: $(OBJS) $(LOCAL_LIBRARIES) $(RM) $@ $(CC) -o $@ $(OBJS) $(LOCAL_LIBRARIES) $(LDFLAGS) $(SYSLAST_LIBRARIES) relink:: $(RM) $(PROGRAM) $(MAKE) $(MFLAGS) $(PROGRAM) install:: xwatchwin $(INSTALL) -c $(INSTALLFLAGS) xwatchwin $(BINDIR) install.man:: xwatchwin.man $(INSTALL) -c $(INSTMANFLAGS) xwatchwin.man $(MANDIR)/xwatchwin.1 depend:: $(DEPEND) depend:: $(DEPEND) -s "# DO NOT DELETE" -- $(CFLAGS) -- $(SRCS) $(DEPEND): @echo "making $@"; \ cd $(DEPENDSRC); $(MAKE) clean:: $(RM) $(PROGRAM) ########################################################################### # Imake.tmpl common rules for all Makefiles - do not edit emptyrule:: clean:: $(RM_CMD) \#* Makefile:: $(IMAKE) Makefile:: Imakefile \ $(IRULESRC)/Imake.tmpl \ $(IRULESRC)/Imake.rules \ $(IRULESRC)/site.def \ $(IRULESRC)/$(MACROFILE) -@if [ -f Makefile ]; then \ echo "$(RM) Makefile.bak; $(MV) Makefile Makefile.bak"; \ $(RM) Makefile.bak; $(MV) Makefile Makefile.bak; \ else exit 0; fi $(IMAKE_CMD) -DTOPDIR=$(TOP) $(IMAKE): @echo "making $@"; \ cd $(IMAKESRC); $(MAKE) BOOTSTRAPCFLAGS=$(BOOTSTRAPCFLAGS) tags:: $(TAGS) -w *.[ch] $(TAGS) -xw *.[ch] > TAGS ########################################################################### # empty rules for directories that do not have SUBDIRS - do not edit install:: @echo "install done" install.man:: @echo "install.man done" Makefiles:: ########################################################################### # dependencies generated by makedepend 25787!Funky!Stuff! echo x - xwatchwin.c cat > xwatchwin.c << '25787!Funky!Stuff!' #include <stdio.h> #include <ctype.h> #include <sys/time.h> #include <X11/Xlib.h> #include <X11/Xutil.h> #define STRINGLENGTH 256 #define SLEEPTIME 0 /* Sleep for this many seconds before redrawing */ char **Argv; int Argc; Display *dpy; main(argc,argv) int argc; char **argv; { Window watchWin,GetWindowByName(); int i,strPos,optIndex; char displayName[64],xWatchName[STRINGLENGTH]; char *optstring; extern char *optarg; extern int optind,opterr; int windowID,windowIDSet,updateTime,updateTimeSet; if (argc < 3) /* Did user enter enough arguments? */ { /* Nope, print an error message and exit. */ printf("Usage:\t%s HostName [-u UpdateTime] [-w windowID] [WindowName]\n", argv[0]); exit(1); } /* Initialize var to update every SLEEPTIME seconds. */ updateTime = SLEEPTIME; updateTimeSet = 0; windowIDSet = 0; optind = 2; /* Skip over first, obligatory argument (hostname) */ while ((optIndex = getopt(argc,argv,"u:w:")) != -1) /* Get user's arguments */ switch (optIndex) { case 'u': /* User wants to update every 'optarg' seconds. */ updateTime = atoi(optarg); updateTimeSet = 1; /* Set flag saying user specified this option. */ break; case 'w': /* User wants to specify window by id instead of by name */ sscanf(&optarg[2],"%lx",&windowID); windowIDSet = 1; /* Set flag saying user specified this option. */ break; } /* end switch (optIndex) */ Argv = argv; Argc = argc; sprintf(displayName,"%s:0.0",argv[1]); /* Set the X display name. */ if (!windowIDSet) /* Did user specify window to watch by number? */ for (i=2, bzero(xWatchName, STRINGLENGTH); i < argc; i++) /* No, parse rest of arguments as window name to watch */ { if (!strcmp(argv[i],"-u")) /* Don't parse optional arguments */ { i++; /* Skip over argument to '-u' switch. */ continue; } /* Get current length of xWatchName string */ strPos = strlen(xWatchName); /* Copy another argument to the end of the string */ strcpy(&xWatchName[strPos],argv[i]); } /* Attempt to open a connection with the remote X server */ if ((dpy = XOpenDisplay(displayName)) == NULL) { /* Couldn't open the display for some reason, so... */ fprintf(stderr,"%s: Could not open Display %s\n", argv[0],displayName); exit(1); /* ...report the error and exit with an error code */ } if (!windowIDSet) /* Did user specify a window to watch by id? */ /* No, get window id from window name. */ watchWin = GetWindowByName(XDefaultRootWindow(dpy),xWatchName); else watchWin = windowID; /* Yes, use user-specified window id. */ if (watchWin) /* Did the user find the window s/he was looking for? */ /* Yes, periodically show the contents of that window */ WatchWindow(watchWin,updateTime); else /* No, report that the window was not found, and exit. */ { printf("Could not find the window you specified.\n"); exit(1); } } /* end function main */ /* Takes two strings, removes spaces from the second,... */ /* ...and compares them.. Returns 1 if equal, 0 if not. */ WinNamesEqual(str1,str2) char *str1,*str2; { char tempStr[STRINGLENGTH],*tempStrPtr; int index; bzero(tempStr,STRINGLENGTH); /* Clear the contents of the string, if any */ /* Go through each character in the second string. */ for (tempStrPtr=tempStr; *str2; str2++) { if (!isspace(*str2)) /* Is this character a space? */ *tempStrPtr++ = *str2; /* No, copy this character to a temp string. */ } if (!strcmp(str1,tempStr)) /* Are the two resulting string equal? */ return(1); /* Yes, return 1 */ else return(0); /* No, return 0 */ } /* end function WinNamesEqual */ WatchWindow(win,updateTime) Window win; int updateTime; { Display *dpy2; Window copyWin; GC gc; XWindowAttributes copyWinInfo,newWinInfo; XSetWindowAttributes copyWinAttrs; XWMHints wmHints; XSizeHints sizeHints; XImage *image; struct timeval currentTime; struct timezone zone; long timeInSecs; Bool srcWinUnmapped; /* Get the window attributes of the window we're watching */ XGetWindowAttributes(dpy,win,©WinInfo); /* Is the original window in a state to be watched? */ if (copyWinInfo.map_state != IsViewable) { /* Nope, tell the user of the problem and exit. */ printf("The window you wish to look at is not in a state to be viewed\n"); printf("(perhaps it is iconified or not mapped)\n"); exit(1); } /* Attempt to open a connection with the local X server */ if ((dpy2 = XOpenDisplay(NULL)) == NULL) { /* Couldn't open the display for some reason, so... */ fprintf(stderr,"%s: Could not open Display.\n", Argv[0]); exit(1); /* ...report the error and exit with an error code */ } /* Set a couple more attributes */ copyWinAttrs.colormap = XDefaultColormap(dpy2,XDefaultScreen(dpy2)); copyWinAttrs.bit_gravity = copyWinInfo.bit_gravity; /* Check for different depths b/w source & dest displays */ if (copyWinInfo.depth != XDefaultDepth(dpy2,XDefaultScreen(dpy2))) { puts("Sorry, the original and copied windows contain incompatible"); puts("information (i.e., they don't use the same number of bits per pixel."); exit(1); } /* Create a copy of the window we're watching */ copyWin = XCreateWindow(dpy2,XDefaultRootWindow(dpy2), copyWinInfo.x,copyWinInfo.y, copyWinInfo.width,copyWinInfo.height, copyWinInfo.border_width, copyWinInfo.depth, CopyFromParent, XDefaultVisual(dpy2,XDefaultScreen(dpy2)), (CWColormap|CWBitGravity), ©WinAttrs); /* Get size hints for window being watched */ XGetNormalHints(dpy,win,&sizeHints); /* Set standard window properties for my window */ XSetStandardProperties(dpy2,copyWin,"XWatchWin","XWatchWin", None,Argv,Argc,&sizeHints); /* Get window mgr hints for the window being watched */ XGetWMHints(dpy,win,&wmHints); /* Tell the X server about my window manager hints */ XSetWMHints(dpy2,copyWin,&wmHints); gc = XDefaultGC(dpy2,0); /* Get a default graphics context */ /* Only interested in exposures and button presses */ XSelectInput(dpy2,copyWin,(ExposureMask|ButtonPressMask)); /* Put the window up on the display */ XMapWindow(dpy2,copyWin); XSelectInput(dpy,win, /* Only interested if the source window is... */ /* ...iconified or if it's mapped/unmapped */ (VisibilityChangeMask|StructureNotifyMask)); /* Store an image of the original window in an XImage */ image = XGetImage(dpy,(Drawable)win, 0,0, copyWinInfo.width,copyWinInfo.height, AllPlanes,XYPixmap); gettimeofday(¤tTime,&zone); /* Get the current time. */ timeInSecs = currentTime.tv_sec; /* Save the current time in seconds */ /* Set variable saying it's okay to watch the src window. */ srcWinUnmapped = 0; while (1) /* Enter an infinite event loop */ { XEvent event; /* Check if the source window was turned into an... */ if (XCheckWindowEvent(dpy,win, (VisibilityChangeMask|StructureNotifyMask), &event)) { /* ...icon or back into a window */ /* Get the window attributes of the window we're watching */ XGetWindowAttributes(dpy,win,&newWinInfo); /* Is the original window in a state to be watched? */ if (newWinInfo.map_state != IsViewable) { /* Let program know that the src window is unwatchable. */ srcWinUnmapped = 1; printf("The window you are watching just became 'invisible'..\n"); printf("I will wait until it is 'visible' again...\n"); continue; } else /* Let program know that src window is watchable again. */ srcWinUnmapped = 0; } /* end if(XCheckWindowEvent... */ /* Look for window events, but don't sit around... */ if (XCheckWindowEvent(dpy2,copyWin, (ExposureMask|ButtonPressMask), /* ...waiting for one (i.e., don't block) */ &event)) switch (event.type) { case ButtonPress: XDestroyImage(image);/* Free memory resources used by the image */ exit(0); /* Get outtahere */ break; case Expose: /* Put the original window's image into the... */ XPutImage(dpy2,(Drawable)copyWin, gc,image,0,0,0,0, /* ...copy of the window. */ copyWinInfo.width,copyWinInfo.height); break; } /* end switch (event.type) */ gettimeofday(¤tTime,&zone); /* Get the current time. */ /* Have 'updateTime' seconds passed? */ if (currentTime.tv_sec > (timeInSecs + updateTime)) { /* Yes, update the local copy of the window. */ if (srcWinUnmapped)/* Is the source window watchable? */ continue; /* No, go through the event loop again. */ else /* Yes, it's watchable, so get a new copy of the window. */ { XDestroyImage(image);/* Free memory resources used by the image */ /* Store an image of the original window in an XImage */ image = XGetImage(dpy,(Drawable)win, 0,0, copyWinInfo.width,copyWinInfo.height, AllPlanes,XYPixmap); } /* end else XDestroyImage... */ /* Put the original window's image into the... */ XPutImage(dpy2,(Drawable)copyWin, gc,image,0,0,0,0, /* ...copy of the window. */ copyWinInfo.width,copyWinInfo.height); timeInSecs = currentTime.tv_sec;/* Update the current time */ } /* end if currentTime... */ } /* end while(1) */ } /* end function WatchWindow */ /* Given the name of a window and the top of a ... */ /* ...window tree, this function will try to find... */ /* the Window ID corresponding to the window name... */ /* ...given as argument. */ Window GetWindowByName(window,windowName) Window window; char *windowName; { Window rootWin,parentWin,wID; Window *childWinList; int numChildren,i; char *childWinName; /* Get information about windows that are children... */ XQueryTree(dpy,window, &rootWin,&parentWin,&childWinList, /* ...of 'window'. */ &numChildren); for (i=0;i<numChildren;i++) /* Look at each child of 'window' */ { /* Get the name of that window */ XFetchName(dpy,childWinList[i],&childWinName); if (childWinName != NULL) /* Is there a name attached to this window? */ { /* Is this the window the user is looking for? */ if (WinNamesEqual(windowName,childWinName)) { XFree(childWinList);/* Free up space taken by list of windows */ XFree(childWinName);/* Return space taken by this window's name */ /* Yes, return the Window ID of this window */ return(childWinList[i]); } XFree(childWinName);/* Return space taken by this window's name */ } /* end if childWinName... */ } /* end for i=0... */ /* If this section of code is reached, then no match was found at this * level of the tree */ for (i=0;i<numChildren;i++) /* Recurse on the children of this window */ { wID = GetWindowByName(childWinList[i],windowName); if (wID) /* Was a match found in this window's children? */ { XFree(childWinList); /* Free up space taken by list of windows */ return(wID); /* Return the ID of the window that matched */ } } /* end for i=0... */ /* If this section of code is reached, then no match was found below * this level of the tree */ XFree(childWinList); /* Free up space taken by list of windows */ return((Window)0); /* No match was found, return 0. */ } /* end function GetWindowByName */ 25787!Funky!Stuff! echo x - xwatchwin.man cat > xwatchwin.man << '25787!Funky!Stuff!' .TH xwatchwin 1 "17 March 1989" "X Version 11" .SH NAME xwatchwin - watch a window on another X server .SH SYNOPSIS .B "xwatchwin" hostname [\-u \fIupdatetime\fP] [\-w \fIwindowID\fP] [window name] .SH DESCRIPTION .PP \fIxwatchwin\fP allows you to peek at a window on another X server. To use it, you must specify the name of the machine you want to watch, then the name of the window on that machine. \fIXwatchwin\fP will attempt to connect with the X server \fIhostname\fP:0.0, and if successful, will try to retrieve a copy of the window in which you specified interest. You may specify the window you want to watch either by name or by its window id, usually a hexidecimal number. Usually specifying the window by name is simpler, although not all windows have names associated with them; in that case you must use the window id option. If the window you want to watch is not in a viewable state, \fIxwatchwin\fP will tell you so and exit. If while you are watching a window it becomes 'unviewable', \fIxwatchwin\fP will print a message to stdout and wait until the window becomes 'viewable' again. \fIxwatchwin\fP was written as an aid to a class for people learning to use X. The idea is that the instructor would type into an xterm window on his/her display and the students would use \fIxwatchwin\fP to see what the instructor typed. The students could then type the same thing in their own terminal windows. I hope that others will find equally (if not more) constructive uses. .SH OPTIONS .TP 8 .B \-u \fIupdatetime\fP This option specifies how often (in seconds) you want to get a new copy of the window you're watching. It is in effect a 'sample rate'. By default, \fIxwatchwin\fP updates your copy of the window as often as it can. The time it takes to actually do the update is dependent on the speed of the X server on both machines, the speed of the intervening network, and other factors. .TP 8 .B \-w \fIwindowID\fP This option specifies the window you want to watch by number, for example, "0x50000b". Use the xlswins(1) command to get a list of window id's and possibly their names on the remote server. You must specify a window to watch either by name or by id. Specifying a window to watch by name is usually easier if you know what you're looking for. .SH EXAMPLES If there is an X server on the remote machine "crow" and if on that server there is a window called "X Terminal Emulator", you can watch that window by typing xwatchwin crow X Terminal Emulator If there is a window on "crow" that has no name but has a window id of "0x50000b", you can watch it by typing xwatchwin crow -w 0x50000b If you want to get new copies of a window only every 30 seconds, you can do so by typing xwatchwin crow -u 30 -w 0x50000b .SH "SEE ALSO" xlswins(1), xwininfo(1), xdpyinfo(1), .SH BUGS \fIxwatchwin\fP doesn't support the \-display option. You must set the display on which the \fIxwatchwin\fP window is created by changing your DISPLAY environment variable. If the window you're watching is unmapped (made 'invisible') while \fIxwatchwin\fP is getting a new copy of that window, the program will crash. The smaller your update interval, the more likely you are to experience this bug (although it hasn't happened all that often to me). Parsing arguments is messy and not as forgiving as it should be. The event loop is a mess. \fIxwatchwin\fP cannot deal with two displays with different depths. For example, if the window you're watching is an 8-bit color window and your display only supports 2-bit color, \fIxwatchwin\fP will not be able to display the window. To understand the issues involved in fixing this, see the paper by David's Lemke and Rosenthal, "\fIVisualizing X11 Clients\fP". .SH COPYRIGHT Copyright 1989, George D. Drapeau .SH AUTHOR George D. Drapeau .PP Stanford University .br Academic Information Resources / Systems Development .br Internet: drapeau@jessica.stanford.edu .br UUCP: labrea!drapeau@jessica 25787!Funky!Stuff!