/*
 * xv.c - main section of xv.  X setup, window creation, etc.
 *
 *  Author:    John Bradley, University of Pennsylvania
 *                (bradley@cis.upenn.edu)
 *
 *  Modified for VMS by:
 *              David Jones, the Ohio State University
 *		   (jonesd@kcgl1.eng.ohio-state.edu)
 *          and Rick Dyson, the University of Iowa
 *                 (dyson@IowaSP.Physics.UIowa.EDU)
 *
 *  Contains:
 *            int  main(argc,argv)
 *     static void Syntax()
 *     static int  openPic(filenum)
 *     static void closePic()
 *     static void OpenFirstPic()
 *     static void OpenNextPic()
 *     static void OpenNextQuit()
 *     static void OpenNextLoop()
 *     static void OpenPrevPic()
 *     static void OpenNamedPic()
 *     static void MainLoop()
 *     static void CreateMainWindow(geom, name)
 *            void FixAspect(grow, w, h)
 *     static void MakeDispNames()
 *     static void stickInList();
 *            void DeleteCmd();
 *            int  rd_int(name)
 *            int  rd_str(name)
 *            int  rd_str_cl(name_str, class_str)
 *            int  rd_flag(name)
 */


/*
 * Copyright 1989, 1990, 1991, 1992 by John Bradley and
 *                       The University of Pennsylvania
 *
 * Permission to use, copy, and distribute for non-commercial purposes,
 * is hereby granted without fee, providing that the above copyright
 * notice appear in all copies and that both the copyright notice and this
 * permission notice appear in supporting documentation. 
 *
 * The software may be modified for your own purposes, but modified versions
 * may not be distributed.
 *
 * This software is provided "as is" without any expressed or implied warranty.
 *
 * The author may be contacted via:
 *    US Mail:   John Bradley
 *               GRASP Lab, Room 301C
 *               3401 Walnut St.  
 *               Philadelphia, PA  19104
 *
 *    Phone:     (215) 898-8813
 *    EMail:     bradley@cis.upenn.edu       
 */


#define MAIN
#define NEEDSTIME
#define NEEDSDIR     /* for value of MAXPATHLEN */

#include "xv.h"
#include "bitmaps.h"
#include "sunras.h"

#include <X11/Xatom.h>

#ifdef VMS
extern Window pseudo_root();
#endif


/* program needs one of the following fonts.  Trys them in ascending order */

#define FONT1 "-*-lucida-medium-r-*-*-12-*"
#define FONT2 "-*-helvetica-medium-r-*-*-12-*"
#define FONT3 "-*-helvetica-medium-r-*-*-11-*"
#define FONT4 "6x13"
#define FONT5 "fixed"

/* a mono-spaced font needed for the 'pixel value tracking' feature */
#define MFONT1 "-misc-fixed-medium-r-normal-*-13-*"
#define MFONT2 "6x13"   
#define MFONT3 "-*-courier-medium-r-*-*-12-*"
#define MFONT4 "fixed"   


/* file types that can be read */
#define UNKNOWN 0
#define GIF     1
#define PM      2
#define PBM     3
#define XBM     4
#define SUNRAS  5
#define UTAHRLE 6


#ifdef HAVE_JPEG
# define JFIF 7
#endif

#ifdef HAVE_TIFF
# ifdef HAVE_JPEG
#  define TIFF 8
# else
#  define TIFF 7
# endif
#endif

#ifdef HAVE_PDS
# if (defined(HAVE_TIFF) && defined(HAVE_JPEG))
#  define PDSVICAR 9
# else
#  if (defined(HAVE_TIFF) || defined(HAVE_JPEG))
#   define PDSVICAR 8
#  else
#   define PDSVICAR 7
#  endif
# endif
#endif

/* yech! */




static int    revvideo   = 0;   /* true if we should reverse video */
static int    dfltkludge = 0;   /* true if we're viewing dfltpic */
static int    keeparound = 1;   /* if true, don't quit after del. last image */
static int    autoquit = 0;     /* quit after loading first pic to rootW */
static int    autosmooth = 0;   /* smooth picture upon loading */
static int    autodither = 0;   /* colordither picture upon loading */
static int    autocrop   = 0;   /* autocrop picture upon loading */
static int    clearonload;      /* clear window/root (on colormap visuals) */
static float  expand = 1.0;     /* '-expand' argument */
static char  *maingeom = NULL;
static char   initpath[MAXPATHLEN];
static Atom   __SWM_VROOT = None;

/* used in DeleteCmd() */
static char  **mainargv;
static int     mainargc;


/* local function pre-definitions */
#ifdef __STDC__
static void Syntax(void);
static void RmodeSyntax(void);
static int  openPic(int);
static int  readpipe(char *, char *);
static void closePic(void);
static void OpenFirstPic(void);
static void OpenNextPic(void);
static void OpenNextQuit(void);
static void OpenNextLoop(void);
static void OpenPrevPic(void);
static void OpenNamedPic(void);
static void MainLoop(void);
static void CreateMainWindow(char *, char *);
static void smoothResize(int, int);
static void MakeDispNames(void);
static void stickInList(void);
static int  argcmp(char *, char *, int);
#else
static void Syntax(), RmodeSyntax(), closePic(), OpenFirstPic(), OpenNextPic();
static void OpenNextQuit(), OpenNextLoop(), OpenPrevPic(), OpenNamedPic();
static void MainLoop(), CreateMainWindow(), MakeDispNames(), stickInList();
static int  openPic(), argcmp(), readpipe();
static void smoothResize();
#endif


/*******************************************/
int main(argc, argv)
int   argc;
char *argv[];
/*******************************************/
{
  int   i, imap, ctrlmap, gmap, clrroot, nopos, limit2x;
  char *display, *whitestr, *blackstr, *histr, *lostr,
       *infogeom, *fgstr, *bgstr, *ctrlgeom, *gamgeom;
  char *rootfgstr, *rootbgstr, *visualstr;
  int  curstype, stdinflag, browseMode, savenorm, preview, pscomp;

  XColor ecdef;
  Window rootReturn, parentReturn, *children;
  unsigned int numChildren;

#ifdef VMS
  /* convert VMS-style arguments to unix names and glob */
  do_vms_wildcard(&argc, &argv);
  getredirection(&argc, &argv);
#endif

  /*****************************************************/
  /*** variable Initialization                       ***/
  /*****************************************************/

  GETWD(initpath);
  mainargv = argv;
  mainargc = argc;

  /* init internal variables */
  display = NULL;
  fgstr = bgstr = rootfgstr = rootbgstr = NULL;
  histr = lostr = whitestr = blackstr = NULL;
  visualstr = NULL;
  pic = epic = cpic = NULL;
  theImage = NULL;
  LocalCmap = 0;
  stdinflag = 0;
  autoclose = 1; 
  cmapInGam = 1;
  cmapinstalled = showzoomcursor = 0;

  /* init default colors */
  fgstr = "#000000";  bgstr = "#B2C0DC";
  histr = "#C6D5E2";  lostr = "#8B99B5";

  cmd = rindex(argv[0],'/');
  if (!cmd) cmd = argv[0]; else cmd++;

  tmpdir = (char *) getenv("TMPDIR");
  if (!tmpdir) tmpdir = "/tmp";

  /* init command-line options flags */
  infogeom = DEFINFOGEOM;  ctrlgeom = DEFCTRLGEOM;  
  gamgeom  = DEFGAMGEOM;

  ncols = -1;  noglob = 0;  mono = 0;  
  perfect = 0;  ninstall = 0;  fixedaspect = 0;
  DEBUG = 0;  bwidth = 2;
  nolimits = useroot = clrroot = noqcheck = rwcolor = owncmap = 0;
  waitsec = -1;  waitloop = 0;  automax = 0;
  rootMode = 0;  hsvmode = 0;
  nopos = limit2x = 0;
  resetroot = 1;
  clearonload = 0;
  curstype = XC_crosshair;
  browseMode = savenorm = nostat = 0;
  preview = 0;
  pscomp = 0;

  conv24 = CONV24_SLOW;  /* use 'slow' algorithm by default */

  defaspect = normaspect = 1.0;
  mainW = dirW = infoW = ctrlW = gamW = psW = (Window) NULL;

#ifdef HAVE_JPEG
  jpegW = (Window) NULL;
#endif

#ifdef HAVE_TIFF
  tiffW = (Window) NULL;
#endif

  imap = ctrlmap = gmap = 0;

  ch_offx = ch_offy = p_offx = p_offy = 0;

  /* init info box variables */
  infoUp = 0;
  infoMode = INF_STR;
  for (i=0; i<NISTR; i++) SetISTR(i,"");

  /* init ctrl box variables */
  ctrlUp = 0;
  curname = 0;
  formatStr[0] ='\0';

  gamUp = 0;

  Init24to8();

  /*****************************************************/
  /*** X Resource Initialization                     ***/
  /*****************************************************/

  /* once through the argument list to find the display name, if any */
  for (i=1; i<argc; i++) {
    if (!strncmp(argv[i],"-help",5)) { 	/* help */
      Syntax();
      exit(0);
    }

    else if (!argcmp(argv[i],"-display",4)) {  /* display */
      i++;
      if (i<argc) display = argv[i];
      break;
    }
  }

  /* open the display */
  if ( (theDisp=XOpenDisplay(display)) == NULL) {
    fprintf(stderr, "%s: Can't open display\n",argv[0]);
    exit(1);
  }



  if (rd_str ("aspect")) {
    int n,d;
    if (sscanf(def_str,"%d:%d",&n,&d)!=2 || n<1 || d<1)
      fprintf(stderr,"%s: unable to parse 'aspect' resource\n",cmd);
    else defaspect = (float) n / (float) d;
  }
      
  if (rd_flag("2xlimit"))        limit2x     = def_int;      
  if (rd_flag("autoClose"))      autoclose   = def_int;
  if (rd_flag("autoCrop"))       autocrop    = def_int;
  if (rd_flag("autoDither"))     autodither  = def_int;
  if (rd_flag("autoSmooth"))     autosmooth  = def_int;
  if (rd_str ("background"))     bgstr       = def_str;
  if (rd_flag("best24") && def_int)  conv24  = CONV24_BEST;
  if (rd_str ("black"))          blackstr    = def_str;
  if (rd_int ("borderWidth"))    bwidth      = def_int;
  if (rd_flag("browseMode"))     browseMode  = def_int;
  if (rd_str ("ctrlGeometry"))   ctrlgeom    = def_str;
  if (rd_flag("ctrlMap"))        ctrlmap     = def_int;
  if (rd_int ("cursor"))         curstype    = def_int;
  if (rd_str ("expand"))         expand      = atof(def_str);
  if (rd_flag("fixed"))          fixedaspect = def_int;
  if (rd_str ("foreground"))     fgstr       = def_str;
  if (rd_str ("geometry"))       maingeom    = def_str;
  if (rd_str ("ceditGeometry"))  gamgeom     = def_str;
  if (rd_flag("ceditMap"))       gmap        = def_int;
  if (rd_flag("ceditColorMap"))  cmapInGam   = def_int;
  if (rd_flag("clearOnLoad"))    clearonload = def_int;
  if (rd_flag("hsvMode"))        hsvmode     = def_int;
  if (rd_str ("highlight"))      histr       = def_str;
  if (rd_str ("infoGeometry"))   infogeom    = def_str;
  if (rd_flag("infoMap"))        imap        = def_int;
  if (rd_flag("keepAround"))     keeparound  = def_int;
  if (rd_str ("lowlight"))       lostr       = def_str;
  if (rd_flag("mono"))           mono        = def_int;
  if (rd_int ("ncols"))        { ncols = def_int; if (ncols>=0) noglob = 1; }
  if (rd_flag("nglobal"))        noglob      = def_int;
  if (rd_flag("ninstall"))       ninstall    = def_int;
  if (rd_flag("nolimits"))       nolimits    = def_int;
  if (rd_flag("nopos"))          nopos       = def_int;
  if (rd_flag("noqcheck"))       noqcheck    = def_int;
  if (rd_flag("nostat"))         nostat      = def_int;
  if (rd_flag("ownCmap"))        owncmap     = def_int;
  if (rd_flag("perfect"))        perfect     = def_int;
  if (rd_flag("pscompress"))     pscomp      = def_int;
  if (rd_flag("pspreview"))      preview     = def_int;
  if (rd_flag("quick24") && def_int)  conv24 = CONV24_FAST;
  if (rd_flag("resetroot"))      resetroot   = def_int;
  if (rd_flag("reverse"))        revvideo    = def_int;
  if (rd_str ("rootBackground")) rootbgstr   = def_str;
  if (rd_str ("rootForeground")) rootfgstr   = def_str;
  if (rd_int ("rootMode"))       rootMode    = def_int;
  if (rd_flag("rwColor"))        rwcolor     = def_int;
  if (rd_flag("saveNormal"))     savenorm    = def_int;
  if (rd_str ("visual"))         visualstr   = def_str;
  if (rd_str ("white"))          whitestr    = def_str;
      

  /*****************************************************/
  /*** Command Line Options                          ***/
  /*****************************************************/
  
  for (i=1, numnames=0; i<argc; i++) {
    if (argv[i][0] != '-') {   		/* a file name.  put it in list */
      if (numnames<MAXNAMES) {
	namelist[numnames++] = argv[i];
	if (numnames==MAXNAMES) {
	  fprintf(stderr,"%s: too many filenames.  Only using first %d.\n",
		  cmd, MAXNAMES);
	}
      }
    }

    else if (!strcmp(argv[i],  "-"))           /* stdin flag */
      stdinflag++;

    else if (!argcmp(argv[i],"-2xlimit",2))    /* 2xlimit */
      limit2x = !limit2x;

    else if (!argcmp(argv[i],"-aspect",2)) {   /* default aspect */
      int n,d;
      if (++i<argc) {
	if (sscanf(argv[i],"%d:%d",&n,&d)!=2 || n<1 || d<1)
	  fprintf(stderr,"%s: bad aspect ratio '%s'\n",cmd,argv[i]);
	else defaspect = (float) n / (float) d;
      }
    }

    else if (!argcmp(argv[i],"-best24",3))     /* ppmquant 24->8 conversion */
      conv24 = CONV24_BEST;
    
    else if (!argcmp(argv[i],"-bg",3))        /* background color */
      { if (++i<argc) bgstr = argv[i]; }

    else if (!argcmp(argv[i],"-black",3))     /* black color */
      { if (++i<argc) blackstr = argv[i]; }
    
    else if (!argcmp(argv[i],"-browse",3))    /* browse mode */
      browseMode = !browseMode;

    else if (!argcmp(argv[i],"-bw",3))        /* border width */
      { if (++i<argc) bwidth=atoi(argv[i]); }

    else if (!argcmp(argv[i],"-cecmap",4))	/* cmapInGam */
      cmapInGam = !cmapInGam;
    
    else if (!argcmp(argv[i],"-cegeometry",4))	/* gammageom */
      { if (++i<argc) gamgeom = argv[i]; }
    
    else if (!argcmp(argv[i],"-cemap",4))	/* gmap */
      gmap++;
    
    else if (!argcmp(argv[i],"-cgeometry",3))	/* ctrlgeom */
      { if (++i<argc) ctrlgeom = argv[i]; }
    
    else if (!argcmp(argv[i],"-clear",4))	/* clear */
      clrroot++;
    
    else if (!argcmp(argv[i],"-close",4))	/* close */
      autoclose = !autoclose;
    
    else if (!argcmp(argv[i],"-cmap",3))	/* ctrlmap */
      ctrlmap++;
    
    else if (!argcmp(argv[i],"-crop",3))        /* autocrop */
      autocrop = !autocrop;

    else if (!argcmp(argv[i],"-cursor",3))	/* cursor */
      { if (++i<argc) curstype = atoi(argv[i]); }

    else if (!argcmp(argv[i],"-DEBUG",2)) 	/* debug */
      { if (++i<argc) DEBUG = atoi(argv[i]); }

    else if (!argcmp(argv[i],"-display",4))     /* display */
      { if (++i<argc) display = argv[i]; }

    else if (!argcmp(argv[i],"-dither",4))      /* autodither */
      autodither = !autodither;

    else if (!argcmp(argv[i],"-expand",2)) 	/* expand factor */
      { if (++i<argc) expand=atof(argv[i]); }

    else if (!argcmp(argv[i],"-fg",3))          /* foreground color */
      { if (++i<argc) fgstr = argv[i]; }
    
    else if (!argcmp(argv[i],"-fixed",3))       /* fixed aspect ratio */
      fixedaspect++;
    
    else if (!argcmp(argv[i],"-geometry",2))	/* geometry */
      { if (++i<argc) maingeom = argv[i]; }
    
    else if (!argcmp(argv[i],"-hi",3))	        /* highlight */
      { if (++i<argc) histr = argv[i]; }
    
    else if (!argcmp(argv[i],"-hsv",3))         /* hsvmode */
      hsvmode = 1;
    
    else if (!argcmp(argv[i],"-igeometry",3))	/* infogeom */
      { if (++i<argc) infogeom = argv[i]; }
    
    else if (!argcmp(argv[i],"-imap",3))	/* imap */
      imap++;
    
    else if (!argcmp(argv[i],"-keeparound",2))  /* keeparound */
      keeparound = !keeparound;
    
    else if (!argcmp(argv[i],"-loadclear",4))   /* toggle clearonload */
      clearonload = !clearonload;

    else if (!argcmp(argv[i],"-lo",3))	        /* lowlight */
      { if (++i<argc) lostr = argv[i]; }
    
    else if (!argcmp(argv[i],"-max",4))	        /* auto maximize */
      automax++;
    
    else if (!argcmp(argv[i],"-maxpect",5))     /* auto maximize */
      { automax++; fixedaspect++; }
    
    else if (!argcmp(argv[i],"-mono",3))	/* mono */
      mono++;
    
    else if (!argcmp(argv[i],"-ncols",3))       /* ncols */
      { if (++i<argc) { ncols=abs(atoi(argv[i])); noglob++; } }
    
    else if (!argcmp(argv[i],"-nglobal",3))     /* no global colors */
      noglob++;
    
    else if (!argcmp(argv[i],"-ninstall",3))	/* don't install colormaps */
      ninstall=1;

    else if (!argcmp(argv[i],"-nolimits",4))    /* nolimits */
      nolimits = !nolimits;

    else if (!argcmp(argv[i],"-nopos",4))       /* nopos */
      nopos++;

    else if (!argcmp(argv[i],"-noqcheck",4))    /* noqcheck */
      noqcheck++;

    else if (!argcmp(argv[i],"-nostat",4))      /* nostat */
      nostat = !nostat;

    else if (!argcmp(argv[i],"-owncmap",2))     /* own colormap */
      owncmap++;

    else if (!argcmp(argv[i],"-perfect",2))     /* perfect colors */
      perfect++;  

    else if (!argcmp(argv[i],"-noresetroot",2)) /* reset root in window mode */
      resetroot = !resetroot;  
    
    else if (!argcmp(argv[i],"-quick24",5))     /* quick 24-to-8 conversion */
      conv24 = CONV24_FAST;
    
    else if (!argcmp(argv[i],"-quit",2))        /* auto-quit if -root */
      autoquit++;
    
    else if (!argcmp(argv[i],"-rbg",3))         /* root background color */
      { if (++i<argc) rootbgstr = argv[i]; }
    
    else if (!argcmp(argv[i],"-rfg",3))         /* root foreground color */
      { if (++i<argc) rootfgstr = argv[i]; }
    
    else if (!argcmp(argv[i],"-rgb",4))         /* rgb mode */
      hsvmode = 0;
    
    else if (!argcmp(argv[i],"-rmode",3))	/* root pattern */
      { if (++i<argc) rootMode = abs(atoi(argv[i])); useroot++; }
    
    else if (!argcmp(argv[i],"-root",3))        /* use root window */
      useroot++;
    
    else if (!argcmp(argv[i],"-rv",3))          /* reverse video */
      revvideo = !revvideo;
    
    else if (!argcmp(argv[i],"-rw",3))          /* use r/w color */
      rwcolor = !rwcolor;
    
    else if (!argcmp(argv[i],"-slow24",3))      /* slow 24-to-8 conversion */
      conv24 = CONV24_SLOW;
    
    else if (!argcmp(argv[i],"-smooth",3))      /* autosmooth */
      autosmooth = !autosmooth;

    else if (!argcmp(argv[i],"-visual",2))	/* visual */
      { if (++i<argc) visualstr = argv[i]; }
    
    else if (!argcmp(argv[i],"-wait",3)) {      /* secs to wait between pics */
      if (++i<argc) {
	waitsec = abs(atoi(argv[i]));
	if (waitsec<0) waitsec = 0;
      }
    }
    
    else if (!argcmp(argv[i],"-white",3))	/* white color */
      { if (++i<argc) whitestr = argv[i]; }
    
    else if (!argcmp(argv[i],"-wloop",3))	/* waitloop */
      waitloop++;
    
    else if (!argcmp(argv[i],"-nolimits",3))	/* waitloop */
      nolimits++;
    
    else Syntax();
  }

  RANGE(curstype,0,254);
  curstype = curstype & 0xfe;   /* clear low bit to make curstype even */

  if (expand == 0.0) Syntax();
  if (rootMode < 0 || rootMode > RM_MAX) RmodeSyntax();

  if (DEBUG) XSynchronize(theDisp, True);

  /* if using root, generally gotta map ctrl window, 'cause there won't be
     any way to ask for it.  (no kbd or mouse events from rootW) */
  if (useroot && !autoquit) ctrlmap = -1;    

  /* must not install colormaps on rootW */
  if (useroot) { owncmap = perfect=0;  noglob = 1; } 

  if (owncmap) perfect = 1;

  if (nopos) { maingeom = infogeom = ctrlgeom = gamgeom = NULL; }

  /* if -root and -maxp, make sure we're using a 'centered' mode */
  if (useroot && fixedaspect && automax && rootMode < RM_CENTER)
    rootMode = RM_CSOLID;


  /*****************************************************/
  /*** X Setup                                       ***/
  /*****************************************************/
  
  theScreen = DefaultScreen(theDisp);
  theCmap   = DefaultColormap(theDisp, theScreen);
  rootW     = RootWindow(theDisp,theScreen);
  theGC     = DefaultGC(theDisp,theScreen);
  theVisual = DefaultVisual(theDisp,theScreen);
  ncells    = DisplayCells(theDisp, theScreen);
  dispDEEP  = DisplayPlanes(theDisp,theScreen);
  vrWIDE = dispWIDE  = DisplayWidth(theDisp,theScreen);
  vrHIGH = dispHIGH  = DisplayHeight(theDisp,theScreen);
  maxWIDE = dispWIDE;  maxHIGH = dispHIGH;

  if (visualstr) {     /* handle non-default visual */
    int vclass = -1;
    lower_str(visualstr);
    if      (!strcmp(visualstr,"staticgray"))  vclass = StaticGray;
    else if (!strcmp(visualstr,"staticcolor")) vclass = StaticColor;
    else if (!strcmp(visualstr,"truecolor"))   vclass = TrueColor;
    else if (!strcmp(visualstr,"grayscale"))   vclass = GrayScale;
    else if (!strcmp(visualstr,"pseudocolor")) vclass = PseudoColor;
    else if (!strcmp(visualstr,"directcolor")) vclass = DirectColor;
    else {
      fprintf(stderr,"%s: Unrecognized visual type '%s'.  %s\n",
	      cmd, visualstr, "Using server default.");
    }

    if (vclass >= 0) {   /* try to find asked-for visual type */
      XVisualInfo *vinfo, rvinfo;
      int numvis, best;

      rvinfo.class = vclass;
      vinfo = XGetVisualInfo(theDisp, VisualClassMask, &rvinfo, &numvis);
      if (vinfo) {       /* choose the 'best' one, if multiple */
	for (i=0, best=0; i<numvis; i++) {
	  if (vinfo[i].depth > vinfo[best].depth) best = i;
	}
	theVisual = vinfo[best].visual;
	if (DEBUG) {
	  fprintf(stderr,"%s: using %s visual, depth = %d, screen = %d\n",
		  cmd, visualstr, vinfo[best].depth, vinfo[best].screen);
	  fprintf(stderr,"\tmasks: (0x%x,0x%x,0x%x), bits_per_rgb=%d\n",
		  vinfo[best].red_mask, vinfo[best].green_mask,
		  vinfo[best].blue_mask, vinfo[best].bits_per_rgb);
	}

	dispDEEP = vinfo[best].depth;
	theScreen = vinfo[best].screen;
	rootW = RootWindow(theDisp, theScreen);
	ncells = vinfo[best].colormap_size;
	theGC = DefaultGC(theDisp, theScreen);
	vrWIDE = dispWIDE  = DisplayWidth(theDisp,theScreen);
	vrHIGH = dispHIGH  = DisplayHeight(theDisp,theScreen);
	maxWIDE = dispWIDE;  maxHIGH = dispHIGH;
	theCmap = XCreateColormap(theDisp, rootW, theVisual, AllocNone);
#ifdef FOO
	theCmap = DefaultColormap(theDisp, theScreen);
#endif

	XFree((char *) vinfo);
      }
      else fprintf(stderr,"%s: Visual type '%s' not available.  %s\n",
		   cmd, visualstr, "Using server default.");
    }
  }
    

  if (!useroot && limit2x) { maxWIDE *= 2;  maxHIGH *= 2; }
  if (nolimits) { maxWIDE = 65000; maxHIGH = 65000; }

  XSetErrorHandler(xvErrorHandler);

  /* always search for virtual root window */
  vrootW = rootW;
#ifndef VMS
  __SWM_VROOT = XInternAtom(theDisp, "__SWM_VROOT", False);
  XQueryTree(theDisp, rootW, &rootReturn, &parentReturn, &children,
	     &numChildren);
  for (i = 0; i < numChildren; i++) {
    Atom actual_type;
    int actual_format;
    unsigned long nitems, bytesafter;
    Window *newRoot = NULL;
    XWindowAttributes xwa;
    if (XGetWindowProperty (theDisp, children[i], __SWM_VROOT, 0, 1,
	  False, XA_WINDOW, &actual_type, &actual_format, &nitems,
	  &bytesafter, (unsigned char **) &newRoot) == Success && newRoot) {
      vrootW = *newRoot;
      XGetWindowAttributes(theDisp, vrootW, &xwa);
      vrWIDE = xwa.width;  vrHIGH = xwa.height;
      dispDEEP = xwa.depth;
      break;
    }
  }
#else  /* VMS */
  vrootW = pseudo_root(theDisp, theScreen);
#endif

  if (clrroot || useroot) {
    /* have enough info to do a '-clear' now */
    KillOldRootInfo();   /* if any */
    if (resetroot || clrroot) ClearRoot();  /* don't clear on '-noresetroot' */
    if (clrroot) Quit(0);
  }


  arrow     = XCreateFontCursor(theDisp,XC_top_left_arrow);
  cross     = XCreateFontCursor(theDisp,curstype);
  tcross    = XCreateFontCursor(theDisp,XC_tcross);
  zoom      = XCreateFontCursor(theDisp,XC_sizing);

  {
    XColor fc, bc;
    fc.red = fc.green = fc.blue = 0xffff;
    bc.red = bc.green = bc.blue = 0x0000;
    
    XRecolorCursor(theDisp, zoom, &fc, &bc);
  }

  { /* create invisible cursor */
    Pixmap pix;
    static char bits[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    XColor col;

    col.red = col.green = col.blue = 0;

    pix = XCreateBitmapFromData(theDisp, rootW, bits, 8, 8);
    inviso = XCreatePixmapCursor(theDisp, pix, pix, &col, &col, 0,0);
    XFreePixmap(theDisp, pix);
  }



  /* set up white,black colors */
  white = WhitePixel(theDisp,theScreen);  whtRGB = 0xffffff;
  black = BlackPixel(theDisp,theScreen);  blkRGB = 0x000000;

  if (whitestr && XParseColor(theDisp, theCmap, whitestr, &ecdef) &&
      xvAllocColor(theDisp, theCmap, &ecdef))  {
    white = ecdef.pixel;
    whtRGB = ((ecdef.red>>8)<<16) | (ecdef.green&0xff00) | (ecdef.blue>>8);
  }

  if (blackstr && XParseColor(theDisp, theCmap, blackstr, &ecdef) &&
      xvAllocColor(theDisp, theCmap, &ecdef))  {
    black = ecdef.pixel;
    blkRGB = ((ecdef.red>>8)<<16) | (ecdef.green&0xff00) | (ecdef.blue>>8);
  }


  /* set up fg,bg colors */
  fg = black;   bg = white;  
  if (fgstr && XParseColor(theDisp, theCmap, fgstr, &ecdef) &&
      xvAllocColor(theDisp, theCmap, &ecdef)) {
    fg = ecdef.pixel;
  }

  if (bgstr && XParseColor(theDisp, theCmap, bgstr, &ecdef) &&
      xvAllocColor(theDisp, theCmap, &ecdef))  {
    bg = ecdef.pixel;
  }


  /* set up root fg,bg colors */
  rootfg = white;   rootbg = black;
  if (rootfgstr && XParseColor(theDisp, theCmap, rootfgstr, &ecdef) &&
      xvAllocColor(theDisp, theCmap, &ecdef))  rootfg = ecdef.pixel;
  if (rootbgstr && XParseColor(theDisp, theCmap, rootbgstr, &ecdef) &&
      xvAllocColor(theDisp, theCmap, &ecdef))  rootbg = ecdef.pixel;


  /* set up hi/lo colors */
  i=0;
  if (dispDEEP > 1) {   /* only if we're on a reasonable display */
    if (histr && XParseColor(theDisp, theCmap, histr, &ecdef) &&
	xvAllocColor(theDisp, theCmap, &ecdef))  { hicol = ecdef.pixel; i|=1; }
    if (lostr && XParseColor(theDisp, theCmap, lostr, &ecdef) &&
	xvAllocColor(theDisp, theCmap, &ecdef))  { locol = ecdef.pixel; i|=2; }
  }

  if      (i==0) ctrlColor = 0;
  else if (i==3) ctrlColor = 1;
  else {  /* only got some of them */
    if (i&1) xvFreeColors(theDisp, theCmap, &hicol, 1, 0L);
    if (i&2) xvFreeColors(theDisp, theCmap, &locol, 1, 0L);
    ctrlColor = 0;
  }

  XSetForeground(theDisp,theGC,fg);
  XSetBackground(theDisp,theGC,bg);

  infofg = fg;  infobg = bg;

  /* if '-mono' not forced, determine if we're on a grey or color monitor */
  if (!mono) {
    if (DEBUG) fprintf(stderr,"%s: VisualClass = %d\n",cmd, theVisual->class);
    if (theVisual->class == StaticGray || theVisual->class == GrayScale)
      mono = 1;
  }
  


  iconPix = XCreatePixmapFromBitmapData(theDisp, rootW, icon_bits,
	     icon_width, icon_height, 1, 0, 1);

  iconmask = XCreatePixmapFromBitmapData(theDisp, rootW, iconmask_bits,
	     icon_width, icon_height, 1, 0, 1);



  /* try to load fonts */
  if ( (mfinfo = XLoadQueryFont(theDisp,FONT1))==NULL && 
       (mfinfo = XLoadQueryFont(theDisp,FONT2))==NULL && 
       (mfinfo = XLoadQueryFont(theDisp,FONT3))==NULL && 
       (mfinfo = XLoadQueryFont(theDisp,FONT4))==NULL && 
       (mfinfo = XLoadQueryFont(theDisp,FONT5))==NULL) {
    sprintf(str,
	    "couldn't open the following fonts:\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s",
	    FONT1, FONT2, FONT3, FONT4, FONT5);
    FatalError(str);
  }
  mfont=mfinfo->fid;
  XSetFont(theDisp,theGC,mfont);
  
  if ( (monofinfo = XLoadQueryFont(theDisp,MFONT1))==NULL && 
       (monofinfo = XLoadQueryFont(theDisp,MFONT2))==NULL && 
       (monofinfo = XLoadQueryFont(theDisp,MFONT3))==NULL && 
       (monofinfo = XLoadQueryFont(theDisp,MFONT4))==NULL) {
    sprintf(str,"couldn't open the following fonts:\n\t%s\n\t%s\n\t%s\n\t%s",
	    MFONT1, MFONT2, MFONT3, MFONT4);
    FatalError(str);
  }
  monofont=monofinfo->fid;
  



  /* if ncols wasn't set, set it to 2^dispDEEP, unless dispDEEP=1, in which
     case ncols = 0;  (ncols = max number of colors allocated.  on 1-bit
     displays, no colors are allocated */

  if (ncols == -1) {
    if (dispDEEP>1) ncols = 1 << ((dispDEEP>8) ? 8 : dispDEEP);
    else ncols = 0;
  }
  else if (ncols>256) ncols = 256;       /* so program doesn't blow up */



  /* no filenames.  build one-name (stdio) list (if stdinflag) */
  if (numnames==0) {
    if (stdinflag) {  namelist[0] = STDINSTR; numnames = 1; }
    else           {  namelist[0] = NULL;     numnames = 0; }
  }

  if (numnames) MakeDispNames();



  /* create the info box window */
  CreateInfo(infogeom);
  XSelectInput(theDisp, infoW, ExposureMask | ButtonPressMask | KeyPressMask
	       | StructureNotifyMask);
  InfoBox(imap);     /* map it (or not) */
  if (imap) {
    RedrawInfo(0,0,INFOWIDE,INFOHIGH);  /* explicit draw if mapped */
    XFlush(theDisp);
  }


  /* create the control box window */
  CreateCtrl(ctrlgeom);
  epicmode = EM_RAW;   SetEpicMode();
  XSelectInput(theDisp, ctrlW, ExposureMask | ButtonPressMask | KeyPressMask
	       | StructureNotifyMask);
  if (ctrlmap < 0) {    /* map iconified */
    XWMHints xwmh;
    xwmh.initial_state = IconicState;
    xwmh.flags = StateHint;
    XSetWMHints(theDisp, ctrlW, &xwmh);
    ctrlmap = 1;
  }
  CtrlBox(ctrlmap);     /* map it (or not) */
  if (ctrlmap) {
    RedrawCtrl(0,0,CTRLWIDE,CTRLHIGH);   /* explicit draw if mapped */
    XFlush(theDisp);
  }


  /* create the directory window */
  CreateDirW(NULL);
  XSelectInput(theDisp, dirW, ExposureMask | ButtonPressMask | KeyPressMask);
  browseCB.val = browseMode;
  savenormCB.val = savenorm;


  /* create the gamma window */
  CreateGam(gamgeom);
  XSelectInput(theDisp, gamW, ExposureMask | ButtonPressMask | KeyPressMask
	       | StructureNotifyMask
	       | (cmapInGam ? ColormapChangeMask : 0));

  GamBox(gmap);     /* map it (or not) */


  /* create the ps window */
  CreatePSD(NULL);
  encapsCB.val = preview;
  pscompCB.val = pscomp;

#ifdef HAVE_JPEG
  CreateJPEGW();
#endif

#ifdef HAVE_TIFF
  CreateTIFFW();
#endif

  GenerateFSGamma();

  LoadFishCursors();
  SetCursors(-1);

  /* if we're not on a colormapped display, turn off rwcolor */
  if (theVisual->class != PseudoColor && 
      theVisual->class != GrayScale && rwcolor) {
    fprintf(stderr,"xv: not a colormapped display.  'rwcolor' turned off.\n");
    rwcolor = 0;
  }

  XSelectInput(theDisp, rootW, ColormapChangeMask);

  /* Do The Thing... */
  MainLoop();
  Quit(0);
  return(0);
}




/***********************************/
static int cpos = 0;
void printoption(st)
char *st;
{
  if (strlen(st) + cpos > 78) {
    fprintf(stderr,"\n   ");
    cpos = 3;
  }

  fprintf(stderr,"%s ",st);
  cpos = cpos + strlen(st) + 1;
}

static void Syntax()
{
  fprintf(stderr, "Usage:\n");
  printoption(cmd);
  printoption("[-]");
  printoption("[-2xlimit]");
  printoption("[-aspect w:h]");
  printoption("[-best24]");
  printoption("[-bg color]");
  printoption("[-black color]");
  printoption("[-browse]");
  printoption("[-bw width]");
  printoption("[-cecmap]");
  printoption("[-cegeometry geom]");
  printoption("[-cemap]");
  printoption("[-cgeometry geom]");
  printoption("[-clear]");
  printoption("[-close]");
  printoption("[-cmap]");
  printoption("[-crop]");
  printoption("[-cursor char#]");
  printoption("[-DEBUG level]");
  printoption("[-display disp]");
  printoption("[-dither]");
  printoption("[-expand exp]");
  printoption("[-fg color]");
  printoption("[-fixed]");
  printoption("[-geometry geom]");
  printoption("[-help]");
  printoption("[-hi color]");
  printoption("[-hsv]");
  printoption("[-igeometry geom]");
  printoption("[-imap]");
  printoption("[-keeparound]");
  printoption("[-lo color]");
  printoption("[-loadclear]");
  printoption("[-max]");
  printoption("[-maxpect]");
  printoption("[-mono]");
  printoption("[-ncols #]");
  printoption("[-nglobal]");
  printoption("[-ninstall]");
  printoption("[-nolimits]");
  printoption("[-nopos]");
  printoption("[-noqcheck]");
  printoption("[-noresetroot]");
  printoption("[-nostat]");
  printoption("[-owncmap]");
  printoption("[-perfect]");
  printoption("[-quick24]");
  printoption("[-quit]");
  printoption("[-rbg color]");
  printoption("[-rfg color]");
  printoption("[-rgb]");
  printoption("[-rmode #]");
  printoption("[-root]");
  printoption("[-rv]");
  printoption("[-rw]");
  printoption("[-slow24]");
  printoption("[-smooth]");
  printoption("[-visual type]");
  printoption("[-wait seconds]");
  printoption("[-white color]");
  printoption("[-wloop]");
  printoption("[filename ...]");
  fprintf(stderr,"\n\n");
  Quit(1);
}


/***********************************/
static void RmodeSyntax()
{
  fprintf(stderr,"%s: unknown root mode '%d'.  Valid modes are:\n", 
	  cmd, rootMode);
  fprintf(stderr,"\t0: tiling\n");
  fprintf(stderr,"\t1: integer tiling\n");
  fprintf(stderr,"\t2: mirrored tiling\n");
  fprintf(stderr,"\t3: integer mirrored tiling\n");
  fprintf(stderr,"\t4: centered tiling\n");
  fprintf(stderr,"\t5: centered on a solid background\n");
  fprintf(stderr,"\t6: centered on a 'warp' background\n");
  fprintf(stderr,"\t7: centered on a 'brick' background\n");
  fprintf(stderr,"\n");
  Quit(1);
}


/***********************************/
static int argcmp(a1, a2, minlen)
char *a1, *a2;
int minlen;
{
  /* does a string compare between a1 and a2.  To return '0', a1 and a2 
     must match to the length of a2, and that length has to
     be at least 'minlen'.  Otherwise, return non-zero */

  if (strlen(a1) < minlen || strlen(a2) < minlen) return 1;
  if (strlen(a1) > strlen(a2)) return 1;

  return (strncmp(a1, a2, strlen(a1)));
}


/***********************************/
static int openPic(filenum)
int filenum;
{
  /* tries to load file #filenum (from 'namelist' list)
   * returns 0 on failure (cleans up after itself)
   * if successful, returns 1, creates mainW
   *
   * By the way, I'd just like to point out that this procedure has gotten
   * *way* out of hand...
   */

  int   i,filetype,freename, nw, nh, frompipe;
  int   oldeWIDE, oldeHIGH;
  char *tmp;
  FILE *fp;
  char *fullname,      /* full name of the original file */
        filename[256], /* full name of the file to be loaded (could be /tmp) */
        globnm[512];   /* globbed version of fullname of orig file */
  byte  magicno[8];    /* first 8 bytes of file */

  normaspect = defaspect;
  freename = dfltkludge = frompipe = 0;
  oldeWIDE = eWIDE;  oldeHIGH = eHIGH;

  WaitCursor();

  if (filenum == DFLTPIC) {
    filename[0] = '\0';  basefname[0] = '\0';  fullname = "";
    dfltkludge = 1;
    LoadDfltPic();
    goto GOTIMAGE;
  }


  if (filenum == GRABBED) {
    filename[0] = '\0';  basefname[0] = '\0';  fullname = "";
    LoadGrab();
    goto GOTIMAGE;
  }


  if (filenum != LOADPIC) {
    if (filenum >= nList.nstr && filenum < 0) goto FAILED;
    curname = filenum;
    nList.selected = curname;
    ScrollToCurrent(&nList);  /* have scrl/list show current */
    XFlush(theDisp);    /* update NOW */
  }

  /* clear any old error messages */

  formatStr[0] = '\0';
  SetISTR(ISTR_INFO,"");
  SetISTR(ISTR_WARNING,"");
  infoMode = INF_STR;

  /* set up fullname and basefname */

  if (filenum == LOADPIC) {
    fullname = GetDirFName();
    if (fullname[0] == '~') {
      strcpy(globnm, fullname);
      Globify(globnm);
      fullname = globnm;
    }
    else if (ISPIPE(fullname[0])) {    /* read from a pipe. */
      strcpy(filename, fullname);
      if (readpipe(fullname, filename)) goto FAILED;
      frompipe = 1;
    }
  }
  else fullname = namelist[filenum];


  /* compute basefname */
  tmp = rindex(fullname,'/');
  if (!tmp) tmp = fullname; else tmp++;
  strcpy(basefname,tmp);


  if (filenum == LOADPIC && ISPIPE(fullname[0])) {
    /* if we're reading from a pipe, 'filename' will have the /tmp/xvXXXXXX
       filename, and we can skip a lot of stuff:  (such as dealing with
       compressed files, prepending 'initpath' to relative paths, dealing
       with reading from stdin */

    /* at this point, fullname = "! do some commands",
                      filename = "/tmp/xv123456",
		  and basefname= "xv123456" */
  }

  else {
#ifndef VMS
    if (strlen(basefname)>2 && strcmp(basefname+strlen(basefname)-2,".Z")==0) 
      basefname[strlen(basefname)-2]='\0';     /* chop off .Z, if any */
#endif

    /* if fullname doesn't start with a '/' (ie, it's a relative path), 
       (and it's not LOADPIC and it's not the special case '<stdin>') 
       prepend 'initpath' to it */
    
    if (filenum != LOADPIC && fullname[0] != '/' && 
	strcmp(fullname,STDINSTR)!=0) {
      char *tmp;

#ifndef VMS
      tmp = (char *) malloc(strlen(fullname) + strlen(initpath) + 2);
      if (!tmp) FatalError("malloc 'filename' failed");
      sprintf(tmp,"%s/%s", initpath, fullname);
#else  /* it is VMS */
      tmp = (char *) malloc(strlen(fullname) + 2);
      if (!tmp) FatalError("malloc 'filename' failed");
      sprintf(tmp,"%s", fullname);
#endif

      fullname = tmp;
      freename = 1;
    }
    

    /* uncompress if it's a .Z file */

    i = strlen(fullname);
#ifndef VMS
    if (i>2 && strcmp(fullname+i-2,".Z")==0) {
      sprintf(filename,"%s/xvXXXXXX",tmpdir);
      mktemp(filename);
      sprintf(str,"%s -c %s >%s",UNCOMPRESS,fullname,filename);
      SetISTR(ISTR_INFO,"Uncompressing '%s'...",basefname);
      if (system(str)) {
	SetISTR(ISTR_INFO,"Unable to uncompress '%s'.", basefname);
	Warning();
	goto FAILED;
      }
      WaitCursor();
    }
    else strcpy(filename,fullname);

#else  /* it is VMS */

    tmp = strstr(basefname,".Z");
    if (!tmp) tmp = strstr(basefname,".z");

#ifdef HAVE_LZW
    if (!tmp) tmp = strstr(basefname,"_Z");
    if (!tmp) tmp = strstr(basefname,"_z");
#else
    if (tmp) *tmp = '\0';
#endif

    if (i>2 && tmp) {
      strcpy(filename,"Sys$Disk:[]xvXXXXXX");
      mktemp(filename);

#ifdef HAVE_LZW
      sprintf(str,"%s %s %s", UNCOMPRESS, basefname, filename);
#else
      sprintf(str,"%s %s", UNCOMPRESS, basefname);
#endif

      if (!system(str)) {
	SetISTR(ISTR_INFO,"Unable to uncompress '%s'.", basefname);
	Warning();
	goto FAILED;
      }

#ifndef HAVE_LZW
      sprintf(str,"Rename %s %s", basefname, filename);
      SetISTR(ISTR_INFO,"Renaming '%s'...",fullname);
      if (!system(str)) {
	SetISTR(ISTR_INFO,"Unable to rename '%s'.", fullname);
	Warning();
	goto FAILED;
      }
#endif /* HAVE_LZW */

      WaitCursor();
    }
    else strcpy(filename,fullname);

#endif /* not VMS */

    
    
    /* if the file is stdio, write it out to a temp file */
    if (strcmp(filename,STDINSTR)==0) {
      FILE *fp;

#ifndef VMS      
      sprintf(filename,"%s/xvXXXXXX",tmpdir);
#else /* it is VMS */
      sprintf(filename, "Sys$Disk:[]xvXXXXXX");
#endif
      mktemp(filename);
      
      fp = fopen(filename,WRITE_BINARY);
      if (!fp) FatalError("openPic(): can't write temporary file");
    
      while ( (i=getchar()) != EOF) putc(i,fp);
      fclose(fp);
    }
  }


  /* now, try to determine what type of file we've got by reading the
     first couple bytes and looking for a Magic Number */

#ifndef VMS
  fp=fopen(filename,READ_BINARY);
#else   /* it is VMS */
  /* Open with optional arguments to force stream access in order to
     get around problems with stream_CR created by pathworks (mac) */ 
  fp=fopen (filename,"rb","ctx=stm");
#endif

  if (!fp) {
    static char *foo[] = { "\nBummer!" };
    char  str[512];
    sprintf(str,"Can't open file '%s'\n\n  %s.",filename,
#ifndef VMS
	    sys_errlist[errno]
#else
    strerror(errno, vaxc$errno)
#endif
	    );

    PopUp(str, foo, 1);
    goto FAILED;
  }

  fread(magicno,8,1,fp);  
  fclose(fp);

  filetype = UNKNOWN;
  if (strncmp((char *) magicno,"GIF87a",6)==0 ||
      strncmp((char *) magicno,"GIF89a",6)==0) filetype = GIF;

  else if (strncmp((char *) magicno,"VIEW",4)==0 ||
	   strncmp((char *) magicno,"WEIV",4)==0) filetype = PM;

  else if (magicno[0] == 'P' && magicno[1]>='1' && 
	   magicno[1]<='6') filetype = PBM;

  else if (strncmp((char *) magicno,"#define",7)==0) filetype = XBM;

  else if (magicno[0]==0x59 && (magicno[1]&0x7f)==0x26 &&
	   magicno[2]==0x6a && (magicno[3]&0x7f)==0x15) filetype = SUNRAS;

  else if (magicno[0]==0x52 && magicno[1]==0xcc) filetype = UTAHRLE;

#ifdef HAVE_JPEG
  else if (magicno[0]==0xff && magicno[1]==0xd8 && 
	   magicno[2]==0xff) filetype = JFIF;
#endif

#ifdef HAVE_TIFF
  else if ((magicno[0]=='M' && magicno[1]=='M') ||
	   (magicno[0]=='I' && magicno[1]=='I')) filetype = TIFF;
#endif

#ifdef HAVE_PDS
  else if (strncmp((char *) magicno,  "NJPL1I00",8)==0 || /* fixed-len pds */
	   strncmp((char *) magicno+2,"NJPL1I",  6)==0 || /* vger+other pds */
           strncmp((char *) magicno,  "CCSD3ZF", 7)==0 || /* vikng pds browse*/
	   strncmp((char *) magicno+2,"CCSD3Z",  6)==0 || /* vik. huffman pds*/
	   strncmp((char *) magicno,  "LBLSIZE=",8)==0)   /* vicar */
      filetype = PDSVICAR;
#endif


  if (filetype == UNKNOWN) {
    SetISTR(ISTR_INFO,"'%s' not in a recognized format.", basefname);
    Warning();
    goto FAILED;
  }


  SetISTR(ISTR_INFO,"Loading...");

  switch (filetype) {
  case GIF:         i = LoadGIF(filename,ncols);     break;
  case PM:          i = LoadPM (filename,ncols);     break;
  case PBM:         i = LoadPBM(filename,ncols);     break;
  case XBM:         i = LoadXBM(filename,ncols);     break;
  case SUNRAS:      i = LoadSunRas(filename, ncols); break;
  case UTAHRLE:     i = LoadRLE(filename, ncols);    break;

#ifdef HAVE_JPEG
  case JFIF:        i = LoadJFIF(filename, ncols);   break;
#endif

#ifdef HAVE_TIFF
  case TIFF:        i = LoadTIFF(filename, ncols);   break;
#endif

#ifdef HAVE_PDS
  case PDSVICAR:    i = LoadPDS(filename, ncols);    break;
#endif
  }


  WaitCursor();

  if (i) {
    SetISTR(ISTR_INFO,"Couldn't load file '%s'.",filename);
    Warning();
    goto FAILED;
  }

  if (pWIDE==0 || pHIGH==0) {  /* shouldn't happen, but let's be safe */
    SetISTR(ISTR_INFO,"Image size '0x0' not allowed.");
    Warning();
    goto FAILED;
  }

    
 GOTIMAGE:
  /* successfully read this picture.  No failures from here on out */

  /* stick this file in the 'ctrlW' name list */
  if (filenum == LOADPIC && !frompipe) stickInList();

  /* clear old image WINDOW before we start dicking with colors... */
  if (mainW && !useroot && clearonload &&
      (theVisual->class == PseudoColor || theVisual->class == GrayScale)) {
    XClearArea(theDisp, mainW, 0, 0, eWIDE, eHIGH, True);
    XFlush(theDisp);
  }

  if (!browseCB.val && filenum == LOADPIC) DirBox(0);   /* close the DirBox */


  /* if we read a /tmp file, delete it.  won't be needing it any more */
  if (strcmp(fullname,filename)!=0) unlink(filename);


  SetISTR(ISTR_INFO,formatStr);
	
  SetInfoMode(INF_PART);
  SetISTR(ISTR_FILENAME, (filenum==DFLTPIC || filenum==GRABBED || frompipe)
	                   ? "<none>" : basefname);

  SetISTR(ISTR_RES,"%d x %d",pWIDE,pHIGH);
  SetISTR(ISTR_COLOR,"");

  /* adjust button in/activity */
  BTSetActive(&but[BCROP],0);  /* new picture, draw no cropping rectangle */
  BTSetActive(&but[BUNCROP], 0);
  BTSetActive(&but[BNEXT], (curname<numnames-1));
  BTSetActive(&but[BPREV], (curname>0));


  normFact = 1;  nw = pWIDE;  nh = pHIGH;
  /* if pic is larger than screen, half picture until it fits on screen */
  while (nw > maxWIDE || nh > maxHIGH) {
    nw = nw / 2;  
    nh = nh / 2;
    normFact = normFact * 2;
  }

  if (normFact != 1) {
    char tstr[128];
    tmp = GetISTR(ISTR_WARNING);
    if (strlen(tmp)>0) sprintf(tstr, "%s  Shrunk %dX.", tmp, normFact);
                  else sprintf(tstr, "Shrunk %dX.", normFact);
    SetISTR(ISTR_WARNING,tstr);
  }

  /* expand:  if expansion is negative, treat it as a reciprocal */
  if (expand < 0.0) { eWIDE=(int)(pWIDE/-expand);  eHIGH=(int)(pHIGH/-expand); }
               else { eWIDE=(int)(pWIDE* expand);  eHIGH=(int)(pHIGH* expand); }

  if (eWIDE>maxWIDE || eHIGH>maxHIGH) {
    eWIDE = eWIDE / normFact;  eHIGH = eHIGH / normFact;
  }


  if (useroot) {
    int i,x,y;  unsigned int w,h;
    i = XParseGeometry(maingeom,&x,&y,&w,&h);
    if (i&WidthValue)  eWIDE = w;
    if (i&HeightValue) eHIGH = h;
    RANGE(eWIDE,1,maxWIDE);  RANGE(eHIGH,1,maxHIGH);

    /* handle case where they only specified width *or* height */
    if (( i&WidthValue && ~i&HeightValue) ||
	(~i&WidthValue &&  i&HeightValue)) {

      if (i&WidthValue) { eHIGH = (pHIGH * eWIDE) / pWIDE; }
                   else { eWIDE = (pWIDE * eHIGH) / pHIGH; }

      RANGE(eWIDE,1,maxWIDE);  RANGE(eHIGH,1,maxHIGH);
    }

    if (rootMode == RM_TILE || rootMode == RM_IMIRROR) {
      /* make picture size a divisor of the rootW size.  round down */
      i = (dispWIDE + eWIDE-1) / eWIDE;   eWIDE = (dispWIDE + i-1) / i;
      i = (dispHIGH + eHIGH-1) / eHIGH;   eHIGH = (dispHIGH + i-1) / i;
    }
  }

  cpic = pic;  cWIDE = pWIDE;  cHIGH = pHIGH;  cXOFF = cYOFF = 0;
  SetCropString(but[BCROP].active);

  if (automax) { 
    eWIDE = dispWIDE;  eHIGH = dispHIGH;
    if (fixedaspect) FixAspect(0,&eWIDE,&eHIGH);
  }

  if (useroot) {
    mainW = vrootW;

    if ((theVisual->class == PseudoColor || theVisual->class == GrayScale)
	&& clearonload) {
      /* clear old root pixmap before doing the 'alloc colors scene' 
	 to avoid annoying 'rainbow' effect as colors are realloced
	 (unless we said '-noresetroot') */
      ClearRoot();
    }
  }


  SortColormap();

  /* see if image is a b/w bitmap.  If so, use '-black' and '-white' colors */
  if (numcols == 2) {
    if ((r[0] == g[0] && r[0] == b[0] && r[0] == 255) &&
	(r[1] == g[1] && r[1] == b[1] && r[1] ==   0)) {  /* 0=wht, 1=blk */
      r[0] = (whtRGB>>16)&0xff;  g[0] = (whtRGB>>8)&0xff;  b[0] = whtRGB&0xff;
      r[1] = (blkRGB>>16)&0xff;  g[1] = (blkRGB>>8)&0xff;  b[1] = blkRGB&0xff;
    }
    else if ((r[0] == g[0] && r[0] == b[0] && r[0] ==   0) &&
	     (r[1] == g[1] && r[1] == b[1] && r[1] == 255)) { /*0=blk, 1=wht*/
      r[0] = (blkRGB>>16)&0xff;  g[0] = (blkRGB>>8)&0xff;  b[0] = blkRGB&0xff;
      r[1] = (whtRGB>>16)&0xff;  g[1] = (whtRGB>>8)&0xff;  b[1] = whtRGB&0xff;
    }
  }
      

  /* reverse image, if desired */
  if (revvideo) {
    for (i=0; i<numcols; i++) {
      r[i] = 255 - r[i];
      g[i] = 255 - g[i];
      b[i] = 255 - b[i];
    }
  }

  /* save the desired RGB colormap (before dicking with it) */
  for (i=0; i<numcols; i++) { 
    rorg[i] = rcmap[i] = r[i];  
    gorg[i] = gcmap[i] = g[i];  
    borg[i] = bcmap[i] = b[i];  
  }

  NewCMap();
  GammifyColors();

  WaitCursor();
  if (rwcolor) AllocRWColors();  else AllocColors();
  ChangeEC(0);

  WaitCursor();
  smoothResize(eWIDE, eHIGH);

  WaitCursor();
  HandleDispMode();   /* create root pic, or mainW, depending... */
    
  if (LocalCmap) {
    XSetWindowAttributes xswa;
    if (!ninstall) XInstallColormap(theDisp,LocalCmap);
    xswa.colormap = LocalCmap;
    XChangeWindowAttributes(theDisp,mainW,CWColormap,&xswa);
    if (cmapInGam) XChangeWindowAttributes(theDisp,gamW,CWColormap,&xswa);
  }

  tmp = GetISTR(ISTR_COLOR);
  SetISTR(ISTR_INFO,"%s  %s",formatStr, tmp);
	
  SetInfoMode(INF_FULL);
  if (freename) free(fullname);

  if (autocrop) AutoCrop();

  SetCursors(-1);

  if (dirUp!=BLOAD) {
    /* put current filename into the 'save-as' filename */
    if      (strcmp(filename,STDINSTR)==0)   SetDirFName("stdin");
    else if (frompipe || filenum == LOADPIC || filenum == GRABBED ||
	     filenum == DFLTPIC) {}             /* leave it alone */
    else SetDirFName(basefname);
  }


  /* since we're now using BitGravity to keep parts of the image window
     valid when the window is resized, we have to force an expose event
     on the *previous* size of the window, as this portion of the new
     window will *not* be given an expose event

     UNLESS the window hasn't changed size at all, in which case an
     appropriate expose event will be generated by CreateMainWindow().

     Yech! */

  if (mainW && !useroot && !clearonload && oldeWIDE>0 && oldeHIGH>0 &&
      (oldeWIDE != eWIDE || oldeHIGH != eHIGH) )
    GenExpose(mainW, 0, 0, oldeWIDE, oldeHIGH);

  return 1;

  
 FAILED:
  SetCursors(-1);
  SetInfoMode(INF_STR);
  if (strcmp(fullname,filename)!=0) unlink(filename);   /* kill /tmp file */
  if (freename) free(fullname);
  return 0;
}


/***********************************/
static int readpipe(cmd, fname)
     char *cmd, *fname;
{
  /* cmd is something like: "! bggen 100 0 0"
   *
   * runs command (with "> /tmp/xv******" appended).  
   * returns "/tmp/xv******" in fname
   * returns '0' if everything's cool, '1' on error
   */

  char fullcmd[512], tmpname[20], str[512];
  int i;

  if (!cmd || strlen(cmd)<2) return 1;

  sprintf(tmpname,"%s/xvXXXXXX", tmpdir);
  mktemp(tmpname);
  if (tmpname[0] == '\0') {   /* mktemp() blew up */
    static char *foo[] = {"\nHow unlikely!"};
    sprintf(str,"Unable to create temporary filename.");
    PopUp(str, foo, 1);
    return 1;
  }

  /* build command */
  strcpy(fullcmd, cmd+1);  /* skip the leading '!' character in cmd */
  strcat(fullcmd, " > ");
  strcat(fullcmd, tmpname);

  /* execute the command */
  sprintf(str, "Doing command: '%s'", fullcmd);
  OpenAlert(str);
  i = system(fullcmd);
  if (i) {
    static char *foo[] = { "\nThat Sucks!" };
    sprintf(str, "Unable to complete command:\n  %s\n\n  exit status: %d",
	    fullcmd, i);
    CloseAlert();
    PopUp(str, foo, 1);
    unlink(tmpname);      /* just in case it was created */
    return 1;
  }

  CloseAlert();
  strcpy(fname, tmpname);
  return 0;
}


/***********************************/
static void closePic()
{
  /* kill all resources used for this picture.
     this would include the window, any allocated colors, pic, epic, 
     theImage, etc. */

  if (!useroot) {
    /* turn off configure events */
    XSelectInput(theDisp, mainW, ExposureMask | KeyPressMask 
		 | ButtonPressMask | KeyReleaseMask
		 | EnterWindowMask | LeaveWindowMask);
  }


  FreeAllColors();


  if (epic != cpic && epic != NULL) free(epic);
  if (cpic !=  pic && cpic != NULL) free(cpic);
  if (pic != NULL) free(pic);
  xvDestroyImage(theImage);   theImage = NULL;
  pic = epic = cpic = NULL;

  SetInfoMode(INF_STR);
}







/****************/
static void OpenFirstPic()
/****************/
{
  int i;

  if (!numnames) {  openPic(DFLTPIC);  return; }

  for (i=0; i<numnames; i++) {
    if (openPic(i)) return;    /* success */
  }

  if (numnames>1) FatalError("couldn't open any pictures");
  else Quit(-1);
}


/****************/
static void OpenNextPic()
/****************/
{
  int i;

  for (i=curname+1; i<numnames; i++) {
    if (openPic(i)) return;    /* success */
  }

  openPic(DFLTPIC);
}


/****************/
static void OpenNextQuit()
/****************/
{
  int i;

  for (i=curname+1; i<numnames; i++) {
    if (openPic(i)) return;    /* success */
  }

  Quit(0);
}


/****************/
static void OpenNextLoop()
/****************/
{
  int i,j;

  j = 0;
  while (1) {
    for (i=curname+1; i<numnames; i++) {  /* load next picture */
      if (openPic(i)) return;
    }
    curname = -1;                         /* back to first in list */
    if (j) break;                         /* we're in a 'failure loop' */
    j++;
  }

  openPic(DFLTPIC);
}


/****************/
static void OpenPrevPic()
/****************/
{
  int i;

  for (i=curname-1; i>=0; i--) {
    if (openPic(i)) return;    /* success */
  }

  openPic(DFLTPIC);
}


/****************/
static void OpenNamedPic()
/****************/
{
  if (!openPic(LOADPIC)) openPic(DFLTPIC);
}





/****************/
static void MainLoop()
/****************/
{
  /* search forward until we manage to display a picture, 
     then call EventLoop.  EventLoop will eventually return 
     NEXTPIC, PREVPIC, NEXTQUIT, QUIT, or, if >= 0, a filenum to GOTO */

  int i;

  OpenFirstPic();   /* find first displayable picture, exit if none */

  if (useroot && autoquit) Quit(0);

  while ((i=EventLoop()) != QUIT) {
    if      (i==NEXTPIC && curname<numnames-1)  { closePic(); OpenNextPic(); }
    else if (i==PREVPIC && curname>0)  { closePic(); OpenPrevPic(); }
    else if (i==NEXTQUIT) { closePic(); OpenNextQuit(); }
    else if (i==NEXTLOOP) { closePic(); OpenNextLoop(); }
    else if (i==LOADPIC)  { closePic(); OpenNamedPic(); }

    else if (i==DELETE)   {
      closePic();
      if (nList.nstr == 0 && !keeparound) Quit(0);

      if (curname >= 0 && curname < numnames) {   /* there's a selection */
	if (!openPic(curname)) openPic(DFLTPIC);
      }
      else openPic(DFLTPIC);
    }

    else if (i==GRABBED)   {
      closePic();
      if (!openPic(GRABBED)) openPic(DFLTPIC);
    }

    else if (i>=0) {
      closePic();
      if (!openPic(i)) openPic(DFLTPIC);
    }

  }
}



/***********************************/
static void CreateMainWindow(geom,name)
     char *geom, *name;
{
  XSetWindowAttributes xswa;
  unsigned int         xswamask;
  XWindowAttributes    xwa;
  XWMHints             xwmh;
  XSizeHints           hints;
  XClassHint           classh;
  int                  i,x,y,ew,eh;
  unsigned int         w,h;
  char                 winname[128], iconname[128];

  /*
   * this function mainly deals with parsing the geometry spec correctly.
   * More trouble than it should be, and probably more trouble than
   * it has to be, but who can tell these days, what with all those
   * Widget-usin' Weenies out there...
   */

  ew = eWIDE;  eh = eHIGH;
  x = y = w = h = 1;
  i = XParseGeometry(geom,&x,&y,&w,&h);

  if (i&WidthValue)  eWIDE = w;
  if (i&HeightValue) eHIGH = h;

  /* handle case where they only specified width *or* height */
  if (( i&WidthValue && ~i&HeightValue) ||
      (~i&WidthValue &&  i&HeightValue)) {
    
    if (i&WidthValue) { eHIGH = (pHIGH * eWIDE) / pWIDE; }
                 else { eWIDE = (pWIDE * eHIGH) / pHIGH; }
  }

  if (eWIDE > maxWIDE || eHIGH > maxHIGH) {
    eWIDE = eWIDE / normFact;
    eHIGH = eHIGH / normFact;
  }
  if (eWIDE < 1) eWIDE = 1;
  if (eHIGH < 1) eHIGH = 1;

  if (fixedaspect && i&WidthValue && i&HeightValue) 
    FixAspect(0,&eWIDE,&eHIGH);
  else if (i&WidthValue && i&HeightValue) 
    { RANGE(eWIDE,1,maxWIDE);  RANGE(eHIGH,1,maxHIGH); }
  else FixAspect(1,&eWIDE,&eHIGH);


  hints.flags = 0;
  if ((i&XValue || i&YValue)) hints.flags = USPosition;

  if (i&XValue && i&XNegative) x = vrWIDE - eWIDE - abs(x);
  if (i&YValue && i&YNegative) y = vrHIGH - eHIGH - abs(y);

  if (x+eWIDE > vrWIDE) x = vrWIDE - eWIDE;   /* keep on screen */
  if (y+eHIGH > vrHIGH) y = vrHIGH - eHIGH;

  if (eWIDE < 1) eWIDE = 1;
  if (eHIGH < 1) eHIGH = 1;

#define VROOT_TRANS
#ifdef VROOT_TRANS
  if (vrootW != rootW) { /* virtual window manager running */
    int x1,y1;
    Window child;
    XTranslateCoordinates(theDisp, rootW, vrootW, x, y, &x1, &y1, &child);
    if (DEBUG) fprintf(stderr,"translate:  %d,%d -> %d,%d\n",x,y,x1,y1);
    x = x1;  y = y1;
  }
#endif

  hints.x = x;                  hints.y = y;
  hints.width = eWIDE;          hints.height = eHIGH;
  hints.max_width  = maxWIDE;   hints.max_height = maxHIGH;
  hints.flags |= USSize | PMaxSize;

  xswa.bit_gravity = StaticGravity;
  xswa.background_pixel = bg;
  xswa.border_pixel     = fg;
  xswa.colormap         = theCmap;
  xswamask = CWBackPixel | CWBorderPixel | CWColormap;
  if (!clearonload) xswamask |= CWBitGravity;

  if (mainW) {
    GetWindowPos(&xwa);

    /* generate an expose event if window hasn't changed size */
    if (xwa.width == eWIDE && xwa.height == eHIGH) 
      GenExpose(mainW, 0,0, eWIDE, eHIGH);  

    xwa.width = eWIDE;  xwa.height = eHIGH;
    SetWindowPos(&xwa);
    hints.flags = PSize | PMaxSize;
  } 

  else {
    mainW = XCreateWindow(theDisp,rootW,x,y,eWIDE,eHIGH,bwidth,dispDEEP,
			  InputOutput, theVisual, xswamask, &xswa);
    if (!mainW) FatalError("can't create window!");

    XSetTransientForHint(theDisp, psW, mainW);

#ifdef HAVE_JPEG
    XSetTransientForHint(theDisp, jpegW, mainW);
#endif

#ifdef HAVE_TIFF
    XSetTransientForHint(theDisp, tiffW, mainW);
#endif
  }


  if (name[0] == '\0') {
    sprintf(winname, "xv");
    sprintf(iconname,"xv");
  }
  else {
    sprintf(winname,"xv %s",name);
    sprintf(iconname,"%s",name);
  }

  XSetStandardProperties(theDisp,mainW,winname,iconname,None,NULL,0,&hints);

  xwmh.input = True;
  xwmh.flags = InputHint;

  if (iconPix) { 
    xwmh.icon_pixmap = iconPix;  
    xwmh.icon_mask   = iconmask;  
    xwmh.flags |= ( IconPixmapHint | IconMaskHint) ;
  }
  XSetWMHints(theDisp, mainW, &xwmh);

  classh.res_name = "xv";
  classh.res_class = "XVroot";
  XSetClassHint(theDisp, mainW, &classh);
}


/***********************************/
void FixAspect(grow,w,h)
int   grow;
int   *w, *h;
{
  /* computes new values of eWIDE and eHIGH which will have aspect ratio
     'normaspect'.  If 'grow' it will preserve aspect by enlarging, 
     otherwise, it will shrink to preserve aspect ratio.  
     Returns these values in 'w' and 'h' */

  float xr,yr,curaspect,a,exp;

  *w = eWIDE;  *h = eHIGH;

  /* xr,yr are expansion factors */
  xr = ((float) eWIDE) / cWIDE;
  yr = ((float) eHIGH) / cHIGH;
  curaspect  = xr / yr;

  /* if too narrow & shrink, shrink height.  too wide and grow, grow height */
  if ((curaspect < normaspect && !grow) || 
      (curaspect > normaspect &&  grow)) {    /* modify height */
    exp = curaspect / normaspect;
    *h = (int) (eHIGH * exp + .5);
  }

  /* if too narrow & grow, grow width.  too wide and shrink, shrink width */
  if ((curaspect < normaspect &&  grow) || 
      (curaspect > normaspect && !grow)) {    /* modify width */
    exp = normaspect / curaspect;
    *w = (int) (eWIDE * exp + .5);
  }


  /* shrink to fit screen without changing aspect ratio */
  if (*w>maxWIDE) {
    int i;
    a = (float) *w / maxWIDE;
    *w = maxWIDE;
    i = (int) (*h / a + .5);        /* avoid freaking some optimizers */
    *h = i;
  }

  if (*h>maxHIGH) {
    a = (float) *h / maxHIGH;
    *h = maxHIGH;
    *w = (int) (*w / a + .5);
  }

  if (*w < 1) *w = 1;
  if (*h < 1) *h = 1;
}


/***********************************/
static void MakeDispNames()
{
  int   prelen, n, i, done;
  char *suffix;

  suffix = namelist[0];
  prelen = 0;   /* length of prefix to be removed */
  n = i = 0;    /* shut up pesky compiler warnings */

  done = 0;
  while (!done) {
    suffix = (char *) strchr(suffix,'/');    /* find next '/' in file name */
    if (!suffix) break;

    suffix++;                       /* go past it */
    n = suffix - namelist[0];
    for (i=1; i<numnames; i++) {
      if (strncmp(namelist[0], namelist[i], n)!=0) { done=1; break; }
    }

    if (!done) prelen = n;
  }

  for (i=0; i<numnames; i++)
    dispnames[i] = namelist[i] + prelen;
}


/***********************************/
static void stickInList()
{
  /* stick current name (from 'load' box) and current working directory
     into 'namelist' */

  char *name, *fullname;
  char cwd[MAXPATHLEN];

  if (numnames == MAXNAMES) return;  /* full up */

  name = GetDirFName();
  if (name[0] != '/') {  /* prepend current working path */
    GETWD(cwd);

    fullname = (char *) malloc(strlen(cwd) + strlen(name) + 2);
    if (!fullname) FatalError("couldn't alloc 'fullname' in stickInList()\n");

    sprintf(fullname, "%s/%s", cwd, name);
  }

  else {                 /* copy name to fullname */
    fullname = (char *) malloc(strlen(name) + 1);
    if (!fullname) FatalError("couldn't alloc 'fullname' in stickInList()\n");
    strcpy(fullname, name);
  }

  namelist[numnames] = fullname;

  /* figure out how much of name can be shown */
  if (StringWidth(fullname) > (CTRL_LISTW-10)) {   /* has to be truncated */
    char *tmp;
    int   prelen = 0;

    tmp = fullname;
    while (1) {
      tmp = (char *) strchr(tmp,'/'); /* find next (forward) '/' in filename */
      if (!tmp) break;

      tmp++;                   /* move to char following the '/' */
      prelen = tmp - fullname;
      if (StringWidth(tmp) <= (CTRL_LISTW-10)) break;   /* we're cool now */
    }

    dispnames[numnames] = fullname + prelen;
  }
  else dispnames[numnames] = fullname;

  numnames++;
  if (numnames>0) BTSetActive(&but[BDELETE],1);


  LSNewData(&nList, dispnames, numnames);
  nList.selected = numnames-1;
  curname = numnames - 1;
  BTSetActive(&but[BNEXT], (curname<numnames-1));
  BTSetActive(&but[BPREV], (curname>0));

  ScrollToCurrent(&nList);
  XClearArea(theDisp, ctrlW, 0, 0, 200, 40, True);  /* redraw part of ctrlW */
}


/***********************************/
int DeleteCmd()
{
  /* 'delete' button was pressed.  Pop up a dialog box to determine
     what should be deleted, then do it.
     returns '1' if THE CURRENTLY VIEWED entry was deleted from the list, 
     in which case the 'selected' filename on the ctrl list is now 
     different, and should be auto-loaded, or something */

  static char *bnames[] = { "\004Disk File", "\nList Entry", "\033Cancel" };
  char str[512];
  int  del, i, delnum, rv;

  /* failsafe */
  delnum = nList.selected;
  if (delnum < 0 || delnum >= numnames) return 0;

  sprintf(str,"What do you wish to delete?\n\n%s%s",
	  "'List Entry' deletes selection from list.\n",
	  "'Disk File' deletes file associated with selection.");

  del = PopUp(str, bnames, 3);
  
  if (del == 2) return 0;   /* cancel */
  
  if (del == 0) {           /* 'Disk File' */
    char *name;
    if (namelist[delnum][0] != '/') {    /* prepend 'initpath' */
      name = (char *) malloc(strlen(namelist[delnum]) + strlen(initpath) + 2);
      if (!name) FatalError("malloc in DeleteCmd failed\n");
      sprintf(name,"%s/%s", initpath, namelist[delnum]);
    }
    else name = namelist[delnum];
    i = unlink(name);
    if (i) {
      static char *foo[] = { "\nPity!" };
      sprintf(str,"Can't delete file '%s'\n\n  %s.", name, sys_errlist[errno]);
      PopUp(str, foo, 1);
      return 0;
    }
    LoadCurrentDirectory();   /* just in case we deleted a file in the cwd */
  }


  /* remove from list on either 'List Entry' or (successful) 'Disk File' */

  /* determine if namelist[delnum] needs to be freed or not */
  for (i=0; i<mainargc && mainargv[i] != namelist[delnum]; i++) ;
  if (i == mainargc) {  /* not found.  free it */
    free(namelist[delnum]);
  }

  if (delnum != numnames-1) {
    /* snip out of namelist and dispnames lists */
    bcopy(&namelist[delnum+1], &namelist[delnum], 
	  (numnames - delnum - 1) * sizeof(namelist[0]));

    bcopy(&dispnames[delnum+1], &dispnames[delnum], 
	  (numnames - delnum - 1) * sizeof(dispnames[0]));
  }
  
  numnames--;
  if (numnames==0) BTSetActive(&but[BDELETE],0);

  nList.nstr = numnames;
  nList.selected = delnum;

  if (nList.selected >= numnames) nList.selected = numnames-1;
  if (nList.selected < 0) nList.selected = 0;

  SCSetRange(&nList.scrl, 0, numnames - nList.nlines, 
	     nList.scrl.val, nList.nlines-1);
  ScrollToCurrent(&nList);
  XClearArea(theDisp, ctrlW, 0, 0, 200, 40, True);  /* redraw part of ctrlW */

  rv = 0;
  if (delnum == curname) {      /* deleted the viewed file */
    curname = nList.selected;
    rv = 1;                     /* auto-load currently 'selected' filename */
  }
  else if (delnum < curname) curname = (curname > 0) ? curname-1 : 0;
  else if (delnum > curname);

  BTSetActive(&but[BNEXT], (curname<numnames-1));
  BTSetActive(&but[BPREV], (curname>0));
  return rv;
}



/***********************************/
static void smoothResize(w,h)
{
  if (autosmooth || normFact != 1) 
    { eWIDE = w;  eHIGH = h; Smooth(); }
  else Resize(w,h);

  if ((autodither || dfltkludge) && !autosmooth && ncols>0)
    ColorDither(NULL, w,h);
}


/***********************************/
void HandleDispMode()
{
  /* handles a change in the display mode (windowed/root).
     Also, called to do the 'right' thing when opening a picture
     displays epic, in current size, UNLESS we've selected an 'integer'
     root tiling thingy, in which case we resize epic appropriately */

  static int haveoldinfo = 0;
  static Window            oldMainW;
  static int               oldPerfect, oldNoglob, oldOwnCmap;
  static int               hadLocalCmap;
  static XSizeHints        oldHints;
  static XWindowAttributes oldXwa;
  int i;
  char wnam[128], inam[128];

  if (dispMB.selected == 0) {        /* windowed */
    if (useroot && resetroot) ClearRoot();

    if (mainW == (Window) NULL || useroot) {  /* window not visible */
      useroot = 0;  

      if (haveoldinfo) {             /* just remap mainW and resize it */
	mainW = oldMainW;

	perfect = oldPerfect;
	noglob  = oldNoglob;
	owncmap = oldOwnCmap;
	if (hadLocalCmap) {          /* use 'perfect' again */
	  FreeAllColors();
	  if (rwcolor) AllocRWColors();  else AllocColors();
	  CreateXImage();
	}

	sprintf(wnam,"xv %s",basefname);
	sprintf(inam,"%s",basefname);
	XSetStandardProperties(theDisp,mainW,wnam,inam,None,NULL,0,&oldHints);

	oldXwa.width = eWIDE;  oldXwa.height = eHIGH;
	SetWindowPos(&oldXwa);
	XMapWindow(theDisp, mainW);
      }

      else {                         /* first time.  need to create mainW */
	int ew, eh;
	mainW = (Window) NULL;  ew = eWIDE;  eh = eHIGH;
	CreateMainWindow(maingeom, basefname);
	XSelectInput(theDisp, mainW, ExposureMask | KeyPressMask 
		     | StructureNotifyMask | ButtonPressMask
		     | KeyReleaseMask | ColormapChangeMask
		     | EnterWindowMask | LeaveWindowMask );

	StoreDeleteWindowProp(mainW);
	XMapWindow(theDisp,mainW);

	if (ew != eWIDE || eh != eHIGH) smoothResize(ew, eh);
      }
    }

    else {                            /* mainW already visible */
      int ew, eh;
      ew = eWIDE;  eh = eHIGH;
      CreateMainWindow(maingeom, basefname);
      XSelectInput(theDisp, mainW, ExposureMask | KeyPressMask 
		   | StructureNotifyMask | ButtonPressMask
		   | KeyReleaseMask | ColormapChangeMask
		   | EnterWindowMask | LeaveWindowMask );

      if (LocalCmap) {                /* AllocColors created local colormap */
	XSetWindowColormap(theDisp, mainW, LocalCmap);
      }
      if (ew != eWIDE || eh != eHIGH) smoothResize(ew, eh);
    }

    useroot = 0;
  }



  else if (dispMB.selected > 0 && (dispMB.selected <= RM_MAX+1)) {
    /* a 'root' mode */
    int ew, eh, regen;

    regen = 0;
    if (!useroot) {                  /* have to kill mainW, etc. */
      /* save current window position */
      haveoldinfo = 1;
      oldMainW = mainW;
      oldPerfect = perfect;
      oldNoglob = noglob;
      oldOwnCmap = owncmap;

      hadLocalCmap = LocalCmap;

      GetWindowPos(&oldXwa);
      if (!XGetNormalHints(theDisp, mainW, &oldHints)) oldHints.flags = 0;
      oldHints.x=oldXwa.x;  oldHints.y=oldXwa.y;  oldHints.flags|=USPosition;


      perfect = owncmap = 0;         /* can't do -perfect on a root window */
      noglob = 1;                    /* Phase 3 color alloc's a bad idea too */
      XUnmapWindow(theDisp, mainW);
      mainW = vrootW;

      if (!ctrlUp) {    /* make sure ctrl is up when going to 'root' mode */
	XWMHints xwmh;
	xwmh.initial_state = IconicState;
	xwmh.flags = StateHint;
	XSetWMHints(theDisp, ctrlW, &xwmh);
	CtrlBox(1);
      }
    }
      
    if (LocalCmap) {                 /* we *were* using -perfect */
      FreeAllColors();
      if (rwcolor) AllocRWColors();  else AllocColors();
      regen = 1;
    }
    

    useroot = 1;
    rootMode = dispMB.selected - 1;
    ew = eWIDE;  eh = eHIGH;

    RANGE(ew,1,maxWIDE);  RANGE(eh,1,maxHIGH);

    if (rootMode == RM_TILE || rootMode == RM_IMIRROR) {
      /* make picture size a divisor of the rootW size.  round down */
      i = (dispWIDE + ew-1) / ew;   ew = (dispWIDE + i-1) / i;
      i = (dispHIGH + eh-1) / eh;   eh = (dispHIGH + i-1) / i;
    }

    if (ew != eWIDE || eh != eHIGH) smoothResize(ew, eh);  /* changed size */
    else {
      /* didn't regen XImage.  If we were using LocalCmap we have to */
      if (regen) CreateXImage();                    
    }

    KillOldRootInfo();
    MakeRootPic();
    SetCursors(-1);
  }

  else {
    fprintf(stderr,"%s:  unknown dispMB value '%d' in HandleDispMode()\n",
	    dispMB.selected);
  }
}

/************************************************************************/
/* following three rd_* functions swiped from xgraph, by David Harrison */
/************************************************************************/

/***********************************/
/* weikart, Mon Sep  2 18:26:36 1991; convert to lower-care for comparisons: */
char *lower_str(string)
char *string;
{
  char *p;
  for (p = string; *p != '\0'; p++)
    if (isupper(*p)) *p = tolower(*p);
  return string;
}


/***********************************
 * If XGetDefault finds a DECW$XDEFAULTS.DAT file, it reads it once and saves
 * the contents for subsequent calls.  It doesn't remember when a lookup
 * fails, however, and will try to lookup the file again for every XGetDefault
 * call.  Replace the XGetDefault calls with rd_getdefault, which is more 
 * intelligent about handling missing DECW$XDEFAULTS.DAT (i.e. most users).
 */
 
static XrmDatabase def_resource;
static int xrm_initted = 0;
 
char *rd_getdefault ( name )
     char *name;
{
#ifndef VMS
  return XGetDefault (theDisp, PROGNAME, name);
#else /* it is VMS */
  char namelist[512], *type;
  XrmValue value;
  
  if ( !xrm_initted ) {
    xrm_initted = 1;
    XrmInitialize();
    def_resource = XrmGetFileDatabase ( "SYS$LOGIN:DECW$XDEFAULTS.DAT" );
  }

  if ( def_resource != NULL ) {
    sprintf ( namelist, "%s.%s", PROGNAME, name );
    XrmGetResource ( def_resource, namelist, "", &type, &value );
    return value.addr;
  }
  else return NULL;	/* no database */
  
#endif
}


/***********************************/
int rd_int(name)
char *name;
{
  /* returns '1' if successful.  result in def_int */

  def_str = rd_getdefault(name);
  if (def_str) {
    if (sscanf(def_str, "%ld", &def_int) == 1) return 1;
    else {
      fprintf(stderr, "%s: couldn't read integer value for %s resource\n", 
	      cmd,name);
      return 0;
    }
  }
  else return 0;

}

/***********************************/
int rd_str(name)
char *name;
{
  /* returns '1' if successful.  result in def_str */
  
  def_str = rd_getdefault(name);
  if (def_str) return 1;
          else return 0;

}


/***********************************/
int rd_flag(name)
char *name;
{
  /* returns '1' if successful.  result in def_int */
  
  char buf[256];

  def_str = rd_getdefault(name);
  if (def_str) {
    strcpy(buf, def_str);
    lower_str(buf);

    def_int = (strcmp(buf, "on")==0) || 
              (strcmp(buf, "1")==0) ||
	      (strcmp(buf, "true")==0) ||
	      (strcmp(buf, "yes")==0);
    return 1;
    }

  else return 0;
}
    

/************************************************************************/
/* rd_str_cl() written by Dean Elhard  (elhard@system-m.az05.bull.com)  */
/************************************************************************/

/* Author's note:
 *
 * I build another routine to go along with rd_str, rd_int, and rd_flag, called
 * rd_str_cl which takes a name and a class string and works with multi-part
 * names.  The code is not really great because it depends on the resource
 * database field in the display structure (which is the one XGetResource uses
 * in X11R5 at least).  It also depends on XGetResource being called first to
 * initialize the database.  I should have probably rewritten rd_str, rd_int,
 * and rd_flag to all call XrmGetResource, but I wanted to keep the change as
 * minimal as possible.  If you want to implement a better fix, I leave that
 * to you.  Context diffs for the two changed files (xv.c and xvgam.c) follow.
 */

int rd_str_cl (name_str, class_str)
     char *name_str;
     char *class_str;
{
  char     q_name[BUFSIZ];
  char     q_class[BUFSIZ];
  char    *type;
  XrmValue result;

  strcpy (q_name, PROGNAME);
  strcat (q_name, ".");
  strcat (q_name, name_str);
  
  strcpy (q_class, "Program");
  strcat (q_class, ".");
  strcat (q_class, class_str);
  
  (void) XrmGetResource(theDisp->db, q_name, q_class, &type, &result);
  
  def_str = result.addr;
  if (def_str) return (1);
  else return (0);
}
