ftp.nice.ch/pub/next/unix/graphics/urt.3.0.s.tar.gz#/urt.3.0.s/get/getx11/getx11.c

This is getx11.c in view mode; [Download] [Up]

/*
 * This software is copyrighted as noted below.  It may be freely copied,
 * modified, and redistributed, provided that the copyright notices are 
 * preserved on all copies.
 * 
 * There is no warranty or other guarantee of fitness for this software,
 * it is provided solely "as is".  Bug reports or fixes may be sent
 * to the author, who may or may not act on them as he desires.
 *
 * You may not include this software in a program or other software product
 * without supplying the source, or without informing the end-user that the 
 * source is available for no extra charge.
 *
 * If you modify this software, you should include a notice giving the
 * name of the person performing the modification, the date of modification,
 * and the reason for such modification.
 */

/* 
 * getx11.c - Put RLE images on X display.
 * 
 * Author:	Spencer W. Thomas  (x10)
 * 		Computer Science Dept.
 * 		University of Utah
 * Date:	Thu Feb 20 1986
 * Copyright (c) 1986, University of Utah
 * 
 * Modified:	Andrew F. Vesper (x 11)
 *		High Performance Workstations
 *		Digital Equipment Corp
 * Date:	Fri, Aug 7, 1987
 *		Thu, Jan 14, 1988
 * Copyright (c) 1987,1988, Digital Equipment Corporation
 * 
 * Modified:	Martin R. Friedmann (better X11, flipbook, mag, pix_info)
 * 		Dept of Electrical Engineering and Computer Science
 *		University of Michigan
 * Date:	Tue, Nov 14, 1989
 * Copyright (c) 1989, University of Michigan
 */

#include "getx11.h"

/* Make Sys V macros map to BSD macros */
#ifndef _tolower
#define _tolower tolower
#define _toupper toupper
#endif


/* must be able to fit 3 * log2 (MAXIMUM_LEVELS) bits in a 32 bit word */

#define DEFAULT_LEVELS		240
#define MAXIMUM_LEVELS		1024

#define icon_name "getx11"

/* number of scan lines to be drawn in incremental mode */
#define LINES_DRAWN		10

static int get_pic ();
static void update_pic();
extern void init_color(), find_appropriate_visual(), determine_icon_size();
extern void create_windows(), set_watch_cursor(), map_rgb_to_bw();
extern void map_rgb_to_rgb(), set_circle_cursor(), check_pixmap_allocation();
extern void set_timer(), wait_timer(), set_left_ptr_cursor();
extern void MapPixWindow(), DrawPixWindow(), UnmapPixWindow();
extern void DrawSpeedWindow();

/* 
 * Global variables
 */

double		display_gamma = 2.5;	/* default gamma for display */
int		iflag = 0;
int		red_shift;
int		green_shift;
int		blue_shift;
Pixel		red_mask;
Pixel		green_mask;
Pixel		blue_mask;
Pixel		pixel_base;
Pixel		black_pixel;
Pixel		white_pixel;

char 		*progname = NULL;
Display		*dpy = NULL;
Window		root_window = NULL;
int		screen = 0;

Boolean		debug_flag = False;	/* set if debug mode -D */
Boolean		verbose_flag = False;	/* set if verbose mode -v */
int 		jump_flag = 0, stingy_flag = 0;
int             specified_levels = 0;
int		no_color_ref_counts = 0;

static struct {
    CONST_DECL char	*string;
    int	type;
}  visual_type_table[] = {
    
{ "staticgray", StaticGray},
{ "grayscale", GrayScale},
{ "staticgrey", StaticGray},
{ "greyscale", GrayScale},
{ "staticcolor", StaticColor},
{ "pseudocolor", PseudoColor},
{ "truecolor", TrueColor},
{ "directcolor", DirectColor}
};


image_information *img_info = NULL;
void init_img_info( i )
image_information *i;
{
    i->window = i->icn_window = NULL;
    i->pixmap = i->icn_pixmap = NULL;
    i->pix_info_window = NULL;
    i->gc = i->icn_gc = NULL;
    i->image = i->icn_image = NULL;
    i->colormap = NULL;
    i->visual_class = -1;
    i->pixmap_failed = False;
    
    i->filename = NULL;
    i->img_num = 0;
    i->title = NULL;
    i->fd = stdin;
    i->img_channels = 0;
    i->dpy_channels = 0;
    i->scan_data = NULL;
    i->map_scanline = NULL;
    i->MAG_scanline = NULL;
    i->gamma = 0.0;
    
    i->x = i->y = 0;
    i->w = i->h = 0;
    i->icn_w = i->icn_h = 0;
    i->icn_factor = 1;

    i->mag_x = i->mag_x = 0;
    i->mag_w = i->mag_h = 0;
    i->mag_fact = 1;
    i->save_mag_fact = 1;
    i->mag_mode = False;
    i->mag_pixmap = NULL;
    
    i->binary_img = False;
    i->dither_img = False;
    i->mono_img = False;
    i->rw_cmap = False;
    i->sep_colors = False;
    i->mono_color = False;
    i->color_dpy = True;

    i->in_cmap = NULL;
    i->ncmap = 0;
    i->cmlen = 0;
    i->modN = NULL;
    i->divN = NULL;
    i->dm16 = NULL;
    i->pixel_table = NULL;
}


/*****************************************************************
 * TAG( main )
 * 
 * Usage:
 *	getx11 [-F] [-D] [-d display] [-= window-geometry] [-l levels] [-z]
 *		[-{iI} gamma] [-g gamma] [-x visualtype] [-n] [-b] [-m] 
 *		[-v] [files]
 * Inputs:
 * 	-F:		Dont fork after putting image on screen.
 *	-D:		Debug mode: print input file as read.
 *	-b:		bitonal; binary black and white
 *	-m:		Black & white: reduce color images to monochrome before
 *			display.  Advantage is that smoother shading can
 *			be achieved.
 *	-v:		Print verbose information.
 *	-d display:	Specify display name.
 *	-= window_geometry:
 *			Specify window geometry (but min size set by file).
 *	-i gamma:	Specify gamma of image. (default 1.0)
 *	-I gamma:	Specify gamma of display image was computed for.
 *	-g gamma:	Specify gamma of display. (default 2.5)
 *	-V vis_type:    Specify visual type to be used (string or int, 0 to 5)
 *	-l levels:	number of levels to display of each color
 *	-z:		hold all output, then blast it all out at once
 *	-n:		Do not dither
 *
 *	file:		Input Run Length Encoded file. Uses stdin if not
 *			specified.
 *
 * 	getx11 will also read picture comments from the input file to determine
 *			the image gamma.  These are
 *
 *	image_gamma=	gamma of image (equivalent to -i)
 *	display_gamma=	gamma of display image was computed for.
 *		
 *	Any command line arguments override the values in the file.
 *		
 * Outputs:
 * 	Puts image on screen.
 * Assumptions:
 * 	Input file is in RLE format.
 * Algorithm:
 *	[None]
 */

void
main (argc, argv)
int		argc;
char 		** argv;
{
    char	** infnames = NULL;
    char	* display_name = NULL;
    char	* window_geometry = NULL;
    int		n_malloced, num_imgs = 0, using_stdin;
    int		levels;
    int		nfile = 0, forkflg = 0, dflag = 0, gflag = 0, wflag = 0;
    int		vflag = 0, aflag = 0, bin_flag = 0, mono_flag = 0;
    int		tflag = 0;
    int		flip_book = 0;
    int		flip_book_frams_sec = 30;
    char	*vis_type = NULL, *title_string = NULL;
    int		visual_type = -1;
    int		status, done = 0, rle_cnt = -1;
    image_information *img, *previous_img;
    double	image_gamma = 0.0;	/* default gamma for image */

    progname = argv[0];

    if ( scanargs( argc, argv,
		  "% =%-window-geometry!s a%- d%-display!s D%- f%- \n\
	g%-gamma!F iI%-gamma!F j%- m%-maxframes/sec%d n%-levels!d \n\
	s%- t%-title!s v%- wW%- x%-visual!s file%*s",
		   &wflag, &window_geometry,
		   &aflag,
		   &dflag, &display_name,
		   &debug_flag,
		   &forkflg,
		   &gflag, &display_gamma,
		   &iflag, &image_gamma,
		   &jump_flag,
		   &flip_book, &flip_book_frams_sec,
		   &specified_levels, &specified_levels,
		   &stingy_flag,
		   &tflag, &title_string,
		   &verbose_flag,
		   &bin_flag,
		   &vflag, &vis_type,
		   &nfile, &infnames ) == 0 )
	exit(1);

    /* Check for -w instead of -W. */
    if ( bin_flag == 2 )
    {
	bin_flag = 0;
	mono_flag = 1;
    }
    using_stdin = ( nfile ? False : True );
    n_malloced = ( using_stdin ? 1 : nfile );

    img_info = (image_information *)malloc(n_malloced *
					   sizeof(image_information) );

    if ( img_info == NULL ){
	perror("malloc returned NULL");
	exit (1);
    }
    
    if ( vflag != 0) {
	if (isdigit (vis_type[0]) ) {
	    visual_type = atoi (vis_type);
	}
	else {
	    register char *ptr;
	    register int  i;
	    visual_type = 9999;
	    
	    for (ptr = vis_type; *ptr != '\0'; ptr++) {
		if (isupper(*ptr)) *ptr = _tolower(*ptr);
	    }

	    for (i = 0; i < COUNT_OF (visual_type_table); i++) {
		if (strcmp (visual_type_table[i].string, vis_type) == 0) {
		    visual_type = visual_type_table[i].type;
		    break;
		}
	    }
	}
	
	if ( visual_type < 0 || visual_type > 5 ) {
	    fprintf ( stderr, "Bad visual type %s, ignoring request\n",
		      vis_type);
	    visual_type = -1;
	}
    }
    
    levels = specified_levels;
    if (levels == 0) levels = DEFAULT_LEVELS;	/* default starting point */
    if (levels > MAXIMUM_LEVELS) levels = MAXIMUM_LEVELS;
    
    /*
     * open the display
     */
    
    if (display_name == NULL || *display_name == '\0')
	display_name = getenv("DISPLAY");

    dpy = XOpenDisplay(display_name);
    
    /* Work around bug in X11/NeWS server colormap allocation. */
    if (strcmp("X11/NeWS - Sun Microsystems Inc.", ServerVendor(dpy)) == 0 &&
	VendorRelease(dpy) == 2000)
	no_color_ref_counts = True;
    
    if (dpy == NULL) {
	fprintf(stderr, "%s: Cant open display %s\n", progname,
		(display_name == NULL) ? "" : display_name);
	exit(1);
    }
    
    /* 
     * For each file, display it.
     */
    
    do {
	if ( num_imgs >= n_malloced )
	    img_info = (image_information *)
		realloc( img_info,
			 ++n_malloced * sizeof(image_information) );
	if ( img_info == 0 )
	{
	    fprintf(stderr, "%s: Out of memory!\n", progname);
	    exit( RLE_NO_SPACE );
	}
	img = &img_info[num_imgs];

	init_img_info( img );
	
	/* we need pixmaps for movie mode... */
	if ( stingy_flag && !flip_book )
	    img_info[0].pixmap_failed = True;

	if ( iflag == 2 && image_gamma != 0.0 ) img->gamma = 1.0 / image_gamma;
	else if ( iflag == 1 ) img->gamma = image_gamma;

	img->binary_img = ( bin_flag ? True : False );
	img->mono_img = ( mono_flag || bin_flag ? True : False );
	if ( img->mono_img )
	    img->color_dpy = False;
	img->dither_img = ( aflag == 0 );
	img->visual_class = visual_type;
	img->lvls = levels;
	img->lvls_squared = levels * levels;

	if ( img->mono_img )
	    img->dpy_channels = 1;

	if ( flip_book && img != &img_info[0] )
	{
	    previous_img = img - 1;
#define INHERIT( thing ) img->thing = previous_img->thing
	    INHERIT(window);
	    INHERIT(icn_window);
	    INHERIT(icn_pixmap);
	    INHERIT(gc);
	    INHERIT(icn_gc);
	    INHERIT(pixmap_failed);
	    INHERIT(divN);
	    INHERIT(modN);
	    INHERIT(dm16);
	    INHERIT(pixel_table);
	    INHERIT(lvls);
	    INHERIT(lvls_squared);
	    INHERIT(colormap);
	    INHERIT(visual_class);
	    INHERIT(dpy_depth);
	    INHERIT(dpy_channels);
	    INHERIT(sep_colors);
	    INHERIT(rw_cmap);
	    INHERIT(dither_img);
	    INHERIT(color_dpy);
	    INHERIT(mono_img);
	    INHERIT(binary_img);
#undef INHERIT
	}
	else previous_img = NULL;

	if ( !using_stdin ) {
	    /* rle_cnt will be set to -1 when it is time to open a new
	     * file.
	     */
	    if ( rle_cnt < 0 )
	    {
		if ( strcmp( *infnames, "-" ) == 0 )
		{
		    img->filename = "Standard Input";
		    img->fd = stdin;
		}
		else
		{
		    img->filename = *infnames;

		    img->fd = rle_open_f_noexit( "getx11", img->filename, "r");
	    
		    if (img->fd == NULL) {
			if ( nfile == 1 )
			    exit (1);
			else {
			    nfile--; infnames++;
			    continue;
			}
		    }
		}
		rle_cnt = 0;
	    }
	    else
	    {
		/* Safe to copy from previous image because this is at
		 * least the second one in this file.
		 */
		img->filename = img[-1].filename;
		img->fd = img[-1].fd;
	    }
	}
	else {
	    img->filename = "Standard Input";
	    img->fd = stdin;
	    if ( rle_cnt < 0 )
		rle_cnt = 0;
	}
	img->img_num = rle_cnt;

	if ( tflag && title_string ) 
	    img->title = title_string;

	status = get_pic( img, window_geometry, previous_img );
	
	switch (status){
	case SUCCESS:
	    num_imgs++;
	    rle_cnt++;
	    break;

	case MALLOC_FAILURE:
	case RLE_NO_SPACE:
	    fprintf(stderr,"%s: Out of Memory! Trying to continue\n",progname);
	    if ( !using_stdin ) 
	    {
		fclose( img->fd );
		nfile--;
		infnames++;
	    }
	    rle_cnt = -1;
	    break;
	    
	case RLE_EMPTY:
	case RLE_EOF:
	case RLE_NOT_RLE:
	    if ( status == RLE_NOT_RLE || rle_cnt == 0 )
		rle_get_error( status, progname, img->filename );
	    if ( using_stdin )
		done = 1;
	    else
	    {
		fclose( img->fd );
		nfile--;
		infnames++;
		if ( nfile == 0 )
		    done = 1;
	    }
	    rle_cnt = -1;
	    break;
	    
	case FATAL_FAILURE:	
	    if ( flip_book )
		fprintf(stderr,"%s: Cannot flip-book, sorry...\n", progname);
	    exit(1);
	    break;
	}
    } while( !done );
    
#ifdef unix
    if ( ! forkflg ) {
	
	if ( fork() == 0 ) {
	    close( 0 );
	    close( 1 );
	    close( 2 );
	    /* Set process group to avoid signals under sh. */
#ifdef SYS_V_SETPGRP
	    (void)setpgrp();
#else
	    (void)setpgrp( 0, getpid() );
#endif
	    update_pic ( img_info, num_imgs, flip_book, flip_book_frams_sec );
	}
	else exit( 0 );
    }
    else 
#endif
	update_pic ( img_info, num_imgs, flip_book, flip_book_frams_sec );

    XCloseDisplay( dpy );
}



/* returns how many lines it blitted */
void
handle_exposure( img, x, y, width, height, img_h )
register image_information *img;
int x, y, width, height, img_h;
{
    /*
     * If window has been resized (bigger), dont bother redrawing
     * the area outside the image. 
     */
    if (x < 0) {
	width -= x;
	x = 0;
    }
    
    /* if the image has not yet read itself in, dont blit any of it 	*/
    /* instead clear out that top portion of the window(not needed oh well) */
    if (y < img->h - img_h ) {
	XClearArea( dpy, img->window, x, y, width, img->h - img_h - y, False );
	height -= img->h - img_h - y;
	y = img->h - img_h;
    }
    
    if ( height <= 0 )
	return;
    
    if (y + height >= img->h)
	height = img->h - y;
    
    /*
     * if bitmap, round beginning pixel to beginning of word
     */
	
    if ( img->binary_img ) {
	int offset = x % BitmapPad (dpy);
	x -= offset;
	width += offset;
    }
    
    if (x + width >= img->w)
	width = img->w - x;
    
    if ( width <= 0 || height <= 0 )
	return;

    if ( img->refresh_pixmap )
	XCopyArea(dpy, img->refresh_pixmap, img->window, img->gc,
		  x, y, width, height, x, y );
    else
	XPutImage(dpy, img->window, img->gc, img->image,
		  x, y, x, y, width, height );
    return;
}

/* 
 * Read an image from the input file and display it.
 */
static int
get_pic( img, window_geometry, previous_img )
register image_information *img, *previous_img;
char *window_geometry;
{
    int		image_xmax, image_ymax;
    int		y;
    unsigned char		*save_scan[3], *read_scan[3];
    register int		i;
    register int		image_y;
    register int		next_y, y_base, lines_buffered;
    int 			rle_err;

    extern void choose_scanline_converter();
    extern XImage * get_X_image();
    extern int eq_cmap();
    extern void get_dither_colors();
    /*
     * Read setup info from file. 
     */
    
    rle_dflt_hdr.rle_file = img->fd;
    
    if ( (rle_err = rle_get_setup(&rle_dflt_hdr)) < 0) 
	return ( rle_err );
    
/* XXX
    if ( debug_flag )
	rle_debug( 1 );
    else rle_debug (0);
*/    
    image_xmax = rle_dflt_hdr.xmax;
    image_ymax = rle_dflt_hdr.ymax;
    
    img->x = rle_dflt_hdr.xmin;
    img->y = rle_dflt_hdr.ymin;
    img->w = image_xmax - rle_dflt_hdr.xmin + 1;
    img->h = image_ymax - rle_dflt_hdr.ymin + 1;
    
    img->mag_w = img->w;
    img->mag_h = img->h;

    rle_dflt_hdr.xmin = 0;
    rle_dflt_hdr.xmax = img->w - 1;
    
    /* Were only interested in R, G, & B */
    
    RLE_CLR_BIT(rle_dflt_hdr, RLE_ALPHA);
    for (i = 3; i < rle_dflt_hdr.ncolors; i++)
	RLE_CLR_BIT(rle_dflt_hdr, i);
    
    img->dpy_channels = img->img_channels = (rle_dflt_hdr.ncolors > 3 ) ?
	3 : rle_dflt_hdr.ncolors;
    
    if ( img->img_channels == 1 ) 
	img->mono_img = True;
    if ( img->mono_img )
	img->dpy_channels = 1;
    
    if ( !previous_img )
	/* find a suitable visual for the image */
	find_appropriate_visual( img );

    if ( img->img_channels < img->dpy_channels )
	fprintf(stderr, "%s: dpy_channels > img_channels, %d > %d\n", progname,
 		img->dpy_channels, img->img_channels);
    
    get_dither_colors( img );

    if ( !previous_img )
	/* Get X color map */
	init_color( img );

    if ( !img->title )
	if ( (img->title = rle_getcom("image_title", &rle_dflt_hdr )) == NULL &&
	     (img->title = rle_getcom("IMAGE_TITLE", &rle_dflt_hdr )) == NULL &&
	     (img->title = rle_getcom("title", &rle_dflt_hdr )) == NULL &&
	     (img->title = rle_getcom("TITLE", &rle_dflt_hdr )) == NULL )
	{
	    if ( img->img_num > 0 )
	    {
		img->title = (char *)malloc( strlen( img->filename ) + 8 );
		sprintf( img->title, "%s(%d)", img->filename,
			 img->img_num + 1 );
	    }
	    else
		img->title = img->filename;
	}

    /*
     * Here if we are flip_booking we gotta get nasty here... img->w, img->h
     * and img->img_channels must match for well surely be screwed if they
     * dont match up  and we flip_book
     */
    if ( previous_img )
    {
	if ( img->w != previous_img->w || img->h != previous_img->h ||
	    img->img_channels != previous_img->img_channels )
	{
	    fprintf( stderr,
		    "%s: Images %s and %s dont match in size or channels\n",
		    progname, previous_img->title, img->title );
	    return( FATAL_FAILURE );
	}
	if ((img->mono_color &&
	     !eq_cmap(previous_img->in_cmap, previous_img->cmlen,
		      img->in_cmap, img->cmlen)))
	{
	    fprintf( stderr, "%s: Images %s and %s have different colormaps\n",
		    progname, previous_img->title, img->title );
	    return( FATAL_FAILURE );
	}
    }


    /*
     * Here we have to conserve memory on the client side and not allocate a
     * new Ximage structure with associated memory for image data.  Also, if
     * the server was unable to supply us with a pixmap in previous_img, then
     * we need to save the XImage there...
     */
    if ( !previous_img || previous_img->pixmap == NULL )
    {
	if ( !img->image )
	    img->image = get_X_image( img, img->w+1, img->h );

	if ( img->image == NULL ) {
	    perror("problem getting XImage");
	    return( MALLOC_FAILURE );
	}
    }
    else 
	img->image = previous_img->image;

    /* only dick with the icon if we are not flip_booking */
    if ( !previous_img ) {
	determine_icon_size( img->w, img->h, &img->icn_w, &img->icn_h,
			    &img->icn_factor);
	
	if ( !img->icn_image )
	    img->icn_image = get_X_image( img, img->icn_w, img->icn_h );
	
	if ( img->icn_image == NULL ) {
	    perror("malloc for fancy icon");
	    return ( MALLOC_FAILURE );
	}
    }

    /*
     * Get image and icon windows of the right size 
     */
    
    create_windows( img, window_geometry );
    set_watch_cursor( img->window );

    if ( !img->map_scanline )
	choose_scanline_converter( img );

    /*
     * If we are going to display a 2 or 3 channel image in one channel (-w)
     * we have to copy the data anyway, so we will always read it into the
     * same spot, and map it into a saved_data array with only one color per
     * scanline this saves memory.
     *
     * read_scan[] is the scan data that we read into with rle_getrow.  If the
     * number of image channels is equal to that which is displayed, we will
     * not mallocate new memory for it, but we will move it throughout the
     * saved_data along with save_scan.
     *
     * save_scan[] is the pointer to the current line in img->saved_data that
     * we are saving rle_getrows into.
     * 
     * If we cant malloc the memory for the save_scan[] or we are doing 
     * flip_book, then we dont want to malloc the memory for this thing.
     */
    
    /*
     * Set up for rle_getrow.  Pretend image x origin is 0. 
     */
    
    /* get img->h rows mem. SAVED_RLE_ROW uses scan_data to calc address!! */
    img->scan_data = NULL;

    /* if we are flip_booking dont mess with this one huh? */
    if ( !previous_img ){
	img->scan_data =
	    (unsigned char *) malloc ( SAVED_RLE_ROW( img, img->h ) );

	if (img->scan_data == NULL)
	    perror ("malloc for scanline buffer failed");

	/* use the macro to point us to the last line... start saving here */
	save_scan[0] = SAVED_RLE_ROW( img, img->h - 1 );
	for (i = 1; i < img->img_channels; i++)
	    save_scan[i] = save_scan[i - 1] + img->w;
    }

    /* get one line of scan data for reading if we are doing monochrome */
    if ( img->mono_img || img->scan_data == NULL ) {
	read_scan[0] = (unsigned char *) malloc ( img->w * img->img_channels );
	if ( read_scan[0] == NULL ) {
	    perror ("malloc for read_scan buffer failed");
	    return ( MALLOC_FAILURE );
	}
	for (i = 1; i < img->img_channels; i++)
	    read_scan[i] = read_scan[i - 1] + img->w;
    }
    else
	read_scan[0] = save_scan[0],
	read_scan[1] = save_scan[1],
	read_scan[2] = save_scan[2];

    if (img->scan_data == NULL)
	save_scan[0] = read_scan[0],
	save_scan[1] = read_scan[1],
	save_scan[2] = read_scan[2];

    /*
     * For each scan line, read it, save it, dither it and display it. 
     */
    
    next_y = img->h - 1;
    
    y_base = next_y;
    lines_buffered = 0;
    
    while ((image_y = rle_getrow(&rle_dflt_hdr, read_scan)) <=
	   rle_dflt_hdr.ymax ) {
	
	y = image_ymax - image_y;

	if ( img->mono_img ) 
	    map_rgb_to_bw ( img, read_scan, save_scan[0] );
	else
	    map_rgb_to_rgb ( img, read_scan, save_scan );
	    
	(*img->map_scanline) (img, save_scan, img->dpy_channels, img->w, 1,
			      y, img->image);
	
	/* Subsample image to create icon */
	
	if ( !previous_img && y % img->icn_factor == 0 ) 
	    (*img->map_scanline)( img, save_scan, img->dpy_channels,
				 img->icn_w, img->icn_factor,
				 y / img->icn_factor, img->icn_image);
	
	next_y = y - 1;

	while ( XPending( dpy ) ) {
	    XEvent event;
	    XNextEvent(dpy, &event);
	    if (event.type == Expose) {
		image_information *eimg;
		
		/* get the right window bro....  */
		for (eimg = img_info; eimg <= img; eimg++ )
		    if ( eimg->window == event.xany.window )
			break;
		if (previous_img) eimg = img; /*flip_book override */
		if (eimg <= img) /* protect against screwy window */
		    handle_exposure(eimg, event.xexpose.x, event.xexpose.y,
				    event.xexpose.width, event.xexpose.height,
				    (eimg == img) ? image_y : eimg->h );
	    }
	    XFlush(dpy);
	}

	if (++lines_buffered >= LINES_DRAWN||(img->fd == stdin && !jump_flag)){
	    y_base -= lines_buffered - 1;
	    XPutImage( dpy, img->pixmap?img->pixmap:img->window, img->gc,
		      img->image, 0, y_base, 0, y_base, img->w,lines_buffered);
	    if ( img->pixmap )
		XCopyArea( dpy, img->pixmap, img->window, img->gc, 0, y_base,
			  img->w, lines_buffered, 0, y_base );
	    y_base = next_y;
	    lines_buffered = 0;
	}

	if ( img->scan_data ) {
	    /* move scan up one line */ 
	    save_scan[0] = SAVED_RLE_ROW ( img, next_y );
	    for( i = 1; i < img->img_channels; i++ )
		save_scan[i] = save_scan[i - 1] + img->w;
	}
	/*
	 * remember? if were saving more than one channel then we dont need
	 * to move the data after we read it...  So kludge read_scan ...
	 */
	if ( !img->mono_img && img->scan_data )
	    read_scan[0] = save_scan[0], read_scan[1] = save_scan[1],
	    read_scan[2] = save_scan[2];	
	if ( img->scan_data == NULL)
	    save_scan[0] = read_scan[0], save_scan[1] = read_scan[1],
	    save_scan[2] = read_scan[2];
    }
    
    if ( lines_buffered > 0 ) {
	y_base -= lines_buffered - 1;
	XPutImage( dpy, img->pixmap?img->pixmap:img->window, img->gc,
		  img->image, 0, y_base, 0, y_base, img->w, lines_buffered );
	if ( img->pixmap )
	    XCopyArea( dpy, img->pixmap, img->window, img->gc, 0, y_base,
		      img->w, lines_buffered, 0, y_base );
    }
    
    if ( !previous_img && img->icn_pixmap ) {
	XPutImage( dpy, img->icn_pixmap, img->icn_gc, img->icn_image,
		  0, 0, 0, 0, img->icn_w, img->icn_h );
	XClearWindow( dpy, img->icn_window );
    }

    set_circle_cursor( img->window );

    /* free this if we mallocated it */
    if ( img->mono_img || !img->scan_data )
	free ( read_scan[0] );

    return (SUCCESS);
}

/* 
 * Track events & redraw image when necessary.
 */

/* now thats my kinda action! */
#define ACTION_MAGNIFY		0
#define ACTION_UNMAGNIFY	1
#define ACTION_PAN		2
#define ACTION_SWITCH_MAG_MODE	3
#define ACTION_FLIP_FORWARD	4
#define ACTION_FLIP_STEP	5
#define ACTION_FLIP_BACKWARD	6
#define ACTION_FLIP_SPEED       7
#define ACTION_PIXEL_INFO	8
#define ACTION_CYCLE	9
#define ACTION_CYCLE_TO_AND_FRO 10
#define ACTION_DEFAULT		ACTION_PAN

/* define what to do on mouse buttons */
static int button_action[3][2] = {
    ACTION_MAGNIFY,	ACTION_PIXEL_INFO,
    ACTION_PAN,		ACTION_SWITCH_MAG_MODE,
    ACTION_UNMAGNIFY,	ACTION_PIXEL_INFO
    };

/* define what to do on flip_book mouse buttons */
static int flip_action[3][2] = {
    ACTION_FLIP_BACKWARD,	ACTION_CYCLE,
    ACTION_FLIP_STEP,		ACTION_FLIP_SPEED,
    ACTION_FLIP_FORWARD,	ACTION_CYCLE_TO_AND_FRO
    };

static
void mag_pan( img, action, bx, by, new_mag_fact )
image_information *img;
int action, bx, by, new_mag_fact;
{
    Boolean fast_pan = False;
    Boolean redraw = False;
    Boolean redither = False;
    int ix = img->mag_x + bx / img->mag_fact;
    int iy = img->mag_y + by / img->mag_fact;
    int omag_x, omag_y;


    /* perhaps we could re-open the img->filename to do this... */
    /* but NAHHHHHHH */
    if ( img->scan_data == NULL )
	return;

    switch ( action ) {
	/*
	 * Normalize has to switch the current mag factor with 1 if they
	 * differ... It also remembers the old mag_x and mag_y and stuff.  It
	 * should use the pixmaps to refresh, so that it will be fast when
	 * toggeling in this mode...
	 */
    case ACTION_SWITCH_MAG_MODE:
	if ( img->mag_fact == img->save_mag_fact )
	    return;
	else {
	    if ( img->mag_fact == 1 ) {
		img->mag_mode = True;
		img->mag_fact = img->save_mag_fact;
		img->mag_x = img->save_mag_x;
		img->mag_y = img->save_mag_y;
		img->mag_w = img->save_mag_w;
		img->mag_h = img->save_mag_h;
		img->save_mag_x = 0; img->save_mag_y = 0;
		img->save_mag_w = img->w; img->save_mag_h = img->h;
		img->save_mag_fact = 1;
		img->refresh_pixmap = img->mag_pixmap;
	    } else {
		img->mag_mode = False;
		img->save_mag_x = img->mag_x;
		img->save_mag_y = img->mag_y;
		img->save_mag_w = img->mag_w;
		img->save_mag_h = img->mag_h;
		img->save_mag_fact = img->mag_fact;
		img->mag_x = 0; img->mag_y = 0;
		img->mag_w = img->w; img->mag_h = img->h;
		img->mag_fact = 1;
		img->refresh_pixmap = img->pixmap;
	    }
	    redraw = True;
	}
	break;
    case ACTION_MAGNIFY:
    case ACTION_UNMAGNIFY:
	img->mag_fact = new_mag_fact;
	if ( img->mag_fact <= 1 )
	{
	    img->mag_fact = 1;
	    img->mag_x = 0; img->mag_y = 0;
	    img->mag_w = img->w; img->mag_h = img->h;
	    img->refresh_pixmap = img->pixmap;
	    if ( img->mag_mode )
		redraw = True;
	    img->mag_mode = False;
	    break;
	} else img->mag_mode = True;
	
	img->mag_x = ix - ((img->w / 2)/img->mag_fact);
	img->mag_y = iy - ((img->h / 2)/img->mag_fact);
	
	img->mag_w = img->w/img->mag_fact;
	img->mag_h = img->h/img->mag_fact;
	
	if ( !img->mag_pixmap && !img->pixmap_failed && !stingy_flag ) {
	    img->mag_pixmap = XCreatePixmap(dpy, img->window, img->w, img->h,
					    img->dpy_depth );
	    check_pixmap_allocation( img );
	}

	img->refresh_pixmap = img->mag_pixmap;
	
	redither = True;
	redraw = True;
	break;

    case ACTION_PAN:
	do {
	    fast_pan = img->mag_mode;  /* are we REALLY just panning around? */
	    omag_x = img->mag_x;
	    omag_y = img->mag_y;
	    
	    if ( !img->mag_mode ) {
		img->mag_fact = img->save_mag_fact;
		img->save_mag_fact = 1;
	    }
	    if ( img->mag_fact > 1 )
		img->mag_mode = True;
	    else
		return;
	    
	    img->refresh_pixmap = img->mag_pixmap;

	    img->mag_x = ix - ((img->w / 2)/img->mag_fact);
	    img->mag_y = iy - ((img->h / 2)/img->mag_fact);

	    /* Isnt this always like this? */
	    img->mag_w = img->w/img->mag_fact;
	    img->mag_h = img->h/img->mag_fact;

	    redither = True;
	    redraw = True;
	} while (0);
	break;
    }    

    /* check bounds */
    if ( img->mag_x < 0 )
	img->mag_x = 0;
    if ( img->mag_y < 0 )
	img->mag_y = 0;
    
    if ( img->mag_x + img->mag_w > img->w )
    {
	img->mag_x = img->w - img->mag_w;
	if ( img->mag_w * img->mag_fact < img->w )
	    img->mag_x -= 1;
    }
    if ( img->mag_y + img->mag_h > img->h )
    {
	img->mag_y = img->h - img->mag_h;
	if ( img->mag_h * img->mag_fact < img->h )
	    img->mag_y -= 1;
    }

    /* check bounds */
    if ( img->mag_x < 0 )
	img->mag_x = 0;
    if ( img->mag_y < 0 )
	img->mag_y = 0;
    
    /* let the suckers know that we are thinking */
    set_watch_cursor( img->window );

    /*
     * Some could argue that this fast_pan shit is a waste of time, but it
     * does speed things up a bunch, and its really hard to understand.
     * Sorry, no fancy pictures in the comments.  Just code.  We figure out
     * which rectangle is blt-able.  We blt it on the server side, and on the
     * client side (my fancy XCopyImage) and then MAG_scanline the exposed
     * area and XPutImage that stuff too...  Dont change it cuz its right.
     */
    if ( fast_pan )
    {
	int width, hight;
	int src_x, src_y, dst_x, dst_y;
	int pwidth = omag_x - img->mag_x;
	int phight = omag_y - img->mag_y;
	
	pwidth = img->mag_w - (( pwidth < 0 ) ? - pwidth : pwidth);
	phight = img->mag_h - (( phight < 0 ) ? - phight : phight);

	/*
	 * pwidth and phight now contain the size of the non-changing
	 * (BLT-able) portion of the viewport in rle_pixel space.
	 */
	width = pwidth * img->mag_fact + (img->w - img->mag_w * img->mag_fact);
	hight = phight * img->mag_fact + (img->h - img->mag_h * img->mag_fact);

	/* Now we compute the src_xy and dst_xy for the pixel copy */
	if ( omag_x < img->mag_x )
	{
	    dst_x = 0; src_x = img->w - width;}
	else
	{
	    src_x = 0; dst_x = img->w - width;}

	if ( omag_y < img->mag_y )
	{
	    dst_y = 0; src_y = img->h - hight;}
	else
	{
	    src_y = 0; dst_y = img->h - hight;}

	/* subtract partial pixels if we are going right */
	if ( omag_x < img->mag_x )
	    width -= img->w - img->mag_w * img->mag_fact;

	/* subtract partial pixels if we are going down */
	if ( omag_y < img->mag_y )
	    hight -= img->h - img->mag_h * img->mag_fact;

	if ( src_x == dst_x && src_y == dst_y )
	{
	    redraw = False;
	    redither = False;
	}
	else {
	    /* XCopyImage is only implemented for 8 and 32 bit image pixels */
	    if (img->refresh_pixmap && XCopyImage(img->image, src_x, src_y,
						  width, hight, dst_x, dst_y))
	    {
		XCopyArea ( dpy, img->refresh_pixmap, img->refresh_pixmap,
			   img->gc, src_x, src_y, width, hight, dst_x, dst_y );
	    
		if ( dst_y ) {
		    (*img->MAG_scanline)(img, img->mag_x, img->mag_y,
					 img->mag_fact, 0, 0,
					 img->w, dst_y, img->image );
		    
		    XPutImage( dpy, img->refresh_pixmap, img->gc, img->image,
			      0, 0, 0, 0, img->w, dst_y);
		}
		else {
		    if (hight < img->h) {
			(*img->MAG_scanline)
			    (img, img->mag_x, img->mag_y + phight,
			     img->mag_fact, 0, hight,
			     img->w, img->h - hight, img->image ); 
			XPutImage(dpy, img->refresh_pixmap, img->gc,
				  img->image, 0, hight, 0, hight,
				  img->w, img->h - hight );
		    }
		}
		
		if ( dst_x ) {
		    if (hight && width < img->w) {
			(*img->MAG_scanline)
			    (img, img->mag_x, img->mag_y +
			     ((dst_y) ? img->mag_h - phight: 0), img->mag_fact,
			     0, dst_y, img->w - width, hight, img->image );
		    
			XPutImage(dpy, img->refresh_pixmap, img->gc,img->image,
				  0, dst_y, 0, dst_y, img->w - width, hight );
		    }
		}
		else {
		    if (hight && width < img->w) {
			(*img->MAG_scanline)
			    (img, img->mag_x + pwidth, img->mag_y +
			     ((dst_y) ? img->mag_h - phight: 0), img->mag_fact,
			     width, dst_y, img->w - width, hight, img->image );
		    
			XPutImage(dpy, img->refresh_pixmap, img->gc,
				  img->image, width, dst_y, width, dst_y,
				  img->w - width, hight );
		    }
		}
		
		/*
		 * We already redithered...  If XCopyImage failed we arent
		 * here and we have to redither the whole thing below.
		 */
		redither = False;
	    }
	}
    }

    /* redither the whole thing */
    if ( redither || ( redraw && !img->refresh_pixmap ) )
    {
	(*img->MAG_scanline)(img, img->mag_x, img->mag_y,
			     img->mag_fact, 0, 0, img->w, img->h,
			     img->image );

	XPutImage( dpy, (img->refresh_pixmap?img->refresh_pixmap:img->window),
		  img->gc, img->image, 0, 0, 0, 0, img->w, img->h );

    }

    if ( redraw && img->refresh_pixmap )
	XCopyArea( dpy, img->refresh_pixmap, img->window, img->gc, 0, 0,
		  img->w, img->h, 0, 0 );

    set_circle_cursor( img->window );
}

image_information *action_flip_forward( img, img_info, flip_book_udelay, n,
				        mask, event, found_event )
image_information *img, *img_info;
int flip_book_udelay, n;
unsigned long mask;
XEvent *event;
Boolean *found_event;
{
    if (mask) *found_event = False;

    if ( img == &img_info[n - 1] )
	img = &img_info[0];
    for ( ; img < &img_info[n]; img++ ) {
	set_timer( flip_book_udelay );
	handle_exposure(img, 0, 0, img->w, img->h, img->h);
	if (img->fd != stdin && flip_book_udelay ) /* as fast as possible */
	    XStoreName(dpy, img->window, img->title);
	XFlush( dpy );
	if (mask && XCheckMaskEvent(dpy, mask, event )){
	    *found_event = True;
	    break;
	}
	wait_timer();
    }
    if (mask && *found_event)
	return img;
    else return img - 1;
}

image_information *action_flip_backward( img, img_info, flip_book_udelay, n,
					 mask, event, found_event )
image_information *img, *img_info;
int flip_book_udelay, n;
unsigned long mask;
XEvent *event;
Boolean *found_event;
{
    if (mask) *found_event = False;
    
    if ( img == img_info )
	img = &img_info[n - 1];
    for ( ; img >= &img_info[0]; img-- ) {
	set_timer( flip_book_udelay );
	handle_exposure(img, 0, 0, img->w, img->h, img->h);
	if (img->fd != stdin && flip_book_udelay ) /* as fast as possible */
	    XStoreName(dpy, img->window, img->title);
	XFlush( dpy );
	if (mask && XCheckMaskEvent(dpy, mask, event )){
	    *found_event = True;
	    break;
	}
	wait_timer();
    }
    if (mask && *found_event)
	return img;
    else return img + 1;
}

image_information *action_flip_book_cycle( img, img_info, n, flip_forward,
					   flip_book_udelay )
image_information *img, *img_info;
int n, flip_book_udelay;
Boolean flip_forward;
{
    int found_event;
    XEvent event;
    
    do {
	img = (* (flip_forward ? action_flip_forward : action_flip_backward))
	    (img, img_info, flip_book_udelay, n, ButtonPressMask|KeyPressMask,
	     &event, &found_event);
	XSync(dpy, False);
    } while ( !found_event );    
    return img;
}



static void
update_pic( img_info, n, flip_book, flip_book_frams_sec )
image_information *img_info;
int n, flip_book_frams_sec;
Boolean flip_book;
{
    int i;
    XEvent event;
    int action;
    register image_information *img;
    int found_event;
    
    /* variables to use for flip_book mode */
    image_information *flip_frame = NULL;
    Boolean flip_forward = True;
    Window dead_window;
    int flip_book_udelay = 0;
    
    if ( flip_book )
	flip_frame = img_info;

    while (n) {
	
        XNextEvent(dpy, &event);

	if ( flip_book ){  /* all windows are the same! */
	    flip_book_udelay =
		(flip_book_frams_sec) ? 1000000 / flip_book_frams_sec : 0;
	    img = flip_frame;
	} else
	    for ( img = img_info; img < &img_info[n]; img++ )
		if ( img->window == event.xany.window )
		    break;
		
	switch (event.type) {
	    
	case ButtonPress:
	    i = event.xbutton.button - Button1;
	    if (i < 0 || i > COUNT_OF(button_action) )
		action = ACTION_DEFAULT;
	    else
		if ( flip_book )
		    action = flip_action[i][event.xbutton.state & ShiftMask];
		else
		    action = button_action[i][event.xbutton.state & ShiftMask];
	    
	    switch (action) {
		
	    case ACTION_PIXEL_INFO:
		if ( img->scan_data ) do
		{/* X calls by John Bradley, U of Penn, hacked up by mrf.... */
		    Boolean first = 1;
		    int lx = 0, ly = 0;

		    set_left_ptr_cursor( img->window );
		    MapPixWindow ( img, (event.xbutton.y > img->h/2));
		    while (1) {     /* loop until button released */
			Window foo,poo;
			int rx, ry, x, y;
			unsigned int mask;
			
			if (XQueryPointer(dpy, img->window, &foo, &poo,
					  &rx, &ry, &x, &y, &mask))
			{
			    if (!(mask&(Button1Mask|Button2Mask|Button3Mask)))
				break; /* released */
			    
			    x /= img->mag_fact;	y /= img->mag_fact;
			    x += img->mag_x;	y += img->mag_y;
				
				/* wait for new pixel */
				if ((first || x != lx || y != ly) && 
				    (x >= 0 && x < img->w &&
				     y >= 0 && y < img->h)) {
				    DrawPixWindow( img, x, y );
				    first = 0;  lx = x;  ly = y;
				}
			} else
			    break;
		    }
		    set_circle_cursor( img->window );
		    UnmapPixWindow ( img );
		} while (0); 
		continue;

	    case ACTION_FLIP_SPEED:
		if (flip_book) do
		{/* X calls by John Bradley, U of Penn, hacked up by mrf.... */
		    Boolean first = 1;
		    int height = DisplayHeight(dpy, screen)/4;  /* use DH/4 */
		    int s, ly = 0;

		    s = flip_book_frams_sec;
		    MapPixWindow ( img, True );

		    while (1) {     /* loop until button released */
			Window foo,poo;
			int rx, ry, x, y, inc;
			unsigned int mask;
			
			if (XQueryPointer(dpy, img->window, &foo, &poo,
					  &rx, &ry, &x, &y, &mask)) {
			    if (!(mask&(Button1Mask|Button2Mask|Button3Mask)))
				break; /* released */
			    
			    if ( first )
				ly = y;
			    inc = (ly - y) * 100 / height;
			    
			    /* wait for new pixel */
			    if ((first || flip_book_frams_sec + inc != s ) ) {
				s = flip_book_frams_sec + inc;
				s = (s < 0) ? 0 : s;
				s = (s > 100) ? 100 : s;
				DrawSpeedWindow( img, s );
				first = 0; 
			    }
			}
			else
			    break;
		    }
		    flip_book_frams_sec = s;
		    UnmapPixWindow ( img );
		} while (0); 
		continue;

	    case ACTION_FLIP_BACKWARD:
		if ( flip_book ) {
		    img = action_flip_backward(img, img_info, flip_book_udelay,
					       n, ButtonPressMask|KeyPressMask,
					       &event, &found_event);
		    flip_frame = img;
		    flip_forward = False;
		}
		continue;
		
	    case ACTION_FLIP_FORWARD:
		if ( flip_book ) { 
		    img = action_flip_forward (img, img_info, flip_book_udelay,
					       n, ButtonPressMask|KeyPressMask,
					       &event, &found_event);
		    flip_frame = img;
		    flip_forward = True;
		}
		continue;

          case ACTION_FLIP_STEP:
              /* step flip_book in current direction. */
              if ( flip_book ) {
                  if ( flip_forward ) {
                      if ( flip_frame == &img_info[n-1] )
                          img = flip_frame = img_info;
                      else
                          img = flip_frame = flip_frame + 1;
                  }
                  else {
                      if ( flip_frame == img_info )
                          img = flip_frame = &img_info[n-1];
                      else
                          img = flip_frame = flip_frame - 1;
                  }
                  handle_exposure(img, 0, 0, img->w, img->h, img->h);
                  XStoreName(dpy, img->window, img->title);
              }
              continue;
		/* cycle in the current (flip_forward) direction */
	    case ACTION_CYCLE:
		if ( flip_book ) {
		    flip_frame = img = action_flip_book_cycle
			( img, img_info, n, flip_forward, flip_book_udelay );
              }
              continue;
          case ACTION_CYCLE_TO_AND_FRO:
              if ( flip_book ) {
                  do {
                      img = (* (flip_forward ?
                                action_flip_forward :
                                action_flip_backward))
                          (img, img_info, flip_book_udelay,
                           n, ButtonPressMask |KeyPressMask,
                           &event, &found_event);
                      if (!found_event){
                          flip_forward = !flip_forward;
                          if ( n > 1 )
                              img = (flip_forward) ? img + 1 : img - 1;
                      }
                      XSync(dpy, False);
                  } while( !found_event );
                  flip_frame = img;
		}
		continue;

	    case ACTION_MAGNIFY:
		mag_pan ( img, action, event.xbutton.x, event.xbutton.y,
			  img->mag_fact + 1 );
		continue;
	    case ACTION_UNMAGNIFY:
		mag_pan ( img, action, event.xbutton.x, event.xbutton.y,
			  img->mag_fact - 1 );
		continue;
	    case ACTION_SWITCH_MAG_MODE:
		mag_pan ( img, action, event.xbutton.x, event.xbutton.y,
			  img->mag_fact );
		continue;

	    case ACTION_PAN:
		mag_pan ( img, action, event.xbutton.x, event.xbutton.y,
			  img->mag_fact );
		continue;

	    default:
		continue;
	    }
	    
	    break; /* not reached */
	    
	case Expose:
	    handle_exposure(img, event.xexpose.x, event.xexpose.y,
			    event.xexpose.width, event.xexpose.height,
			    img->h );

	    continue;
	    
	case NoExpose:
	    continue;
	    
	case KeyPress:
	{
	    char string[256];
	    char *symstr, *XKeysymToString( );
	    KeySym keysym;
	    int length;
	    XComposeStatus stat;
	    Boolean handled_key = True;
	    Boolean shifted_key;
	  
	    length = XLookupString( &event, string, 256, &keysym, &stat );
	    string [length] = '\0';
	    symstr = XKeysymToString( keysym ); 
	    shifted_key = event.xkey.state & ShiftMask;
	    
	    if ( length == 1 && (string[0] == 'q' || string[0] == 'Q' ||
				 string[0] == '\003' )) /* q, Q or ^C */
		break;

	    if ( length == 1 )
		switch (string[0])
		{
		case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8':
		case '9':
		    mag_pan ( img, ACTION_MAGNIFY, img->w/2, img->h/2,
			     atoi(string));
		    break;

		    /* back and forth mode */
		case 'b':
		    if ( flip_book ) {
			do {
			    img = (* (flip_forward ?
				      action_flip_forward :
				      action_flip_backward))
				(img, img_info, flip_book_udelay,
				 n, ButtonPressMask |KeyPressMask,
				 &event, &found_event);
			    if (!found_event){
				flip_forward = !flip_forward;
				if ( n > 1 )
				    img = (flip_forward) ? img + 1 : img - 1;
			    }
			    XSync(dpy, False);
			} while( !found_event );
			flip_frame = img;
		    }
		    break;

		case 'c':
		case 'C':
		    if ( flip_book ) {
			flip_forward = (string[0] == 'c');
			flip_frame = img = action_flip_book_cycle
			    (img, img_info, n, flip_forward, flip_book_udelay);
		    }
		    break;

		case ' ':
		    /* step flip_book in current direction. */
		    if ( flip_book ) {
			if ( flip_forward ) {
			    if ( flip_frame == &img_info[n-1] )
				img = flip_frame = img_info;
			    else
				img = flip_frame = flip_frame + 1;
			}
			else {
			    if ( flip_frame == img_info )
				img = flip_frame = &img_info[n-1];
			    else
				img = flip_frame = flip_frame - 1;
			}
			handle_exposure(img, 0, 0, img->w, img->h, img->h);
			XStoreName(dpy, img->window, img->title);
		    }
		    break;

		case 'i':
		case 'I':
		    /* Install/deinstall colormap.
		     * Should only do this if no window manager
		     * running, but that's hard to tell.  Let user
		     * deal with it...
		     */
		    if ( img->colormap )
			if ( string[0] == 'i' )
			    XInstallColormap( dpy, img->colormap );
			else
			    XUninstallColormap( dpy, img->colormap );
		    break;

		default:
		    handled_key = False;
		}
	    else handled_key = False;
		
	    DPRINTF(stderr, "%s %x, %s String '%s' - %d\n", symstr, keysym,
		    ( shifted_key )? "shifted":"unshifted", string, length );

	    if ( !handled_key )
	    {
		/* GACK! the F28-34 keysyms are for the suns!              */
		/* on the DECs they are Left Right Up and Down w/ShiftMask */
		/* on the ardent they are KP_4 KP_6 KP_8 KP_2 w/ShiftMask  */
		/* insert your favorite shifted arrow keysyms here!        */
		
		if ( !strcmp( symstr, "Left" ) || !strcmp( symstr, "F30" ) )
		    mag_pan( img, ACTION_PAN, (shifted_key ? 0 : img->w/4),
			     img->h/2, img->mag_fact);
		if ( !strcmp( symstr, "Up" ) || !strcmp( symstr, "F28" ) )
		    mag_pan( img, ACTION_PAN, img->w/2,
			    (shifted_key ? 0 : img->h/4), img->mag_fact);
		if ( !strcmp( symstr, "Right" ) || !strcmp( symstr, "F32" ) )
		    mag_pan( img, ACTION_PAN,
			    (shifted_key ? img->w-1 : img->w/2 + img->w/4),
			     img->h/2, img->mag_fact);
		if ( !strcmp( symstr, "Down" ) || !strcmp( symstr, "F34" ) )
		    mag_pan( img, ACTION_PAN, img->w/2,
			    (shifted_key ? img->h-1 : img->h/2 + img->h/4),
			     img->mag_fact);
	    }
	    continue;
	}

	case MappingNotify:
	    XRefreshKeyboardMapping( &event.xmapping );
	    continue;

	default: 
	    fprintf(stderr, "%s: Event type %x?\n", progname, event.type);
	    break;

	}

	/* exit this window */
	
	if (img->scan_data)
	    free ( img->scan_data );
	if (img->icn_image)
	    XDestroyImage( img->icn_image );
	if (img->image)
	    XDestroyImage( img->image );
	if (img->pixmap)
	    XFreePixmap( dpy, img->pixmap );
	if (img->mag_pixmap)
	    XFreePixmap( dpy, img->mag_pixmap );
	if (img->icn_pixmap)
	    XFreePixmap( dpy, img->icn_pixmap );
	if (img->icn_gc)
	    XFreeGC( dpy, img->icn_gc );
	if (img->gc)
	    XFreeGC( dpy, img->gc );
	if (img->icn_window)
	    XDestroyWindow ( dpy, img->icn_window );

	dead_window = img->window;
	if (img->window)
	    XDestroyWindow ( dpy, img->window );
	
	n--;
	if ( n )   /* pack imgs in there good */  
	    for ( ; img < &img_info[n]; img++ )
		*img = *(img+1);
	else break;

	/* flipbook only has one window, so if its dead_window then die too */
	if ( img_info[0].window == dead_window )
	    break;
    }
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.