/*
 * Copyright 1990 by Baylor College of Medicine ALL RIGHTS RESERVED. 
 *
 * This program is subject to a license agreement between 
 * Baylor College of Medicine and MIT. Any use inconsistent with
 * said license and any use by persons other than the faculty, 
 * students and staff at MIT or any use on a computer not operated 
 * as part of the Athena Computing Environment (ACE) is expressly 
 * prohibited.
 */
/****************************************************************
 * File: xsnap.c
 * Date: 04/02/91
 *
 * Description:
 *   This file contains the code for creating image by sweeping
 *   out a portion of the screen.
 *
 * Revisions:
 * 04/02/91 (BDM): removed dependency on vns.h and moved to the 
 *   Vt library from the vns library.
 ****************************************************************/
#include <stdio.h>
#include <X11/Intrinsic.h>
#include <X11/cursorfont.h>

#include "VtP.h"

/****************************************************************
 *                 Local Static Variables
 ****************************************************************/
static GC gc ;
static int x1,y1,x2,y2 ;
static int (*callback)() ;
static char *callback_args ;
static is_dragging ;
static Cursor top_left_cursor;
static Cursor bottom_right_cursor;

static void button_select();
static void button_motion();
static void button_release();

/****************************************************************
 *                 Private Functions
 ****************************************************************/

PRIVATE
int
get_bounds(X1,Y1,w,h)
	int *X1 ;
	int *Y1 ;
	unsigned *w ;
	unsigned *h ;
{
	int wid ;
	int hgt ;
	*X1 = x1 ;
	*Y1 = y1 ;
	wid = x2 - x1 + 1 ;
	hgt = y2 - y1 + 1 ;
	if (wid < 0)
	{
		*X1 = x2 ;
		wid = x1 - x2 + 1 ;
	}
	if (hgt < 0)
	{
		*Y1 = y2 ;
		hgt = y1 - y2 + 1 ;
	}
	*w = wid ;
	*h = hgt ;
	return wid && hgt ;
}

/*
 * Draws line along x
 */
PRIVATE
void
dlinex(wd,x,y,w)
	Widget wd;
	int x;
	int y;
	unsigned w;
{
	XDrawLine(XtDisplay(wd),DefaultRootWindow(XtDisplay(wd)),gc,x,y,(int)(x + w),y) ;
}

/*
 * Draws line along y
 */
PRIVATE
void
dliney(wd,x,y,h)
	Widget wd;
	int x;
	int y;
	unsigned h;
{
	XDrawLine(XtDisplay(wd),DefaultRootWindow(XtDisplay(wd)),gc,x,y,x,(int)(y + h)) ;
}

/* 
 * Draw a 3x3 grid outline box with given coordinates
 */
PRIVATE
void
draw_box(wd)
	Widget wd;
{
	int X1,Y1 ;
	unsigned w,h ;
	if (get_bounds(&X1,&Y1,&w,&h))
	{
		int x13 = X1 + w / 3 ;
		int x23 = X1 + 2 * w / 3 ;
		int y13 = Y1 + h / 3 ;
		int y23 = Y1 + 2 * h / 3 ;
		XDrawRectangle(XtDisplay(wd),DefaultRootWindow(XtDisplay(wd)), gc,
				X1, Y1, w, h);
		dliney(wd,x23,Y1,h) ;
		dliney(wd,x13,Y1,h) ;
		dlinex(wd,X1,y13,w) ;
		dlinex(wd,X1,y23,w) ;
	}
}

/* 
 * Event handler when button is pressed
 */
/*ARGSUSED*/
PRIVATE
void
button_select(wd,data,event)
	Widget wd ;
	char *data ;
	XButtonEvent *event ;
{
	XGCValues values;

	/* before we do anything, check for deactivation via button 2,3 */
	if (event->button == Button2 || event->button == Button3)
	{
		/* ungrab the pointer and the server */
		XUngrabPointer(XtDisplay(wd),CurrentTime) ;
		XUngrabServer(XtDisplay(wd)) ;

		/* remove the event handlers */
		XtRemoveEventHandler(wd,ButtonPressMask,False,button_select,(XtPointer)NULL) ;
		XtRemoveEventHandler(wd,ButtonMotionMask,False,button_motion,(XtPointer)NULL) ;
		XtRemoveEventHandler(wd,ButtonReleaseMask,False,button_release,(XtPointer)NULL) ;

		return;
	}

	is_dragging = TRUE ;

	x1 = event->x_root ;
	x2 = x1 - 1 ;
	y1 = event->y_root ;
	y2 = y1 - 1 ;

	if (gc == NULL)
	{
		values.background = WhitePixel(XtDisplay(wd),DefaultScreen(XtDisplay(wd))) ;
		values.line_width = 0 ;
		values.function = GXxor ;
		values.subwindow_mode = IncludeInferiors ;
		values.foreground = values.plane_mask =
			BlackPixel(XtDisplay(wd),DefaultScreen(XtDisplay(wd))) ^ WhitePixel(XtDisplay(wd),DefaultScreen(XtDisplay(wd)));

		gc = XCreateGC(XtDisplay(wd),DefaultRootWindow(XtDisplay(wd)),
			GCForeground |
			GCBackground |
			GCLineWidth |
			GCFunction |
			GCPlaneMask |
			GCSubwindowMode,
			&values) ;
	}

	draw_box(wd);

	if (bottom_right_cursor == NULL)
	{
		bottom_right_cursor = XCreateFontCursor(XtDisplay(wd),XC_bottom_right_corner);
	}

	XChangeActivePointerGrab(XtDisplay(wd),
		(unsigned int)ButtonPressMask | (unsigned int)ButtonReleaseMask | (unsigned int)PointerMotionMask,
		bottom_right_cursor, CurrentTime) ;
}

/* 
 * Event handler when button is in motion (sweeping)
 */
/*ARGSUSED*/
PRIVATE
void
button_motion(wd,data,event)
	Widget wd;
	char *data;
	XMotionEvent *event;
{
	if (is_dragging)
	{
		draw_box(wd);
		x2 = event->x_root;
		y2 = event->y_root;
		draw_box(wd);
	}
}

/*
 * Event handler when mouse is realeased (all done)
 */
/*ARGSUSED*/
PRIVATE
void
button_release(wd,data,event)
	Widget wd;
	char *data;
	XButtonEvent *event;
{
	if (is_dragging)
	{
		is_dragging = FALSE;
		draw_box(wd);
		x2 = event->x_root;
		y2 = event->y_root;

		/* ungrab the pointer and the server */
		XUngrabPointer(XtDisplay(wd),CurrentTime) ;
		XUngrabServer(XtDisplay(wd)) ;

		/* remove the event handlers */
		XtRemoveEventHandler(wd,ButtonPressMask,False,button_select,(XtPointer)NULL) ;
		XtRemoveEventHandler(wd,ButtonMotionMask,False,button_motion,(XtPointer)NULL) ;
		XtRemoveEventHandler(wd,ButtonReleaseMask,False,button_release,(XtPointer)NULL) ;

		/* get the image and pass it to the callback functions */
		{
			unsigned w,h ;
			int X1,Y1 ;
			if (get_bounds(&X1,&Y1,&w,&h))
			{
				XImage *image ;
				image = XGetImage(XtDisplay(wd),DefaultRootWindow(XtDisplay(wd)),
					X1,Y1,w,h,(unsigned long)AllPlanes,ZPixmap) ;
				callback(callback_args,image) ;
			}
		}
	}
}

/****************************************************************
 *                 Public Functions
 ****************************************************************/

PUBLIC
void
VtSelectScreenArea(parent,cb,args)
	Widget parent;
	int (*cb)() ;
	char *args ;
{
	callback = cb ;
	callback_args = args ;

	if (top_left_cursor == NULL)
	{
		top_left_cursor = XCreateFontCursor(XtDisplay(parent),XC_top_left_corner);
	}

	/* add event handlers for sweeping out image */
	XtAddEventHandler(parent,ButtonPressMask,False,button_select,(XtPointer)NULL) ;
	XtAddEventHandler(parent,ButtonMotionMask,False,button_motion,(XtPointer)NULL) ;
	XtAddEventHandler(parent,ButtonReleaseMask,False,button_release,(XtPointer)NULL) ;

	/* ugrab the pointer if it is grabbed */
	XUngrabPointer(XtDisplay(parent),CurrentTime) ;

	/* grab the server */
	XGrabServer(XtDisplay(parent)) ;

	/* grab the pointer */
	XGrabPointer(XtDisplay(parent),XtWindow(parent),False,
		(unsigned int)ButtonPressMask | (unsigned int)ButtonReleaseMask | (unsigned int)PointerMotionMask,
		GrabModeAsync,
		GrabModeAsync, None, top_left_cursor, CurrentTime) ;
}
