/*   Copyright
 *
 *       Copyright (C) 2000-2013 Jean-Pierre Demailly <jean-pierre.demailly@ujf-grenoble.fr>
 *
 *   License
 *
 *       This program is free software; you can redistribute it and/or modify
 *       it under the terms of the GNU General Public License as published by
 *       the Free Software Foundation; either version 3 of the License, or
 *       (at your option) any later version.
 *
 *       This program is distributed in the hope that it will be useful,
 *       but WITHOUT ANY WARRANTY; without even the implied warranty of
 *       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *       GNU General Public License for more details.
 *
 *       You should have received a copy of the GNU General Public License
 *       along with this program. If not, see <http://www.gnu.org/licenses/>.
 */


/*
 *   xsnap -- take a snapshot of a portion of the screen.
 *
 *   Copyright 1989 Clauss Strauch
 *                  cbs@cad.cs.cmu.edu
 *
 *   Permission to use, copy, modify, and distribute this software and its
 *   documentation for any purpose and without fee is hereby granted.
 *   This software is provided "as is", without express or implied warranty.
 *
 *   Modified by Bill Janssen janssen@parc.xerox.com
 *    add -xwd and -region options
 *   Modified by Arnaud Le Hors lehors@mirsa.inria.fr  November 14 1990
 *    fix code about region option
 *    add -xpm option
 *   Modified by Jean-Pierre Demailly jean-pierre.demailly@ujf-grenoble.fr :
 *    added -jpg, -png, -ppm, -pnm formats, compression, and GUI dialog.
 *    (04/2007) added OCR capability.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#include <zlib.h>
#include <locale.h>

#include <X11/Xlib.h>
#include <X11/Xos.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>
#ifdef PNG
#include <png.h>
#endif
#ifdef ENABLE_NLS
#include <X11/Xft/Xft.h>
#endif

#include "patchlevel.h"
#include "intl.h"

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

#ifndef P_tmpdir
#define P_tmpdir "/tmp"
#endif


/*  Leave arguments as globals, since there are so many of them.
 *  They'll only be referenced in process_args and main.
 */

enum { JPG_F=0, PNG_F, PNM_F, PPM_F, XPM_F, XWD_F, TXT_F, NUM_F };
enum { BD_COLOR=0, BG_COLOR, FG_COLOR,
       BT1_COLOR, BT2_COLOR, BT3_COLOR, BT4_COLOR, NUM_COLORS };

static char * suffix[2][NUM_F] = {
  { ".jpg", ".png", ".pnm", ".ppm", ".xpm", ".xwd", ".txt" },
  { ".jpg", ".png", ".pnm.gz", ".ppm.gz", ".xpm.gz", ".xwd.gz", ".txt.gz" }
};

char *color_string[NUM_COLORS] = {
  NULL,				     /* name of border color */
  "#DCDAD5",                         /* name of background color */
  "black",                           /* name of foreground color */
  "black",                           /* name of 1st button color */
  "grey50",                          /* name of 2nd button color */
  "grey95",                          /* name of 3rd button color */
  "white"                            /* name of 4th button color */
};
unsigned long color_pixel[NUM_COLORS]; /* pixels */

Display *display;
int screen;

char *display_string = NULL;         /* display we'll open */
int  border_width = 2;               /* border_width  of the snapshot window */
int  set_iconic = FALSE;             /* start snap window in iconic state?  */
char *window_geom_string = NULL;     /* geometry of snapshot window */
char *icon_geom_string = NULL;       /* icon geometry */
char *region_geom_string = NULL;     /* location of region to copy */
char win_name0[128]="";              /* name of window snapshot */
char win_name[256]="";               /* name of window snapshot */
#ifdef ENABLE_NLS
char *font_name = "Liberation-9";
#else
char *font_name = "7x13";
#endif

#ifdef PNG
int  output_fmt = PNG_F;	             /* output format */
#else
int  output_fmt = PPM_F;	             /* output format */
#endif
char output_name[256] = "";          /* file to store in */
char root_name[256] = "?";	     /* root name of file */
int  grab_server = 0;                /* grab the server? */
int  op_mode = 1;                    /* 0 = grab + save to file + exit */
                                     /* 1 = grab + open gui */
                                     /* 2 = open gui + wait for events */
int  zip = TRUE;                     /* use compression */
int  clicked =-1;                    /* button pressed */
int  delay = 10000;
int  start_move = 0;
unsigned int  gui_width = 0;

Colormap private_cmap = 0;
Cursor snap_cursor;
XSizeHints wm_size_hints;
Atom wm_delete_window, wm_protocols;

/*
 *  create_event_window returns the ID of a InputOnly window that covers
 *  the given window.
 */

Window
create_event_window(win, init_cursor)
Window win;
{
	XSetWindowAttributes xswa;
	unsigned long xswa_valuemask;
	Window root_win;
	Window event_window;
	unsigned int win_width, win_height;
	unsigned int  win_border_width, win_depth;
	int win_x, win_y;

	/* get the geometry of the window  */

	XGetGeometry(display, win, &root_win, &win_x, &win_y,
	    &win_width, &win_height, &win_border_width, &win_depth);

	/* make an input only window to get events from  */

	xswa.cursor = init_cursor;
	xswa.override_redirect = True;
	xswa.event_mask = ButtonPressMask | ButtonReleaseMask | Button1MotionMask;
	xswa_valuemask = CWCursor | CWOverrideRedirect | CWEventMask;
	event_window = XCreateWindow(display, win, win_x, win_y, win_width,
	    win_height, 0, 0, InputOnly, CopyFromParent,
	    xswa_valuemask, &xswa);
	return(event_window);
}

/*
 *   draw box draws a box on the given window, with the given GC
 *
 */

void draw_box(win, a_gc, x1, y1, x2, y2)
GC a_gc;
Window win;
int x1, y1, x2, y2;
{
	XSegment segments[4];
	segments[0].x1 = (short)x1;
	segments[0].y1 = (short)y1;
	segments[0].x2 = (short)x1;
	segments[0].y2 = (short)y2;

	segments[1].x1 = (short)x1;
	segments[1].y1 = (short)y1;
	segments[1].x2 = (short)x2;
	segments[1].y2 = (short)y1;

	segments[2].x1 = (short)x2;
	segments[2].y1 = (short)y2;
	segments[2].x2 = (short)x1;
	segments[2].y2 = (short)y2;

	segments[3].x1 = (short)x2;
	segments[3].y1 = (short)y2;
	segments[3].x2 = (short)x2;
	segments[3].y2 = (short)y1;

	XDrawSegments(display, win, a_gc, segments, 4);
}

/*
 *  get_region
 * takes as input:
 *    display
 *    window to get region from
 *    pointers to x1, y1, width, height
 *
 *   returns:  the position and width and height of a
 *             user selected region via the given pointers.
 *
 */

void
find_window(bool, x, y, u, v, width, height)
     int bool;
     int x, y;	
     int *u, *v, *width, *height;
{
  XWindowAttributes wa;
  Window findW = DefaultRootWindow(display), stopW=0, childW, initW;

  XTranslateCoordinates(display, findW, findW, x, y, &x, &y, &stopW);

  if (stopW)
    initW = stopW;
  else
    initW = findW;

  while (stopW) {
    XTranslateCoordinates(display, findW, stopW, x, y, &x, &y, &childW);
    findW = stopW;
    if (childW &&
	XGetWindowAttributes(display, childW, &wa) &&
	wa.class != InputOutput)
	break;
    stopW = childW;
    }

  if (!bool) findW = initW;

  XGetWindowAttributes(display, findW, &wa);
  *width = wa.width;
  *height = wa.height;
  private_cmap = wa.colormap;

  XTranslateCoordinates(display, findW, DefaultRootWindow(display), 0, 0, u, v,&stopW);
}

void get_region(win,  x, y, width, height)
Window win;
int *x, *y;
unsigned int *height, *width;

{
	Window event_window;
	Cursor up_right_curs, up_left_curs;
	Cursor low_right_curs, low_left_curs;
	Cursor current_cursor;
	int done;
	int init_x, init_y;
	int last_x, last_y;
	XEvent event;
	GC xor_gc;
	XGCValues xor_gc_values;             /* for creating xor_gc */
	unsigned long xor_gc_valuemask;       /* valuemask for creating xor_gc */

	/* make the GC and cursors we'll need */

	up_right_curs = XCreateFontCursor(display, XC_ur_angle);

	up_left_curs = XCreateFontCursor(display, XC_ul_angle);

	low_right_curs = XCreateFontCursor(display, XC_lr_angle);
	
	low_left_curs = XCreateFontCursor(display, XC_ll_angle);
	


	xor_gc_valuemask = GCFunction | GCSubwindowMode  | GCForeground;
	xor_gc_values.function = GXxor;
	xor_gc_values.foreground = 0xfd;
	xor_gc_values.subwindow_mode = IncludeInferiors;
	xor_gc = XCreateGC(display, win, xor_gc_valuemask, &xor_gc_values);

	event_window = create_event_window(win,up_left_curs);
	XMapRaised(display, event_window);

	if (XGrabPointer(display, event_window, True,
	    ButtonPressMask,
	    GrabModeAsync, GrabModeAsync, None, up_left_curs,
			 CurrentTime) != 0)
	  {
	    fprintf(stderr, _("Cannot grab pointer."));
	    exit(1);
	  }


	/* get the initial button  press */
	done = 0;
	while (done == 0)
	{
	        XNextEvent(display, &event);
		switch(event.type)
		{
		case MappingNotify:
			XRefreshKeyboardMapping((XMappingEvent *)&event);
			break;
		case ButtonPress:
			if (event.xbutton.button == 1)
			{
				init_x = event.xbutton.x;
				init_y = event.xbutton.y;
				done = 1;
				break;
			}
			if (event.xbutton.button == 2)
			{
				init_x = event.xbutton.x;
				init_y = event.xbutton.y;
				done = 2;
				break;
			}
			if (event.xbutton.button == 3)
			  {
			    if (grab_server)
			      {
				XGrabServer(display);
				XSync(display, 0);
			      }
			    exit(0);
			  }

		}
	}


	/*  now we have the location of one corner of the box.   change the cursor,
 *  and have the user drag out the area.
 */
	last_x = init_x;
	last_y = init_y;
        if (done == 1)
	  {
	  current_cursor = low_right_curs;
	  XChangeActivePointerGrab(display, ButtonReleaseMask | Button1MotionMask,
	    current_cursor, CurrentTime);
	  done = 0;
 	  draw_box(win, xor_gc, init_x, init_y, last_x, last_y);
	  }
	while (! done)
	{
		XNextEvent(display, &event);
		switch(event.type)
		{
		case MappingNotify:
			XRefreshKeyboardMapping((XMappingEvent *)&event);
			break;
		case MotionNotify:
			draw_box(win, xor_gc,
			    init_x, init_y, last_x, last_y);  /* erase old */
			last_x = event.xmotion.x;
			last_y = event.xmotion.y;
			draw_box(win, xor_gc,
			    init_x, init_y, last_x, last_y); /* draw new  */
			/*  Change cursor to correspond to position of pointer */
			if ((init_x < last_x) && (init_y < last_y)
			    && (current_cursor != low_right_curs))
			{
				current_cursor = low_right_curs;
				XChangeActivePointerGrab(display,
				    ButtonReleaseMask | Button1MotionMask,
				    low_right_curs, CurrentTime);
			}
			else if ((last_x < init_x) && (last_y < init_y)
			    &&  (current_cursor != up_left_curs))
			{
				current_cursor = up_left_curs;
				XChangeActivePointerGrab(display,
				    ButtonReleaseMask | Button1MotionMask,
				    up_left_curs, CurrentTime);
			}

			else if ((init_x < last_x) && (last_y < init_y)
			    && (current_cursor != up_right_curs))
			{
				current_cursor = up_right_curs;
				XChangeActivePointerGrab(display,
				    ButtonReleaseMask | Button1MotionMask,
				    up_right_curs, CurrentTime);

			}
			else if ((last_x < init_x) && (init_y < last_y)
			    && (current_cursor != low_left_curs))
			{
				current_cursor = low_left_curs;
				XChangeActivePointerGrab(display,
				    ButtonReleaseMask | Button1MotionMask,
				    low_left_curs, CurrentTime);
			}

			break;
		case ButtonRelease:
			if (event.xbutton.button == 1)
			{
				done = TRUE;
				draw_box(win, xor_gc,
				    init_x, init_y, last_x, last_y);  /* erase last box drawn */
			}
			break;
		}
	}
	XFlush(display);   /*  gets rid of last box on screen  */
	if (init_x < last_x) *x = init_x;
	else *x = last_x;
	if (init_y < last_y) *y = init_y;
	else *y = last_y;
	*width = (unsigned int)abs(last_x - init_x);
	*height = (unsigned int)abs(last_y - init_y);
	/* clean up after ourself: */

	XDestroyWindow(display, event_window);
	XFreeGC(display, xor_gc);

	/* we'll let the caller ungrab the pointer */

        if (done == 1)
	  {
	  int u;
          if (*width==0 && *height==0)
             find_window(0, init_x,init_y, x, y, width, height);
	  else
 	     find_window(0, init_x, init_y, &u, &u, &u, &u);
	  }

        if (done == 2)
          find_window(1, init_x,init_y, x, y, width, height);
}

/*
 *  get_pixmap_region
 *
 *       input :
 *               a display, a window, x, y, width, height, interactive.
 *               if interactive, the user is prompted for a region,
 *               other wise the given region is copied to a pixmap.
 *       returns : a pixmap containing a copy of a user-specified area
 *                 of the given window;
 *
 */

Pixmap
get_pixmap_region(win, a_gc,  x, y,
width, height, depth, interactive)
GC a_gc;
Window win;
int *x, *y;
unsigned int *width, *height, *depth;
int interactive;

{
	int reg_x, reg_y;
	unsigned int reg_width, reg_height;
	Pixmap pixmap_returned;
        int junk_left, junk_top;
	unsigned int junk_width, junk_height, junk_border_width;
	Window junk_root;

	if (interactive){
		get_region(win, &reg_x, &reg_y,
		    &reg_width, &reg_height);
		*x = reg_x;
		*y = reg_y;
		*width = reg_width;
		*height = reg_height;
	}

        if (*width==0 || *height==0) exit(1);

	/* Use the depth of `win' for the depth of the pixmap */
	
	XGetGeometry (display, win, &junk_root, &junk_left, &junk_top,
		      &junk_width, &junk_height, &junk_border_width, depth);

	pixmap_returned = XCreatePixmap(display,
					DefaultRootWindow(display),
					*width, *height, *depth);

	/*  now copy the area we specified  */

	XCopyArea(display, win, pixmap_returned, a_gc, *x, *y,
		  *width, *height, 0, 0);
	if (interactive)  XUngrabPointer(display, CurrentTime);
	return(pixmap_returned);
}


/*
 *  X error handler for,  mainly for future use
 */

int
my_X_error_handler(myerr)
XErrorEvent *myerr;
{
  char msg[80];

/* Ignore errors - alea jacta est ! */
  return 1;
/* if we get a BadAlloc error, it's probably 'cuz we couldn't allocate
 * the pixmap.  Tell the user this.
 */
  fprintf(stderr,"xsnap:\n");
  if ((myerr->error_code) == BadAlloc)
    {
      fprintf(stderr, _("Insufficient resources for operation.\n"));
      fprintf(stderr, _("Probably not enough memory to allocate pixmap.\n"));
    }

/* Now print out the error string that X generates */

  XGetErrorText(display, myerr->error_code, msg,80);
  fprintf(stderr, "%s %s\n", _("Error code"), msg);
/* no way to continue, so we'll just bag it  */
  exit(1);
}


/*
 *  print a usage message to the user
 */

char * usage_msg[] = {
N_("Usage:  xsnap [-options...]\n"),
N_("where options are:"),
N_("\t[-display host:display] Display to connect to."),
N_("\t[-fn name]              Name of font to be used."),
N_("\t[-guiwidth size]        Width of GUI window."),
N_("\t[-bg bgcolor]           Background color"),
N_("\t[-fg bgcolor]           Foreground color"),
N_("\t[-bd bordercolor]       Color of border"),
N_("\t[-bw borderwidth]       Width of border"),
N_("\t[-geometry geom]        Geometry of snapshot window"),
N_("\t[-icongeometry geom]    Location of icon."),
N_("\t[-region geom]          Region of screen to be copied."),
N_("\t[-iconic]               Start up in iconic state"),
N_("\t[-name name]            Passed to window manager for name of window."),
N_("\t[-grab]                 Grab server during selection of region."),
N_("\t[-nograb]               Don't grab server during selection of region."),
N_("\t[-jpg]                  Write output in 'jpg' format."),
N_("\t[-png]                  Write output in 'png' format [default]."),
N_("\t[-pnm]                  Write output in 'pnm(.gz)' format."),
N_("\t[-ppm]                  Write output in 'ppm(.gz)' format."),
N_("\t[-xpm]                  Write output in 'xpm(.gz)' format."),
N_("\t[-xwd]                  Write output in 'xwd(.gz)' format."),
N_("\t[-txt]                  Write output in 'txt(.gz)' format (OCR)."),
N_("\t[-gui]                  Open GUI command window first."),
N_("\t[-nogui]                Select region and immediately dump to file."),
N_("\t[-snap]                 Select region and show it in an X window."),
N_("\t[-file filename]        Write output to file 'filename'."),
N_("\t[-stdout]               Write output to stdout."),
N_("\t[-nozip]                Don't use compression (compression is default)."),
};

	
char *bindings_msg[] = {
N_("Button bindings at runtime:"),
N_("\tMouse button 1: select region of capture, or framed window"),
N_("\tMouse button 2: click on window to be captured"),
N_("\tMouse button 3: cancel snapshot"),
N_("Key bindings at runtime:"),
N_("\tq/Q\texit xsnap"),
N_("\tz/Z\tenable/disable compression (gzip)"),
N_("\tb/B\tgrab/don't grab server"),
N_("\tj/J\tsave snapshot as .jpg file"),
N_("\tg/G\tsave snapshot as .png file"),
N_("\tn/N\tsave snapshot as .pnm(.gz) file"),
N_("\tp/P\tsave snapshot as .ppm(.gz) file"),
N_("\tx/X\tsave snapshot as .xpm(.gz) file"),
N_("\tw/W\tsave snapshot as .xwd(.gz) file"),
N_("\tt/T\tsave snapshot as .txt(.gz) file (OCR)"),
N_("\t ! \tstart new xsnap instance"),
N_("\tother\tshow menu and key bindings.")
};

void
usage(q)
int q;
{
  int i;
	printf("\nXsnap, %s %s.%s\n\n", _("version"), VERSION, PATCHLEVEL);
        for (i=0; i<sizeof(usage_msg)/sizeof(char *); i++)
	    printf("%s\n", gettext(usage_msg[i]));
        for (i=0; i<sizeof(bindings_msg)/sizeof(char *); i++) {
            printf("%s\n", gettext(bindings_msg[i]));
            if (i==3) printf("\n");
	}
	if (q) exit(1);
}

/*
 *        get the arguments.   arguments are matched to the first
 *        distinguishing substring.
 */
void
process_args(argc, argv)
int argc;
char **argv;
{
	int i;

	for (i = 1; i < argc; i++)
	{
		if (strcmp(argv[i], "-h") == 0)
		{
			usage(1);
			continue;
		}
		if (strncmp(argv[i], "-disp", 5) == 0)
		{
			display_string = argv[++i];
			continue;
		}
		if (strncmp(argv[i], "-fn", 3) == 0)
		{
			font_name = argv[++i];
			continue;
		}
		if (strncmp(argv[i], "-guiwidth", 5) == 0)
		{
			gui_width = atoi(argv[++i]);
			continue;
		}
		if (strncmp(argv[i], "-bw", 3) == 0)
		{
			border_width = atoi(argv[++i]);
			continue;
		}
		if (strncmp(argv[i], "-bg", 3) == 0)
		{
			color_string[BG_COLOR] = argv[++i];
			continue;
		}
		if (strncmp(argv[i], "-fg", 3) == 0)
		{
			color_string[FG_COLOR] = argv[++i];
			continue;
		}
		if (strncmp(argv[i], "-bd", 3) == 0)
		{
			color_string[BD_COLOR] = argv[++i];
			continue;
		}
		if (strncmp(argv[i], "-bt", 3) == 0 &&
                    argv[i][3]>='1' && argv[i][3]<='4')
		{
			color_string[BT1_COLOR+argv[i][3]-'1'] = argv[i+1];
			++i;
			continue;
		}
		if (strncmp(argv[i], "-geom", 5) == 0)
		{
			window_geom_string = argv[++i];
			continue;
		}
		if (strncmp(argv[i], "-icongeom", 9) == 0)
		{
			icon_geom_string = argv[++i];
			continue;
		}
		if (strncmp(argv[i], "-region", 7) == 0)
		{
			region_geom_string = argv[++i];
			continue;
		}
		if (strncmp(argv[i], "-iconic", 7) == 0)
		{
			set_iconic = TRUE;
			continue;
		}
		if (strncmp(argv[i], "-name", 5) == 0)
		{
			strncpy(win_name0 , argv[++i], 126);
			win_name0[127] = '\0';
			continue;
		}
		if (strncmp(argv[i], "-grab", 5) == 0)
		{
			grab_server = 1;
			continue;
		}
		if (strncmp(argv[i], "-nograb", 7) == 0)
		{
			grab_server = 0;
			continue;
		}
		if (strncmp(argv[i], "-nogui", 6) == 0)
		{
                  op_mode = 0;
		  continue;
		}
		if (strncmp(argv[i], "-gui", 4) == 0)
		{
		  op_mode = 2;
		  continue;
		}
		if (strncmp(argv[i], "-snap", 5) == 0)
		{
		  op_mode = 1;
		  continue;
		}
		if (strncmp(argv[i], "-nozip", 5) == 0)
		{
		  zip = FALSE;
		  continue;
		}
		if (strncmp(argv[i], "-jpg", 4) == 0)
		{
		  output_fmt = JPG_F;
		  continue;
		}
		if (strncmp(argv[i], "-png", 4) == 0)
		{
		  output_fmt = PNG_F;
		  continue;
		}
		if (strncmp(argv[i], "-pnm", 4) == 0)
		{
		  output_fmt = PNM_F;
		  continue;
		}
		if (strncmp(argv[i], "-ppm", 4) == 0)
		{
		  output_fmt = PPM_F;
		  continue;
		}
		if (strncmp(argv[i], "-xpm", 4) == 0)
		{
		  output_fmt = XPM_F;
		  continue;
		}
		if (strncmp(argv[i], "-xwd", 4) == 0)
		{
		  output_fmt = XWD_F;
		  continue;
		}
		if (strncmp(argv[i], "-txt", 4) == 0)
		{
		  output_fmt = TXT_F;
		  continue;
		}
		if (strncmp(argv[i], "-stdout", 7) == 0)
		{
                  *root_name='\0';
		  op_mode = 0;
		  continue;
		}
		if (strncmp(argv[i], "-file", 5) == 0 && i< argc-1)
		{
                  strcpy(root_name, argv[++i]);
		  op_mode = 0;
		  continue;
		}
		usage(1);
	}

        if (!*win_name0)
		strcpy(win_name0, "xsnap");
}

static char err_malloc[] =
   N_("xsnap: out of memory trying to allocate %s\n");

/*
 * Get the XColors of all pixels in image - returns # of colors
 */
int
Get_XColors(win_info, colors)
     XWindowAttributes *win_info;
     XColor **colors;
{
    int i, ncolors;

    if (!win_info->colormap)
	return(0);

    if (win_info->visual->class == TrueColor ||
	win_info->visual->class == DirectColor)
	return(0);    /* XXX punt for now */

    ncolors = win_info->visual->map_entries;
    if (!(*colors = (XColor *) malloc (sizeof(XColor) * ncolors)))
      fprintf (stderr, err_malloc, _("colors"));

    for (i=0; i<ncolors; i++)
      (*colors)[i].pixel = i;

    XQueryColors(display, win_info->colormap, *colors, ncolors);

    return(ncolors);
}

/*
 * Determine the pixmap size.
 */

int
Image_Size(image, format)
     XImage *image;
     int format;
{
    if (format != ZPixmap)
      return(image->bytes_per_line * image->height * image->depth);

    return(image->bytes_per_line * image->height);
}

void
_swapshort (bp, n)
    register char *bp;
    register unsigned n;
{
    register char c;
    register char *ep = bp + n;

    while (bp < ep) {
	c = *bp;
	*bp = *(bp + 1);
	bp++;
	*bp++ = c;
    }
}

void
_swaplong (bp, n)
    register char *bp;
    register unsigned n;
{
    register char c;
    register char *ep = bp + n;
    register char *sp;

    while (bp < ep) {
	sp = bp + 3;
	c = *sp;
	*sp = *bp;
	*bp++ = c;
	sp = bp + 1;
	c = *sp;
	*sp = *bp;
	*bp++ = c;
	bp += 2;
    }
}

void
check_filename()
{
      int i, j, found;
      struct stat buf;
      char *suf, *dir;

      dir = getenv("HOME");
      if (!dir) 
        dir = getenv("TMPDIR");
      if (!dir) 
        dir = P_tmpdir;

      suf = suffix[zip][output_fmt];

      i=0;
      found=1;

      if (*root_name=='?')
      while(found && i<10000) {
	found = 0;
	j = zip;
	do {
	    j = 1-j;
	    sprintf(output_name,"%s/snap%04d%s", dir, i, suffix[j][output_fmt]);
            found |= !(-stat(output_name, &buf));	
	} while (j!=zip);
        ++i;
      } else {
        strcpy(output_name, root_name);
	if (!strstr(output_name, suf) ||
            strcmp(output_name+(strlen(output_name)-strlen(suf)),suf))
 	  strcat(output_name, suf);
      }
}

void *
any_wopen(char *name)
{
    if (zip)
        return (void *) gzopen (output_name, "wb6");
    else
        return (void *) fopen(output_name, "w");
}

void any_close (void * ptr)
{
    if (zip)
        gzclose(ptr);
    else
        fclose((FILE *)ptr);
}

void any_write(void * ptr, char * str, int num)
{
    if (!ptr)
        fwrite(str, num, 1, stdout);
    else {
        if (zip)
            gzwrite(ptr, str, num);
        else
        fwrite(str, num, 1, (FILE *)ptr);
    }
}

#include "X11/XWDFile.h"

void
save_as_xwd_file (win_info, pixmap, width, height, depth)
     XWindowAttributes *win_info;
     Pixmap pixmap;
     unsigned int width, height, depth;
{
    unsigned long swaptest = 1;
    XColor *colors;
    unsigned buffer_size;
    int header_size;
    int ncolors, i;

    XImage *image;

    XWDFileHeader header;
    void *output;

    image = XGetImage (display, pixmap, 0, 0, width, height, AllPlanes, ZPixmap);

    if (!image) {
	fprintf (stderr, "xsnap:  %s  %dx%d+%d+%d\n",
                 _("unable to get image at"),
		 width, height, 0, 0);
	exit (1);
    }

    /*
     * Find the output file
     */

    if (*root_name == '\0')
        output = NULL;
    else {
        check_filename();
        output = any_wopen (output_name);
    }

    /*
     * Determine the pixmap size.
     */
    buffer_size = Image_Size(image, ZPixmap);

    win_info->colormap = private_cmap;
    ncolors = Get_XColors(win_info, &colors);

    /*
     * Calculate header size.
     */
    header_size = sizeof(header);

    /*
     * Write out header information.
     */
    header.header_size = (xwdval) header_size + 4;
    header.file_version = (xwdval) XWD_FILE_VERSION;
    header.pixmap_format = (xwdval) ZPixmap;
    header.pixmap_depth = (xwdval) image->depth;
    header.pixmap_width = (xwdval) image->width;
    header.pixmap_height = (xwdval) image->height;
    header.xoffset = (xwdval) image->xoffset;
    header.byte_order = (xwdval) image->byte_order;
    header.bitmap_unit = (xwdval) image->bitmap_unit;
    header.bitmap_bit_order = (xwdval) image->bitmap_bit_order;
    header.bitmap_pad = (xwdval) image->bitmap_pad;
    header.bits_per_pixel = (xwdval) image->bits_per_pixel;
    header.bytes_per_line = (xwdval) image->bytes_per_line;
    header.visual_class = (xwdval) win_info->visual->class;
    header.red_mask = (xwdval) win_info->visual->red_mask;
    header.green_mask = (xwdval) win_info->visual->green_mask;
    header.blue_mask = (xwdval) win_info->visual->blue_mask;
    header.bits_per_rgb = (xwdval) win_info->visual->bits_per_rgb;
    header.colormap_entries = (xwdval) win_info->visual->map_entries;
    header.ncolors = ncolors;
    header.window_width = (xwdval) win_info->width;
    header.window_height = (xwdval) win_info->height;
    header.window_x = 0;
    header.window_y = 0;
    header.window_bdrwidth = (xwdval) win_info->border_width;

    if (*(char *) &swaptest) {
      _swaplong((char *) &header, sizeof(header));
      for (i = 0; i < ncolors; i++) {
        _swaplong((char *) &colors[i].pixel, sizeof(long));
        _swapshort((char *) &colors[i].red, 3 * sizeof(short));
      }
    }

    any_write(output, (char *)&header, sizeof(header));
    any_write(output, "xwd\0", 4);

    /*
     * Write out the color maps, if any
     */
    any_write(output, (char *) colors, sizeof(XColor)*ncolors);

    /*
     *    This copying of the bit stream (data) to a file is to be replaced
     *  by an Xlib call which hasn't been written yet.  It is not clear
     *  what other functions of xwd will be taken over by this (as yet)
     *  non-existant X function.
     */
    any_write(output, image->data, (int) buffer_size);

    /*
     * close the file
     */
    if (output)
      any_close (output);

    /*
     * free the color buffer.
     */
    if(ncolors > 0) free(colors);

    /*
     * Free image
     */
    XDestroyImage(image);
}

void
scan_ximage_line(char *scanp, char *ximp, int width, int depth)
{
int i;
unsigned char u, v;

  if (depth>0) {
    if (depth==8)
	memcpy(scanp, ximp, width);
    else
    if (depth==15)
        for (i=0; i<width; i++) {
	    u = *ximp++;
            v = (*ximp++)&127;
	    *scanp++ = ((v>>2)*255)/31;
	    *scanp++ = ((((v&3)<<3) | (u>>5))*255)/31;
	    *scanp++ = ((u&31)*255)/31;
        }
    else
    if (depth==16)
        for (i=0; i<width; i++) {
	    u = *ximp++;
            v = *ximp++;
	    *scanp++ = ((v>>3)*255)/31;
	    *scanp++ = ((((v&7)<<3) | (u>>5))*255)/63;
	    *scanp++ = ((u&31)*255)/31;
        }
    else
    if (depth==24)
        for (i=0; i<width; i++) {
	    scanp[2] = *ximp++;
	    scanp[1] = *ximp++;
	    scanp[0] = *ximp++;
            scanp+=3;
        }
    else
    if (depth==32)
        for (i=0; i<width; i++) {
	    scanp[2] = *ximp++;
	    scanp[1] = *ximp++;
	    scanp[0] = *ximp++;
	    ximp++;
            scanp+=3;
        }
  } else {
    if (depth==-8)
	memcpy(scanp, ximp, width);
    else
    if (depth==-15)
        for (i=0; i<width; i++) {
	    v = *ximp++;
            u = (*ximp++)&127;
	    *scanp++ = ((v>>2)*255)/31;
	    *scanp++ = ((((v&3)<<3) | (u>>5))*255)/31;
	    *scanp++ = ((u&31)*255)/31;
        }
    else
    if (depth==-16)
        for (i=0; i<width; i++) {
	    v = *ximp++;
            u = *ximp++;
	    *scanp++ = ((v>>3)*255)/31;
	    *scanp++ = ((((v&7)<<3) | (u>>5))*255)/63;
	    *scanp++ = ((u&31)*255)/31;
        }
    else
    if (depth==-24)
        memcpy(scanp, ximp, 3*width);
    else
    if (depth==-32)
        for (i=0; i<width; i++) {
	    ximp++;
	    *scanp++ = *ximp++;
	    *scanp++ = *ximp++;
	    *scanp++ = *ximp++;
        }
  }
}

void	
save_as_ppm_file (win_info, pixmap, width, height, depth)
     XWindowAttributes *win_info;
     Pixmap pixmap;
     unsigned int width, height, depth;
{
    int           i, j, color_depth, ncolors, raw;
    /*    int           bit_depth=0, color_type=0, num_palette, rowbytes; */
    unsigned char * palette = NULL;
    unsigned char *scanp, *ximp;
    char          outstr[60];
    Visual        *visual;
    XColor *colors;
    XImage *image;
    gzFile *output;

    image = XGetImage (display, pixmap, 0,0, width,height, AllPlanes, ZPixmap);
    color_depth = image->bits_per_pixel;
    visual = DefaultVisual(display, screen);
    if (color_depth == 16 && visual->green_mask==992) color_depth = 15;
    if (ImageByteOrder(display) == MSBFirst) color_depth = -color_depth;

    if (!image) {
        fprintf (stderr, "xsnap:  %s  %dx%d+%d+%d\n",
		 _("unable to get image at"), width, height, 0, 0);
	return;
    }

    /*
     * Find the output file
     */

    if (*root_name == '\0')
        output = NULL;
    else {
        check_filename();
        output = any_wopen(output_name);
        if (!output) return;
    }

    if (image->depth == 8) {
        unsigned char *ptr;
        win_info->colormap = private_cmap;
        ncolors = Get_XColors(win_info, &colors);
        palette = ptr = (unsigned char *)malloc(3*ncolors);
        if (!palette) {
	  fprintf (stderr, err_malloc, _("palette"));
	  if (output)
              any_close(output);
           return;
	}
        for (i=0;i<ncolors;i++) {
	   *ptr++ = colors[i].red>>8;
	   *ptr++ = colors[i].green>>8;
	   *ptr++ = colors[i].blue>>8;
	}
    }
    scanp = (unsigned char *)malloc(3*image->width);
    if (!scanp) {
        fprintf (stderr, err_malloc, _("scanline"));
        free(palette);
	if (output)
            any_close(output);
        return;
    }

    raw = (output_fmt == PPM_F);
    sprintf(outstr, "P%d\n# Creator xsnap %s.%s\n%d %d\n255\n",
            3+3*raw, VERSION, PATCHLEVEL, image->width, image->height);
    any_write(output, outstr, strlen(outstr));

    for (j = 0;  j < image->height;  ++j) {
        ximp = (unsigned char *) image->data + j * image->bytes_per_line;
	if (image->depth==8) {
	   i = image->width-1;
           while(i>=0) {
              memcpy(&scanp[3*i], &palette[3*ximp[i]], 3);
              --i;
	   }
 	} else
           scan_ximage_line((char *)scanp, (char *)ximp,
			    image->width, color_depth);
       for (i=0; i<image->width; i++) {
	    if (raw) {
	        sprintf(outstr, "%c%c%c%c",
			scanp[3*i], scanp[3*i+1], scanp[3*i+2], 0);
                any_write(output, outstr, 3);
            } else {
	        sprintf(outstr, "%03d %03d %03d %s",
		        (int)scanp[3*i], (int)scanp[3*i+1], (int)scanp[3*i+2],
                        ((i==image->width-1)?"\n":
	                 ((i%6==5)?"\n  ":" ")));
                any_write(output, outstr, strlen(outstr));
 	    }
	}
    }
    if (output)
       any_close(output);
    free(palette);
    free(scanp);
}

void
save_as_txt_file (win_info, pixmap, width, height, depth)
     XWindowAttributes *win_info;
     Pixmap pixmap;
     unsigned int width, height, depth;
{
int oldzip;
char cmd[256];
    oldzip = zip;
    zip = FALSE;
    output_fmt = PPM_F;
    save_as_ppm_file (win_info, pixmap, width, height, depth);
    zip = oldzip;
    output_fmt = TXT_F;
    sprintf(cmd, "xsnap-ocr %s %d &", output_name, zip);
    system(cmd);
}

#ifdef XPM
#include <X11/xpm.h>

void
save_as_xpm_file (win_info, pixmap, width, height, depth)
     XWindowAttributes *win_info;
     Pixmap pixmap;
     unsigned int width, height, depth;
{
    XpmAttributes attributes;

    attributes.colormap = private_cmap;  /* win_info->colormap; */
    attributes.width = width;
    attributes.height = height;
    attributes.valuemask = XpmColormap | XpmSize;
    if (*root_name != '\0')
      check_filename();
    XpmWriteFileFromPixmap(display,
      (*root_name)?output_name:NULL,pixmap,(Pixmap)0,&attributes);
}
#endif

#ifdef PNG

typedef struct _jmpbuf_wrapper {
  jmp_buf jmpbuf;
} jmpbuf_wrapper;

static jmpbuf_wrapper jmpbuf_struct;
static char *png_err_msg = N_("Fatal libpng error\n");

void
png_error_handler (png_structp png_ptr, png_const_charp msg)
{
    jmpbuf_wrapper  *jmpbuf_ptr;

    fprintf(stderr, "%s", png_err_msg);
    fflush(stderr);

    jmpbuf_ptr = png_get_error_ptr(png_ptr);
    if (jmpbuf_ptr == NULL) {         /* we are completely hosed now */
        fprintf(stderr, "%s %s", _("Extremely"), png_err_msg);
        fflush(stderr);
        exit(1);
    }

    longjmp(jmpbuf_ptr->jmpbuf, 1);
}

void
save_as_png_file (win_info, pixmap, width, height, depth)
     XWindowAttributes *win_info;
     Pixmap pixmap;
     unsigned int width, height, depth;
{
    int           i, j, color_depth, ncolors;
    int           bit_depth=0, color_type=0, rowbytes;
    png_colorp    palette = NULL;
    png_structp   png_ptr;
    png_infop     info_ptr;
    png_bytep     *row_pointers, png_data;
    char          *scanp, *ximp;
    Visual        *visual;
    XColor *colors;
    XImage *image;
    FILE *output;

    image = XGetImage (display, pixmap, 0,0, width,height, AllPlanes, ZPixmap);
    color_depth = image->bits_per_pixel;
    visual = DefaultVisual(display, screen);
    if (color_depth == 16 && visual->green_mask==992) color_depth = 15;
    if (ImageByteOrder(display) == MSBFirst) color_depth = -color_depth;

    if (!image) {
	fprintf (stderr, "xsnap:  unable to get image at %dx%d+%d+%d\n",
		 width, height, 0, 0);
	return;
    }

    /*
     * Find the output file
     */

    if (*root_name == '\0')
      output = stdout;
    else
      {
      check_filename();
      output = fopen (output_name, "wb");
      }
    if (!output) return;

    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
      &jmpbuf_struct, png_error_handler, NULL);
    if (!png_ptr) {
        fprintf(stderr, "%s", png_err_msg);
        fclose(output);
        return;
    }

    info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) {
        fprintf(stderr, "%s", png_err_msg);
        png_destroy_read_struct(&png_ptr, NULL, NULL);
        fclose(output);
        return;
    }

    if (setjmp(jmpbuf_struct.jmpbuf)) {
        png_destroy_write_struct(&png_ptr, &info_ptr);
        fclose(output);
        return;
    }

    png_init_io(png_ptr, output);

    color_type = -1;  /* quiet compiler warning (all cases actually covered) */
    bit_depth = -1;

    if (image->depth == 1) {
        color_type = PNG_COLOR_TYPE_GRAY;
        bit_depth = 1;
    } else if (image->depth == 8) {
        char *ptr;
        color_type = PNG_COLOR_TYPE_PALETTE;
        bit_depth = 8;
        win_info->colormap = private_cmap;
        ncolors = Get_XColors(win_info, &colors);
        ptr = malloc(3*ncolors);
        palette = (png_colorp)ptr;
        if (!palette) {
	   fprintf(stderr, err_malloc, "palette");
           png_destroy_write_struct(&png_ptr, &info_ptr);
           fclose(output);
           return;
	}
        for (i=0;i<ncolors;i++) {
	   *ptr++ = colors[i].red>>8;
	   *ptr++ = colors[i].green>>8;
	   *ptr++ = colors[i].blue>>8;
	}
        png_set_PLTE(png_ptr, info_ptr, palette, ncolors);
    } else if (image->depth > 8) {
        color_type = PNG_COLOR_TYPE_RGB;
        bit_depth = 8;
    }

    png_set_IHDR(png_ptr, info_ptr, image->width, image->height, bit_depth,
      color_type, 0, PNG_COMPRESSION_TYPE_DEFAULT,
      PNG_FILTER_TYPE_DEFAULT);

    /* store the modification time, at least */
    {
        png_time  modtime;
        png_convert_from_time_t(&modtime, time(NULL));
        png_set_tIME(png_ptr, info_ptr, &modtime);
    }

    /* only one text comment:  Software */
    {
        png_text textdata;
        char software_text[40];
        sprintf(software_text, "Xsnap %s.%s", VERSION, PATCHLEVEL);
        textdata.compression = PNG_TEXT_COMPRESSION_NONE;
        textdata.key = _("Software");
        textdata.text = software_text;
        png_set_text(png_ptr, info_ptr, &textdata, 1);
    }

    /* write all chunks up to (but not including) first IDAT */
    png_write_info(png_ptr, info_ptr);
    png_write_flush(png_ptr);

    /* any transformations must be set *after* png_write_info() is called */
    png_set_packing(png_ptr);   /* squish multiple pixels into each byte */
/*  png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);  */

    rowbytes = png_get_rowbytes(png_ptr, info_ptr);

    png_data = (png_bytep) malloc(image->height*rowbytes);
    row_pointers = (png_bytep *)malloc(image->height * sizeof(png_bytep));

    if (!png_data || !row_pointers) {
        fprintf(stderr, err_malloc, "png buffer");
        free(png_data);
        free(row_pointers);
        fprintf(stderr, "%s", png_err_msg);
        fflush(stderr);
        png_destroy_write_struct(&png_ptr, &info_ptr);
        fclose(output);
        return;
    }

    for (j = 0;  j < image->height;  ++j) {
        scanp = (char *) ( row_pointers[j] = (png_bytep)png_data + j*rowbytes );
        ximp = image->data + j * image->bytes_per_line;
        scan_ximage_line(scanp, ximp, image->width, color_depth);
    }

    png_write_image(png_ptr, row_pointers);
    free(row_pointers);
    free(png_data);
    free(palette);

    png_write_end(png_ptr, NULL);

    png_destroy_write_struct(&png_ptr, &info_ptr);
    if (output!=stdout)
        fclose(output);
}
#endif

#ifdef JPG

#include <setjmp.h>
#include <jpeglib.h>

struct error_mgr {
    struct jpeg_error_mgr pub;	/* "public" fields */
    jmp_buf setjmp_buffer;	/* for return to caller */
};

static struct error_mgr jerr;

typedef struct error_mgr *error_ptr;

void
error_exit(j_common_ptr cinfo)
{
    char buf[JMSG_LENGTH_MAX];
    /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
    error_ptr err = (error_ptr) cinfo->err;

    cinfo->err->format_message(cinfo, buf);
    fprintf(stderr, "%s\n", buf);

    /* Return control to the setjmp point */
    longjmp(err->setjmp_buffer, 1);
}

int
save_as_jpg_file (win_info, pixmap, width, height, depth)
     XWindowAttributes *win_info;
     Pixmap pixmap;
     unsigned int width, height, depth;
{
    int           i, j, color_depth, ncolors;
    unsigned char * palette = NULL;
    unsigned char *scanp=NULL, *ximp;
    Visual        *visual;
    XColor *colors;
    XImage *image;
    struct jpeg_compress_struct cinfo;
    FILE *output;
    JSAMPROW scanline[1];


    /*
	  char          outstr[60];
     */
    image = XGetImage (display, pixmap, 0,0, width,height, AllPlanes, ZPixmap);
    color_depth = image->bits_per_pixel;
    visual = DefaultVisual(display, screen);
    if (color_depth == 16 && visual->green_mask==992) color_depth = 15;
    if (ImageByteOrder(display) == MSBFirst) color_depth = -color_depth;

    if (!image) {
        fprintf (stderr, "xsnap:  unable to get image at %dx%d+%d+%d\n",
		 width, height, 0, 0);
	return 0;
    }

    /*
     * Find the output file
     */

    if (*root_name == '\0')
      output = stdout;
    else
      {
      check_filename();
      output = fopen (output_name, "wb");
      if (!output) return 0;
      }

    if (image->depth == 8) {
        unsigned char *ptr;
        win_info->colormap = private_cmap;
        ncolors = Get_XColors(win_info, &colors);
        palette = ptr = (unsigned char *)malloc(3*ncolors);
        if (!palette) {
	  fprintf (stderr, err_malloc, "palette");
	  if (output)
              any_close(output);
           return 0;
	}
        for (i=0;i<ncolors;i++) {
	   *ptr++ = colors[i].red>>8;
	   *ptr++ = colors[i].green>>8;
	   *ptr++ = colors[i].blue>>8;
	}
    }

    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = error_exit;

    if (setjmp(jerr.setjmp_buffer)) {
	/* If we get here, the JPEG code has signaled an error.
	 * We need to clean up the JPEG object, close the output file,
	 * and return.
	 */
	jpeg_destroy_compress(&cinfo);
	fclose(output);
	return 1;
    }
    jpeg_create_compress(&cinfo);
    jpeg_stdio_dest(&cinfo, output);

    cinfo.image_height = image->height;
    cinfo.image_width = image->width;
    cinfo.in_color_space = JCS_RGB;
    cinfo.input_components = 3;

    jpeg_set_defaults(&cinfo);
    jpeg_start_compress(&cinfo, TRUE);

    scanp = (unsigned char *)malloc(3*image->width);
    if (!scanp) {
        fprintf (stderr, err_malloc, "scanline");
        free(palette);
	if (output)
            any_close(output);
        return 0;
    }

    scanline[0] = scanp;
    for (j = 0;  j < image->height;  ++j) {
        ximp = (unsigned char *) (image->data + j * image->bytes_per_line);
	if (image->depth==8) {
	   i = image->width-1;
           while(i>=0) {
              memcpy(&scanp[3*i], &palette[3*ximp[i]], 3);
              --i;
	   }
 	} else
           scan_ximage_line((char *)scanp, (char *)ximp, image->width, color_depth);
	jpeg_write_scanlines(&cinfo, scanline, (JDIMENSION) 1);
    }
    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress(&cinfo);

    if (output!=stdout)
       fclose(output);

    free(palette);
    free(scanp);
    if (jerr.pub.num_warnings > 0) {	/* XXX */
	longjmp(jerr.setjmp_buffer, 1);
    }

    return 0;
}
#endif

void
save_image_file(win_to_snap, pixmap, width, height, depth)
     Window win_to_snap;
     Pixmap pixmap;
     unsigned int width, height, depth;
{
        XWindowAttributes window_attributes;

        if (!pixmap) return;
        memset(&window_attributes, 0, sizeof(XWindowAttributes));

        XGetWindowAttributes (display, win_to_snap, &window_attributes);
	if (output_fmt==PNM_F || output_fmt==PPM_F)
	  {
	    save_as_ppm_file (&window_attributes,
			      pixmap, width, height, depth);
	  }

	if (output_fmt==PNG_F)
	  {
#ifdef PNG
	    save_as_png_file (&window_attributes,
                pixmap, width, height, depth);
#else
	    fprintf(stderr, _("This version of xsnap has be compiled without PNG support,\n"));
	    fprintf(stderr, _("recompile with the -DPNG flag set.\n"));
#endif
	  }

	if (output_fmt==JPG_F)
	  {
#ifdef JPG
	    save_as_jpg_file (&window_attributes,
                pixmap, width, height, depth);
#else
	    fprintf(stderr, _("This version of xsnap has be compiled without JPG support,\n"));
	    fprintf(stderr, _("recompile with the -DJPG flag set.\n"));
#endif
	  }

	if (output_fmt==XPM_F)
	  {
#ifdef XPM
	    save_as_xpm_file (&window_attributes,
                pixmap, width, height, depth);
#else
	    fprintf(stderr, _("This version of xsnap has be compiled without XPM support,\n"));
	    fprintf(stderr, _("recompile with the -DXPM flag set.\n"));
#endif
	  }

	if (output_fmt==XWD_F)
	{
	    save_as_xwd_file (&window_attributes,
                pixmap, width, height, depth);
	}

	if (output_fmt==TXT_F)
	{
	    save_as_txt_file (&window_attributes,
                pixmap, width, height, depth);
	}
}

void
exitXsnap(snapshot, gui_window, snap_pixmap, copy_gc)
     Window snapshot;
     Window gui_window;
     Pixmap snap_pixmap;
     GC copy_gc;
{
  if (snapshot)
     XDestroyWindow(display, snapshot);
  if (gui_window)
     XDestroyWindow(display, gui_window);
  if (snap_pixmap)
     XFreePixmap(display, snap_pixmap);
  XFreeGC(display, copy_gc);
  XCloseDisplay(display);
  exit(0);
}

#ifdef ENABLE_NLS

void
XftDrawImageString(Window win, GC gc, XftFont * font, XftColor color,
                   int x, int y, FcChar8 * s, int n)
{
   XGlyphInfo extents = {};
   static XftDraw *draw = NULL;
   static Pixmap pixmap = None;
   static Window win0 = None;
   static w0 = 0, h0 = 0;
   int i, j, d, w, h;

   if (!font || !n) return;
   w = font->max_advance_width;
   h = font->ascent + font->descent;

   if (h > h0 || win != win0) {
       if (draw) XftDrawDestroy(draw);
       if (pixmap) XFreePixmap(display, pixmap);
       draw = NULL;
   }
   win0 = win;
   w0 = w;
   h0 = h;

   if (!draw) {
       pixmap = XCreatePixmap(display, win, w, h,
                              DefaultDepth(display, screen));
       draw = XftDrawCreate(display, pixmap,
		 DefaultVisual(display, DefaultScreen(display)),
                 private_cmap);
   }

   i = 0;
   while (i<n) {
      j = i + 1;
      /* find length of UTF8 character */
      if ((s[i] & 0x80))
	 while (j < n && (s[j]&0xc0) == 0x80) j++;
      d = j - i;
      XftTextExtentsUtf8(display, font, (FcChar8*)(s+i), d,
                      (XGlyphInfo*)&extents);
      XSetForeground(display, gc, color_pixel[BG_COLOR]);
      XFillRectangle(display, pixmap, gc, 0, 0, extents.xOff, h);
      XftDrawStringUtf8(draw, &color, font, 0, font->ascent,
                        (FcChar8*)(s+i), d);
      XCopyArea(display, pixmap, win, gc, 0, 0, extents.xOff, h,
                x, y-font->ascent);
      x += extents.xOff;
      i = j;
   }
}

void
draw_dialog(Window win, XftFont * font, GC gc, int u, int v)
{
   static XftColor black;
   static XRenderColor xre_color = {0, 0, 0, 0};

   if (!xre_color.alpha) {
       xre_color.alpha = 0xffff;
       xre_color.red = 0;
       xre_color.green = 0;
       xre_color.blue = 0;
       XftColorAllocValue(display,
                          DefaultVisual(display, DefaultScreen(display)),
		          private_cmap, &xre_color, &black);
   }

#define DrawString(x,y,s,n) \
   XftDrawImageString(win, gc, font, black, (x), (y), (s), (n))

#define DrawImageString(x,y,s,n) \
   XftDrawImageString(win, gc, font, black, (x), (y), (s), (n))

#else

void
draw_dialog(Window win, XFontStruct * font, GC gc, int u, int v)
{
#define DrawString(x,y,s,n) \
   XDrawString(display, win, gc, (x), (y), (s), (n))
#define DrawImageString(x,y,s,n) \
   XDrawImageString(display, win, gc, (x), (y), (s), (n))

#endif
  int i, x, y, wshift;
  static int oldzip = -1, oldgrab = -1;
  static int i0 = 0;
  static char old_name[256] = "/";
  char msg[512];
  char *ptr1, *ptr2;

  for (i=1; i<=3; i++) {
     ptr1 = gettext(bindings_msg[i]) + 1;
     DrawString(10, 20*i, ptr1, strlen(ptr1));
  }
  for (i=0; i<11; i++) {
    if (i0 & (1<<i)) {
       XClearArea(display, win, 11, 83+30*i, 82, 2, FALSE);
       XClearArea(display, win, 11, 102+30*i, 82, 2, FALSE);
       XClearArea(display, win, 11, 83+30*i, 2, 20, FALSE);
       XClearArea(display, win, 92, 83+30*i, 2, 20, FALSE);
       i0 &= ~(1<<i);
     }
     XSetForeground(display, gc, color_pixel[(i==clicked)?BT1_COLOR:BT3_COLOR]);
     XDrawLine(display, win, gc, 9, 81+30*i, 9, 105+30*i);
     XDrawLine(display, win, gc, 9, 81+30*i, 95, 81+30*i);
     XSetForeground(display, gc, color_pixel[(i==clicked)?BT2_COLOR:BT4_COLOR]);
     XDrawLine(display, win, gc, 10, 82+30*i, 10, 104+30*i);
     XDrawLine(display, win, gc, 10, 82+30*i, 94, 82+30*i);
     XSetForeground(display, gc, color_pixel[(i==clicked)?BT4_COLOR:BT2_COLOR]);
     XDrawLine(display, win, gc, 10, 104+30*i, 94, 104+30*i);
     XDrawLine(display, win, gc, 94, 82+30*i, 94, 104+30*i);
     XSetForeground(display, gc, color_pixel[(i==clicked)?BT3_COLOR:BT1_COLOR]);
     XDrawLine(display, win, gc,  9, 105+30*i, 95, 105+30*i);
     XDrawLine(display, win, gc, 95, 81+30*i, 95, 105+30*i);
     XSetForeground(display, gc, color_pixel[FG_COLOR]);
     if (u>10 && u<94 && v>82+30*i && v<104+30*i) {
       i0 |= (1<<i);
       for(y=84+30*i; y<=102+30*i; y+=18)
	 for(x=12; x<=92; x++) if ((x+y)&1) XDrawPoint(display, win, gc, x, y);
       for(y=84+30*i; y<=102+30*i; y++)
	 for(x=12; x<=92; x+=80) if ((x+y)&1) XDrawPoint(display, win, gc, x, y);
     }
     ptr1 = gettext(bindings_msg[i+5])+1;
     ptr2 = index(ptr1, '\t');

     DrawString(40, 98+30*i, ptr1, ptr2-ptr1);
     ++ptr2;
     DrawString(140, 98+30*i, ptr2, strlen(ptr2));
  }

  ptr1 = (zip)?_("[active]"):_("[inactive]");

  wshift = (gui_width*82)/100;
  if (zip!=oldzip) {
     XClearArea(display, win, wshift, 110, 300, 40, FALSE);
     oldzip = zip;
     DrawImageString(wshift, 128, ptr1, strlen(ptr1));
  } else
     DrawString(wshift, 128, ptr1, strlen(ptr1));

  ptr1 = (grab_server)? _("[yes]"):_("[no]");
  if (grab_server!=oldgrab) {
     XClearArea(display, win, wshift, 140, 300, 40, FALSE);
     oldgrab = grab_server;
     DrawImageString(wshift, 158, ptr1, strlen(ptr1));
  } else
     DrawString(wshift, 158, ptr1, strlen(ptr1));

  if (strcmp(old_name,output_name) || (u==0 && v==0)) {
     XClearArea(display, win, 0, 420, gui_width, 30, FALSE);
     if (*output_name)
       sprintf(msg, "%s  %s", _("Image saved in"), output_name);
     else
       strcpy(msg, _("Image not saved"));
     DrawString(10, 438, msg, strlen(msg));
     strcpy(old_name,output_name);
  }

  XSync(display, (clicked<0));
}

void
set_snapshot_wmhints(snapshot, x, y, width, height)
Window snapshot;
int x, y;
unsigned int width, height;
{
XWMHints wmhints;
long wmhints_mask;
int x_return, y_return;           /* for XParseGeometry */

 unsigned int w, h;
unsigned int width_return, height_return;  /* ditto              */
int geom_mask;                    /* bitmask for XParseGeometry fields */

        wm_size_hints.flags = 0;
        x_return = x;
        y_return = y;
        width_return = width;
        height_return = height;
	w = 3*DisplayWidth(display,screen)/4;
        h = 3*DisplayHeight(display,screen)/4;
	if (width_return > w) width_return = w;
	if (height_return > h) height_return = h;

        if (window_geom_string)
           {
           geom_mask = XParseGeometry(window_geom_string,
                                           &x_return,
                                           &y_return,
                                           &width_return,
                                           &height_return);
           if (geom_mask & XValue)
              {
              if (geom_mask & XNegative)
                  wm_size_hints.x = DisplayWidth(display, screen)
                       - width_return + x_return - (border_width * 2);
              else
                  wm_size_hints.x = x_return;
              }
           if (geom_mask & YValue)
              {
              if (geom_mask & YNegative)
                  wm_size_hints.y = DisplayHeight(display, screen)
                          - height_return + y_return  - (border_width * 2);
              else
                  wm_size_hints.y = y_return;
              }
           }

        if ((geom_mask & XValue) || (geom_mask & YValue))
           wm_size_hints.flags |= USPosition;

        if ((geom_mask & WidthValue) || (geom_mask & HeightValue))
           wm_size_hints.flags |= USSize;

        wm_size_hints.width = width_return;
        wm_size_hints.height = height_return;

	wm_size_hints.flags |=  PMaxSize;
	wm_size_hints.max_width = width;
	wm_size_hints.max_height = height;

        wm_size_hints.flags |= PPosition;
        wm_size_hints.x = x_return;
        wm_size_hints.y = y_return;

        XSetNormalHints(display, snapshot, &wm_size_hints);
        XResizeWindow(display, snapshot, width_return, height_return);

        wmhints_mask = 0;

        /* set the input hint to true explicitly for OpenWindows */
        wmhints_mask |= InputHint;
        wmhints.input = True;

        if (set_iconic)
          {
            wmhints_mask |= StateHint;
            wmhints.initial_state = IconicState;
          }
        x_return = y_return = 0;

        /* Icon geometry is sorta broken, since we don't supply an icon, and
         * there isn't any way I know of to get the geometry  of the icon that
         * the window manager will provide.   So, we'll pretend that our icon
         * is 32x32 (ugh).
         */

        if (icon_geom_string)
          {
            geom_mask = XParseGeometry(icon_geom_string, &x_return,
                                       &y_return, &width_return,
                                       &height_return);
            if (geom_mask & XValue)
              {
                if (geom_mask & XNegative)
                  wmhints.icon_x = DisplayWidth(display,
                                                screen)
                    - 32 + x_return - (border_width * 2);
                else
                  wmhints.icon_x = x_return;
              }
            if (geom_mask & YValue)
              {
                if (geom_mask & YNegative)
                  wmhints.icon_y =  DisplayHeight(display,
                                                  screen)
                    - 32 + y_return  - (border_width * 2);

                else
                  wmhints.icon_y = y_return;
              }
            wmhints_mask |= IconPositionHint;
          }

        if (wmhints_mask)
          {
            wmhints.flags = wmhints_mask;
            XSetWMHints(display, snapshot, &wmhints);
          }
}

void
create_snapshot_window(win, x, y, w, h, pix)
int x, y;
unsigned int w, h;
Window *win;
Pixmap *pix;
{
        if (!*win) {
	   *win = XCreateSimpleWindow(display,
			   XRootWindow(display, screen),
			   x, y, w, h, border_width,
			   color_pixel[BD_COLOR],
			   color_pixel[BG_COLOR]);

	   /* sollicit expose events */
	   XSelectInput(display, *win, StructureNotifyMask |
                ExposureMask | KeyPressMask | PointerMotionMask |
                ButtonPressMask | ButtonReleaseMask);

           /* Set colormap and protocols */
	   XSetWindowColormap(display, *win, private_cmap);
           XSetWMProtocols(display, *win, &wm_delete_window, True);

	   /* give it a different cursor */
	   XDefineCursor(display, *win, snap_cursor);

	   sprintf(win_name, "%s %d x %d", win_name0, w, h);
	   XStoreName(display, *win, win_name);
	}
	else
	   XResizeWindow(display, *win, w, h);


	if (*pix)
           XSetWindowBackgroundPixmap(display, *win, *pix);
	sprintf(win_name, "%s %d x %d", win_name0, w, h);
	XStoreName(display, *win, win_name);
	set_snapshot_wmhints(*win, x, y, w, h);
        XMapRaised(display, *win);
}

void
get_snapshot(window_to_snap, copy_gc, geom_mask, snapshot, snap_pixmap,
reg_x, reg_y, reg_width, reg_height, reg_depth)
Window window_to_snap;
GC copy_gc;
int *geom_mask;
Window *snapshot;
Pixmap *snap_pixmap;
int *reg_x, *reg_y;
unsigned int *reg_width, *reg_height, *reg_depth;
{
	if (*snap_pixmap) {
	   if (*snapshot)
	      XDestroyWindow(display, *snapshot);
	   *snapshot = None;
           XFreePixmap(display, *snap_pixmap);
	   *snap_pixmap = None;
           XFlush(display);
           usleep(delay);
	}

	if (grab_server)
	{
		XGrabServer(display);
		XSync(display, 0);
	}

	if (region_geom_string)
	{
		*geom_mask = XParseGeometry(region_geom_string, reg_x, reg_y,
		    reg_width, reg_height);
		if ((*geom_mask & XValue) && (*geom_mask & YValue) &&
		    (*geom_mask & WidthValue) && (*geom_mask & HeightValue))
		/* must specify complete geometry for region */
		{
			if (*geom_mask & XNegative)
				*reg_x += DisplayWidth(display, screen) - *reg_width;
			if (*geom_mask & YNegative)
				*reg_y += DisplayHeight(display, screen) - *reg_height;
			*snap_pixmap = get_pixmap_region(
			    window_to_snap, copy_gc,
			    reg_x, reg_y, reg_width,
			    reg_height, reg_depth, FALSE);
		}
		else {
		    if (grab_server) {
			XUngrabServer(display);
			XSync(display, 0);
		    }
		    usage(1);
		}
	}
	else
		*snap_pixmap = get_pixmap_region(window_to_snap, copy_gc,
						reg_x, reg_y,
						reg_width, reg_height,
						reg_depth, TRUE);

	/* ungrab the server */

	if (grab_server) XUngrabServer(display);

        if (op_mode == 0) return;

        create_snapshot_window(snapshot,
                               *reg_x, *reg_y,
                               *reg_width, *reg_height, snap_pixmap);

	start_move = 0;
}

Window
create_gui_window(x, y)
int x, y;
{
Window  win;
  	win = XCreateSimpleWindow(display,
	        XRootWindow(display, screen),
		x, y, gui_width, 452, border_width,
	        color_pixel[BD_COLOR],
		color_pixel[BG_COLOR]);
	XSetWindowColormap(display, win, private_cmap);
  	XStoreName(display, win, "xsnap / menu");
        XSetWMProtocols(display, win, &wm_delete_window, True);

        /* sollicit expose events */
	XSelectInput(display, win,
                ExposureMask | KeyPressMask | PointerMotionMask |
                ButtonPressMask | ButtonReleaseMask);

	return win;
}

int
main(argc, argv)
int argc;
char **argv;
{
	XEvent ev;
	GC copy_gc;
	unsigned long copy_gc_valuemask;
	XGCValues copy_gc_values;
	XColor color;
        KeySym keysym = 0;
	Colormap cmap;
#ifdef ENABLE_NLS
        XftFont *font;
#else
        XFontStruct * font;
#endif

	Window window_to_snap;            /* always root window, now */
	Window childW;                    /* dummy window variable */
	Window snapshot = None;
   	Window gui_window = None;
	Pixmap  snap_pixmap = None;
	XWindowAttributes xwa;
        int geom_mask;
	int i, i0=-10, reg_x=0, reg_y=0;
	int ix=0, iy=0, dx=0, dy=0, dx0 = 0, dy0 = 0; /* displacement */
        int delta_w, delta_h; /* max displacement */
	unsigned int reg_width, reg_height, reg_depth;
	int done;
	char buffer [4];                     /* key press buffer for XLookupString  */
	int string_length;                       /* length of returned string */

#ifdef ENABLE_NLS
        setlocale (LC_ALL, "");
        /*  This is not for gettext but
            all i18n software should have
            this line.  */

        bindtextdomain (PACKAGE, LOCALEDIR);
        textdomain (PACKAGE);
#endif

	process_args(argc, argv);

	if ((display = XOpenDisplay(display_string)) == NULL)
	  {
	    fprintf(stderr, "%s   %s\n", _("Cannot open display:"),
		    XDisplayName(display_string));
	    exit(1);
	  }
	XSetErrorHandler(my_X_error_handler);

	screen = DefaultScreen(display);
	window_to_snap = XRootWindow(display, screen);
#ifdef ENABLE_NLS
        XftInitFtLibrary();
        font = XftFontOpenName(display, DefaultScreen(display), font_name);
        if (font == NULL) {
	   fprintf(stderr, "%s %s\n",
		   _("xsnap error : impossible to load font"), font_name);
           exit(-1);
	}
	if (gui_width==0) {
            XGlyphInfo extents = {};
            XftTextExtents8(display, font, (FcChar8*)"n", 1,
                         (XGlyphInfo*)&extents);
            gui_width = extents.xOff*80;
	}
#else
	font = XLoadQueryFont(display, font_name);
        if (font == NULL) {
	   fprintf(stderr, "%s %s\n",
		   _("xsnap error : impossible to load font"), font_name);
           exit(-1);
	}
	if (gui_width==0) gui_width = XTextWidth(font, "n", 1)*80;
#endif

        /* WM protocols */
        wm_protocols = XInternAtom(display, "WM_PROTOCOLS", False);
        wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);

	/* process colors */

        if (private_cmap && DefaultDepth(display, screen)<15)
            cmap = private_cmap;
        else
	    cmap = DefaultColormap(display, screen);

        for (i=0; i<NUM_COLORS; i++) {
            if ((color_string[i]) &&
                (XParseColor(display, cmap, color_string[i], &color)) &&
	        (XAllocColor(display, cmap, &color)))
	        color_pixel[i] = color.pixel;
	    else
	        color_pixel[i] = (i==BG_COLOR)?
	            WhitePixel(display, screen):BlackPixel(display, screen);
	}

	/* make copy GC */
	copy_gc_values.subwindow_mode = IncludeInferiors;
	copy_gc_values.background = color_pixel[BG_COLOR];
	copy_gc_values.foreground = color_pixel[FG_COLOR];
#ifdef ENABLE_NLS
	copy_gc_valuemask = GCSubwindowMode | GCForeground | GCBackground;
#else
	copy_gc_valuemask = GCSubwindowMode | GCFont |
                            GCForeground | GCBackground;
	copy_gc_values.font = font->fid;
#endif
	copy_gc = XCreateGC(display, window_to_snap, copy_gc_valuemask, &copy_gc_values);

        /* Create specific cursor */
	snap_cursor = XCreateFontCursor(display, XC_diamond_cross);

        if (op_mode <= 1)
           get_snapshot(window_to_snap, copy_gc, &geom_mask,
                     &snapshot, &snap_pixmap,
                     &reg_x, &reg_y, &reg_width, &reg_height, &reg_depth);

       /* map it */

	if (op_mode ==1) {
	    if (set_iconic)
               XIconifyWindow(display, snapshot, screen);
	    else
               XMapRaised(display, snapshot);
	    XFlush (display);
	}

        if (op_mode == 0) {
	    save_image_file(window_to_snap,
		 	   snap_pixmap, reg_width, reg_height, reg_depth);
	}

	if (op_mode == 0)
	    exitXsnap (NULL, NULL, snap_pixmap, copy_gc);

	if (!root_name)
		strcpy(output_name, "(stdout)");

	if (op_mode == 2) {
            gui_window = create_gui_window(reg_x, reg_y);
            XMapRaised(display, gui_window);
	    XFlush(display);
	    usleep(delay);
	    draw_dialog(gui_window, font, copy_gc, 0, 0);
	}

	/* process events */
	done = FALSE;

	while (!done)
	{
		XNextEvent(display, &ev);
		switch (ev.type)
		{
		case ClientMessage:
		        if (ev.xclient.message_type == wm_protocols &&
                            ev.xclient.format == 32 &&
                            ev.xclient.data.l[0] == wm_delete_window) {
			    if (ev.xexpose.window == gui_window) {
			        XDestroyWindow(display, gui_window);
				gui_window = None;
			    }
			    if (ev.xexpose.window == snapshot) {
			        XDestroyWindow(display, snapshot);
				snapshot = None;
			    }
                            if (gui_window == None && snapshot == None)
			        exitXsnap(snapshot,
                                     gui_window, snap_pixmap, copy_gc);
			}
                        break;
		case ConfigureNotify:
		        if (ev.xexpose.window == snapshot) {
			   dx0 = dy0 = -1;
			   goto reconfigure;
			}
		        break;
		case Expose:
		case MappingNotify:
			XRefreshKeyboardMapping((XMappingEvent *)&ev);
		        if (ev.xexpose.window == gui_window) {
			    draw_dialog(gui_window, font, copy_gc, 0, 0);
		        }
		        if (ev.xexpose.window == snapshot) {
			redraw:
	                    if (snap_pixmap)
                               XCopyArea(display,
                                   snap_pixmap, snapshot, copy_gc, dx, dy,
		                   reg_width, reg_height, 0, 0);
			}
			break;
		case ButtonPress:
		case ButtonRelease:
		case MotionNotify:
		        if (ev.xexpose.window == snapshot) {
			    if (ev.xbutton.button==1 &&
			        ev.type==ButtonPress) {
			       ix = ev.xbutton.x + dx;
			       iy = ev.xbutton.y + dy;
                               start_move = 1;
			    }
			    if (start_move && XPending(display)%10==0) {
			       dx = ix - ev.xbutton.x;
			       dy = iy - ev.xbutton.y;
			    reconfigure:
			       XGetWindowAttributes(display, snapshot, &xwa);
			       delta_w = reg_width - xwa.width;
			       delta_h = reg_height - xwa.height;
			       if (delta_w<0) delta_w = 0;
			       if (delta_h<0) delta_h = 0;
			       if (dx<0) dx=0;
			       if (dy<0) dy=0;
			       if (dx>delta_w) dx=delta_w;
			       if (dy>delta_h) dy=delta_h;
	                       if (snap_pixmap && (dx!=dx0 || dy != dy0))
                                  XCopyArea(display,
                                     snap_pixmap, snapshot, copy_gc, dx, dy,
		                     reg_width, reg_height, 0, 0);
			       if (ev.type==ConfigureNotify)
                                  XSync(display, 1);
                               dx0 = dx;
			       dy0 = dy;
			    }
			    if (ev.xbutton.button==1 &&
			        ev.type==ButtonRelease) {
                               start_move = 0;
			    }
			}
		        if (ev.xexpose.window == gui_window) {
			    if (ev.type==MotionNotify) {
			       draw_dialog(gui_window, font, copy_gc,
                                        ev.xbutton.x, ev.xbutton.y);
			       continue;
			    }
			    if (ev.xbutton.button==3 &&
                               ev.type==ButtonRelease) {
			    raise_snapshot:
			       if (snap_pixmap ) {
				  if (snapshot && ev.type==ButtonRelease) {
				     XDestroyWindow(display, snapshot);
				     snapshot = None;
				  }
                                  XTranslateCoordinates(display, gui_window,
			             XRootWindow(display, screen),
                                     0, 0, &reg_x, &reg_y,
                                     &childW);
			          reg_x += ev.xbutton.x;
			          reg_y += ev.xbutton.y;
				  if (reg_x>=40) reg_x -= 40;
				  if (reg_y>=40) reg_y -= 40;
			          create_snapshot_window(&snapshot,
                                     reg_x, reg_y, reg_width, reg_height,
                                     &snap_pixmap);
			       }
			       continue;
			    }
			    i0 = -1;
			    for (i=0; i<11; i++)
                               if (ev.xbutton.x>10 && ev.xbutton.x<94 &&
                                   ev.xbutton.y>82+30*i &&
                                   ev.xbutton.y<104+30*i) {
				   i0 = i;
				   break;
			       }
			    if (ev.type==ButtonRelease &&
                                (i0==-1 || i0!=clicked)) {
			        clicked = -1;
#ifndef ENABLE_NLS
			        draw_dialog(gui_window,
                                            font, copy_gc,
					    ev.xbutton.x, ev.xbutton.y);
#endif
                                break;
			    }
			    clicked = (ev.type==ButtonPress)? i0:-1;
			    draw_dialog(gui_window,
                                        font, copy_gc,
					ev.xbutton.x, ev.xbutton.y);
                            if (ev.type==ButtonPress) break;
			    clicked = -1;
			    if (i0== 0) {
			        exitXsnap(snapshot,
                                    gui_window, snap_pixmap, copy_gc);
			        done = TRUE;
			    }
			    if (i0== 1) {
			        zip = 1-zip;
			        draw_dialog(gui_window,
                                            font, copy_gc, 50, 126);
				break;
			    }
			    if (i0== 2) {
			        grab_server = 1-grab_server;
			        draw_dialog(gui_window,
                                            font, copy_gc, 50, 126);
				break;
			    }
			    i0 -= 3;
			    if (i0>=JPG_F && i0<NUM_F) {
			        output_fmt = i0;
				i0 += 2;
			      save_file:
	                        save_image_file(window_to_snap, snap_pixmap,
                                     reg_width, reg_height, reg_depth);
			        draw_dialog(gui_window,
                                        font, copy_gc,
                                        50, 130);
                                break;
			    }
			    if (i0 == NUM_F) {
	                        XDestroyWindow(display, gui_window);
				gui_window = None;
				XFlush(display);
				usleep(delay);
                                get_snapshot(window_to_snap, copy_gc,
                                      &geom_mask, &snapshot, &snap_pixmap,
                                      &reg_x, &reg_y,
                                      &reg_width, &reg_height, &reg_depth);
			    }
			    break;
		        }
			clicked = -1;
			if (ev.xbutton.button==3 &&
                            ev.type==ButtonRelease) {
                            XTranslateCoordinates(display, snapshot,
			       XRootWindow(display, screen),
                               0, 0, &reg_x, &reg_y,
                               &childW);
                            wm_size_hints.flags = USPosition | PPosition;
	                    wm_size_hints.x = reg_x+ev.xbutton.x;
 	                    wm_size_hints.y = reg_y+ev.xbutton.y;
			    if (wm_size_hints.x>=40) wm_size_hints.x -= 40;
			    if (wm_size_hints.y>=40) wm_size_hints.y -= 40;
		     raise_dialog:
			    if (gui_window && ev.type==ButtonRelease) {
			       XDestroyWindow(display, gui_window);
			       gui_window = None;
			    }
			    if (gui_window == None) {
			       gui_window = create_gui_window(
					    wm_size_hints.x,
                                            wm_size_hints.y);
                               XSetNormalHints(display, gui_window,
                                            &wm_size_hints);
			    }
	                    XMapRaised(display, gui_window);
			}
			break;
		case KeyPress:
			string_length = XLookupString((XKeyEvent *)&ev,
                            buffer, sizeof(buffer), &keysym, NULL);
                        buffer[0] = tolower(keysym);
			if (buffer[0] == 'q' || buffer[0] == '\033')
			    {
			    /*  clean up  */
			    exitXsnap(snapshot,
                               gui_window, snap_pixmap, copy_gc, 0);
			    done = TRUE;
			    }
                        else
			if (buffer[0] == '!')
			    {
			    if (snapshot)
                               XDestroyWindow(display, snapshot);
			    snapshot = None;
	                    if (gui_window)
                               XDestroyWindow(display, gui_window);
			    gui_window = None;
			    XFlush(display);
                            usleep(delay);
                            get_snapshot(window_to_snap, copy_gc, &geom_mask,
                                      &snapshot, &snap_pixmap,
                                      &reg_x, &reg_y,
                                      &reg_width, &reg_height, &reg_depth);
			    }
                        else
			if (buffer[0] == 'j')
			    {
			    output_fmt = JPG_F;
			    i0 = output_fmt+2;
			    goto save_file;
			    }
                        else
			if (buffer[0] == 'n')
			    {
			    output_fmt = PNM_F;
			    i0 = output_fmt+2;
			    goto save_file;
			    }
                        else
			if (buffer[0] == 'p')
			    {
			    output_fmt = PPM_F;
			    i0 = output_fmt+2;
			    goto save_file;
			    }
                        else
			if (buffer[0] == 'g')
			    {
			    output_fmt = PNG_F;
			    i0 = output_fmt+2;
			    goto save_file;
			    }
                        else
			if (buffer[0] == 'x')
			    {
			    output_fmt = XPM_F;
			    i0 = output_fmt+2;
			    goto save_file;
			    }
                        else
			if (buffer[0] == 'w')
			    {
			    output_fmt = XWD_F;
			    i0 = output_fmt+2;
			    goto save_file;
			    }
                        else
			if (buffer[0] == 't')
			    {
			    output_fmt = TXT_F;
			    i0 = output_fmt+2;
			    goto save_file;
			    }
                        else
			if (buffer[0] == 'b')
			    {
			    grab_server = 1-grab_server;
			    draw_dialog(gui_window,
                                        font, copy_gc,
                                        50, 130);
			    }
                        else
			if (buffer[0] == 'z')
			    {
			    zip = 1-zip;
			    draw_dialog(gui_window,
                                        font, copy_gc,
                                        50, 130);
			    }
			else
			if (buffer[0] == ' ')
			    {
			       if (ev.xexpose.window == gui_window)
				  goto raise_snapshot;
			       if (ev.xexpose.window == snapshot)
				  goto raise_dialog;
			    }
                        else {
			    usage(0);
			    if (!isalpha(keysym) && !isspace(keysym))
			       break;
			}
			break;
		      }
	}
	return 0;
}

