/*****************************************************************************/
/**       Copyright 1988 by Evans & Sutherland Computer Corporation,        **/
/**                          Salt Lake City, Utah                           **/
/**  Portions Copyright 1989 by the Massachusetts Institute of Technology   **/
/**                        Cambridge, Massachusetts                         **/
/**                                                                         **/
/**                           All Rights Reserved                           **/
/**                                                                         **/
/**    Permission to use, copy, modify, and distribute this software and    **/
/**    its documentation  for  any  purpose  and  without  fee is hereby    **/
/**    granted, provided that the above copyright notice appear  in  all    **/
/**    copies and that both  that  copyright  notice  and  this  permis-    **/
/**    sion  notice appear in supporting  documentation,  and  that  the    **/
/**    names of Evans & Sutherland and M.I.T. not be used in advertising    **/
/**    in publicity pertaining to distribution of the  software  without    **/
/**    specific, written prior permission.                                  **/
/**                                                                         **/
/**    EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD    **/
/**    TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES  OF  MERCHANT-    **/
/**    ABILITY  AND  FITNESS,  IN  NO  EVENT SHALL EVANS & SUTHERLAND OR    **/
/**    M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL  DAM-    **/
/**    AGES OR  ANY DAMAGES WHATSOEVER  RESULTING FROM LOSS OF USE, DATA    **/
/**    OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER    **/
/**    TORTIOUS ACTION, ARISING OUT OF OR IN  CONNECTION  WITH  THE  USE    **/
/**    OR PERFORMANCE OF THIS SOFTWARE.                                     **/
/*****************************************************************************/
/* 
 *  [ ctwm ]
 *
 *  Copyright 1992 Claude Lecommandeur.
 *            
 * Permission to use, copy, modify  and distribute this software  [ctwm] and
 * its documentation for any purpose is hereby granted without fee, provided
 * that the above  copyright notice appear  in all copies and that both that
 * copyright notice and this permission notice appear in supporting documen-
 * tation, and that the name of  Claude Lecommandeur not be used in adverti-
 * sing or  publicity  pertaining to  distribution of  the software  without
 * specific, written prior permission. Claude Lecommandeur make no represen-
 * tations  about the suitability  of this software  for any purpose.  It is
 * provided "as is" without express or implied warranty.
 *
 * Claude Lecommandeur DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL  IMPLIED WARRANTIES OF  MERCHANTABILITY AND FITNESS.  IN NO
 * EVENT SHALL  Claude Lecommandeur  BE LIABLE FOR ANY SPECIAL,  INDIRECT OR
 * CONSEQUENTIAL  DAMAGES OR ANY  DAMAGES WHATSOEVER  RESULTING FROM LOSS OF
 * USE, DATA  OR PROFITS,  WHETHER IN AN ACTION  OF CONTRACT,  NEGLIGENCE OR
 * OTHER  TORTIOUS ACTION,  ARISING OUT OF OR IN  CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * Author:  Claude Lecommandeur [ lecom@sic.epfl.ch ][ April 1992 ]
 */


/***********************************************************************
 *
 * $XConsortium: util.c,v 1.47 91/07/14 13:40:37 rws Exp $
 *
 * utility routines for twm
 *
 * 28-Oct-87 Thomas E. LaStrange	File created
 *
 * Do the necessary modification to be integrated in ctwm.
 * Can no longer be used for the standard twm.
 *
 * 22-April-92 Claude Lecommandeur.
 *
 * Changed behavior of DontMoveOff/MoveOffResistance to allow
 * moving a window off screen less than #MoveOffResistance pixels.
 * New code will no longer "snap" windows to #MoveOffResistance
 * pixels off screen and instead movements will just be stopped and
 * then resume once movement of #MoveOffResistance have been attempted.
 *
 * 15-December-02 Bjorn Knutsson 
 *
 ***********************************************************************/

#define LEVITTE_TEST

#include "twm.h"
#include "util.h"
#include "events.h"
#include "add_window.h"
#include "gram.tab.h"
#include "screen.h"
#include "icons.h"
#include "cursor.h"
#include <stdio.h>

/*
 * Need this for the fixed-size uint_*'s used below.  stdint.h would be
 * the more appropriate include, but there exist systems that don't have
 * it, but do have inttypes.h (FreeBSD 4, Solaris 7-9 I've heard of,
 * probably more).
 */
#include <inttypes.h>

#ifdef VMS
#include <decw$include/Xos.h>
#include <decw$include/Xatom.h>
#include <decw$include/Xutil.h>
#include <X11Xmu/Drawing.h>
#include <X11Xmu/CharSet.h>
#include <X11Xmu/WinUtil.h>
#ifdef HAVE_XWDFILE_H
#include "XWDFile.h"		/* We do some tricks, since the original
				   has bugs...		/Richard Levitte */
#endif /* HAVE_XWDFILE_H */
#include <unixlib.h>
#include <starlet.h>
#include <ssdef.h>
#include <psldef.h>
#include <lib$routines.h>
#ifdef __DECC
#include <unistd.h>
#endif /* __DECC */
#define USE_SIGNALS
#ifndef F_OK
#  define F_OK 0
#endif
#ifndef X_OK
#  define X_OK 1
#endif
#ifndef W_OK
#  define W_OK 2
#endif
#ifndef R_OK
#  define R_OK 4
#endif
#else /* !VMS */
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <X11/Xmu/Drawing.h>
#include <X11/Xmu/CharSet.h>
#include <X11/Xmu/WinUtil.h>
#include <X11/XWDFile.h>
#endif /* VMS */

#if defined(USE_SIGNALS) && defined(__sgi)
#  define _BSD_SIGNALS
#endif

#include <signal.h>
#ifndef VMS
#include <sys/time.h>
#endif

#if defined (XPM)
#ifdef VMS
#include "xpm.h"
#else
#   include <X11/xpm.h>
#endif
#endif

#ifdef JPEG
# include <setjmp.h>
# include <jpeglib.h>
# include <jerror.h>
  static Image *LoadJpegImage (char *name);
  static Image *GetJpegImage  (char *name);

  struct jpeg_error {
    struct jpeg_error_mgr pub;
    sigjmp_buf setjmp_buffer;
  };

  typedef struct jpeg_error *jerr_ptr;
#endif /* JPEG */

#ifdef IMCONV
#   include "im.h"
#   include "sdsc.h"
#endif

#define MAXANIMATIONSPEED 20

extern Atom _XA_WM_WORKSPACESLIST;

static Image *LoadBitmapImage (char  *name, ColorPair cp);
static Image *GetBitmapImage  (char  *name, ColorPair cp);
#if !defined(VMS) || defined(HAVE_XWDFILE_H)
static Image *LoadXwdImage    (char  *filename, ColorPair cp);
static Image *GetXwdImage     (char  *name, ColorPair cp);
#endif
#ifdef XPM
static Image *LoadXpmImage    (char  *name, ColorPair cp);
static Image *GetXpmImage     (char  *name, ColorPair cp);
static void   xpmErrorMessage (int status, char *name, char *fullname);
#endif
#ifdef IMCONV
static Image *GetImconvImage  (char *filename,
			       unsigned int *widthp, unsigned int *heightp);
#endif
static Pixmap CreateXLogoPixmap(unsigned int *widthp, unsigned int *heightp);
static Pixmap CreateResizePixmap(unsigned int *widthp, unsigned int *heightp);
static Pixmap CreateQuestionPixmap(unsigned int *widthp, unsigned int *heightp);
static Pixmap CreateMenuPixmap(unsigned int *widthp, unsigned int *heightp);
static Pixmap CreateDotPixmap (unsigned int *widthp, unsigned int *heightp);
static Image  *Create3DMenuImage (ColorPair cp);
static Image  *Create3DDotImage (ColorPair cp);
static Image  *Create3DResizeImage (ColorPair cp);
static Image  *Create3DZoomImage (ColorPair cp);
static Image  *Create3DBarImage (ColorPair cp);
static Image  *Create3DVertBarImage (ColorPair cp);
static Image  *Create3DResizeAnimation (Bool in, Bool left, Bool top,
					ColorPair cp);
static Image  *Create3DCrossImage (ColorPair cp);
static Image  *Create3DIconifyImage (ColorPair cp);
static Image  *Create3DSunkenResizeImage (ColorPair cp);
static Image  *Create3DBoxImage (ColorPair cp);

extern FILE *tracefile;

void FreeImage (Image *image);

void _swapshort (register char *bp, register unsigned n);
void _swaplong (register char *bp, register unsigned n);

static int    reportfilenotfound = 1;
static Colormap AlternateCmap = None;

int  HotX, HotY;

int  AnimationSpeed   = 0;
Bool AnimationActive  = False;
Bool MaybeAnimate     = True;
#ifdef USE_SIGNALS
   Bool AnimationPending = False;
#else
   struct timeval AnimateTimeout;
#endif /* USE_SIGNALS */

/***********************************************************************
 *
 *  Procedure:
 *	MoveOutline - move a window outline
 *
 *  Inputs:
 *	root	    - the window we are outlining
 *	x	    - upper left x coordinate
 *	y	    - upper left y coordinate
 *	width	    - the width of the rectangle
 *	height	    - the height of the rectangle
 *      bw          - the border width of the frame
 *      th          - title height
 *
 ***********************************************************************
 */

/* ARGSUSED */
void MoveOutline(Window root,
		 int x, int y, int width, int height, int bw, int th)
{
    static int	lastx = 0;
    static int	lasty = 0;
    static int	lastWidth = 0;
    static int	lastHeight = 0;
    static int	lastBW = 0;
    static int	lastTH = 0;
    int		xl, xr, yt, yb, xinnerl, xinnerr, yinnert, yinnerb;
    int		xthird, ythird;
    XSegment	outline[18];
    register XSegment	*r;

    if (x == lastx && y == lasty && width == lastWidth && height == lastHeight
	&& lastBW == bw && th == lastTH)
	return;
    
    r = outline;

#define DRAWIT() \
    if (lastWidth || lastHeight)			\
    {							\
	xl = lastx;					\
	xr = lastx + lastWidth - 1;			\
	yt = lasty;					\
	yb = lasty + lastHeight - 1;			\
	xinnerl = xl + lastBW;				\
	xinnerr = xr - lastBW;				\
	yinnert = yt + lastTH + lastBW;			\
	yinnerb = yb - lastBW;				\
	xthird = (xinnerr - xinnerl) / 3;		\
	ythird = (yinnerb - yinnert) / 3;		\
							\
	r->x1 = xl;					\
	r->y1 = yt;					\
	r->x2 = xr;					\
	r->y2 = yt;					\
	r++;						\
							\
	r->x1 = xl;					\
	r->y1 = yb;					\
	r->x2 = xr;					\
	r->y2 = yb;					\
	r++;						\
							\
	r->x1 = xl;					\
	r->y1 = yt;					\
	r->x2 = xl;					\
	r->y2 = yb;					\
	r++;						\
							\
	r->x1 = xr;					\
	r->y1 = yt;					\
	r->x2 = xr;					\
	r->y2 = yb;					\
	r++;						\
							\
	r->x1 = xinnerl + xthird;			\
	r->y1 = yinnert;				\
	r->x2 = r->x1;					\
	r->y2 = yinnerb;				\
	r++;						\
							\
	r->x1 = xinnerl + (2 * xthird);			\
	r->y1 = yinnert;				\
	r->x2 = r->x1;					\
	r->y2 = yinnerb;				\
	r++;						\
							\
	r->x1 = xinnerl;				\
	r->y1 = yinnert + ythird;			\
	r->x2 = xinnerr;				\
	r->y2 = r->y1;					\
	r++;						\
							\
	r->x1 = xinnerl;				\
	r->y1 = yinnert + (2 * ythird);			\
	r->x2 = xinnerr;				\
	r->y2 = r->y1;					\
	r++;						\
							\
	if (lastTH != 0) {				\
	    r->x1 = xl;					\
	    r->y1 = yt + lastTH;			\
	    r->x2 = xr;					\
	    r->y2 = r->y1;				\
	    r++;					\
	}						\
    }

    /* undraw the old one, if any */
    DRAWIT ();

    lastx = x;
    lasty = y;
    lastWidth = width;
    lastHeight = height;
    lastBW = bw;
    lastTH = th;

    /* draw the new one, if any */
    DRAWIT ();

#undef DRAWIT


    if (r != outline)
    {
	XDrawSegments(dpy, root, Scr->DrawGC, outline, r - outline);
    }
}

/***********************************************************************
 *
 *  Procedure:
 *	Zoom - zoom in or out of an icon
 *
 *  Inputs:
 *	wf	- window to zoom from
 *	wt	- window to zoom to
 *
 ***********************************************************************
 */

void Zoom(Window wf, Window wt)
{
    int fx, fy, tx, ty;			/* from, to */
    unsigned int fw, fh, tw, th;	/* from, to */
    long dx, dy, dw, dh;
    long z;
    int j;

    if ((Scr->IconifyStyle != ICONIFY_NORMAL) || !Scr->DoZoom || Scr->ZoomCount < 1) return;

    if (wf == None || wt == None) return;

    XGetGeometry (dpy, wf, &JunkRoot, &fx, &fy, &fw, &fh, &JunkBW, &JunkDepth);
    XGetGeometry (dpy, wt, &JunkRoot, &tx, &ty, &tw, &th, &JunkBW, &JunkDepth);

    dx = (long) tx - (long) fx;	/* going from -> to */
    dy = (long) ty - (long) fy;	/* going from -> to */
    dw = (long) tw - (long) fw;	/* going from -> to */
    dh = (long) th - (long) fh;	/* going from -> to */
    z = (long) (Scr->ZoomCount + 1);

    for (j = 0; j < 2; j++) {
	long i;

	XDrawRectangle (dpy, Scr->Root, Scr->DrawGC, fx, fy, fw, fh);
	for (i = 1; i < z; i++) {
	    int x = fx + (int) ((dx * i) / z);
	    int y = fy + (int) ((dy * i) / z);
	    unsigned width = (unsigned) (((long) fw) + (dw * i) / z);
	    unsigned height = (unsigned) (((long) fh) + (dh * i) / z);
	
	    XDrawRectangle (dpy, Scr->Root, Scr->DrawGC,
			    x, y, width, height);
	}
	XDrawRectangle (dpy, Scr->Root, Scr->DrawGC, tx, ty, tw, th);
    }
}


char *ExpandFilePath (char *path)
{
    char *ret, *colon, *p;
    int  len;

    len = 0;
    p   = path;
    while ((colon = strchr (p, ':'))) {
	len += colon - p + 1;
	if (*p == '~') len += HomeLen - 1;
	p = colon + 1;
    }
    if (*p == '~') len += HomeLen - 1;
    len += strlen (p);
    ret = (char*) malloc (len + 1);
    *ret = 0;

    p   = path;
    while ((colon = strchr (p, ':'))) {
	*colon = '\0';
	if (*p == '~') {
	    strcat (ret, Home);
	    strcat (ret, p + 1);
	} else {
	    strcat (ret, p);
	}
	*colon = ':';
	strcat (ret, ":");
	p = colon + 1;
    }
    if (*p == '~') {
	strcat (ret, Home);
	strcat (ret, p + 1);
    } else {
	strcat (ret, p);
    }
    return ret;
}

/***********************************************************************
 *
 *  Procedure:
 *	ExpandFilename - expand the tilde character to HOME
 *		if it is the first character of the filename
 *
 *  Returned Value:
 *	a pointer to the new name
 *
 *  Inputs:
 *	name	- the filename to expand
 *
 ***********************************************************************
 */

char *ExpandFilename(char *name)
{
    char *newname;

    if (name[0] != '~') return name;

#ifdef VMS
    newname = (char *) malloc (HomeLen + strlen(name) + 1);
    if (!newname) {
        fprintf (stderr, 
 		 "%s:  unable to allocate %d bytes to expand filename %s%s\n",
 		 ProgramName, HomeLen + strlen(name) + 1, Home, &name[1]);
    } else {
        (void) sprintf (newname, "%s%s", Home, &name[1]);
    }
#else
    newname = (char *) malloc (HomeLen + strlen(name) + 2);
    if (!newname) {
	fprintf (stderr, 
		 "%s:  unable to allocate %lu bytes to expand filename %s/%s\n",
		 ProgramName, (unsigned long) HomeLen + strlen(name) + 2,
		 Home, &name[1]);
    } else {
	(void) sprintf (newname, "%s/%s", Home, &name[1]);
    }
#endif

    return newname;
}

char *ExpandPixmapPath (char *name)
{
    char    *ret, *colon;

    ret = NULL;
#ifdef VMS
    if (name[0] == '~') {
	ret = (char *) malloc (HomeLen + strlen (name) + 1);
	sprintf (ret, "%s%s", Home, &name[1]);
    }
    if (name[0] == '/') {
	ret = (char *) malloc (strlen (name));
	sprintf (ret, "%s", &name[1]);
    }
    else
    if (Scr->PixmapDirectory) {
	char *p = Scr->PixmapDirectory;
	while (colon = strchr (p, ':')) {
	    *colon = '\0';
	    ret = (char *) malloc (strlen (p) + strlen (name) + 1);
	    sprintf (ret, "%s%s", p, name);
	    *colon = ':';
	    if (!access (ret, R_OK)) return (ret);
	    p = colon + 1;
	}
        ret = (char *) malloc (strlen (Scr->PixmapDirectory) + strlen (name) + 1);
	sprintf (ret, "%s%s", Scr->PixmapDirectory, name);
    }
#else
    if (name[0] == '~') {
	ret = (char *) malloc (HomeLen + strlen (name) + 2);
	sprintf (ret, "%s/%s", Home, &name[1]);
    }
    else
    if (name[0] == '/') {
	ret = (char *) malloc (strlen (name) + 1);
	strcpy (ret, name);
    }
    else
    if (Scr->PixmapDirectory) {
	char *p = Scr->PixmapDirectory;
	while ((colon = strchr (p, ':'))) {
	    *colon = '\0';
	    ret = (char *) malloc (strlen (p) + strlen (name) + 2);
	    sprintf (ret, "%s/%s", p, name);
	    *colon = ':';
	    if (!access (ret, R_OK)) return (ret);
	    p = colon + 1;
	}
	ret = (char *) malloc (strlen (p) + strlen (name) + 2);
	sprintf (ret, "%s/%s", p, name);
    }
#endif
    return (ret);
}

/***********************************************************************
 *
 *  Procedure:
 *	GetUnknownIcon - read in the bitmap file for the unknown icon
 *
 *  Inputs:
 *	name - the filename to read
 *
 ***********************************************************************
 */

void GetUnknownIcon(char *name)
{
    Scr->UnknownImage = GetImage (name, Scr->IconC);
}

/***********************************************************************
 *
 *  Procedure:
 *	FindBitmap - read in a bitmap file and return size
 *
 *  Returned Value:
 *	the pixmap associated with the bitmap
 *      widthp	- pointer to width of bitmap
 *      heightp	- pointer to height of bitmap
 *
 *  Inputs:
 *	name	- the filename to read
 *
 ***********************************************************************
 */

Pixmap FindBitmap (char *name, unsigned int *widthp, unsigned int *heightp)
{
    char *bigname;
    Pixmap pm;

    if (!name) return None;

    /*
     * Names of the form :name refer to hardcoded images that are scaled to
     * look nice in title buttons.  Eventually, it would be nice to put in a
     * menu symbol as well....
     */
    if (name[0] == ':') {
	int i;
	static struct {
	    char *name;
	    Pixmap (*proc)(unsigned int *wp, unsigned int *hp);
	} pmtab[] = {
	    { TBPM_DOT,		CreateDotPixmap },
	    { TBPM_ICONIFY,	CreateDotPixmap },
	    { TBPM_RESIZE,	CreateResizePixmap },
	    { TBPM_XLOGO,	CreateXLogoPixmap },
	    { TBPM_DELETE,	CreateXLogoPixmap },
	    { TBPM_MENU,	CreateMenuPixmap },
	    { TBPM_QUESTION,	CreateQuestionPixmap },
	};
	
	for (i = 0; i < (sizeof pmtab)/(sizeof pmtab[0]); i++) {
	    if (XmuCompareISOLatin1 (pmtab[i].name, name) == 0)
	      return (*pmtab[i].proc) (widthp, heightp);
	}
	fprintf (stderr, "%s:  no such built-in bitmap \"%s\"\n",
		 ProgramName, name);
	return None;
    }

    /*
     * Generate a full pathname if any special prefix characters (such as ~)
     * are used.  If the bigname is different from name, bigname will need to
     * be freed.
     */
    bigname = ExpandFilename (name);
    if (!bigname) return None;

    /*
     * look along bitmapFilePath resource same as toolkit clients
     */
    pm = XmuLocateBitmapFile (ScreenOfDisplay(dpy, Scr->screen), bigname, NULL,
			      0, (int *)widthp, (int *)heightp, &HotX, &HotY);
    if (pm == None && Scr->IconDirectory && bigname[0] != '/') {
	if (bigname != name) free (bigname);
	/*
	 * Attempt to find icon in old IconDirectory (now obsolete)
	 */
#ifdef VMS
	bigname = (char *) malloc (strlen(name) + strlen(Scr->IconDirectory) + 1);
	if (!bigname) {
	    fprintf (stderr,
		"%s:  unable to allocate memory for \"%s%s\"\n",
		ProgramName, Scr->IconDirectory, name);
	    return None;
	}
	(void) sprintf (bigname, "%s%s", Scr->IconDirectory, name);
#else
	bigname = (char *) malloc (strlen(name) + strlen(Scr->IconDirectory) + 2);
	if (!bigname) {
	    fprintf (stderr,
		     "%s:  unable to allocate memory for \"%s/%s\"\n",
		     ProgramName, Scr->IconDirectory, name);
	    return None;
	}
	(void) sprintf (bigname, "%s/%s", Scr->IconDirectory, name);
#endif
	if (XReadBitmapFile (dpy, Scr->Root, bigname, widthp, heightp, &pm,
			     &HotX, &HotY) != BitmapSuccess) {
	    pm = None;
	}
    }
    if (bigname != name) free (bigname);
    if ((pm == None) && reportfilenotfound) {
	fprintf (stderr, "%s:  unable to find bitmap \"%s\"\n", ProgramName, name);
    }

    return pm;
}

Pixmap GetBitmap (char *name)
{
    return FindBitmap (name, &JunkWidth, &JunkHeight);
}

static Image *LoadBitmapImage (char  *name, ColorPair cp)
{
    Image	 *image;
    Pixmap	 bm;
    unsigned int width, height;
    XGCValues	 gcvalues;

    if (Scr->rootGC == (GC) 0) Scr->rootGC = XCreateGC (dpy, Scr->Root, 0, &gcvalues);
    bm = FindBitmap (name, &width, &height);
    if (bm == None) return (None);

    image = (Image*) malloc (sizeof (struct _Image));
    image->pixmap = XCreatePixmap (dpy, Scr->Root, width, height, Scr->d_depth);
    gcvalues.background = cp.back;
    gcvalues.foreground = cp.fore;
    XChangeGC   (dpy, Scr->rootGC, GCForeground | GCBackground, &gcvalues);
    XCopyPlane  (dpy, bm, image->pixmap, Scr->rootGC, 0, 0, width, height,
		0, 0, (unsigned long) 1);
    XFreePixmap (dpy, bm);
    image->mask   = None;
    image->width  = width;
    image->height = height;
    image->next   = None;
    return (image);
}

static Image *GetBitmapImage (char  *name, ColorPair cp)
{
    Image	*image, *r, *s;
    char	path [128], pref [128];
    char	*perc;
    int		i;

    if (! strchr (name, '%')) return (LoadBitmapImage (name, cp));
    s = image = None;
    strcpy (pref, name);
    perc  = strchr (pref, '%');
    *perc = '\0';
    reportfilenotfound = 0;
    for (i = 1;; i++) {
	sprintf (path, "%s%d%s", pref, i, perc + 1);
	r = LoadBitmapImage (path, cp);
	if (r == None) break;
	r->next = None;
	if (image == None) s = image = r;
	else {
	    s->next = r;
	    s = r;
	}
    }
    reportfilenotfound = 1;
    if (s != None) s->next = image;
    if (image == None) {
	fprintf (stderr, "Cannot open any %s bitmap file\n", name);
    }
    return (image);
}

#ifdef XPM
static int reportxpmerror = 1;

static Image *LoadXpmImage (char *name, ColorPair cp)
{
    char	*fullname;
    Image	*image;
    int		status;
    Colormap    stdcmap = Scr->RootColormaps.cwins[0]->colormap->c;
    XpmAttributes attributes;
    static XpmColorSymbol overrides[] = {
	{"Foreground", NULL, 0},
	{"Background", NULL, 0},
	{"HiShadow", NULL, 0},
	{"LoShadow", NULL, 0}
    };

    fullname = ExpandPixmapPath (name);
    if (! fullname) return (None);

    image = (Image*) malloc (sizeof (struct _Image));
    if (image == None) return (None);

    attributes.valuemask  = 0;
    attributes.valuemask |= XpmSize;
    attributes.valuemask |= XpmReturnPixels;
    attributes.valuemask |= XpmColormap;
    attributes.valuemask |= XpmDepth;
    attributes.valuemask |= XpmVisual;
    attributes.valuemask |= XpmCloseness;
    attributes.valuemask |= XpmColorSymbols;

    attributes.numsymbols = 4;
    attributes.colorsymbols = overrides;
    overrides[0].pixel = cp.fore;
    overrides[1].pixel = cp.back;
    overrides[2].pixel = cp.shadd;
    overrides[3].pixel = cp.shadc;


    attributes.colormap  = AlternateCmap ? AlternateCmap : stdcmap;
    attributes.depth     = Scr->d_depth;
    attributes.visual    = Scr->d_visual;
    attributes.closeness = 65535; /* Never fail */
    status = XpmReadFileToPixmap(dpy, Scr->Root, fullname,
				 &(image->pixmap), &(image->mask), &attributes);
    if (status != XpmSuccess) {
	xpmErrorMessage (status, name, fullname);
	free (image);
	return (None);
    }
    free (fullname);
    image->width  = attributes.width;
    image->height = attributes.height;
    image->next   = None;
    return (image);
}

static Image *GetXpmImage (char *name, ColorPair cp)
{
    char    path [128], pref [128];
    Image   *image, *r, *s;
    char    *perc;
    int     i;

    if (! strchr (name, '%')) return (LoadXpmImage (name, cp));
    s = image = None;
    strcpy (pref, name);
    perc  = strchr (pref, '%');
    *perc = '\0';
    reportfilenotfound = 0;
    for (i = 1;; i++) {
	sprintf (path, "%s%d%s", pref, i, perc + 1);
	r = LoadXpmImage (path, cp);
	if (r == None) break;
	r->next = None;
	if (image == None) s = image = r;
	else {
	    s->next = r;
	    s = r;
	}
    }
    reportfilenotfound = 1;
    if (s != None) s->next = image;
    if (image == None) {
	fprintf (stderr, "Cannot open any %s XPM file\n", name);
    }
    return (image);
}

static void xpmErrorMessage (int status, char *name, char *fullname)
{
    switch (status) {
	case XpmSuccess:
	    break;

	case XpmColorError:
	    if (reportxpmerror)
		fprintf (stderr,
			"Could not parse or alloc requested color : %s\n",
			fullname);
	    return;

	case XpmOpenFailed:
	    if (reportxpmerror && reportfilenotfound)
		fprintf (stderr, "unable to locate XPM file : %s\n", fullname);
	    return;

	case XpmFileInvalid:
	    fprintf (stderr, "invalid XPM file : %s\n", fullname);
	    return;

	case XpmNoMemory:
	    if (reportxpmerror)
		fprintf (stderr, "Not enough memory for XPM file : %s\n", fullname);
	    return;

	case XpmColorFailed:
	    if (reportxpmerror)
		fprintf (stderr, "Color not found in : %s\n", fullname);
	    return;

	default :
	    fprintf (stderr, "Unknown error in : %s\n", fullname);
	    return;
    }
}

#endif

void MaskScreen (char *file)
{
    unsigned long valuemask;
    XSetWindowAttributes attributes;
    XEvent event;
    Cursor waitcursor;
    int x, y;
    ColorPair WelcomeCp;
    XColor black;

    NewFontCursor (&waitcursor, "watch");

    valuemask = (CWBackingStore | CWSaveUnder | CWBackPixel |
		 CWOverrideRedirect | CWEventMask | CWCursor);
    attributes.backing_store	 = NotUseful;
    attributes.save_under	 = False;
    attributes.override_redirect = True;
    attributes.event_mask	 = ExposureMask;
    attributes.cursor		 = waitcursor;
    attributes.background_pixel	 = Scr->Black;
    Scr->WindowMask = XCreateWindow (dpy, Scr->Root, 0, 0,
			(unsigned int) Scr->rootw,
			(unsigned int) Scr->rooth,
			(unsigned int) 0,
			CopyFromParent, (unsigned int) CopyFromParent,
			(Visual *) CopyFromParent, valuemask,
			&attributes);
    XMapWindow (dpy, Scr->WindowMask);
    XMaskEvent (dpy, ExposureMask, &event);

    if (Scr->Monochrome != COLOR) return;

    WelcomeCp.fore = Scr->Black;
    WelcomeCp.back = Scr->White;
    Scr->WelcomeCmap  = XCreateColormap (dpy, Scr->WindowMask, Scr->d_visual, AllocNone);
    if (! Scr->WelcomeCmap) return;
    XSetWindowColormap (dpy, Scr->WindowMask, Scr->WelcomeCmap);
    black.red   = 0;
    black.green = 0;
    black.blue  = 0;
    XAllocColor (dpy, Scr->WelcomeCmap, &black);

    reportfilenotfound = 0;
    AlternateCmap = Scr->WelcomeCmap;
    if (! file) {
#ifdef CTWM_WELCOME_FILE
	Scr->WelcomeImage  = GetImage (CTWM_WELCOME_FILE, WelcomeCp);
	if (Scr->WelcomeImage == None)
#endif
	Scr->WelcomeImage  = GetImage ("xwd:welcome.xwd", WelcomeCp);
#ifdef XPM
	if (Scr->WelcomeImage == None)
		Scr->WelcomeImage  = GetImage ("xpm:welcome.xpm", WelcomeCp);
#endif
    }
    else {
	Scr->WelcomeImage  = GetImage (file, WelcomeCp);
    }
    AlternateCmap = None;
    reportfilenotfound = 1;
    if (Scr->WelcomeImage == None) return;

    if (captive) {
	XSetWindowColormap (dpy, Scr->WindowMask, Scr->WelcomeCmap);
	XSetWMColormapWindows (dpy, Scr->Root, &(Scr->WindowMask), 1);
    }
    else XInstallColormap (dpy, Scr->WelcomeCmap);

    Scr->WelcomeGC = XCreateGC (dpy, Scr->WindowMask, 0, NULL);
    x = (Scr->rootw  -  Scr->WelcomeImage->width) / 2;
    y = (Scr->rooth - Scr->WelcomeImage->height) / 2;

    XSetWindowBackground (dpy, Scr->WindowMask, black.pixel);
    XClearWindow (dpy, Scr->WindowMask);
    XCopyArea (dpy, Scr->WelcomeImage->pixmap, Scr->WindowMask, Scr->WelcomeGC, 0, 0,
		Scr->WelcomeImage->width, Scr->WelcomeImage->height, x, y);
}

void UnmaskScreen (void)
{
#ifdef VMS
    float  timeout;
#else
    struct timeval	timeout;
#endif
    Colormap            stdcmap = Scr->RootColormaps.cwins[0]->colormap->c;
    Colormap		cmap;
    XColor		colors [256], stdcolors [256];
    int			i, j, usec;

#ifdef VMS
    timeout = 0.017;
#else
    usec = 10000;
    timeout.tv_usec = usec % (unsigned long) 1000000;
    timeout.tv_sec  = usec / (unsigned long) 1000000;
#endif

    if (Scr->WelcomeImage) {
	Pixel pixels [256];

	cmap = Scr->WelcomeCmap;
	for (i = 0; i < 256; i++) {
	    pixels [i] = i;
	    colors [i].pixel = i;
	}
	XQueryColors (dpy, cmap, colors, 256);
	XFreeColors  (dpy, cmap, pixels, 256, 0L);
	XFreeColors  (dpy, cmap, pixels, 256, 0L); /* Ah Ah */

	for (i = 0; i < 256; i++) {
	    colors [i].pixel = i;
	    colors [i].flags = DoRed | DoGreen | DoBlue;
	    stdcolors [i].red   = colors [i].red;
	    stdcolors [i].green = colors [i].green;
	    stdcolors [i].blue  = colors [i].blue;
	}
	for (i = 0; i < 128; i++) {
	    for (j = 0; j < 256; j++) {
		colors [j].red   = stdcolors [j].red   * ((127.0 - i) / 128.0);
		colors [j].green = stdcolors [j].green * ((127.0 - i) / 128.0);
		colors [j].blue  = stdcolors [j].blue  * ((127.0 - i) / 128.0);
	    }
	    XStoreColors (dpy, cmap, colors, 256);
#ifdef VMS
	    lib$wait(&timeout);
#else
	    select (0, (void *) 0, (void *) 0, (void *) 0, &timeout);
#endif
	}
	XFreeColors   (dpy, cmap, pixels, 256, 0L);
	XFreeGC       (dpy, Scr->WelcomeGC);
	FreeImage     (Scr->WelcomeImage);
    }
    if (Scr->Monochrome != COLOR) goto fin;

    cmap = XCreateColormap (dpy, Scr->Root, Scr->d_visual, AllocNone);
    if (! cmap) goto fin;
    for (i = 0; i < 256; i++) {
	colors [i].pixel = i;
	colors [i].red   = 0;
	colors [i].green = 0;
	colors [i].blue  = 0;
	colors [i].flags = DoRed | DoGreen | DoBlue;
    }
    XStoreColors (dpy, cmap, colors, 256);

    if (captive) XSetWindowColormap (dpy, Scr->Root, cmap);
    else XInstallColormap (dpy, cmap);

    XUnmapWindow (dpy, Scr->WindowMask);
    XClearWindow (dpy, Scr->Root);
    XSync (dpy, 0);
    PaintAllDecoration ();

    for (i = 0; i < 256; i++) stdcolors [i].pixel = i;
    XQueryColors (dpy, stdcmap, stdcolors, 256);
    for (i = 0; i < 128; i++) {
	for (j = 0; j < 256; j++) {
	    colors [j].pixel = j;
	    colors [j].red   = stdcolors [j].red   * (i / 127.0);
	    colors [j].green = stdcolors [j].green * (i / 127.0);
	    colors [j].blue  = stdcolors [j].blue  * (i / 127.0);
	    colors [j].flags = DoRed | DoGreen | DoBlue;
	}
	XStoreColors (dpy, cmap, colors, 256);
#ifdef VMS
        lib$wait(&timeout);
#else
	select (0, (void *) 0, (void *) 0, (void *) 0, &timeout);
#endif
    }

    if (captive) XSetWindowColormap (dpy, Scr->Root, stdcmap);
    else XInstallColormap (dpy, stdcmap);

    XFreeColormap (dpy, cmap);

fin:
    if (Scr->WelcomeCmap) XFreeColormap (dpy, Scr->WelcomeCmap);
    XDestroyWindow (dpy, Scr->WindowMask);
    Scr->WindowMask = (Window) 0;
}

#ifdef VMS

/* use the VMS system services to request the timer to issue an AST */
void AnimateHandler (void);

unsigned int tv[2];
int status;
static unsigned long timefi;
/* unsigned long timefe = 17; */
unsigned long timefe;

#define TIMID 12L

void StartAnimation (void)
{
    if (AnimationSpeed > MAXANIMATIONSPEED) AnimationSpeed = MAXANIMATIONSPEED;
    if (AnimationSpeed <= 0) return;
    if (AnimationActive) return;

    if (!timefi) lib$get_ef(&timefi);
    if (!timefe) lib$get_ef(&timefe);

    tv[1] = 0xFFFFFFFF;                   /* quadword negative for relative */
    tv[0] = -(10000000 / AnimationSpeed); /* time. In units of 100ns. */
    sys$clref(timefe);
    status = sys$setimr (timefi, &tv, AnimateHandler, TIMID );
    if (status != SS$_NORMAL) lib$signal(status);
    AnimationActive = True;
}

void StopAnimation () {
    if (AnimationSpeed <= 0) return;
    if (! AnimationActive) return;
    AnimationActive = False;

    status = sys$cantim(TIMID, PSL$C_USER);
    if (status != SS$_NORMAL) lib$signal(status);
}

void SetAnimationSpeed (int speed)
{
    AnimationSpeed = speed;
    if (AnimationSpeed > MAXANIMATIONSPEED) AnimationSpeed = MAXANIMATIONSPEED;
}

void ModifyAnimationSpeed (int incr)
{
    if ((AnimationSpeed + incr) < 0) return;
    if ((AnimationSpeed + incr) == 0) {
	if (AnimationActive) StopAnimation ();
	AnimationSpeed = 0;
	return;
    }
    AnimationSpeed += incr;

    status = sys$cantim(TIMID, PSL$C_USER);
    if (status != SS$_NORMAL) lib$signal(status);

    tv[1] = 0xFFFFFFFF;
    tv[0] = -(10000000 / AnimationSpeed);

    sys$clref(timefe);
    status = sys$setimr (timefi, &tv, AnimateHandler, TIMID);
    if (status != SS$_NORMAL) lib$signal(status);

    AnimationActive = True;
}

void AnimateHandler (void) {
    AnimationPending = True;

    sys$setef(timefe);
    status = sys$setimr (timefi, &tv, AnimateHandler, TIMID);
    if (status != SS$_NORMAL) lib$signal(status);
}
#else /* VMS */

#ifdef USE_SIGNALS
SIGNAL_T AnimateHandler ();
#endif

#ifndef USE_SIGNALS
void TryToAnimate (void)
{
    struct timeval  tp;
    struct timezone tzp;
    static unsigned long lastsec;
    static long lastusec;
    unsigned long gap;

    gettimeofday (&tp, &tzp);
    gap = ((tp.tv_sec - lastsec) * 1000000) + (tp.tv_usec - lastusec);
    if (tracefile) {
	fprintf (tracefile, "Time = %lu, %ld, %ld, %ld, %lu\n", lastsec,
		lastusec, (long)tp.tv_sec, (long)tp.tv_usec, gap);
	fflush (tracefile);
    }
    gap *= AnimationSpeed;
    if (gap < 1000000) return;
    if (tracefile) {
	fprintf (tracefile, "Animate\n");
	fflush (tracefile);
    }
    Animate ();
    lastsec  = tp.tv_sec;
    lastusec = tp.tv_usec;
}
#endif /* USE_SIGNALS */

void StartAnimation (void)
{
#ifdef USE_SIGNALS
    struct itimerval tv;
#endif

    if (AnimationSpeed > MAXANIMATIONSPEED) AnimationSpeed = MAXANIMATIONSPEED;
    if (AnimationSpeed <= 0) AnimationSpeed = 0;
    if (AnimationActive) return;
#ifdef USE_SIGNALS
    if (AnimationSpeed == 0) return;
    signal (SIGALRM, AnimateHandler);
    if (AnimationSpeed == 1) {
	tv.it_interval.tv_sec  = 1;
	tv.it_interval.tv_usec = 0;
	tv.it_value.tv_sec     = 1;
	tv.it_value.tv_usec    = 0;
    }
    else {
	tv.it_interval.tv_sec  = 0;
	tv.it_interval.tv_usec = 1000000 / AnimationSpeed;
	tv.it_value.tv_sec     = 0;
	tv.it_value.tv_usec    = 1000000 / AnimationSpeed;
    }
    setitimer (ITIMER_REAL, &tv, (struct itimerval*) NULL);
#else /* USE_SIGNALS */
    switch (AnimationSpeed) {
	case 0 :
	    return;
	case 1 :
	    AnimateTimeout.tv_sec  = 1;
	    AnimateTimeout.tv_usec = 0;
	    break;
	default :
	    AnimateTimeout.tv_sec  = 0;
	    AnimateTimeout.tv_usec = 1000000 / AnimationSpeed;
    }
#endif /* USE_SIGNALS */
    AnimationActive = True;
}

void StopAnimation (void)
{
#ifdef USE_SIGNALS
    struct itimerval tv;

    if (AnimationSpeed <= 0) return;
    if (! AnimationActive) return;
    signal (SIGALRM, SIG_IGN);

    tv.it_value.tv_sec     = 0;
    tv.it_value.tv_usec    = 0;
    setitimer (ITIMER_REAL, &tv, (struct itimerval*) NULL);
#endif
    AnimationActive = False;
}

void SetAnimationSpeed (int speed)
{
    AnimationSpeed = speed;
    if (AnimationSpeed > MAXANIMATIONSPEED) AnimationSpeed = MAXANIMATIONSPEED;
}

void ModifyAnimationSpeed (int incr)
{
#ifdef USE_SIGNALS
    struct itimerval tv;
#endif

    if ((AnimationSpeed + incr) < 0) return;
    if ((AnimationSpeed + incr) == 0) {
	if (AnimationActive) StopAnimation ();
	AnimationSpeed = 0;
	return;
    }
    AnimationSpeed += incr;
    if (AnimationSpeed > MAXANIMATIONSPEED) AnimationSpeed = MAXANIMATIONSPEED;

#ifdef USE_SIGNALS
    signal (SIGALRM, AnimateHandler);
    if (AnimationSpeed == 1) {
	tv.it_interval.tv_sec  = 1;
	tv.it_interval.tv_usec = 0;
	tv.it_value.tv_sec     = 1;
	tv.it_value.tv_usec    = 0;
    }
    else {
	tv.it_interval.tv_sec  = 0;
	tv.it_interval.tv_usec = 1000000 / AnimationSpeed;
	tv.it_value.tv_sec     = 0;
	tv.it_value.tv_usec    = 1000000 / AnimationSpeed;
    }
    setitimer (ITIMER_REAL, &tv, (struct itimerval*) NULL);
#else /* USE_SIGNALS */
    if (AnimationSpeed == 1) {
	AnimateTimeout.tv_sec  = 1;
	AnimateTimeout.tv_usec = 0;
    }
    else {
	AnimateTimeout.tv_sec  = 0;
	AnimateTimeout.tv_usec = 1000000 / AnimationSpeed;
    }
#endif /* USE_SIGNALS */
    AnimationActive = True;
}

#ifdef USE_SIGNALS
SIGNAL_T AnimateHandler (int dummy)
{
    signal (SIGALRM, AnimateHandler);
    AnimationPending = True;
}
#endif
#endif /* VMS */

void Animate (void)
{
    TwmWindow	*t;
    int		scrnum;
    ScreenInfo	*scr;
    int		i;
    TBWindow	*tbw;
    int		nb;

    if (AnimationSpeed == 0) return;
#ifdef USE_SIGNALS
    AnimationPending = False;
#endif

    MaybeAnimate = False;
    for (scrnum = 0; scrnum < NumScreens; scrnum++) {
	if ((scr = ScreenList [scrnum]) == NULL) continue;

	for (t = scr->FirstWindow; t != NULL; t = t->next) {
	    if (! visible (t)) continue;
	    if (t->icon_on && t->icon && t->icon->bm_w && t->icon->image &&
		t->icon->image->next) {
		AnimateIcons (scr, t->icon);
		MaybeAnimate = True;
	    }
	    else
	    if (t->mapped && t->titlebuttons) {
		nb = scr->TBInfo.nleft + scr->TBInfo.nright;
		for (i = 0, tbw = t->titlebuttons; i < nb; i++, tbw++) {
		    if (tbw->image && tbw->image->next) {
			AnimateButton (tbw);
			MaybeAnimate = True;
		    }
		}
	    }
	}
	if (scr->Focus) {
	    t = scr->Focus;
	    if (t->mapped && t->titlehighlight && t->title_height &&
		t->HiliteImage && t->HiliteImage->next) {
		AnimateHighlight (t);
		MaybeAnimate = True;
	    }
	}
    }
    MaybeAnimate |= AnimateRoot ();
    XFlush (dpy);
    return;
}

void InsertRGBColormap (Atom a, XStandardColormap *maps, int nmaps,
			Bool replace)
{
    StdCmap *sc = NULL;

    if (replace) {			/* locate existing entry */
	for (sc = Scr->StdCmapInfo.head; sc; sc = sc->next) {
	    if (sc->atom == a) break;
	}
    }

    if (!sc) {				/* no existing, allocate new */
	sc = (StdCmap *) malloc (sizeof (StdCmap));
	if (!sc) {
	    fprintf (stderr, "%s:  unable to allocate %lu bytes for StdCmap\n",
		     ProgramName, (unsigned long) sizeof(StdCmap));
	    return;
	}
    }

    if (replace) {			/* just update contents */
	if (sc->maps) XFree ((char *) maps);
	if (sc == Scr->StdCmapInfo.mru) Scr->StdCmapInfo.mru = NULL;
    } else {				/* else appending */
	sc->next = NULL;
	sc->atom = a;
	if (Scr->StdCmapInfo.tail) {
	    Scr->StdCmapInfo.tail->next = sc;
	} else {
	    Scr->StdCmapInfo.head = sc;
	}
	Scr->StdCmapInfo.tail = sc;
    }
    sc->nmaps = nmaps;
    sc->maps = maps;

    return;
}

void RemoveRGBColormap (Atom a)
{
    StdCmap *sc, *prev;

    prev = NULL;
    for (sc = Scr->StdCmapInfo.head; sc; sc = sc->next) {  
	if (sc->atom == a) break;
	prev = sc;
    }
    if (sc) {				/* found one */
	if (sc->maps) XFree ((char *) sc->maps);
	if (prev) prev->next = sc->next;
	if (Scr->StdCmapInfo.head == sc) Scr->StdCmapInfo.head = sc->next;
	if (Scr->StdCmapInfo.tail == sc) Scr->StdCmapInfo.tail = prev;
	if (Scr->StdCmapInfo.mru == sc) Scr->StdCmapInfo.mru = NULL;
    }
    return;
}

void LocateStandardColormaps(void)
{
    Atom *atoms;
    int natoms;
    int i;

    atoms = XListProperties (dpy, Scr->Root, &natoms);
    for (i = 0; i < natoms; i++) {
	XStandardColormap *maps = NULL;
	int nmaps;

	if (XGetRGBColormaps (dpy, Scr->Root, &maps, &nmaps, atoms[i])) {
	    /* if got one, then append to current list */
	    InsertRGBColormap (atoms[i], maps, nmaps, False);
	}
    }
    if (atoms) XFree ((char *) atoms);
    return;
}

void GetColor(int kind, Pixel *what, char *name)
{
    XColor color;
    Colormap cmap = Scr->RootColormaps.cwins[0]->colormap->c;

#ifndef TOM
    if (!Scr->FirstTime)
	return;
#endif

    if (Scr->Monochrome != kind)
	return;

    if (! XParseColor (dpy, cmap, name, &color)) {
	fprintf (stderr, "%s:  invalid color name \"%s\"\n", ProgramName, name);
	return;
    }
    if (! XAllocColor (dpy, cmap, &color))
    {
	/* if we could not allocate the color, let's see if this is a
	 * standard colormap
	 */
	XStandardColormap *stdcmap = NULL;

	if (! XParseColor (dpy, cmap, name, &color)) {
	    fprintf (stderr, "%s:  invalid color name \"%s\"\n", ProgramName, name);
	    return;
	}

	/*
	 * look through the list of standard colormaps (check cache first)
	 */
	if (Scr->StdCmapInfo.mru && Scr->StdCmapInfo.mru->maps &&
	    (Scr->StdCmapInfo.mru->maps[Scr->StdCmapInfo.mruindex].colormap ==
	     cmap)) {
	    stdcmap = &(Scr->StdCmapInfo.mru->maps[Scr->StdCmapInfo.mruindex]);
	} else {
	    StdCmap *sc;

	    for (sc = Scr->StdCmapInfo.head; sc; sc = sc->next) {
		int i;

		for (i = 0; i < sc->nmaps; i++) {
		    if (sc->maps[i].colormap == cmap) {
			Scr->StdCmapInfo.mru = sc;
			Scr->StdCmapInfo.mruindex = i;
			stdcmap = &(sc->maps[i]);
			goto gotit;
		    }
		}
	    }
	}

      gotit:
	if (stdcmap) {
            color.pixel = (stdcmap->base_pixel +
			   ((Pixel)(((float)color.red / 65535.0) *
				    stdcmap->red_max + 0.5) *
			    stdcmap->red_mult) +
			   ((Pixel)(((float)color.green /65535.0) *
				    stdcmap->green_max + 0.5) *
			    stdcmap->green_mult) +
			   ((Pixel)(((float)color.blue  / 65535.0) *
				    stdcmap->blue_max + 0.5) *
			    stdcmap->blue_mult));
        } else {
	    fprintf (stderr, "%s:  unable to allocate color \"%s\"\n", 
		     ProgramName, name);
	    return;
	}
    }

    *what = color.pixel;
    return;
}

void GetShadeColors (ColorPair *cp)
{
    XColor	xcol;
    Colormap	cmap = Scr->RootColormaps.cwins[0]->colormap->c;
    int		save;
    float	clearfactor;
    float	darkfactor;
    char	clearcol [32], darkcol [32];

    clearfactor = (float) Scr->ClearShadowContrast / 100.0;
    darkfactor  = (100.0 - (float) Scr->DarkShadowContrast)  / 100.0;
    xcol.pixel = cp->back;
    XQueryColor (dpy, cmap, &xcol);

    sprintf (clearcol, "#%04x%04x%04x",
		xcol.red   + (unsigned short) ((65535 -   xcol.red) * clearfactor),
		xcol.green + (unsigned short) ((65535 - xcol.green) * clearfactor),
		xcol.blue  + (unsigned short) ((65535 -  xcol.blue) * clearfactor));
    sprintf (darkcol,  "#%04x%04x%04x",
		(unsigned short) (xcol.red   * darkfactor),
		(unsigned short) (xcol.green * darkfactor),
		(unsigned short) (xcol.blue  * darkfactor));

    save = Scr->FirstTime;
    Scr->FirstTime = True;
    GetColor (Scr->Monochrome, &cp->shadc, clearcol);
    GetColor (Scr->Monochrome, &cp->shadd,  darkcol);
    Scr->FirstTime = save;
}

void GetFont(MyFont *font)
{
    char *deffontname = "fixed,*";
    char **missing_charset_list_return;
    int missing_charset_count_return;
    char *def_string_return;
    XFontSetExtents *font_extents;
    XFontStruct **xfonts;
    char **font_names;
    register int i;
    int ascent;
    int descent;
    int fnum;
    char *basename2;

    if (font->font_set != NULL){
	XFreeFontSet(dpy, font->font_set);
    }

    basename2 = (char *)malloc(strlen(font->basename) + 3);
    if (basename2) sprintf(basename2, "%s,*", font->basename);
    else basename2 = font->basename;
    if( (font->font_set = XCreateFontSet(dpy, basename2,
				    &missing_charset_list_return,
				    &missing_charset_count_return,
				    &def_string_return)) == NULL) {
	if (Scr->DefaultFont.basename) {
	    deffontname = Scr->DefaultFont.basename;
	}
	if ((font->font_set = XCreateFontSet(dpy, deffontname,
					     &missing_charset_list_return,
					     &missing_charset_count_return,
					     &def_string_return)) == NULL)
	{
	    fprintf (stderr, "%s:  unable to open fonts \"%s\" or \"%s\"\n",
		     ProgramName, font->basename, deffontname);
	    exit(1);
	}
    }
    if (basename2 != font->basename) free(basename2);
    font_extents = XExtentsOfFontSet(font->font_set);

    fnum = XFontsOfFontSet(font->font_set, &xfonts, &font_names);
    for( i = 0, ascent = 0, descent = 0; i<fnum; i++){
	ascent = MaxSize(ascent, (*xfonts)->ascent);
	descent = MaxSize(descent, (*xfonts)->descent);
	xfonts++;
    }

    font->height = font_extents->max_logical_extent.height;
    font->y = ascent;
    font->ascent = ascent;
    font->descent = descent;
}


void SetFocusVisualAttributes (TwmWindow *tmp_win, Bool focus)
{
    if (! tmp_win) return;

    if (focus == tmp_win->hasfocusvisible) return;
    if (tmp_win->highlight) {
	if (Scr->use3Dborders) {
	    PaintBorders (tmp_win, focus);
	}
	else {
	    if (focus) {
		XSetWindowBorder (dpy, tmp_win->frame, tmp_win->borderC.back);
		if (tmp_win->title_w)
		    XSetWindowBorder (dpy, tmp_win->title_w, tmp_win->borderC.back);
	    } else {
		XSetWindowBorderPixmap (dpy, tmp_win->frame, tmp_win->gray);
		if (tmp_win->title_w)
		    XSetWindowBorderPixmap (dpy, tmp_win->title_w, tmp_win->gray);
	    }
	}
    }

    if (focus) {
	Bool hil = False;

	if (tmp_win->lolite_wl) XUnmapWindow (dpy, tmp_win->lolite_wl);
	if (tmp_win->lolite_wr) XUnmapWindow (dpy, tmp_win->lolite_wr);
	if (tmp_win->hilite_wl) {
	    XMapWindow (dpy, tmp_win->hilite_wl);
	    hil = True;
	}
	if (tmp_win->hilite_wr) {
	    XMapWindow (dpy, tmp_win->hilite_wr);
	    hil = True;
	}
	if (hil && tmp_win->HiliteImage && tmp_win->HiliteImage->next) {
	    MaybeAnimate = True;
	}
	if (tmp_win->iconmanagerlist)
	    ActiveIconManager (tmp_win->iconmanagerlist);
    }
    else {
	if (tmp_win->hilite_wl) XUnmapWindow (dpy, tmp_win->hilite_wl);
	if (tmp_win->hilite_wr) XUnmapWindow (dpy, tmp_win->hilite_wr);
	if (tmp_win->lolite_wl) XMapWindow (dpy, tmp_win->lolite_wl);
	if (tmp_win->lolite_wr) XMapWindow (dpy, tmp_win->lolite_wr);
	if (tmp_win->iconmanagerlist)
	    NotActiveIconManager (tmp_win->iconmanagerlist);
    }
    if (Scr->use3Dtitles && Scr->SunkFocusWindowTitle && tmp_win->title_height) {
	ButtonState bs;

	bs = focus ? on : off;
	Draw3DBorder (tmp_win->title_w, Scr->TBInfo.titlex, 0,
			tmp_win->title_width - Scr->TBInfo.titlex -
			Scr->TBInfo.rightoff - Scr->TitlePadding,
			Scr->TitleHeight, Scr->TitleShadowDepth,
			tmp_win->title, bs, False, False);
    }
    tmp_win->hasfocusvisible = focus;
}

static void move_to_head (TwmWindow *t)
{
    if (t == NULL) return;
    if (Scr->FirstWindow == t) return;

    if (t->prev) t->prev->next = t->next;
    if (t->next) t->next->prev = t->prev;

    t->next = Scr->FirstWindow;
    if (Scr->FirstWindow != NULL)
	Scr->FirstWindow->prev = t;
    t->prev = NULL;
    Scr->FirstWindow = t;
}
 
/*
 * SetFocus - separate routine to set focus to make things more understandable
 * and easier to debug
 */
void SetFocus (TwmWindow *tmp_win, Time	tim)
{
    Window w = (tmp_win ? tmp_win->w : PointerRoot);
    int f_iconmgr = 0;

    if (Scr->Focus && (Scr->Focus->iconmgr)) f_iconmgr = 1;
    if (Scr->SloppyFocus && (w == PointerRoot) && (!f_iconmgr)) return;

    XSetInputFocus (dpy, w, RevertToPointerRoot, tim);
    if (Scr->Focus == tmp_win) return;

    if (Scr->Focus) {
	if (Scr->Focus->AutoSqueeze && !Scr->Focus->squeezed) {
	    AutoSqueeze (Scr->Focus);
	}
	SetFocusVisualAttributes (Scr->Focus, False);
    }
    if (tmp_win)    {
	if (tmp_win->AutoSqueeze && tmp_win->squeezed) {
	    AutoSqueeze (tmp_win);
	}
	SetFocusVisualAttributes (tmp_win, True);
    }
    Scr->Focus = tmp_win;
    move_to_head (tmp_win);
}

#
#ifdef NOPUTENV
/*
 * define our own putenv() if the system doesn't have one.
 * putenv(s): place s (a string of the form "NAME=value") in
 * the environment; replacing any existing NAME.  s is placed in
 * environment, so if you change s, the environment changes (like
 * putenv on a sun).  Binding removed if you putenv something else
 * called NAME.
 */
int
putenv(s)
    char *s;
{
    char *v;
    int varlen, idx;
    extern char **environ;
    char **newenv;
    static int virgin = 1; /* true while "environ" is a virgin */

    v = strchr(s, '=');
    if(v == 0)
	return 0; /* punt if it's not of the right form */
    varlen = (v + 1) - s;

    for (idx = 0; environ[idx] != 0; idx++) {
	if (strncmp(environ[idx], s, varlen) == 0) {
	    if(v[1] != 0) { /* true if there's a value */
		environ[idx] = s;
		return 0;
	    } else {
		do {
		    environ[idx] = environ[idx+1];
		} while(environ[++idx] != 0);
		return 0;
	    }
	}
    }
    
    /* add to environment (unless no value; then just return) */
    if(v[1] == 0)
	return 0;
    if(virgin) {
	register i;

	newenv = (char **) malloc((unsigned) ((idx + 2) * sizeof(char*)));
	if(newenv == 0)
	    return -1;
	for(i = idx-1; i >= 0; --i)
	    newenv[i] = environ[i];
	virgin = 0;     /* you're not a virgin anymore, sweety */
    } else {
	newenv = (char **) realloc((char *) environ,
				   (unsigned) ((idx + 2) * sizeof(char*)));
	if (newenv == 0)
	    return -1;
    }

    environ = newenv;
    environ[idx] = s;
    environ[idx+1] = 0;
    
    return 0;
}
#endif /* NOPUTENV */


static Pixmap CreateXLogoPixmap (unsigned int *widthp, unsigned int *heightp)
{
    int h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
    if (h < 0) h = 0;

    *widthp = *heightp = (unsigned int) h;
    if (Scr->tbpm.xlogo == None) {
	GC gc, gcBack;

	Scr->tbpm.xlogo = XCreatePixmap (dpy, Scr->Root, h, h, 1);
	gc = XCreateGC (dpy, Scr->tbpm.xlogo, 0L, NULL);
	XSetForeground (dpy, gc, 0);
	XFillRectangle (dpy, Scr->tbpm.xlogo, gc, 0, 0, h, h);
	XSetForeground (dpy, gc, 1);
	gcBack = XCreateGC (dpy, Scr->tbpm.xlogo, 0L, NULL);
	XSetForeground (dpy, gcBack, 0);

	/*
	 * draw the logo large so that it gets as dense as possible; then white
	 * out the edges so that they look crisp
	 */
	XmuDrawLogo (dpy, Scr->tbpm.xlogo, gc, gcBack, -1, -1, h + 2, h + 2);
	XDrawRectangle (dpy, Scr->tbpm.xlogo, gcBack, 0, 0, h - 1, h - 1);

	/*
	 * done drawing
	 */
	XFreeGC (dpy, gc);
	XFreeGC (dpy, gcBack);
    }
    return Scr->tbpm.xlogo;
}


static Pixmap CreateResizePixmap (unsigned int *widthp, unsigned int *heightp)
{
    int h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
    if (h < 1) h = 1;

    *widthp = *heightp = (unsigned int) h;
    if (Scr->tbpm.resize == None) {
	XPoint	points[3];
	GC gc;
	int w;
	int lw;

	/*
	 * create the pixmap
	 */
	Scr->tbpm.resize = XCreatePixmap (dpy, Scr->Root, h, h, 1);
	gc = XCreateGC (dpy, Scr->tbpm.resize, 0L, NULL);
	XSetForeground (dpy, gc, 0);
	XFillRectangle (dpy, Scr->tbpm.resize, gc, 0, 0, h, h);
	XSetForeground (dpy, gc, 1);
	lw = h / 16;
	if (lw == 1)
	    lw = 0;
	XSetLineAttributes (dpy, gc, lw, LineSolid, CapButt, JoinMiter);

	/*
	 * draw the resize button, 
	 */
	w = (h * 2) / 3;
	points[0].x = w;
	points[0].y = 0;
	points[1].x = w;
	points[1].y = w;
	points[2].x = 0;
	points[2].y = w;
	XDrawLines (dpy, Scr->tbpm.resize, gc, points, 3, CoordModeOrigin);
	w = w / 2;
	points[0].x = w;
	points[0].y = 0;
	points[1].x = w;
	points[1].y = w;
	points[2].x = 0;
	points[2].y = w;
	XDrawLines (dpy, Scr->tbpm.resize, gc, points, 3, CoordModeOrigin);

	/*
	 * done drawing
	 */
	XFreeGC(dpy, gc);
    }
    return Scr->tbpm.resize;
}

static Pixmap CreateDotPixmap (unsigned int *widthp, unsigned int *heightp)
{
    int h = Scr->TBInfo.width - Scr->TBInfo.border * 2;

    h = h * 3 / 4;
    if (h < 1) h = 1;
    if (!(h & 1))
	h--;
    *widthp = *heightp = (unsigned int) h;
    if (Scr->tbpm.delete == None) {
	GC  gc;
	Pixmap pix;

	pix = Scr->tbpm.delete = XCreatePixmap (dpy, Scr->Root, h, h, 1);
	gc = XCreateGC (dpy, pix, 0L, NULL);
	XSetLineAttributes (dpy, gc, h, LineSolid, CapRound, JoinRound);
	XSetForeground (dpy, gc, 0L);
	XFillRectangle (dpy, pix, gc, 0, 0, h, h);
	XSetForeground (dpy, gc, 1L);
	XDrawLine (dpy, pix, gc, h/2, h/2, h/2, h/2);
	XFreeGC (dpy, gc);
    }
    return Scr->tbpm.delete;
}

static Image *Create3DCrossImage (ColorPair cp)
{
    Image *image;
    int        h;
    int    point;
    int midpoint;

    h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
    if (!(h & 1)) h--;
    point = 4;
    midpoint = h/2;

    image = (Image*) malloc (sizeof (struct _Image));
    if (! image) return (None);
    image->pixmap = XCreatePixmap (dpy, Scr->Root, h, h, Scr->d_depth);
    if (image->pixmap == None) return (None);

    Draw3DBorder (image->pixmap, 0, 0, h, h, Scr->TitleButtonShadowDepth, cp, off, True, False);

#ifdef LEVITTE_TEST
    FB (cp.shadc, cp.shadd);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point+1, point-1, point-1, point+1);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point+1, point, point, point+1);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point-1, point+1, midpoint-2, midpoint);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, midpoint, midpoint+2, h-point-3, h-point-1);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point, point+1, h-point-3, h-point-2);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point-1, h-point-2, midpoint-2, midpoint);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, midpoint, midpoint-2, h-point-2, point-1);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point, h-point-2, h-point-2, point);
#endif

    FB (cp.shadd, cp.shadc);
#ifdef LEVITTE_TEST
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point+2, point+1, h-point-1, h-point-2);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point+2, point, midpoint, midpoint-2);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, midpoint+2, midpoint, h-point, h-point-2);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, h-point, h-point-2, h-point-2, h-point);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, h-point-1, h-point-2, h-point-2, h-point-1);
#else
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point, point, h-point-1, h-point-1);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point-1, point, h-point-1, h-point);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point, point-1, h-point, h-point-1);
#endif

#ifdef LEVITTE_TEST
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point, h-point-1, point, h-point-1);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, h-point-1, point, h-point-1, point);
#else
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point, h-point-1, h-point-1, point);
#endif
#ifdef LEVITTE_TEST
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point+1, h-point-1, h-point-1, point+1);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point+1, h-point, midpoint, midpoint+2);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, midpoint+2, midpoint, h-point, point+1);
#else
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point-1, h-point-1, h-point-1, point-1);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point, h-point, h-point, point);
#endif

    image->mask   = None;
    image->width  = h;
    image->height = h;
    image->next   = None;

    return (image);
}

static Image *Create3DIconifyImage (ColorPair cp)
{
    Image *image;
    int     h;
    int point;

    h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
    if (!(h & 1)) h--;
    point = ((h/2-2) * 2+1) / 3;

    image = (Image*) malloc (sizeof (struct _Image));
    if (! image) return (None);
    image->pixmap = XCreatePixmap (dpy, Scr->Root, h, h, Scr->d_depth);
    if (image->pixmap == None) return (None);

    Draw3DBorder (image->pixmap, 0, 0, h, h, Scr->TitleButtonShadowDepth, cp, off, True, False);
    FB (cp.shadd, cp.shadc);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point, point, h/2, h-point);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, point, point, h-point, point);

    FB (cp.shadc, cp.shadd);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, h-point, point, h/2+1, h-point);
    XDrawLine (dpy, image->pixmap, Scr->NormalGC, h-point-1, point+1, h/2+1, h-point-1);

    image->mask   = None;
    image->width  = h;
    image->height = h;
    image->next   = None;

    return (image);
}

static Image *Create3DSunkenResizeImage (ColorPair cp)
{
    int     h;
    Image *image;

    h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
    if (!(h & 1)) h--;

    image = (Image*) malloc (sizeof (struct _Image));
    if (! image) return (None);
    image->pixmap = XCreatePixmap (dpy, Scr->Root, h, h, Scr->d_depth);
    if (image->pixmap == None) return (None);

    Draw3DBorder (image->pixmap, 0, 0, h, h, Scr->TitleButtonShadowDepth, cp, off, True, False);
    Draw3DBorder (image->pixmap, 3, 3, h-6, h-6, 1, cp, on, True, False);
    Draw3DBorder (image->pixmap, 3, ((h-6)/3)+3, ((h-6)*2/3)+1,
     ((h-6)*2/3)+1, 1, cp, on, True, False);
    Draw3DBorder (image->pixmap, 3, ((h-6)*2/3)+3, ((h-6)/3)+1,
     ((h-6)/3)+1, 1, cp, on, True, False);

    image->mask   = None;
    image->width  = h;
    image->height = h;
    image->next   = None;

    return (image);
}

static Image *Create3DBoxImage (ColorPair cp)
{
    int     h;
    Image   *image;

    h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
    if (! (h & 1)) h--;

    image = (Image*) malloc (sizeof (struct _Image));
    if (! image) return (None);
    image->pixmap = XCreatePixmap (dpy, Scr->Root, h, h, Scr->d_depth);
    if (image->pixmap == None) return (None);

    Draw3DBorder (image->pixmap, 0, 0, h, h, Scr->TitleButtonShadowDepth, cp, off, True, False);
    Draw3DBorder (image->pixmap, (h / 2) - 4, (h / 2) - 4, 9, 9, 1, cp,
     off, True, False);

    image->mask   = None;
    image->width  = h;
    image->height = h;
    image->next   = None;

    return (image);
}

static Image *Create3DDotImage (ColorPair cp)
{
    Image *image;
    int	  h;
    static int idepth = 2;

    h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
    if (!(h & 1)) h--;

    image = (Image*) malloc (sizeof (struct _Image));
    if (! image) return (None);
    image->pixmap = XCreatePixmap (dpy, Scr->Root, h, h, Scr->d_depth);
    if (image->pixmap == None) return (None);

    Draw3DBorder (image->pixmap, 0, 0, h, h, Scr->TitleButtonShadowDepth, cp, off, True, False);
    Draw3DBorder (image->pixmap, (h / 2) - idepth,
				 (h / 2) - idepth,
				 2 * idepth + 1,
				 2 * idepth + 1,
				 idepth, cp, off, True, False);
    image->mask   = None;
    image->width  = h;
    image->height = h;
    image->next   = None;
    return (image);
}

static Image *Create3DBarImage (ColorPair cp)
{
    Image *image;
    int	  h;
    static int idepth = 2;

    h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
    if (!(h & 1)) h--;

    image = (Image*) malloc (sizeof (struct _Image));
    if (! image) return (None);
    image->pixmap = XCreatePixmap (dpy, Scr->Root, h, h, Scr->d_depth);
    if (image->pixmap == None) return (None);

    Draw3DBorder (image->pixmap, 0, 0, h, h, Scr->TitleButtonShadowDepth, cp, off, True, False);
    Draw3DBorder (image->pixmap,
			Scr->TitleButtonShadowDepth + 2,
			(h / 2) - idepth,
			h - 2 * (Scr->TitleButtonShadowDepth + 2),
			2 * idepth + 1,
			idepth, cp, off, True, False);
    image->mask   = None;
    image->width  = h;
    image->height = h;
    image->next   = None;
    return (image);
}

static Image *Create3DVertBarImage (ColorPair cp)
{
    Image *image;
    int	  h;
    static int idepth = 2;

    h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
    if (!(h & 1)) h--;

    image = (Image*) malloc (sizeof (struct _Image));
    if (! image) return (None);
    image->pixmap = XCreatePixmap (dpy, Scr->Root, h, h, Scr->d_depth);
    if (image->pixmap == None) return (None);

    Draw3DBorder (image->pixmap, 0, 0, h, h, Scr->TitleButtonShadowDepth, cp, off, True, False);
    Draw3DBorder (image->pixmap,
			(h / 2) - idepth,
			Scr->TitleButtonShadowDepth + 2,
			2 * idepth + 1,
			h - 2 * (Scr->TitleButtonShadowDepth + 2),
			idepth, cp, off, True, False);
    image->mask   = None;
    image->width  = h;
    image->height = h;
    image->next   = None;
    return (image);
}

static Image *Create3DMenuImage (ColorPair cp)
{
    Image *image;
    int	  h, i;

    h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
    if (!(h & 1)) h--;

    image = (Image*) malloc (sizeof (struct _Image));
    if (! image) return (None);
    image->pixmap = XCreatePixmap (dpy, Scr->Root, h, h, Scr->d_depth);
    if (image->pixmap == None) return (None);

    Draw3DBorder (image->pixmap, 0, 0, h, h, Scr->TitleButtonShadowDepth, cp, off, True, False);
    for (i = 4; i < h - 7; i += 5) {
	Draw3DBorder (image->pixmap, 4, i, h - 8, 4, 2, cp, off, True, False);
    }
    image->mask   = None;
    image->width  = h;
    image->height = h;
    image->next   = None;
    return (image);
}

static Image *Create3DResizeImage (ColorPair cp)
{
    Image *image;
    int	  h;

    h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
    if (!(h & 1)) h--;

    image = (Image*) malloc (sizeof (struct _Image));
    if (! image) return (None);
    image->pixmap = XCreatePixmap (dpy, Scr->Root, h, h, Scr->d_depth);
    if (image->pixmap == None) return (None);

    Draw3DBorder (image->pixmap, 0, 0, h, h, Scr->TitleButtonShadowDepth, cp, off, True, False);
    Draw3DBorder (image->pixmap, 0, h / 4, ((3 * h) / 4) + 1, ((3 * h) / 4) + 1, 2,
		cp, off, True, False);
    Draw3DBorder (image->pixmap, 0, h / 2, (h / 2) + 1, (h / 2) + 1, 2, cp, off, True, False);
    image->mask   = None;
    image->width  = h;
    image->height = h;
    image->next   = None;
    return (image);
}

static Image *Create3DZoomImage (ColorPair cp)
{
    Image *image;
    int		h;
    static int idepth = 2;

    h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
    if (!(h & 1)) h--;

    image = (Image*) malloc (sizeof (struct _Image));
    if (! image) return (None);
    image->pixmap = XCreatePixmap (dpy, Scr->Root, h, h, Scr->d_depth);
    if (image->pixmap == None) return (None);

    Draw3DBorder (image->pixmap, 0, 0, h, h, Scr->TitleButtonShadowDepth, cp, off, True, False);
    Draw3DBorder (image->pixmap, Scr->TitleButtonShadowDepth + 2,
				 Scr->TitleButtonShadowDepth + 2,
				 h - 2 * (Scr->TitleButtonShadowDepth + 2),
				 h - 2 * (Scr->TitleButtonShadowDepth + 2),
				 idepth, cp, off, True, False);

    image->mask   = None;
    image->width  = h;
    image->height = h;
    image->next   = None;
    return (image);
}

struct Colori {
    Pixel color;
    Pixmap pix;
    struct Colori *next;
};

Pixmap Create3DMenuIcon (unsigned int height,
			 unsigned int *widthp, unsigned int *heightp,
			 ColorPair cp)
{
    unsigned int h, w;
    int		i;
    struct Colori *col;
    static struct Colori *colori = NULL;

    h = height;
    w = h * 7 / 8;
    if (h < 1) h = 1;
    if (w < 1) w = 1;
    *widthp  = w;
    *heightp = h;

    for (col = colori; col; col = col->next) {
	if (col->color == cp.back) break;
    }
    if (col != NULL) {
	return (col->pix);
    }
    col = (struct Colori*) malloc (sizeof (struct Colori));
    col->color = cp.back;
    col->pix   = XCreatePixmap (dpy, Scr->Root, h, h, Scr->d_depth);
    col->next = colori;
    colori = col;

    Draw3DBorder (col->pix, 0, 0, w, h, 1, cp, off, True, False);
    for (i = 3; i + 5 < h; i += 5) {
	Draw3DBorder (col->pix, 4, i, w - 8, 3, 1, Scr->MenuC, off, True, False);
    }
    return (colori->pix);
}

#include "siconify.bm"

Pixmap Create3DIconManagerIcon (ColorPair cp)
{
    unsigned int w, h;
    struct Colori *col;
    static struct Colori *colori = NULL;

    w = (unsigned int) siconify_width;
    h = (unsigned int) siconify_height;

    for (col = colori; col; col = col->next) {
	if (col->color == cp.back) break;
    }
    if (col != NULL) {
	return (col->pix);
    }
    col = (struct Colori*) malloc (sizeof (struct Colori));
    col->color = cp.back;
    col->pix   = XCreatePixmap (dpy, Scr->Root, w, h, Scr->d_depth);
    Draw3DBorder (col->pix, 0, 0, w, h, 4, cp, off, True, False);
    col->next = colori;
    colori = col;

    return (colori->pix);
}

static Image *Create3DResizeAnimation (Bool in, Bool left, Bool top,
				       ColorPair cp)
{
    int		h, i, j;
    Image	*image, *im, *im1;

    h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
    if (!(h & 1)) h--;

    image = im1 = None;
    for (i = (in ? 0 : (h/4)-1); (i < h/4) && (i >= 0); i += (in ? 1 : -1)) {
	im = (Image*) malloc (sizeof (struct _Image));
	if (! im) return (None);
	im->pixmap = XCreatePixmap (dpy, Scr->Root, h, h, Scr->d_depth);
	if (im->pixmap == None) {
	    free (im);
	    return (None);
	}
	Draw3DBorder (im->pixmap, 0, 0, h, h, Scr->TitleButtonShadowDepth, cp, off, True, False);
	for (j = i; j <= h; j += (h/4)){
	    Draw3DBorder (im->pixmap, (left ? 0 : j), (top ? 0 : j),
			  h - j, h - j, 2, cp, off, True, False);
	}
	im->mask   = None;
	im->width  = h;
	im->height = h;
	im->next   = None;
	if (image == None) {
	    image = im1 = im;
	}
	else {
	    im1->next = im;
	    im1 = im;
	}
    }
    if (im1 != None) im1->next = image;
    return (image);
}

static Image *Create3DResizeInTopAnimation (ColorPair cp)
{
    return Create3DResizeAnimation (TRUE, FALSE, TRUE, cp);
}

static Image *Create3DResizeOutTopAnimation (ColorPair cp)
{
    return Create3DResizeAnimation (False, FALSE, TRUE, cp);
}

static Image *Create3DResizeInBotAnimation (ColorPair cp)
{
    return Create3DResizeAnimation (TRUE, TRUE, FALSE, cp);
}

static Image *Create3DResizeOutBotAnimation (ColorPair cp)
{
    return Create3DResizeAnimation (False, TRUE, FALSE, cp);
}

static Image *Create3DMenuAnimation (Bool up, ColorPair cp)
{
    int               h, i, j;
    Image     *image, *im, *im1;

    h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
    if (!(h & 1)) h--;

    image = im1 = None;
    for (j = (up ? 4 : 0); j != (up ? -1 : 5); j+= (up ? -1 : 1)) {
	im = (Image*) malloc (sizeof (struct _Image));
	if (! im) return (None);
	im->pixmap = XCreatePixmap (dpy, Scr->Root, h, h, Scr->d_depth);
	if (im->pixmap == None) {
	    free (im);
	    return (None);
	}
	Draw3DBorder (im->pixmap, 0, 0, h, h, Scr->TitleButtonShadowDepth, cp, off, True, False);
	for (i = j; i < h - 3; i += 5) {
	    Draw3DBorder (im->pixmap, 4, i, h - 8, 4, 2, cp, off, True, False);
	}
	im->mask   = None;
	im->width  = h;
	im->height = h;
	im->next   = None;
	if (image == None) {
	    image = im1 = im;
	}
	else {
	    im1->next = im;
	    im1 = im;
	}
    }
    if (im1 != None) im1->next = image;
    return (image);
}

static Image *Create3DMenuUpAnimation (ColorPair cp)
{
    return Create3DMenuAnimation (TRUE, cp);
}

static Image *Create3DMenuDownAnimation (ColorPair cp)
{
    return Create3DMenuAnimation (False, cp);
}

static Image *Create3DZoomAnimation (Bool in, Bool out, int n, ColorPair cp)
{
    int		h, i, j, k;
    Image	*image, *im, *im1;

    h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
    if (!(h & 1)) h--;

    if (n == 0) n = (h/2) - 2;

    image = im1 = None;
    for (j = (out ? -1 : 1) ; j < (in ? 2 : 0); j += 2){
	for(k = (j > 0 ? 0 : n-1) ; (k >= 0) && (k < n); k += j){
	    im = (Image*) malloc (sizeof (struct _Image));
	    im->pixmap = XCreatePixmap (dpy, Scr->Root, h, h, Scr->d_depth);
	    Draw3DBorder (im->pixmap, 0, 0, h, h, Scr->TitleButtonShadowDepth, cp, off, True, False);
	    for (i = 2 + k; i < (h / 2); i += n) {
		Draw3DBorder (im->pixmap, i, i, h - (2 * i), h - (2 * i), 2, cp, off, True, False);
	    }
	    im->mask   = None;
	    im->width  = h;
	    im->height = h;
	    im->next   = None;
	    if (image == None) {
		image = im1 = im;
	    }
	    else {
		im1->next = im;
		im1 = im;
	    }
	}
    }
    if (im1 != None) im1->next = image;
    return (image);
}

static Image *Create3DMazeInAnimation (ColorPair cp)
{
    return Create3DZoomAnimation(TRUE, FALSE, 6, cp);
}

static Image *Create3DMazeOutAnimation (ColorPair cp)
{
    return Create3DZoomAnimation(FALSE, TRUE, 6, cp);
}

static Image *Create3DZoomInAnimation (ColorPair cp)
{
    return Create3DZoomAnimation(TRUE, FALSE, 0, cp);
}

static Image *Create3DZoomOutAnimation (ColorPair cp)
{
    return Create3DZoomAnimation(FALSE, TRUE, 0, cp);
}

static Image *Create3DZoomInOutAnimation (ColorPair cp)
{
    return Create3DZoomAnimation(TRUE, TRUE, 0, cp);
}

#define questionmark_width 8
#define questionmark_height 8
static char questionmark_bits[] = {
   0x38, 0x7c, 0x64, 0x30, 0x18, 0x00, 0x18, 0x18};

static Pixmap CreateQuestionPixmap (unsigned int *widthp,
				    unsigned int *heightp)
{
    *widthp = questionmark_width;
    *heightp = questionmark_height;
    if (Scr->tbpm.question == None) {
	Scr->tbpm.question = XCreateBitmapFromData (dpy, Scr->Root,
						    questionmark_bits,
						    questionmark_width,
						    questionmark_height);
    }
    /*
     * this must succeed or else we are in deep trouble elsewhere
     */
    return Scr->tbpm.question;
}


static Pixmap CreateMenuPixmap (unsigned int *widthp, unsigned int *heightp)
{
    return (CreateMenuIcon (Scr->TBInfo.width - Scr->TBInfo.border * 2,widthp,heightp));
}

Pixmap CreateMenuIcon (int height, unsigned int *widthp, unsigned int *heightp)
{
    int h, w;
    int ih, iw;
    int	ix, iy;
    int	mh, mw;
    int	tw, th;
    int	lw, lh;
    int	lx, ly;
    int	lines, dly;
    int offset;
    int	bw;

    h = height;
    w = h * 7 / 8;
    if (h < 1)
	h = 1;
    if (w < 1)
	w = 1;
    *widthp = w;
    *heightp = h;
    if (Scr->tbpm.menu == None) {
	Pixmap  pix;
	GC	gc;

	pix = Scr->tbpm.menu = XCreatePixmap (dpy, Scr->Root, w, h, 1);
	gc = XCreateGC (dpy, pix, 0L, NULL);
	XSetForeground (dpy, gc, 0L);
	XFillRectangle (dpy, pix, gc, 0, 0, w, h);
	XSetForeground (dpy, gc, 1L);
	ix = 1;
	iy = 1;
	ih = h - iy * 2;
	iw = w - ix * 2;
	offset = ih / 8;
	mh = ih - offset;
	mw = iw - offset;
	bw = mh / 16;
	if (bw == 0 && mw > 2)
	    bw = 1;
	tw = mw - bw * 2;
	th = mh - bw * 2;
	XFillRectangle (dpy, pix, gc, ix, iy, mw, mh);
	XFillRectangle (dpy, pix, gc, ix + iw - mw, iy + ih - mh, mw, mh);
	XSetForeground (dpy, gc, 0L);
	XFillRectangle (dpy, pix, gc, ix+bw, iy+bw, tw, th);
	XSetForeground (dpy, gc, 1L);
	lw = tw / 2;
	if ((tw & 1) ^ (lw & 1))
	    lw++;
	lx = ix + bw + (tw - lw) / 2;

	lh = th / 2 - bw;
	if ((lh & 1) ^ ((th - bw) & 1))
	    lh++;
	ly = iy + bw + (th - bw - lh) / 2;

	lines = 3;
	if ((lh & 1) && lh < 6)
	{
	    lines--;
	}
	dly = lh / (lines - 1);
	while (lines--)
	{
	    XFillRectangle (dpy, pix, gc, lx, ly, lw, bw);
	    ly += dly;
	}
	XFreeGC (dpy, gc);
    }
    return Scr->tbpm.menu;
}

#define FBGC(gc, fix_fore, fix_back)\
    Gcv.foreground = fix_fore;\
    Gcv.background = fix_back;\
    XChangeGC(dpy, gc, GCForeground|GCBackground,&Gcv)

void Draw3DBorder (Window w, int x, int y, int width, int height, int bw,
		   ColorPair cp, int state, int fill, int forcebw)
{
    int		  i;
    XGCValues	  gcv;
    unsigned long gcm;

    if ((width < 1) || (height < 1)) return;
    if (Scr->Monochrome != COLOR) {
	if (fill) {
	    gcm = GCFillStyle;
	    gcv.fill_style = FillOpaqueStippled;
	    XChangeGC (dpy, Scr->BorderGC, gcm, &gcv);
	    XFillRectangle (dpy, w, Scr->BorderGC, x, y, width, height);
	}
	gcm  = 0;
	gcm |= GCLineStyle;		
	gcv.line_style = (state == on) ? LineSolid : LineDoubleDash;
	gcm |= GCFillStyle;
	gcv.fill_style = FillSolid;
	XChangeGC (dpy, Scr->BorderGC, gcm, &gcv);
	for (i = 0; i < bw; i++) {
	    XDrawLine (dpy, w, Scr->BorderGC, x,                 y + i,
					    x + width - i - 1, y + i);
	    XDrawLine (dpy, w, Scr->BorderGC, x + i,                  y,
					    x + i, y + height - i - 1);
	}

	gcm  = 0;
	gcm |= GCLineStyle;		
	gcv.line_style = (state == on) ? LineDoubleDash : LineSolid;
	gcm |= GCFillStyle;
	gcv.fill_style = FillSolid;
	XChangeGC (dpy, Scr->BorderGC, gcm, &gcv);
	for (i = 0; i < bw; i++) {
	    XDrawLine (dpy, w, Scr->BorderGC, x + width - i - 1,          y + i,
					    x + width - i - 1, y + height - 1);
	    XDrawLine (dpy, w, Scr->BorderGC, x + i,         y + height - i - 1,
					    x + width - 1, y + height - i - 1);
	}
	return;
    }

    if (fill) {
	FBGC (Scr->BorderGC, cp.back, cp.fore);
	XFillRectangle (dpy, w, Scr->BorderGC, x, y, width, height);
    }
    if (Scr->BeNiceToColormap) {
	int dashoffset = 0;

	gcm  = 0;
	gcm |= GCLineStyle;		
	gcv.line_style = (forcebw) ? LineSolid : LineDoubleDash;
	gcm |= GCBackground;
	gcv.background = cp.back;
	XChangeGC (dpy, Scr->BorderGC, gcm, &gcv);
	    
	if (state == on)
	    XSetForeground (dpy, Scr->BorderGC, Scr->Black);
	else
	    XSetForeground (dpy, Scr->BorderGC, Scr->White);
	for (i = 0; i < bw; i++) {
	    XDrawLine (dpy, w, Scr->BorderGC, x + i,     y + dashoffset,
					    x + i, y + height - i - 1);
	    XDrawLine (dpy, w, Scr->BorderGC, x + dashoffset,    y + i,
					    x + width - i - 1, y + i);
	    dashoffset = 1 - dashoffset;
	}
	XSetForeground (dpy, Scr->BorderGC, ((state == on) ? Scr->White : Scr->Black));
	for (i = 0; i < bw; i++) {
	    XDrawLine (dpy, w, Scr->BorderGC, x + i,         y + height - i - 1,
					    x + width - 1, y + height - i - 1);
	    XDrawLine (dpy, w, Scr->BorderGC, x + width - i - 1,          y + i,
					    x + width - i - 1, y + height - 1);
	}
	return;
    }
    if (state == on) { FBGC (Scr->BorderGC, cp.shadd, cp.shadc); }
    else             { FBGC (Scr->BorderGC, cp.shadc, cp.shadd); }
    for (i = 0; i < bw; i++) {
	XDrawLine (dpy, w, Scr->BorderGC, x,                 y + i,
					  x + width - i - 1, y + i);
	XDrawLine (dpy, w, Scr->BorderGC, x + i,                  y,
					  x + i, y + height - i - 1);
    }

    if (state == on) { FBGC (Scr->BorderGC, cp.shadc, cp.shadd); }
    else             { FBGC (Scr->BorderGC, cp.shadd, cp.shadc); }
    for (i = 0; i < bw; i++) {
	XDrawLine (dpy, w, Scr->BorderGC, x + width - i - 1,          y + i,
					  x + width - i - 1, y + height - 1);
	XDrawLine (dpy, w, Scr->BorderGC, x + i,         y + height - i - 1,
					  x + width - 1, y + height - i - 1);
    }
    return;
}

void Draw3DCorner (Window w,
		   int x, int y, int width, int height, int thick, int bw,
		   ColorPair cp, int type)
{
    XRectangle rects [2];

    switch (type) {
	case 0 :
	    Draw3DBorder (w, x, y, width, height, bw, cp, off, True, False);
	    Draw3DBorder (w, x + thick - bw, y + thick - bw,
			width - thick + 2 * bw, height - thick + 2 * bw,
			bw, cp, on, True, False);
	    break;
	case 1 :
	    Draw3DBorder (w, x, y, width, height, bw, cp, off, True, False);
	    Draw3DBorder (w, x, y + thick - bw,
			width - thick + bw, height - thick,
			bw, cp, on, True, False);
	    break;
	case 2 :
	    rects [0].x      = x + width - thick;
	    rects [0].y      = y;
	    rects [0].width  = thick;
	    rects [0].height = height;
	    rects [1].x      = x;
	    rects [1].y      = y + width - thick;
	    rects [1].width  = width - thick;
	    rects [1].height = thick;
	    XSetClipRectangles (dpy, Scr->BorderGC, 0, 0, rects, 2, Unsorted);
	    Draw3DBorder (w, x, y, width, height, bw, cp, off, True, False);
	    Draw3DBorder (w, x, y,
			width - thick + bw, height - thick + bw,
			bw, cp, on, True, False);
	    XSetClipMask (dpy, Scr->BorderGC, None);
	    break;
	case 3 :
	    rects [0].x      = x;
	    rects [0].y      = y;
	    rects [0].width  = thick;
	    rects [0].height = height;
	    rects [1].x      = x + thick;
	    rects [1].y      = y + height - thick;
	    rects [1].width  = width - thick;
	    rects [1].height = thick;
	    XSetClipRectangles (dpy, Scr->BorderGC, 0, 0, rects, 2, Unsorted);
	    Draw3DBorder (w, x, y, width, height, bw, cp, off, True, False);
	    Draw3DBorder (w, x + thick - bw, y,
			width - thick, height - thick + bw,
			bw, cp, on, True, False);
	    XSetClipMask (dpy, Scr->BorderGC, None);
	    break;
    }
    return;
}

void PaintAllDecoration (void)
{
    TwmWindow *tmp_win;
    virtualScreen *vs;

    for (tmp_win = Scr->FirstWindow; tmp_win != NULL; tmp_win = tmp_win->next) {
	if (! visible (tmp_win)) continue;
	if (tmp_win->mapped == TRUE) {
	    if (tmp_win->frame_bw3D) {
		if (tmp_win->highlight && tmp_win == Scr->Focus)
		    PaintBorders (tmp_win, True);
		else
		    PaintBorders (tmp_win, False);
	    }
	    if (tmp_win->title_w)      PaintTitle        (tmp_win);
	    if (tmp_win->titlebuttons) PaintTitleButtons (tmp_win);
	}
	else
	if ((tmp_win->icon_on == TRUE)  &&
		!tmp_win->icon_not_ours &&
		!Scr->NoIconTitlebar    &&
		tmp_win->icon           &&
		tmp_win->icon->w        &&
		! LookInList (Scr->NoIconTitle, tmp_win->full_name, &tmp_win->class)) {
	    PaintIcon (tmp_win);
	}
    }
    for (vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
      PaintWorkSpaceManager (vs);
    }
}

void PaintBorders (TwmWindow *tmp_win, Bool focus)
{
    ColorPair cp;

    cp = (focus && tmp_win->highlight) ? tmp_win->borderC : tmp_win->border_tile;
    if (tmp_win->title_height == 0) {
	Draw3DBorder (tmp_win->frame,
	    0,
	    0,
	    tmp_win->frame_width,
	    tmp_win->frame_height,
	    Scr->BorderShadowDepth, cp, off, True, False);
	Draw3DBorder (tmp_win->frame,
	    tmp_win->frame_bw3D - Scr->BorderShadowDepth,
	    tmp_win->frame_bw3D - Scr->BorderShadowDepth,
	    tmp_win->frame_width  - 2 * tmp_win->frame_bw3D + 2 * Scr->BorderShadowDepth,
	    tmp_win->frame_height - 2 * tmp_win->frame_bw3D + 2 * Scr->BorderShadowDepth,
	    Scr->BorderShadowDepth, cp, on, True, False);
	return;
    }
    Draw3DCorner (tmp_win->frame,
		tmp_win->title_x - tmp_win->frame_bw3D,
		0,
		Scr->TitleHeight + tmp_win->frame_bw3D,
		Scr->TitleHeight + tmp_win->frame_bw3D,
		tmp_win->frame_bw3D, Scr->BorderShadowDepth, cp, 0);
    Draw3DCorner (tmp_win->frame,
		tmp_win->title_x + tmp_win->title_width - Scr->TitleHeight,
		0,
		Scr->TitleHeight + tmp_win->frame_bw3D,
		Scr->TitleHeight + tmp_win->frame_bw3D,
		tmp_win->frame_bw3D, Scr->BorderShadowDepth, cp, 1);
    Draw3DCorner (tmp_win->frame,
		tmp_win->frame_width  - (Scr->TitleHeight + tmp_win->frame_bw3D),
		tmp_win->frame_height - (Scr->TitleHeight + tmp_win->frame_bw3D),
		Scr->TitleHeight + tmp_win->frame_bw3D,
		Scr->TitleHeight + tmp_win->frame_bw3D,
		tmp_win->frame_bw3D, Scr->BorderShadowDepth, cp, 2);
    Draw3DCorner (tmp_win->frame,
		0,
		tmp_win->frame_height - (Scr->TitleHeight + tmp_win->frame_bw3D),
		Scr->TitleHeight + tmp_win->frame_bw3D,
		Scr->TitleHeight + tmp_win->frame_bw3D,
		tmp_win->frame_bw3D, Scr->BorderShadowDepth, cp, 3);

    Draw3DBorder (tmp_win->frame,
		tmp_win->title_x + Scr->TitleHeight,
		0,
		tmp_win->title_width - 2 * Scr->TitleHeight,
		tmp_win->frame_bw3D,
		Scr->BorderShadowDepth, cp, off, True, False);
    Draw3DBorder (tmp_win->frame,
		tmp_win->frame_bw3D + Scr->TitleHeight,
		tmp_win->frame_height - tmp_win->frame_bw3D,
		tmp_win->frame_width - 2 * (Scr->TitleHeight + tmp_win->frame_bw3D),
		tmp_win->frame_bw3D,
		Scr->BorderShadowDepth, cp, off, True, False);
    Draw3DBorder (tmp_win->frame,
		0,
		Scr->TitleHeight + tmp_win->frame_bw3D,
		tmp_win->frame_bw3D,
		tmp_win->frame_height - 2 * (Scr->TitleHeight + tmp_win->frame_bw3D),
		Scr->BorderShadowDepth, cp, off, True, False);
    Draw3DBorder (tmp_win->frame,
		tmp_win->frame_width  - tmp_win->frame_bw3D,
		Scr->TitleHeight + tmp_win->frame_bw3D,
		tmp_win->frame_bw3D,
		tmp_win->frame_height - 2 * (Scr->TitleHeight + tmp_win->frame_bw3D),
		Scr->BorderShadowDepth, cp, off, True, False);

    if (tmp_win->squeeze_info && !tmp_win->squeezed) {
	Draw3DBorder (tmp_win->frame,
		0,
		Scr->TitleHeight,
		tmp_win->title_x,
		tmp_win->frame_bw3D,
		Scr->BorderShadowDepth, cp, off, True, False);
	Draw3DBorder (tmp_win->frame,
		tmp_win->title_x + tmp_win->title_width,
		Scr->TitleHeight,
		tmp_win->frame_width - tmp_win->title_x - tmp_win->title_width,
		tmp_win->frame_bw3D,
		Scr->BorderShadowDepth, cp, off, True, False);
    }
}

void PaintTitle (TwmWindow *tmp_win)
{
    int width, mwidth, len;
    XRectangle inc_rect;
    XRectangle logical_rect;

    if (Scr->use3Dtitles) {
	if (Scr->SunkFocusWindowTitle && (Scr->Focus == tmp_win) &&
	    (tmp_win->title_height != 0))
	    Draw3DBorder (tmp_win->title_w, Scr->TBInfo.titlex, 0,
		tmp_win->title_width - Scr->TBInfo.titlex -
		Scr->TBInfo.rightoff - Scr->TitlePadding,
		Scr->TitleHeight, Scr->TitleShadowDepth,
		tmp_win->title, on, True, False);
	else
	    Draw3DBorder (tmp_win->title_w, Scr->TBInfo.titlex, 0,
		tmp_win->title_width - Scr->TBInfo.titlex -
		Scr->TBInfo.rightoff - Scr->TitlePadding,
		Scr->TitleHeight, Scr->TitleShadowDepth,
		tmp_win->title, off, True, False);
    }
    FB(tmp_win->title.fore, tmp_win->title.back);
    if (Scr->use3Dtitles) {
	len    = strlen(tmp_win->name);
	XmbTextExtents(Scr->TitleBarFont.font_set,
		       tmp_win->name, strlen (tmp_win->name),
		       &inc_rect, &logical_rect);
	width = logical_rect.width;
	mwidth = tmp_win->title_width  - Scr->TBInfo.titlex -
		 Scr->TBInfo.rightoff  - Scr->TitlePadding  -
		 Scr->TitleShadowDepth - 4;
	while ((len > 0) && (width > mwidth)) {
	    len--;
	    XmbTextExtents(Scr->TitleBarFont.font_set,
			   tmp_win->name, len,
			   &inc_rect, &logical_rect);
	    width = logical_rect.width;
	}
	if (Scr->Monochrome != COLOR) {
	    XmbDrawImageString(dpy, tmp_win->title_w, Scr->TitleBarFont.font_set,
			     Scr->NormalGC,
			     tmp_win->name_x,
			     Scr->TitleBarFont.y + Scr->TitleShadowDepth, 
			     tmp_win->name, len);
	}
	else {
	    XmbDrawString (dpy, tmp_win->title_w, Scr->TitleBarFont.font_set,
			   Scr->NormalGC, tmp_win->name_x,
			   Scr->TitleBarFont.y + Scr->TitleShadowDepth, 
			   tmp_win->name, len);
	}
    }
    else
        XmbDrawString (dpy, tmp_win->title_w, Scr->TitleBarFont.font_set,
		       Scr->NormalGC,
		       tmp_win->name_x, Scr->TitleBarFont.y, 
		       tmp_win->name, strlen(tmp_win->name));
}

void PaintIcon (TwmWindow *tmp_win)
{
    int		width, twidth, mwidth, len, x;
    Icon	*icon;
    XRectangle inc_rect;
    XRectangle logical_rect;

    if (!tmp_win || !tmp_win->icon) return;
    icon = tmp_win->icon;
    if (!icon->has_title) return;

    x     = 0;
    width = icon->w_width;
    if (Scr->ShrinkIconTitles && icon->title_shrunk) {
	x     = GetIconOffset (icon);
	width = icon->width;
    }
    len    = strlen (tmp_win->icon_name);
    XmbTextExtents(Scr->IconFont.font_set,
		   tmp_win->icon_name, len,
		   &inc_rect, &logical_rect);
    twidth = logical_rect.width;
    mwidth = width - 2 * Scr->IconManagerShadowDepth - 6;
    if (Scr->use3Diconmanagers) {
	Draw3DBorder (icon->w, x, icon->height, width,
		Scr->IconFont.height + 2 * Scr->IconManagerShadowDepth + 6,
		Scr->IconManagerShadowDepth, icon->iconc, off, False, False);
    }
    while ((len > 0) && (twidth > mwidth)) {
	len--;
	XmbTextExtents(Scr->IconFont.font_set,
		       tmp_win->icon_name, len,
		       &inc_rect, &logical_rect);
	twidth = logical_rect.width;
    }
    FB (icon->iconc.fore, icon->iconc.back);
    XmbDrawString(dpy, icon->w, Scr->IconFont.font_set, Scr->NormalGC,
		  x + ((mwidth - twidth)/2) + Scr->IconManagerShadowDepth + 3,
		  icon->y, tmp_win->icon_name, len);
}

void PaintTitleButton (TwmWindow *tmp_win, TBWindow  *tbw)
{
    TitleButton *tb = tbw->info;

    XCopyArea (dpy, tbw->image->pixmap, tbw->window, Scr->NormalGC,
		tb->srcx, tb->srcy, tb->width, tb->height,
		tb->dstx, tb->dsty);
    return;
}

void PaintTitleButtons (TwmWindow *tmp_win)
{
    int i;
    TBWindow *tbw = tmp_win->titlebuttons;
    int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;

    if (tbw == NULL)
	return;

    for (i = 0; i < nb; i++, tbw++) {
	PaintTitleButton (tmp_win, tbw);
    }
}

void adoptWindow (void)
{
    unsigned long	data [2];
    Window		localroot, w;
    unsigned char	*prop;
    unsigned long	bytesafter;
    unsigned long	len;
    Atom		actual_type;
    int			actual_format;
    XEvent		event;
    Window		root, parent, child, *children;
    unsigned int	nchildren, key_buttons;
    int			root_x, root_y, win_x, win_y;
    int			ret;

    localroot = w = RootWindow (dpy, Scr->screen);
    XGrabPointer (dpy, localroot, False,
			ButtonPressMask | ButtonReleaseMask,
			GrabModeAsync, GrabModeAsync,
		        None, Scr->SelectCursor, CurrentTime);

    XMaskEvent (dpy, ButtonPressMask | ButtonReleaseMask, &event);
    child = event.xbutton.subwindow;
    while (1) {
	if (child == (Window) 0) break;

	w = XmuClientWindow (dpy, child);
	ret = XGetWindowProperty (dpy, w, _XA_WM_WORKSPACESLIST, 0L, 512,
			False, XA_STRING, &actual_type, &actual_format, &len,
			&bytesafter, &prop);
	XFree ((char *)prop); /* Don't ever do anything with it */
	if (ret != Success)
		break;
	if (len == 0) /* it is not a local root window */
		break; /* it is not a local root window */
	localroot = w;
	XQueryPointer (dpy, localroot, &root, &child, &root_x, &root_y,
					&win_x, &win_y, &key_buttons);
    }
    XMaskEvent (dpy, ButtonPressMask | ButtonReleaseMask, &event);
    XUngrabPointer (dpy, CurrentTime);

    if (localroot == Scr->Root) return;
    if (w == localroot) {  /* try to not adopt an ancestor */
	XQueryTree (dpy, Scr->Root, &root, &parent, &children, &nchildren);
	while (parent != (Window) 0) {
	    XFree ((char *) children);
	    if (w == parent) return;
	    XQueryTree (dpy, parent, &root, &parent, &children, &nchildren);
	}
	XFree ((char *) children);
	if (w == root) return;
    }
    if (localroot == RootWindow (dpy, Scr->screen)) {
	XWithdrawWindow (dpy, w, Scr->screen);
    }
    else {
	XUnmapWindow (dpy, w);
    }
    XReparentWindow (dpy, w, Scr->Root, 0, 0);

    data [0] = (unsigned long) NormalState;
    data [1] = (unsigned long) None;

    XChangeProperty (dpy, w, _XA_WM_STATE, _XA_WM_STATE, 32, 
			PropModeReplace, (unsigned char *) data, 2);
    XFlush (dpy);
    SimulateMapRequest (w);
    return;
}

void DebugTrace (char *file)
{
    if (!file) return;
    if (tracefile) {
	fprintf (stderr, "stop logging events\n");
	if (tracefile != stderr) fclose (tracefile);
	tracefile = NULL;
    }
    else {
	if (strcmp (file, "stderr"))
	    tracefile = fopen (file, "w");
	else
	    tracefile = stderr;
	fprintf (stderr, "logging events to : %s\n", file);
    }
}

extern Cursor	TopRightCursor, TopLeftCursor, BottomRightCursor, BottomLeftCursor,
		LeftCursor, RightCursor, TopCursor, BottomCursor;

void SetBorderCursor (TwmWindow *tmp_win, int x, int y)
{
    Cursor cursor;
    XSetWindowAttributes attr;
    int h, fw, fh, wd;

    if (!tmp_win)
	return;

    /* Use the max of these, but since one is always 0 we can add them. */
    wd = tmp_win->frame_bw + tmp_win->frame_bw3D;
    h = Scr->TitleHeight + wd;
    fw = tmp_win->frame_width;
    fh = tmp_win->frame_height;

#if defined DEBUG && DEBUG
    fprintf(stderr, "wd=%d h=%d, fw=%d fh=%d x=%d y=%d\n",
	    wd, h, fw, fh, x, y);
#endif

    /*
     * If not using 3D borders:
     *
     * The left border has negative x coordinates,
     * The top border (above the title) has negative y coordinates.
     * The title is TitleHeight high, the next wd pixels are border.
     * The bottom border has coordinates >= the frame height.
     * The right border has coordinates >= the frame width.
     *
     * If using 3D borders: all coordinates are >= 0, and all coordinates
     * are higher by the border width.
     *
     * Since we only get events when we're actually in the border, we simply
     * allow for both cases at the same time.
     */

    if ((x < -wd) || (y < -wd)) {
	cursor = Scr->FrameCursor;
    } else if (x < h) {
	if (y < h)
	    cursor = TopLeftCursor;
	else if (y >= fh - h)
	    cursor = BottomLeftCursor;
	else
	    cursor = LeftCursor;
    } else if (x >= fw - h) {
	if (y < h)
	    cursor = TopRightCursor;
	else if (y >= fh - h)
	    cursor = BottomRightCursor;
	else
	    cursor = RightCursor;
    } else if (y < h) {	/* also include title bar in top border area */
	cursor = TopCursor;
    } else if (y >= fh - h) {
	cursor = BottomCursor;
    } else {
	cursor = Scr->FrameCursor;
    }
    attr.cursor = cursor;
    XChangeWindowAttributes (dpy, tmp_win->frame, CWCursor, &attr);
    tmp_win->curcurs = cursor;
}

Image *GetImage (char *name, ColorPair cp)
{
    name_list **list;
    char fullname [256];
    Image *image;

    if (name == NULL) return (None);
    image = None;

    list = &Scr->ImageCache;
#ifdef XPM
    if ((name [0] == '@') || (strncmp (name, "xpm:", 4) == 0)) {
	sprintf (fullname, "%s%dx%d", name, (int) cp.fore, (int) cp.back);

	if ((image = (Image*) LookInNameList (*list, fullname)) == None) {
	    int startn = (name [0] == '@') ? 1 : 4;
	    if ((image = GetXpmImage (name + startn, cp)) != None) {
	        AddToList (list, fullname, (char*) image);
	    }
	}
    }
    else
#endif
#ifdef JPEG
    if (strncmp (name, "jpeg:", 5) == 0) {
	if ((image = (Image*) LookInNameList (*list, name)) == None) {
	    if ((image = GetJpegImage (&name [5])) != None) {
		AddToList (list, name, (char*) image);
	    }
	}
    }
    else
#endif
#ifdef IMCONV
    if (strncmp (name, "im:", 3) == 0) {
	if ((image = (Image*) LookInNameList (*list, name)) == None) {
	    if ((image = GetImconvImage (&name [3])) != None) {
		AddToList (list, name, (char*) image);
	    }
	}
    }
    else
#endif
#if !defined(VMS) || defined(HAVE_XWDFILE_H)
    if ((strncmp (name, "xwd:", 4) == 0) || (name [0] == '|')) {
	int startn = (name [0] == '|') ? 0 : 4;
	if ((image = (Image*) LookInNameList (*list, name)) == None) {
	    if ((image = GetXwdImage (&name [startn], cp)) != None) {
		AddToList (list, name, (char*) image);
	    }
	}
    }
    else
#endif
    if (strncmp (name, ":xpm:", 5) == 0) {
	int    i;
	static struct {
	    char *name;
	    Image* (*proc)(ColorPair colorpair);
	} pmtab[] = {
	    { TBPM_3DDOT,	Create3DDotImage },
	    { TBPM_3DRESIZE,	Create3DResizeImage },
	    { TBPM_3DMENU,	Create3DMenuImage },
	    { TBPM_3DZOOM,	Create3DZoomImage },
	    { TBPM_3DBAR,	Create3DBarImage },
	    { TBPM_3DVBAR,	Create3DVertBarImage },
	    { TBPM_3DCROSS,     Create3DCrossImage },
	    { TBPM_3DICONIFY,   Create3DIconifyImage },
	    { TBPM_3DSUNKEN_RESIZE,     Create3DSunkenResizeImage },
	    { TBPM_3DBOX,       Create3DBoxImage }
	};
	
	sprintf (fullname, "%s%dx%d", name, (int) cp.fore, (int) cp.back);
	if ((image = (Image*) LookInNameList (*list, fullname)) == None) {
	    for (i = 0; i < (sizeof pmtab) / (sizeof pmtab[0]); i++) {
		if (XmuCompareISOLatin1 (pmtab[i].name, name) == 0) {
		    image = (*pmtab[i].proc) (cp);
		    if (image == None) {
			fprintf (stderr,
			    "%s:  unable to build pixmap \"%s\"\n", ProgramName, name);
			return (None);
		    }
		    break;
		}
	    }
	    if (image == None) {
		fprintf (stderr, "%s:  no such built-in pixmap \"%s\"\n", ProgramName, name);
		return (None);
	    }
	    AddToList (list, fullname, (char*) image);
	}
    }
    else
    if (strncmp (name, "%xpm:", 5) == 0) {
	int    i;
	static struct {
	    char *name;
	    Image* (*proc)(ColorPair colorpair);
	} pmtab[] = {
	    { "%xpm:menu-up", Create3DMenuUpAnimation },
	    { "%xpm:menu-down", Create3DMenuDownAnimation },
	    { "%xpm:resize", Create3DZoomOutAnimation }, /* compatibility */
	    { "%xpm:resize-out-top", Create3DResizeInTopAnimation },
	    { "%xpm:resize-in-top", Create3DResizeOutTopAnimation },
	    { "%xpm:resize-out-bot", Create3DResizeInBotAnimation },
	    { "%xpm:resize-in-bot", Create3DResizeOutBotAnimation },
	    { "%xpm:maze-out", Create3DMazeOutAnimation },
	    { "%xpm:maze-in", Create3DMazeInAnimation },
	    { "%xpm:zoom-out", Create3DZoomOutAnimation },
	    { "%xpm:zoom-in", Create3DZoomInAnimation },
	    { "%xpm:zoom-inout", Create3DZoomInOutAnimation }
	};
	
	sprintf (fullname, "%s%dx%d", name, (int) cp.fore, (int) cp.back);
	if ((image = (Image*) LookInNameList (*list, fullname)) == None) {
	    for (i = 0; i < (sizeof pmtab) / (sizeof pmtab[0]); i++) {
		if (XmuCompareISOLatin1 (pmtab[i].name, name) == 0) {
		    image = (*pmtab[i].proc) (cp);
		    if (image == None) {
			fprintf (stderr,
			    "%s:  unable to build pixmap \"%s\"\n", ProgramName, name);
			return (None);
		    }
		    break;
		}
	    }
	    if (image == None) {
		fprintf (stderr, "%s:  no such built-in pixmap \"%s\"\n", ProgramName, name);
		return (None);
	    }
	    AddToList (list, fullname, (char*) image);
	}
    }
    else
    if (name [0] == ':') {
	int		i;
	unsigned int	width, height;
	Pixmap		pm = 0;
	XGCValues	gcvalues;
	static struct {
	    char *name;
	    Pixmap (*proc)(unsigned int *widthp, unsigned int *heightp);
	} pmtab[] = {
	    { TBPM_DOT,		CreateDotPixmap },
	    { TBPM_ICONIFY,	CreateDotPixmap },
	    { TBPM_RESIZE,	CreateResizePixmap },
	    { TBPM_XLOGO,	CreateXLogoPixmap },
	    { TBPM_DELETE,	CreateXLogoPixmap },
	    { TBPM_MENU,	CreateMenuPixmap },
	    { TBPM_QUESTION,	CreateQuestionPixmap },
	};
	
	sprintf (fullname, "%s%dx%d", name, (int) cp.fore, (int) cp.back);
	if ((image = (Image*) LookInNameList (*list, fullname)) == None) {
	    for (i = 0; i < (sizeof pmtab) / (sizeof pmtab[0]); i++) {
		if (XmuCompareISOLatin1 (pmtab[i].name, name) == 0) {
		    pm = (*pmtab[i].proc) (&width, &height);
		    if (pm == None) {
			fprintf (stderr,
			    "%s:  unable to build pixmap \"%s\"\n", ProgramName, name);
			return (None);
		    }
		    break;
		}
	    }
	    if (pm == None) {
		fprintf (stderr, "%s:  no such built-in bitmap \"%s\"\n", ProgramName, name);
		return (None);
	    }
	    image = (Image*) malloc (sizeof (struct _Image));
	    image->pixmap = XCreatePixmap (dpy, Scr->Root, width, height, Scr->d_depth);
	    if (Scr->rootGC == (GC) 0) Scr->rootGC = XCreateGC (dpy, Scr->Root, 0, &gcvalues);
	    gcvalues.background = cp.back;
	    gcvalues.foreground = cp.fore;
	    XChangeGC   (dpy, Scr->rootGC, GCForeground | GCBackground, &gcvalues);
	    XCopyPlane  (dpy, pm, image->pixmap, Scr->rootGC, 0, 0, width, height, 0, 0,
			(unsigned long) 1);
	    image->mask   = None;
	    image->width  = width;
	    image->height = height;
	    image->next   = None;
	    AddToList (list, fullname, (char*) image);
	}
    }
    else {
	sprintf (fullname, "%s%dx%d", name, (int) cp.fore, (int) cp.back);
	if ((image = (Image*) LookInNameList (*list, fullname)) == None) {
	    if ((image = GetBitmapImage (name, cp)) != None) {
		AddToList (list, fullname, (char*) image);
	    }
	}
    }
    return (image);
}

void FreeImage (Image *image)
{
    Image *im, *im2;

    im = image;
    while (im != None) {
	if (im->pixmap) XFreePixmap (dpy, im->pixmap);
	if (im->mask)   XFreePixmap (dpy, im->mask);
	im2 = im->next;
	free (im);
	im = im2;
    }
}

#if !defined(VMS) || defined(HAVE_XWDFILE_H)
static void compress (XImage *image, XColor *colors, int *ncolors);

static Image *LoadXwdImage (char *filename, ColorPair cp)
{
    FILE	*file;
    char	*fullname;
    XColor	colors [256];
    XWDColor	xwdcolors [256];
    unsigned	buffer_size;
    XImage	*image;
    unsigned char *imagedata;
    Pixmap	pixret;
    Visual	*visual;
    char	win_name [256];
    int		win_name_size;
    int		ispipe;
    int		i, len;
    int		w, h, depth, ncolors;
    int		scrn;
    Colormap	cmap;
    Colormap	stdcmap = Scr->RootColormaps.cwins[0]->colormap->c;
    GC		gc;
    XGCValues   gcvalues;
    XWDFileHeader header;
    Image	*ret;
    Bool	anim;
    unsigned long swaptest = 1;

    ispipe = 0;
    anim   = False;
#ifndef VMS
    if (filename [0] == '|') {
	file = (FILE*) popen (filename + 1, "r");
	if (file == NULL) return (None);
	ispipe = 1;
	anim = AnimationActive;
	if (anim) StopAnimation ();
	goto file_opened;
    }
#endif
    fullname = ExpandPixmapPath (filename);
    if (! fullname) return (None);
    file = fopen (fullname, "r");
    free (fullname);
    if (file == NULL) {
	if (reportfilenotfound) fprintf (stderr, "unable to locate %s\n", filename);
        return (None);
    }
file_opened:
    len = fread ((char *) &header, sizeof (header), 1, file);
    if (len != 1) {
	fprintf (stderr, "ctwm: cannot read %s\n", filename);
#ifdef USE_SIGNALS
	if (ispipe && anim) StartAnimation ();
#endif
	return (None);
    }
    if (*(char *) &swaptest) _swaplong ((char *) &header, sizeof (header));
    if (header.file_version != XWD_FILE_VERSION) {
	fprintf(stderr,"ctwm: XWD file format version mismatch : %s\n", filename);
	return (None);
    }
    win_name_size = header.header_size - sizeof (header);
    len = fread (win_name, win_name_size, 1, file);
    if (len != 1) {
	fprintf (stderr, "file %s has not the correct format\n", filename);
#ifdef USE_SIGNALS
	if (ispipe && anim) StartAnimation ();
#endif
	return (None);
    }

    if (header.pixmap_format == XYPixmap) {
	fprintf (stderr,"ctwm: XYPixmap XWD file not supported : %s\n", filename);
	return (None);
    }
    w       = header.pixmap_width;
    h       = header.pixmap_height;
    depth   = header.pixmap_depth;
    ncolors = header.ncolors;
    len = fread ((char *) xwdcolors, sizeof (XWDColor), ncolors, file);
    if (len != ncolors) {
	fprintf (stderr, "file %s has not the correct format\n", filename);
#ifdef USE_SIGNALS
	if (ispipe && anim) StartAnimation ();
#endif
	return (None);
    }
    if (*(char *) &swaptest) {
	for (i = 0; i < ncolors; i++) {
	    _swaplong  ((char *) &xwdcolors [i].pixel, 4);
	    _swapshort ((char *) &xwdcolors [i].red, 3 * 2);
	}
    }
    for (i = 0; i < ncolors; i++) {
	colors [i].pixel = xwdcolors [i].pixel;
	colors [i].red   = xwdcolors [i].red;
	colors [i].green = xwdcolors [i].green;
	colors [i].blue  = xwdcolors [i].blue;
	colors [i].flags = xwdcolors [i].flags;
	colors [i].pad   = xwdcolors [i].pad;
    }

    scrn    = Scr->screen;
    cmap    = AlternateCmap ? AlternateCmap : stdcmap;
    visual  = Scr->d_visual;
    gc      = DefaultGC (dpy, scrn);

    buffer_size = header.bytes_per_line * h;
    imagedata = (unsigned char*) malloc (buffer_size);
    if (! imagedata) {
	fprintf (stderr, "cannot allocate memory for image %s\n", filename);
#ifdef USE_SIGNALS
	if (ispipe && anim) StartAnimation ();
#endif
	return (None);
    }
    len = fread (imagedata, (int) buffer_size, 1, file);
    if (len != 1) {
	free (imagedata);
	fprintf (stderr, "file %s has not the correct format\n", filename);
#ifdef USE_SIGNALS
	if (ispipe && anim) StartAnimation ();
#endif
	return (None);
    }
#ifndef VMS
    if (ispipe)
      pclose (file);
    else
#endif
      fclose (file);

    image = XCreateImage (dpy, visual,  depth, header.pixmap_format,
                          0, (char*) imagedata, w, h,
                          header.bitmap_pad, header.bytes_per_line);
    if (image == None) {
	free (imagedata);
	fprintf (stderr, "cannot create image for %s\n", filename);
#ifdef USE_SIGNALS
	if (ispipe && anim) StartAnimation ();
#endif
	return (None);
    }
    if (header.pixmap_format == ZPixmap) {
	compress (image, colors, &ncolors);
    }
    if (header.pixmap_format != XYBitmap) {
	for (i = 0; i < ncolors; i++) {
            XAllocColor (dpy, cmap, &(colors [i]));
	}
	for (i = 0; i < buffer_size; i++) {
            imagedata [i] = (unsigned char) colors [imagedata [i]].pixel;
	}
    }
    if (w > Scr->rootw)  w = Scr->rootw;
    if (h > Scr->rooth) h = Scr->rooth;

    ret = (Image*) malloc (sizeof (struct _Image));
    if (! ret) {
	fprintf (stderr, "unable to allocate memory for image : %s\n", filename);
	free (image);
	free (imagedata);
	for (i = 0; i < ncolors; i++) {
            XFreeColors (dpy, cmap, &(colors [i].pixel), 1, 0L);
	}
#ifdef USE_SIGNALS
	if (ispipe && anim) StartAnimation ();
#endif
	return (None);
    }
    if (header.pixmap_format == XYBitmap) {
	gcvalues.foreground = cp.fore;
	gcvalues.background = cp.back;
	XChangeGC (dpy, gc, GCForeground | GCBackground, &gcvalues);
    }
    if ((w > (Scr->rootw / 2)) || (h > (Scr->rooth / 2))) {
	int x, y;

	pixret = XCreatePixmap (dpy, Scr->Root, Scr->rootw,
				Scr->rooth, Scr->d_depth);
	x = (Scr->rootw  - w) / 2;
	y = (Scr->rooth - h) / 2;
	XFillRectangle (dpy, pixret, gc, 0, 0, Scr->rootw, Scr->rooth);
	XPutImage (dpy, pixret, gc, image, 0, 0, x, y, w, h);
	ret->width  = Scr->rootw;
	ret->height = Scr->rooth;
    }
    else {
	pixret = XCreatePixmap (dpy, Scr->Root, w, h, depth);
	XPutImage (dpy, pixret, gc, image, 0, 0, 0, 0, w, h);
	ret->width  = w;
	ret->height = h;
    }
    XDestroyImage (image);

    ret->pixmap = pixret;
    ret->mask   = None;
    ret->next   = None;
#ifdef USE_SIGNALS
    if (ispipe && anim) StartAnimation ();
#endif
    return (ret);
}

static Image *GetXwdImage (char *name, ColorPair cp)
{
    Image *image, *r, *s;
    char  path [128];
    char  pref [128], *perc;
    int   i;

    if (! strchr (name, '%')) return (LoadXwdImage (name, cp));
    s = image = None;
    strcpy (pref, name);
    perc  = strchr (pref, '%');
    *perc = '\0';
    reportfilenotfound = 0;
    for (i = 1;; i++) {
	sprintf (path, "%s%d%s", pref, i, perc + 1);
	r = LoadXwdImage (path, cp);
	if (r == None) break;
	r->next   = None;
	if (image == None) s = image = r;
	else {
	    s->next = r;
	    s = r;
	}
    }
    reportfilenotfound = 1;
    if (s != None) s->next = image;
    if (image == None) {
	fprintf (stderr, "Cannot open any %s xwd file\n", name);
    }
    return (image);
}

static void compress (XImage *image, XColor *colors, int *ncolors)
{
    unsigned char ind  [256];
    unsigned int  used [256];  
    int           i, j, size, nused;
    unsigned char color;
    XColor        newcolors [256];
    unsigned char *imagedata;

    for (i = 0; i < 256; i++) {
	used [i] = 0;
	ind  [i] = 0;
    }
    nused = 0;
    size  = image->bytes_per_line * image->height;
    imagedata = (unsigned char *) image->data;
    for (i = 0; i < size; i++) {
	if ((i % image->bytes_per_line) > image->width) continue;
        color = imagedata [i];
        if (used [color] == 0) {
            for (j = 0; j < nused; j++) {
                if ((colors [color].red   == newcolors [j].red)   &&
                    (colors [color].green == newcolors [j].green) &&
                    (colors [color].blue  == newcolors [j].blue)) break;
            }
            ind  [color] = j;
            used [color] = 1;
            if (j == nused) {
                newcolors [j].red   = colors [color].red;
                newcolors [j].green = colors [color].green;
                newcolors [j].blue  = colors [color].blue;
                nused++;
            }
        }
    }
    for (i = 0; i < size; i++) {
        imagedata [i] = ind [imagedata [i]];
    }
    for (i = 0; i < nused; i++) {
        colors [i] = newcolors [i];
    }
    *ncolors = nused;
}
#endif

#ifdef IMCONV

static void free_images  ();

static Image *GetImconvImage (char *filename,
			      unsigned int *widthp, unsigned int *heightp)
{
    TagTable		*toolInTable;
    ImVfb		*sourceVfb;
    ImVfbPtr		vptr;
    ImClt		*clt;
    int			i, j, ij, k, retval;

    XColor		colors [256];
    unsigned		buffer_size;
    XImage		*image;
    unsigned char	*imagedata;
    Pixmap		pixret;
    Visual		*visual;
    int			w, h, depth, ncolors;
    int			scrn;
    Colormap		cmap;
    Colormap		stdcmap = Scr->RootColormaps.cwins[0]->colormap->c;
    GC			gc;
    unsigned char	red, green, blue;
    int			icol;
    char		*fullname;

    TagEntry		*dataEntry;
    FILE		*fp;
    char		the_format[1024];
    char		*tmp_format;
    Image		*ret;

    if (*filename == NULL) return (None);
    fullname = ExpandPixmapPath (filename);
    if (! fullname) return (None);

    fp = fopen (fullname, "r");
    if (!fp) {
	if (reportfilenotfound) fprintf (stderr, "Cannot open the image %s\n", filename);
	free (fullname);
	return (None);
    }
    if ((toolInTable = TagTableAlloc ()) == TAGTABLENULL ) {
	fprintf (stderr, "TagTableAlloc failed\n");
	free_images (toolInTable);
	free (fullname);
	return (None);
    }
    if ((tmp_format = ImFileQFFormat (fp, fullname)) == NULL)  {
	fprintf (stderr, "Cannot determine image type of %s\n", filename);
	free_images  (toolInTable);
	free (fullname);
	return (None);
    }
    strcpy (the_format, tmp_format);
    retval = ImFileFRead (fp, the_format, NULL, toolInTable);
    if(retval < 0) {
	fprintf(stderr, "Cannot read image file %s: ", fullname);
	switch(ImErrNo) {
	    case IMESYS:
		fprintf (stderr, "System call error\n");
		break;
	    case IMEMALLOC:
		fprintf (stderr, "Cannot allocate memory\n");
		break;
	    case IMEFORMAT:
		fprintf (stderr, "Data in file is corrupt\n");
		break;
	    case IMENOREAD:
		fprintf (stderr, "Sorry, this format is write-only\n");
		break;
	    case IMEMAGIC:
		fprintf (stderr, "Bad magic number in image file\n");
		break;
	    case IMEDEPTH:
		fprintf (stderr, "Unknown image depth\n");
		break;
	    default:
		fprintf(stderr, "Unknown error\n");
		break;
	}
	free_images (toolInTable);
	free (fullname);
	return (None);
    }

    if (TagTableQNEntry (toolInTable, "image vfb") == 0)  {
	fprintf (stderr, "Image file %s contains no images\n", fullname);
	free_images (toolInTable);
	free (fullname);
	return (None);
    }
    dataEntry = TagTableQDirect (toolInTable, "image vfb", 0);
    TagEntryQValue (dataEntry, &sourceVfb);
    fclose (fp);

    w = ImVfbQWidth  (sourceVfb);
    h = ImVfbQHeight (sourceVfb);
    depth = 8 * ImVfbQNBytes (sourceVfb);
    if (depth != 8) {
	fprintf (stderr, "I don't know yet how to deal with images not of 8 planes depth\n");
	free_images (toolInTable);
	return (None);
    }

    *width  = w;
    *height = h;

    scrn   = Scr->screen;
    cmap   = AlternateCmap ? AlternateCmap : stdcmap;
    visual = Scr->d_visual;
    gc     = DefaultGC (dpy, scrn);

    buffer_size = w * h;
    imagedata = (unsigned char*) malloc (buffer_size);
    if (imagedata == (unsigned char*) 0) {
	fprintf (stderr, "Can't alloc enough space for background images\n");
	free_images (toolInTable);
	return (None);
    }

    clt  = ImVfbQClt   (sourceVfb);
    vptr = ImVfbQFirst (sourceVfb);
    ncolors = 0;
    for (i = 0; i < h - 1; i++) {
	for (j = 0; j < w; j++) {
	    ij = (i * w) + j;
	    red   = ImCltQRed   (ImCltQPtr (clt, ImVfbQIndex (sourceVfb, vptr)));
	    green = ImCltQGreen (ImCltQPtr (clt, ImVfbQIndex (sourceVfb, vptr)));
	    blue  = ImCltQBlue  (ImCltQPtr (clt, ImVfbQIndex (sourceVfb, vptr)));
	    for (k = 0; k < ncolors; k++) {
		if ((colors [k].red   == red) &&
		    (colors [k].green == green) &&
		    (colors [k].blue  == blue)) {
		    icol = k;
		    break;
		}
	    }
	    if (k == ncolors) {
		icol = ncolors;
		ncolors++;
	    }
	    imagedata [ij] = icol;
	    colors [icol].red   = red;
	    colors [icol].green = green;
	    colors [icol].blue  = blue;
	    ImVfbSInc (sourceVfb, vptr);
	}
    }
    for (i = 0; i < ncolors; i++) {
	colors [i].red   *= 256;
	colors [i].green *= 256;
	colors [i].blue  *= 256;
    }
    for (i = 0; i < ncolors; i++) {
        if (! XAllocColor (dpy, cmap, &(colors [i]))) {
	    fprintf (stderr, "can't alloc color for image %s\n", filename);
	}
    }
    for (i = 0; i < buffer_size; i++) {
        imagedata [i] = (unsigned char) colors [imagedata [i]].pixel;
    }

    image  = XCreateImage  (dpy, visual, depth, ZPixmap, 0, (char*) imagedata, w, h, 8, 0);
    if (w > Scr->rootw)  w = Scr->rootw;
    if (h > Scr->rooth) h = Scr->rooth;

    if ((w > (Scr->rootw / 2)) || (h > (Scr->rooth / 2))) {
	int x, y;

	pixret = XCreatePixmap (dpy, Scr->Root, Scr->rootw, Scr->rooth, depth);
	x = (Scr->rootw  - w) / 2;
	y = (Scr->rooth - h) / 2;
	XFillRectangle (dpy, pixret, gc, 0, 0, Scr->rootw, Scr->rooth);
	XPutImage (dpy, pixret, gc, image, 0, 0, x, y, w, h);
	ret->width  = Scr->rootw;
	ret->height = Scr->rooth;
    }
    else {
	pixret = XCreatePixmap (dpy, Scr->Root, w, h, depth);
	XPutImage (dpy, pixret, gc, image, 0, 0, 0, 0, w, h);
	ret->width  = w;
	ret->height = h;
    }
    XFree (image);
    ret = (Image*) malloc (sizeof (struct _Image));
    ret->pixmap = pixret;
    ret->mask   = None;
    ret->next   = None;
    return (ret);

}

static void free_images (table)
TagTable *table;
{
    int		i, n;
    ImVfb	*v;
    ImClt	*c;
    TagEntry	*dataEntry;

    n = TagTableQNEntry (table, "image vfb");
    for (i = 0 ; i < n ; i++) {
	dataEntry = TagTableQDirect (table, "image vfb", i);
	TagEntryQValue (dataEntry, &v);
	ImVfbFree (v);
    }
    n = TagTableQNEntry (table, "image clt");
    for (i = 0 ; i < n ; i++) {
	dataEntry = TagTableQDirect (table, "image clt", i );
	TagEntryQValue (dataEntry, &c);
	ImCltFree (c);
    }
    TagTableFree (table);
}

#endif

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

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

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

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

/***********************************************************************
 *
 *  Procedure:
 *	GetWMPropertyString - Get Window Manager text property and
 *				convert it to a string.
 *
 *  Returned Value:
 *	(char *) - pointer to the malloc'd string or NULL
 *
 *  Inputs:
 *	w	- the id of the window whose property is to be retrieved
 *	prop	- property atom (typically WM_NAME or WM_ICON_NAME)
 *
 ***********************************************************************
 */

unsigned char *GetWMPropertyString(Window w, Atom prop)
{
    XTextProperty	text_prop;
    char 		**text_list;
    int 		text_list_count;
    Atom 		XA_COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
    unsigned char	*stringptr;
    int			status, len = -1;

    (void)XGetTextProperty(dpy, w, &text_prop, prop);
    if (text_prop.value != NULL) {
	if (text_prop.encoding == XA_STRING
	    || text_prop.encoding == XA_COMPOUND_TEXT) {
	    /* property is encoded as compound text - convert to locale string */
	    status = XmbTextPropertyToTextList(dpy, &text_prop,
					       &text_list, &text_list_count);
	    if (text_list_count == 0) {
		stringptr = NULL;
	    } else
	    if (text_list == (char **)0) {
		stringptr = NULL;
	    } else
	    if (text_list [0] == (char *)0) {
		stringptr = NULL;
	    } else
	    if (status < 0 || text_list_count < 0) {
		switch (status) {
		case XConverterNotFound:
		    fprintf (stderr, "%s: Converter not found; unable to convert property %s of window ID %lx.\n",
			     ProgramName, XGetAtomName(dpy, prop), w);
		    break;
		case XNoMemory:
		    fprintf (stderr, "%s: Insufficient memory; unable to convert property %s of window ID %lx.\n",
			     ProgramName, XGetAtomName(dpy, prop), w);
		    break;
		case XLocaleNotSupported:
		    fprintf (stderr, "%s: Locale not supported; unable to convert property %s of window ID %lx.\n",
			     ProgramName, XGetAtomName(dpy, prop), w);
		    break;
		}
		stringptr = NULL;
		/*
		   don't call XFreeStringList - text_list appears to have
		   invalid address if status is bad
		   XFreeStringList(text_list);
		*/
	    } else {
		len = strlen(text_list[0]);
		stringptr = memcpy(malloc(len+1), text_list[0], len+1);
		XFreeStringList(text_list);
	    }
	} else {
	    /* property is encoded in a format we don't understand */
	    fprintf (stderr, "%s: Encoding not STRING or COMPOUND_TEXT; unable to decode property %s of window ID %lx.\n",
		     ProgramName, XGetAtomName(dpy, prop), w);
	    stringptr = NULL;
	}
	XFree (text_prop.value); 
    } else {
	stringptr = NULL;
    }

    return stringptr;
}

void FreeWMPropertyString(char *prop)
{
    if (prop && (char *)prop != NoName) {
	free(prop);
    }
}

static void ConstrainLeftTop (int *value, int border)
{
  if (*value < border) {
    if (Scr->MoveOffResistance < 0 ||
	*value > border - Scr->MoveOffResistance)
    {
        *value = border;
    } else if (Scr->MoveOffResistance > 0 &&
	       *value <= border - Scr->MoveOffResistance)
    {
      *value = *value + Scr->MoveOffResistance;
    }
    }
}

static void ConstrainRightBottom (int *value, int size1, int border, int size2)
{
    if (*value + size1 > size2 - border) {
      if (Scr->MoveOffResistance < 0 ||
         *value + size1 < size2 - border + Scr->MoveOffResistance)
    {
        *value = size2 - size1 - border;
	} else if (Scr->MoveOffResistance > 0 &&
		   *value + size1 >= size2 - border + Scr->MoveOffResistance) {
	  *value = *value - Scr->MoveOffResistance;
	}
    }
}

void ConstrainByBorders1 (int *left, int width, int *top, int height)
{
    ConstrainRightBottom (left, width, Scr->BorderRight, Scr->rootw);
    ConstrainLeftTop     (left, Scr->BorderLeft);
    ConstrainRightBottom (top, height, Scr->BorderBottom, Scr->rooth);
    ConstrainLeftTop     (top, Scr->BorderTop);
}

void ConstrainByBorders (TwmWindow *twmwin,
			 int *left, int width, int *top, int height)
{
    if (twmwin->winbox) {
	XWindowAttributes attr;
	XGetWindowAttributes (dpy, twmwin->winbox->window, &attr);
	ConstrainRightBottom (left, width, 0, attr.width);
	ConstrainLeftTop     (left, 0);
	ConstrainRightBottom (top, height, 0, attr.height);
	ConstrainLeftTop     (top, 0);
    } else {
	ConstrainByBorders1 (left, width, top, height);
    }
}

#ifdef JPEG

uint16_t *buffer_16bpp;
uint32_t *buffer_32bpp;

static void convert_for_16 (int w, int x, int y, int r, int g, int b) {
  buffer_16bpp [y * w + x] = ((r >> 3) << 11) + ((g >> 2) << 5) + (b >> 3);
}

static void convert_for_32 (int w, int x, int y, int r, int g, int b) {
  buffer_32bpp [y * w + x] = ((r << 16) + (g << 8) + b) & 0xFFFFFFFF; 
}

static void jpeg_error_exit (j_common_ptr cinfo) {
  jerr_ptr errmgr = (jerr_ptr) cinfo->err;
  cinfo->err->output_message (cinfo);
  siglongjmp (errmgr->setjmp_buffer, 1);
  return;
}

static Image *GetJpegImage (char *name)
{
    Image *image, *r, *s;
    char  path [128];
    char  pref [128], *perc;
    int   i;

    if (! strchr (name, '%')) return (LoadJpegImage (name));
    s = image = None;
    strcpy (pref, name);
    perc  = strchr (pref, '%');
    *perc = '\0';
    reportfilenotfound = 0;
    for (i = 1;; i++) {
	sprintf (path, "%s%d%s", pref, i, perc + 1);
	r = LoadJpegImage (path);
	if (r == None) break;
	r->next   = None;
	if (image == None) s = image = r;
	else {
	    s->next = r;
	    s = r;
	}
    }
    reportfilenotfound = 1;
    if (s != None) s->next = image;
    if (image == None) {
	fprintf (stderr, "Cannot open any %s jpeg file\n", name);
    }
    return (image);
}

static Image *LoadJpegImage (char *name)
{
  char   *fullname;
  XImage *ximage;
  FILE   *infile;
  Image  *image;
  Pixmap pixret;
  void   (*store_data) (int w, int x, int y, int r, int g, int b);
  struct jpeg_decompress_struct cinfo;
  struct jpeg_error jerr;
  JSAMPARRAY buffer;
  int width, height;
  int row_stride;
  int g, i, a;
  int bpix;
  GC  gc;

  fullname = ExpandPixmapPath (name);
  if (! fullname) return (None);

  image = (Image*) malloc (sizeof (struct _Image));
  if (image == None) return (None);

  if ((infile = fopen (fullname, "rb")) == NULL) {
    if (!reportfilenotfound) fprintf (stderr, "unable to locate %s\n", fullname);
    fflush (stdout);
    return None;
  }
  cinfo.err = jpeg_std_error (&jerr.pub);
  jerr.pub.error_exit = jpeg_error_exit;

  if (sigsetjmp(jerr.setjmp_buffer, 1)) {
    jpeg_destroy_decompress (&cinfo);
    fclose (infile);
    return None;
  }
  jpeg_create_decompress (&cinfo);
  jpeg_stdio_src (&cinfo, infile);
  jpeg_read_header (&cinfo, FALSE);
  cinfo.do_fancy_upsampling = FALSE;
  cinfo.do_block_smoothing = FALSE;
  jpeg_start_decompress (&cinfo);
  width  = cinfo.output_width;
  height = cinfo.output_height;

  if (Scr->d_depth == 16) {
    store_data = &convert_for_16;
    buffer_16bpp = (unsigned short int *) malloc ((width) * (height) * 2);
    ximage = XCreateImage (dpy, CopyFromParent, Scr->d_depth, ZPixmap, 0,
			   (char *) buffer_16bpp, width, height, 16, width * 2);
  } else {
    if (Scr->d_depth == 24) {
      store_data = &convert_for_32;
      buffer_32bpp = malloc (width * height * 4);
      ximage = XCreateImage (dpy, CopyFromParent, Scr->d_depth, ZPixmap, 0,
			     (char *) buffer_32bpp, width, height, 32, width * 4);
    } else
    if (Scr->d_depth == 32) {
      store_data = &convert_for_32;
      buffer_32bpp = malloc (width * height * 4);
      ximage = XCreateImage (dpy, CopyFromParent, Scr->d_depth, ZPixmap, 0,
			     (char *) buffer_32bpp, width, height, 32, width * 4);
    } else {
      fprintf (stderr, "Image %s unsupported depth : %d\n", name, Scr->d_depth);
      return None;
    }
  }
  if (ximage == None) {
    fprintf (stderr, "cannot create image for %s\n", name);
  }
  g = 0;
  row_stride = cinfo.output_width * cinfo.output_components;
  buffer = (*cinfo.mem->alloc_sarray)
    ((j_common_ptr) & cinfo, JPOOL_IMAGE, row_stride, 1);

  bpix = cinfo.output_components;
  while (cinfo.output_scanline < cinfo.output_height) {
    jpeg_read_scanlines (&cinfo, buffer, 1);
    a = 0;
    for (i = 0; i < bpix * cinfo.output_width; i += bpix) {
      (*store_data) (width, a, g, buffer[0][i],  buffer[0][i + 1], buffer[0][i + 2]);
      a++;
    }
    g++;
  }
  jpeg_finish_decompress (&cinfo);
  jpeg_destroy_decompress (&cinfo);
  fclose (infile);

  gc = DefaultGC (dpy, Scr->screen);
  if ((width > (Scr->rootw / 2)) || (height > (Scr->rooth / 2))) {
    int x, y;

    pixret = XCreatePixmap (dpy, Scr->Root, Scr->rootw, Scr->rooth, Scr->d_depth);
    x = (Scr->rootw  -  width) / 2;
    y = (Scr->rooth  - height) / 2;
    XFillRectangle (dpy, pixret, gc, 0, 0, Scr->rootw, Scr->rooth);
    XPutImage (dpy, pixret, gc, ximage, 0, 0, x, y, width, height);
    image->width  = Scr->rootw;
    image->height = Scr->rooth;
  } else {
    pixret = XCreatePixmap (dpy, Scr->Root, width, height, Scr->d_depth);
    XPutImage (dpy, pixret, gc, ximage, 0, 0, 0, 0, width, height);
    image->width  = width;
    image->height = height;
  }
  XDestroyImage (ximage);
  image->pixmap = pixret;
  image->mask   = None;
  image->next   = None;

  return image;
}

#endif /* JPEG */
