/*
 RubberWin.c; from olwm as distributed in X11R4
 *	Written for Sun Microsystems by Crucible, Santa Cruz, CA.

        (c) Copyright 1989 Sun Microsystems, Inc. Sun design patents
        pending in the U.S. and foreign countries. OPEN LOOK is a
        trademark of AT&T. Used by written permission of the owners.


        (c) Copyright Bigelow & Holmes 1986, 1985. Lucida is a registered
        trademark of Bigelow & Holmes. Permission to use the Lucida
        trademark is hereby granted only in association with the images
        and fonts described in this file.

        SUN MICROSYSTEMS, INC., AT&T, AND BIGELOW & HOLMES
        MAKE NO REPRESENTATIONS ABOUT THE SUITABILITY OF
        THIS SOURCE CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS"
        WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND.
        SUN  MICROSYSTEMS, INC., AT&T AND BIGELOW  & HOLMES,
        SEVERALLY AND INDIVIDUALLY, DISCLAIM ALL WARRANTIES
        WITH REGARD TO THIS SOURCE CODE, INCLUDING ALL IMPLIED
        WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
        PARTICULAR PURPOSE. IN NO EVENT SHALL SUN MICROSYSTEMS,
        INC., AT&T OR BIGELOW & HOLMES BE LIABLE FOR ANY
        SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
        OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
        OR PROFITS, WHETHER IN AN ACTION OF  CONTRACT, NEGLIGENCE
        OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
        WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE.
*/

#include <errno.h>
#include <stdio.h>

#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>

int	DefScreen;
GC	RootGC;
Cursor	MoveWinCursor;
int	lastButton;


/*
 * TraceBoundingBox -- trace an XOR box with the initial point specified
 *	by pEvent.  Return the dimensions of the box in boxX, boxY, boxW,
 *	and boxH.
 */
TraceBoundingBox(dpy, win, pEvent, pboxX, pboxY, pboxW, pboxH)
Display	*dpy;
XEvent	*pEvent;
Window win;
short	*pboxX, *pboxY, *pboxW, *pboxH;
{
	int	initX, initY;
	int	mouseX, mouseY;
	XEvent	event;
	Bool	boxFinished;
	XPoint	points[5];
	int	anchorX, anchorY;
	XGCValues       values;

	/*
	 * Change the cursor.
	 */
	MoveWinCursor = XCreateFontCursor(dpy, XC_top_left_arrow);

	/* Create a GC for drawing in the root window. */
        values.function = GXxor;
	values.plane_mask = AllPlanes;
 	values.foreground = 1;
        values.subwindow_mode = IncludeInferiors;
        RootGC = XCreateGC( dpy, RootWindow(dpy,DefaultScreen(dpy)), 
                        ( GCPlaneMask | GCFunction | GCForeground |
                          GCBackground | GCSubwindowMode ),
                        &values );

	XGrabPointer(dpy, win,
		     True, 
		     (ButtonReleaseMask | ButtonPressMask | PointerMotionMask),
		     GrabModeAsync, GrabModeAsync,
		     win,
		     MoveWinCursor,
		     CurrentTime);
	DefScreen = DefaultScreen( dpy );
	/*
	 * Grab the server, so that the outline(s) don't
	 * corrupt or get corrupted.
	 */
	XGrabServer(dpy);

	/* Due to a bug (in the X11R3 server?) we have to set the
	 * lastButton, used in rootEvent of RootWin.c, to a
	 * button that is not checked for.  There are problems
	 * with MotionNotify events getting through to the rootEvent
	 * loop when they shouldn't.  They cause TraceBoundingBox
	 * to be called again, without a button down.
	 */
	lastButton = Button5;

	/* these values are fixed */
	points[0].x = pEvent->xbutton.x;
	points[0].y = pEvent->xbutton.y;
	points[1].x = pEvent->xbutton.x;
	points[3].y = pEvent->xbutton.y;
	points[4].x = pEvent->xbutton.x;
	points[4].y = pEvent->xbutton.y;

	/* these are the values that vary */
	points[1].y = pEvent->xbutton.y;
	points[2].x = pEvent->xbutton.x;
	points[2].y = pEvent->xbutton.y;
	points[3].x = pEvent->xbutton.x;

	/*
	 * Draw initial outline
	 */
	XDrawLines(dpy, win,
		       RootGC, points, 5, CoordModeOrigin);

	for (boxFinished = False; boxFinished != True; )
	{
		XMaskEvent(dpy, ButtonReleaseMask|PointerMotionMask, &event);
		switch (event.type)
		{
		case ButtonRelease:
			boxFinished = True;
			break;
		
		case MotionNotify:
			/* erase old box */
			XDrawLines(dpy, win,
				       RootGC, points, 5, CoordModeOrigin);

			/*
			 * there may be multiple motion events queued up,
			 * consume them and just use the last one.
			 */
			while (XCheckTypedEvent(dpy, MotionNotify, &event))
				;

			/* set new position */
			points[1].y = event.xmotion.y;
			points[2].x = event.xmotion.x;
			points[2].y = event.xmotion.y;
			points[3].x = event.xmotion.x;

			/* draw new box */
			XDrawLines(dpy, win,
				       RootGC, points, 5, CoordModeOrigin);
			break;
		
		default:
			/*if (PrintOrphans)
				DebugEvent(&event, "TraceBounding");*/
			break;
		}
	}

	/*
	 * erase outline
	 */
	XDrawLines(dpy, win,
		       RootGC, points, 5, CoordModeOrigin);

	/*
	 * let go of the server
	 */
	XUngrabServer(dpy);
	XUngrabPointer(dpy, CurrentTime);

	if (points[0].x < points[2].x)
	{
		*pboxX = points[0].x;
		*pboxW = points[2].x - points[0].x;
	}
	else
	{
		*pboxX = points[2].x;
		*pboxW = points[0].x - points[2].x;
	}

	if (points[0].y < points[2].y)
	{
		*pboxY = points[0].y;
		*pboxH = points[2].y - points[0].y;
	}
	else
	{
		*pboxY = points[2].y;
		*pboxH = points[0].y - points[2].y;
	}
	XFlush(dpy);
}

Window Select_Window(dpy, win, pboxX, pboxY, pboxW, pboxH)
     Display *dpy;
	Window win;
	short *pboxX, *pboxY, *pboxW, *pboxH;
{
  int status;
  Cursor cursor;
  XEvent event;
  Window target_win = None;
  int buttons = 0;

  /* Make the target cursor */
  cursor = XCreateFontCursor(dpy, XC_crosshair);

  /* Grab the pointer using target cursor, letting it room all over */
  status = XGrabPointer(dpy, win, False,
			ButtonPressMask|ButtonReleaseMask, GrabModeSync,
			GrabModeAsync, win, cursor, CurrentTime);
  if (status != GrabSuccess) 
    {
      fprintf(stderr, "Fatal Error - Can't grab the mouse.");
      exit(1);
    }

  /* Let the user select a window... */
  while ((target_win == None)/* || (buttons != 0)*/) {
    /* allow one more event */
    XAllowEvents(dpy, SyncPointer, CurrentTime);
    XWindowEvent(dpy, win,
		 ButtonPressMask|ButtonReleaseMask, &event);
    switch (event.type) {
    case ButtonPress:
      if (target_win == None) {
	target_win = win; /* window selected */
	if (target_win == None)
	  target_win = win;
      }
      buttons++;
      break;
    case ButtonRelease:
      if (buttons > 0) /* there may have been some down before we started */
	buttons--;
       break;
    }
  } 

  XUngrabPointer(dpy, CurrentTime);      /* Done with pointer */
  TraceBoundingBox(dpy, target_win, &event, pboxX, pboxY, pboxW, pboxH);
  return(target_win);
}
