Subject: xwd: additional methods to specify area to dump [long]

From: david@craft (David B. Lewis)
Date: Fri, 7 Feb 1992 11:39:08 -0500

VERSION:
    R5, public-patch-8

CLIENT MACHINE and OPERATING SYSTEM:
    Sparc/SunOS 4.1.1

DISPLAY TYPE:
	Visual XBASE12 8-bit color, beta software

WINDOW MANAGER:
	typically XDSwm

COMPILER:
	typically native cc

AREA:
	clients/xwd/

SYNOPSIS:
	Code for additional methods of specifying the area to dump to the 
xwd image

DESCRIPTION:

In order to make xwd more useful as a standard client:
	1) add the same -source [-geometry] option that xmag supports
	2) add an on-root -rubber interactive selection
	3) add a -sleep option to permit xwd to be used to grab images after
some short amount of time has passed; permits dumping of menus, e.g.

REPEAT BY:
	Helps permit dumping of collections of windows in active state.

SAMPLE FIX:

Here's the set of changes that I'm using:

Bugs:
	- one of my goals was adding as little code as possible, so these 
	changes have the same organization as the original code
	- there is still a lot of unused code in dsimple.[ch]. Not removed.
	- naive GC setup
	- xgrabsc and other tools are probably better to use anyway

*** dsimple.c.orig	Sat May 11 21:01:54 1991
--- dsimple.c	Thu Feb  6 10:46:00 1992
***************
*** 499,501 ****
--- 499,632 ----
  	fprintf(stderr, "\n");
  	exit(1);
  }
+ 
+ #define ABS(a)          (((a) >= 0) ? (a) : -(a))
+ #define Min(x,y)        (((x) < (y)) ? (x) : (y))
+ 
+ static int RubberBand (dpy, w, gc, x1, y1, x2, y2)
+ Display *dpy;
+ Window w;
+ GC gc;
+ int x1, y1, x2, y2;
+ {
+ int retval = (x2>x1)+2*(y2>y1);
+ /*
+           increasing x ->
+                   |           y
+                 0 | 1         |
+           -----------------   |
+                 2 | 3         \/
+                   |
+ */
+ 
+ /* assume a rectangle */
+ XDrawRectangle(dpy, w, gc,
+                 Min(x1, x2), Min(y1, y2),
+                 ABS(x2-x1), ABS(y2-y1));
+ 
+ return retval;
+ }
+ 
+ 
+ 
+ /* 0 is OK */
+ int Get_Rubber_Values (dpy, ulx, uly, width, height, grabFlag)
+ Display *dpy;
+ int *ulx, *uly;				/* RETURN */
+ unsigned int *width, *height;		/* RETURN */
+ Bool grabFlag;
+ {
+   unsigned long	valuemask;
+   GC Rubber_GC = NULL;
+   XGCValues	gcVal;
+   Window	root;
+   int		screen;
+   XEvent	event;
+   int		i;
+ 
+ /* globals for rubber-banding */
+ /* note: order is significant; see below */
+ #define UpperLeft       0
+ #define UpperRight      1
+ #define LowerLeft       2
+ #define LowerRight      3
+ Cursor           corners[4];
+ int corner, save_corner;
+ 
+ int start_x, start_y, end_x, end_y;
+ 
+   root = XDefaultRootWindow (dpy);
+   screen = XDefaultScreen (dpy);
+ 
+ corners[LowerLeft] =    XCreateFontCursor(dpy, XC_ll_angle);
+ corners[LowerRight] =   XCreateFontCursor(dpy, XC_lr_angle);
+ corners[UpperLeft]=     XCreateFontCursor(dpy, XC_ul_angle);
+ corners[UpperRight]=    XCreateFontCursor(dpy, XC_ur_angle);
+ 
+    if (grabFlag) XGrabServer(dpy);
+    if (XGrabPointer(dpy, root, False,
+ 	       ButtonPressMask, GrabModeAsync, GrabModeAsync, root,
+ 	       corners[corner=UpperLeft], CurrentTime) != GrabSuccess) {
+ 	return 1;
+     }
+ 
+   valuemask = GCFunction | GCPlaneMask | GCForeground | 
+ 		GCBackground | GCLineWidth;
+   gcVal.function = GXxor;
+   gcVal.plane_mask = BlackPixel(dpy, screen) ^ WhitePixel(dpy, screen);
+   gcVal.foreground = 0xffffffff;
+   gcVal.background = 0;
+   gcVal.line_width = 0;
+   Rubber_GC = XCreateGC(dpy, root, valuemask, &gcVal);
+   XSetSubwindowMode(dpy, Rubber_GC, IncludeInferiors);
+ 
+ 	XMaskEvent(dpy, ButtonPressMask, &event);
+ 	start_x = end_x = event.xbutton.x_root;
+ 	start_y = end_y = event.xbutton.y_root;
+ 
+ 	/* we have one corner. draw; set appropriate cursor. */
+ 	corner=RubberBand(dpy, root, Rubber_GC, start_x, start_y, end_x, end_y);
+ 	XChangeActivePointerGrab (dpy, ButtonMotionMask | ButtonReleaseMask,
+ 		corners[corner], CurrentTime);
+ 
+     while (1) {
+ 	XNextEvent(dpy, &event);
+ 	switch (event.type) {
+ 	case MotionNotify:
+ 		save_corner = corner;
+ 		corner = RubberBand (dpy, root, Rubber_GC, 
+ 			start_x, start_y, end_x, end_y);
+ 
+ 		if (save_corner != corner)
+ 		XChangeActivePointerGrab (dpy, 
+ 			ButtonMotionMask | ButtonReleaseMask,
+ 			corners[corner], CurrentTime);
+ 
+ 		while (XCheckTypedEvent(dpy, MotionNotify, &event))
+ 			{}
+ 
+ 		end_x = event.xbutton.x_root;
+ 		end_y = event.xbutton.y_root;
+ 		RubberBand(dpy,root, Rubber_GC, start_x, start_y, end_x, end_y);
+ 
+ 		break;
+ 
+ 	case ButtonRelease:
+ 		RubberBand(dpy,root,Rubber_GC, start_x, start_y, end_x, end_y);
+ 		*ulx= Min(start_x, end_x);
+ 		*uly= Min(start_y, end_y);
+ 		*width= ABS(start_x - end_x);
+ 		*height=ABS(start_y - end_y);
+ 		XFreeGC(dpy, Rubber_GC);
+ 		XUngrabPointer (dpy, CurrentTime);
+ 		if (grabFlag) XUngrabServer(dpy);
+ 		for (i=0 ; i < 4; i++) XFreeCursor(dpy, corners[i]);
+ 		XSync(dpy, 0);
+ 		return 0;
+ 	    break;
+ 	    
+ 	default:
+ 	    break;
+ 	}
+     }
+ }
*** dsimple.h.orig	Sat Apr 28 12:07:15 1990
--- dsimple.h	Thu Feb  6 10:09:51 1992
***************
*** 35,41 ****
  
  #define X_USAGE "[host:display]"              /* X arguments handled by
  						 Get_Display_Name */
! #define SELECT_USAGE "[-root|-id <id>|-name <name>]"
  
  /*
   * Other_stuff.h: Definitions of routines in other_stuff.
--- 35,41 ----
  
  #define X_USAGE "[host:display]"              /* X arguments handled by
  						 Get_Display_Name */
! #define SELECT_USAGE "[-root | -id <id> | -name <name> | -geometry geomspec | -rubber]"
  
  /*
   * Other_stuff.h: Definitions of routines in other_stuff.
***************
*** 51,53 ****
--- 51,54 ----
  void out();
  void blip();
  Window Window_With_Name();
+ int Get_Rubber_Values();
*** xwd.c.orig	Thu Jul 25 18:01:04 1991
--- xwd.c	Thu Feb  6 10:32:40 1992
***************
*** 65,70 ****
--- 65,71 ----
  Bool debug = False;
  Bool use_installed = False;
  long add_pixel_value = 0;
+ int delay = 0;
  
  extern int (*_XErrorFunction)();
  extern int _XDefaultError();
***************
*** 93,98 ****
--- 94,101 ----
      Window target_win;
      FILE *out_file = stdout;
      Bool frame_only = False;
+     int use_x=0, use_y=0; 
+     unsigned int use_width=0, use_height=0;
  
      INIT_NAME;
  
***************
*** 140,145 ****
--- 143,180 ----
  	    frame_only = True;
  	    continue;
  	}
+ 	if (!strcmp(argv[i], "-geometry") || !strcmp(argv[i], "-source")) {
+ 	        if (++i >= argc) usage();
+ 		if (target_win)
+   	  outl("Use only one of -root -id -name -source -rubber; continuing.");
+ 		if (    (AllValues != XParseGeometry(argv[i], &use_x, &use_y,
+ 				&use_width, &use_height))
+ 			|| (use_x + (int)use_width < 0) 
+ 			|| (use_y + (int)use_height < 0) 
+ 			|| (use_x > XDisplayWidth(dpy, screen)) 
+ 			|| (use_y > XDisplayHeight(dpy, screen))
+ 			)
+ Fatal_Error("Please fully specify on-screen width x height + positive X, Y.");
+ 		target_win = RootWindow(dpy,screen);
+ 		on_root = True;
+ 		continue;
+ 	}
+ 	if (!strcmp(argv[i], "-rubber")) {
+ 		if (target_win)
+   	  outl("Use only one of -root -id -name -source -rubber; continuing.");
+ 		target_win = RootWindow(dpy,screen);
+ 		on_root = True;
+ 		if (Get_Rubber_Values(dpy, &use_x, &use_y, 
+ 			&use_width, &use_height, True))
+ 			Fatal_Error ("Error getting portion of root window.");
+ 		continue;
+ 	}
+ 	if (!strcmp(argv[i], "-sleep")) {
+ 	    if (++i >= argc) usage();
+ 	    delay = atoi(argv[i]);
+ 	    if (delay < 0) delay = 0;
+ 	    continue;
+ 	}
  	usage();
      }
      
***************
*** 164,172 ****
      /*
       * Dump it!
       */
!     Window_Dump(target_win, out_file);
  
      fclose(out_file);
  }

--- 199,208 ----
      /*
       * Dump it!
       */
!     Window_Dump(target_win, out_file, use_x, use_y, use_width, use_height);
  
      fclose(out_file);
+     exit(0);
  }

***************
*** 173,178 ****
--- 209,216 ----
  /*
   * Window_Dump: dump a window to a file which must already be open for
   *              writting.
+  * a width/height of 0 indicates that this function is being used for dumping
+  * a real non-root window, not a portion of the root window
   */
  
  char *calloc();
***************
*** 179,187 ****
  
  #include "X11/XWDFile.h"
  
! Window_Dump(window, out)
       Window window;
       FILE *out;
  {
      unsigned long swaptest = 1;
      XColor *colors;
--- 217,227 ----
  
  #include "X11/XWDFile.h"
  
! Window_Dump(window, out, use_x, use_y, use_width, use_height)
       Window window;
       FILE *out;
+      int use_x, use_y;
+      unsigned int use_width, use_height;
  {
      unsigned long swaptest = 1;
      XColor *colors;
***************
*** 200,206 ****
      Window dummywin;
      XWDFileHeader header;
  
!     
      /*
       * Inform the user not to alter the screen.
       */
--- 240,246 ----
      Window dummywin;
      XWDFileHeader header;
  
!     if (delay) sleep(delay); 
      /*
       * Inform the user not to alter the screen.
       */
***************
*** 207,218 ****
      Beep();
  
      /*
!      * Get the parameters of the window being dumped.
       */
      if (debug) outl("xwd: Getting target window information.\n");
      if(!XGetWindowAttributes(dpy, window, &win_info)) 
        Fatal_Error("Can't get target window attributes.");
  
      /* handle any frame window */
      if (!XTranslateCoordinates (dpy, window, RootWindow (dpy, screen), 0, 0,
  				&absx, &absy, &dummywin)) {
--- 247,262 ----
      Beep();
  
      /*
!      * Get the parameters of the window being dumped; check dumpability.
       */
      if (debug) outl("xwd: Getting target window information.\n");
      if(!XGetWindowAttributes(dpy, window, &win_info)) 
        Fatal_Error("Can't get target window attributes.");
+     if(!(IsViewable == win_info.map_state))
+       Fatal_Error("Window doesn't appear to be viewable.");
  
+     if (0 == use_width)	/* no size specified; prepare to fetch a window */
+     {
        /* handle any frame window */
        if (!XTranslateCoordinates (dpy, window, RootWindow (dpy, screen), 0, 0,
  				&absx, &absy, &dummywin)) {
***************
*** 225,230 ****
--- 269,286 ----
        win_info.y = absy;
        width = win_info.width;
        height = win_info.height;
+     }
+     else
+     {
+ 	/* use the values passed in; the rest of the calculations below
+ 	** can be executed anyway, because window == Root in this case
+ 	*/
+ 	win_info.x = absx = use_x;
+ 	win_info.y = absy = use_y;
+ 	width = use_width;
+ 	height = use_height;
+     }
+ 
      bw = 0;
  
      if (!nobdrs) {
***************
*** 381,387 ****
  usage()
  {
      fprintf (stderr,
! "usage: %s [-display host:dpy] [-debug] [-help] %s [-nobdrs] [-out <file>]",
  	   program_name, SELECT_USAGE);
      fprintf (stderr, " [-xy] [-add value] [-frame]\n");
      exit(1);
--- 437,443 ----
  usage()
  {
      fprintf (stderr,
! "usage: %s [-display host:dpy] [-debug] [-help] \n%s\n[-sleep seconds] [-nobdrs] [-out <file>]",
  	   program_name, SELECT_USAGE);
      fprintf (stderr, " [-xy] [-add value] [-frame]\n");
      exit(1);
*** xwd.man.orig	Sat Jul 27 18:41:24 1991
--- xwd.man	Thu Feb  6 10:09:58 1992
***************
*** 4,11 ****
  .SH SYNOPSIS
  .B "xwd"
  [-debug] [-help] [-nobdrs] [-out \fIfile\fP] [-xy] [-frame] [-add \fIvalue\fP]
! [-root | -id \fIid\fP | -name \fIname\fP ] [-icmap] [-screen]
! [-display \fIdisplay\fP]
  .SH DESCRIPTION
  .PP
  .I Xwd
--- 4,11 ----
  .SH SYNOPSIS
  .B "xwd"
  [-debug] [-help] [-nobdrs] [-out \fIfile\fP] [-xy] [-frame] [-add \fIvalue\fP]
! [-root | -id \fIid\fP | -name \fIname\fP | -geometry \fIgeom_spec\fP | -rubber]
! [-sleep \fIseconds\fP] [-icmap] [-screen] [-display \fIdisplay\fP]
  .SH DESCRIPTION
  .PP
  .I Xwd
***************
*** 14,21 ****
  allows X users to store window images in a specially formatted dump
  file.  This file can then be read by various other X utilities for
  redisplay, printing, editing, formatting, archiving, image processing, etc.
! The target window is selected by clicking the pointer in the desired window.
! The keyboard bell is rung once at the beginning of the dump and twice when
  the dump is completed.
  .SH OPTIONS
  .PP
--- 14,24 ----
  allows X users to store window images in a specially formatted dump
  file.  This file can then be read by various other X utilities for
  redisplay, printing, editing, formatting, archiving, image processing, etc.
! The target window is selected by clicking the pointer in the desired window;
! with the -rubber option, a portion of the root window may be selected
! interactively.
! The keyboard bell is rung once at the beginning of the dump, after
! any delay, and twice when
  the dump is completed.
  .SH OPTIONS
  .PP
***************
*** 69,74 ****
--- 72,95 ----
  This option indicates that the window with the specified WM_NAME property
  should be selected for the window dump, without requiring the user to
  select a window with the pointer.
+ .PP
+ .TP 8
+ .B "-geometry \fIgeom_spec\fP"
+ This option indicates that the specified portion of the root window should be
+ selected for the window dump, without requiring the user to
+ select a window with the pointer.
+ .PP
+ .TP 8
+ .B "-rubber"
+ This option indicates that a portion of the root window should
+ be selected for the window dump, with user interaction.
+ .PP
+ .TP 8
+ .B "-sleep \fIseconds\fP"
+ This option indicates that xwd should delay for \fIseconds\fP after the
+ window area is specified but before dumping the window; this gives you
+ some time to pop up menus or do other set-up after specifying the portion
+ of the screen to dump.
  .PP
  .TP 8
  .B "-icmap"

-- 
David B. Lewis  	[email-masked] (ignore bogus return address)
"Sir, What was the best thing before sliced bread? Yours faithfully,
Winty M. Thornton" -- The Times [London], 8 May 1991