text: /*                          D M - X . C
   * BRL-CAD
   *
   * Copyright (c) 1988-2016 United States Government as represented by
   * the U.S. Army Research Laboratory.
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public License
   * version 2.1 as published by the Free Software Foundation.
   *
   * This library is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this file; see the file named COPYING for more
   * information.
   */
  /** @file libdm/dm-X.c
   *
   * An X Window System Display Manager.
   *
   */
  
  #include "common.h"
  
  #ifdef DM_X
  
  #include <stdlib.h>
  #include <stdio.h>
  #include <limits.h>
  #include <string.h>
  #include <math.h>
  
  #define class REDEFINE_CLASS_STRING_TO_AVOID_CXX_CONFLICT
  #include <X11/Xlib.h>
  #include <X11/Xutil.h>
  
  #define USE_DIALS_AND_BUTTONS 1
  
  #ifdef HAVE_X11_XOSDEFS_H
  #  include <X11/Xfuncproto.h>
  #  include <X11/Xosdefs.h>
  #endif
  #if USE_DIALS_AND_BUTTONS
  #  include <X11/extensions/XInput.h>
  #endif
  #if defined(linux)
  #  undef X_NOT_STDC_ENV
  #  undef X_NOT_POSIX
  #endif
  
  #ifdef HAVE_TK
  #  include "tk.h"
  #endif
  
  #include "vmath.h"
  #include "bu/endian.h"
  #include "bn.h"
  #include "dm.h"
  #include "dm-X.h"
  #include "dm-Null.h"
  #include "dm/dm_xvars.h"
  #include "fb.h"
  #include "fb/fb_X.h"
  
  #include "rt/solid.h"
  
  #include "dm_private.h"
  
  #define PLOTBOUND 1000.0	/* Max magnification in Rot matrix */
  
  #define DM_X_DEFAULT_POINT_SIZE 1.0
  
  extern void X_allocate_color_cube(Display *, Colormap, long unsigned int *, int, int, int);
  extern unsigned long X_get_pixel(unsigned char, unsigned char, unsigned char, long unsigned int *, int);
  
  
  struct allocated_colors {
      struct bu_list l;
      int r;
      int g;
      int b;
      XColor c;
  };
  
  
  HIDDEN void
  get_color(Display *dpy, Colormap cmap, XColor *color)
  {
      Status st;
      static struct allocated_colors *colors = NULL;
      struct allocated_colors *c;
      int r, g, b;
  
      if (!colors) {
  	BU_ALLOC(colors, struct allocated_colors);
  	BU_LIST_INIT(&(colors->l));
  	colors->r = colors->g = colors->b = -1;
      }
  
      /* allocated colors are stashed into a list in order to avoid
       * subsequent repeat allocations.
       */
      for (BU_LIST_FOR(c, allocated_colors, &(colors->l))) {
  	if (DM_SAME_COLOR(c->r, c->g, c->b, color->red, color->green, color->blue)) {
  	    *color = c->c; /* struct copy */
  	    return;
  	}
      }
  
      r = color->red;
      g = color->green;
      b = color->blue;
  
      st = XAllocColor(dpy, cmap, color);
      switch (st) {
  	case 1:
  	    break;
  	case BadColor:
  	    bu_log("XAllocColor failed (BadColor) for (%3d, %3d, %3d) %04x, %04x, %04x\n",
  		   (r >> 8), (g >> 8), (b >> 8),
  		   r, g, b);
  	    break;
  
  	default:
  	    bu_log("XAllocColor error for (%3d, %3d, %3d) %04x, %04x, %04x\n",
  		   (r >> 8), (g >> 8), (b >> 8),
  		   r, g, b);
  	    break;
      }
  
      /* got new valid color, add it to our list */
      BU_ALLOC(c, struct allocated_colors);
      c->r = r;
      c->g = g;
      c->b = b;
      c->c = *color; /* struct copy */
      BU_LIST_PUSH(&(colors->l), &(c->l));
  }
  
  
  HIDDEN void
  X_reshape(struct dm_internal *dmp, int width, int height)
  {
      dmp->dm_height = height;
      dmp->dm_width = width;
      dmp->dm_aspect = (fastf_t)dmp->dm_width / (fastf_t)dmp->dm_height;
  }
  
  
  HIDDEN int
  X_configureWin_guts(struct dm_internal *dmp, int force)
  {
      XWindowAttributes xwa;
      XFontStruct *newfontstruct;
      XGCValues gcv;
      struct dm_xvars *pubvars = (struct dm_xvars *)dmp->dm_vars.pub_vars;
      struct x_vars *privars = (struct x_vars *)dmp->dm_vars.priv_vars;
  
      XGetWindowAttributes(pubvars->dpy,
  			 pubvars->win, &xwa);
  
      /* nothing to do */
      if (!force &&
  	dmp->dm_height == xwa.height &&
  	dmp->dm_width == xwa.width)
  	return TCL_OK;
  
      X_reshape(dmp, xwa.width, xwa.height);
  
      if (dmp->dm_debugLevel) {
  	bu_log("X_configureWin_guts()\n");
  	bu_log("width = %d, height = %d\n", dmp->dm_width, dmp->dm_height);
      }
  
  #ifdef HAVE_TK
      Tk_FreePixmap(pubvars->dpy,
  		  privars->pix);
      privars->pix =
  	Tk_GetPixmap(pubvars->dpy,
  		     DefaultRootWindow(pubvars->dpy),
  		     dmp->dm_width,
  		     dmp->dm_height,
  		     Tk_Depth(pubvars->xtkwin));
  #endif
  
      /* First time through, load a font or quit */
      if (pubvars->fontstruct == NULL) {
  	if ((pubvars->fontstruct =
  	     XLoadQueryFont(pubvars->dpy, FONT9)) == NULL) {
  	    /* Try hardcoded backup font */
  	    if ((pubvars->fontstruct =
  		 XLoadQueryFont(pubvars->dpy, FONTBACK)) == NULL) {
  		bu_log("dm-X: Can't open font '%s' or '%s'\n", FONT9, FONTBACK);
  		return TCL_ERROR;
  	    }
  	}
  
  	gcv.font = pubvars->fontstruct->fid;
  	XChangeGC(pubvars->dpy,
  		  privars->gc, GCFont, &gcv);
      }
  
      /* Always try to choose a the font that best fits the window size.
       */
  
      if (dmp->dm_width < 582) {
  	if (pubvars->fontstruct->per_char->width != 5) {
  	    if ((newfontstruct = XLoadQueryFont(pubvars->dpy,
  						FONT5)) != NULL) {
  		XFreeFont(pubvars->dpy,
  			  pubvars->fontstruct);
  		pubvars->fontstruct = newfontstruct;
  		gcv.font = pubvars->fontstruct->fid;
  		XChangeGC(pubvars->dpy,
  			  privars->gc, GCFont, &gcv);
  	    }
  	}
      } else if (dmp->dm_width < 679) {
  	if (pubvars->fontstruct->per_char->width != 6) {
  	    if ((newfontstruct = XLoadQueryFont(pubvars->dpy,
  						FONT6)) != NULL) {
  		XFreeFont(pubvars->dpy,
  			  pubvars->fontstruct);
  		pubvars->fontstruct = newfontstruct;
  		gcv.font = pubvars->fontstruct->fid;
  		XChangeGC(pubvars->dpy,
  			  privars->gc, GCFont, &gcv);
  	    }
  	}
      } else if (dmp->dm_width < 776) {
  	if (pubvars->fontstruct->per_char->width != 7) {
  	    if ((newfontstruct = XLoadQueryFont(pubvars->dpy,
  						FONT7)) != NULL) {
  		XFreeFont(pubvars->dpy,
  			  pubvars->fontstruct);
  		pubvars->fontstruct = newfontstruct;
  		gcv.font = pubvars->fontstruct->fid;
  		XChangeGC(pubvars->dpy,
  			  privars->gc, GCFont, &gcv);
  	    }
  	}
      } else if (dmp->dm_width < 873) {
  	if (pubvars->fontstruct->per_char->width != 8) {
  	    if ((newfontstruct = XLoadQueryFont(pubvars->dpy,
  						FONT8)) != NULL) {
  		XFreeFont(pubvars->dpy,
  			  pubvars->fontstruct);
  		pubvars->fontstruct = newfontstruct;
  		gcv.font = pubvars->fontstruct->fid;
  		XChangeGC(pubvars->dpy,
  			  privars->gc, GCFont, &gcv);
  	    }
  	}
      } else {
  	if (pubvars->fontstruct->per_char->width != 9) {
  	    if ((newfontstruct = XLoadQueryFont(pubvars->dpy,
  						FONT9)) != NULL) {
  		XFreeFont(pubvars->dpy,
  			  pubvars->fontstruct);
  		pubvars->fontstruct = newfontstruct;
  		gcv.font = pubvars->fontstruct->fid;
  		XChangeGC(pubvars->dpy,
  			  privars->gc, GCFont, &gcv);
  	    }
  	}
      }
  
      return TCL_OK;
  }
  
  
  HIDDEN XVisualInfo *
  X_choose_visual(struct dm_internal *dmp)
  {
      XVisualInfo *vip, vitemp, *vibase, *maxvip;
      int num, i, j;
      int tries, baddepth;
      int desire_trueColor = 1;
      int min_depth = 8;
      int *good = NULL;
      int screen;
      struct dm_xvars *pubvars = (struct dm_xvars *)dmp->dm_vars.pub_vars;
      struct x_vars *privars = (struct x_vars *)dmp->dm_vars.priv_vars;
  
      vibase = XGetVisualInfo(pubvars->dpy, 0, &vitemp, &num);
      screen = DefaultScreen(((struct dm_xvars *)dmp->dm_vars.pub_vars)->dpy);
  
      good = (int *)bu_malloc(sizeof(int)*num, "alloc good visuals");
  
      while (1) {
  	for (i=0, j=0, vip=vibase; i<num; i++, vip++) {
  	    /* requirements */
  	    if (vip->screen != screen)
  		continue;
  	    if (vip->depth < min_depth)
  		continue;
  	    if (desire_trueColor) {
  		if (vip->class != TrueColor)
  		    continue;
  	    } else if (vip->class != PseudoColor)
  		continue;
  
  	    /* this visual meets criteria */
  	    good[j++] = i;
  	}
  
  	baddepth = 1000;
  	for (tries = 0; tries < j; ++tries) {
  	    maxvip = vibase + good[0];
  	    for (i=1; i<j; i++) {
  		vip = vibase + good[i];
  		if ((vip->depth > maxvip->depth)&&(vip->depth < baddepth)) {
  		    maxvip = vip;
  		}
  	    }
  
  	    /* make sure Tk handles it */
  	    if (desire_trueColor) {
  		pubvars->cmap =
  		    XCreateColormap(pubvars->dpy,
  				    RootWindow(pubvars->dpy,
  					       maxvip->screen),
  				    maxvip->visual, AllocNone);
  		privars->is_trueColor = 1;
  	    } else {
  		pubvars->cmap =
  		    XCreateColormap(pubvars->dpy,
  				    RootWindow(pubvars->dpy,
  					       maxvip->screen),
  				    maxvip->visual, AllocAll);
  		privars->is_trueColor = 0;
  	    }
  
  #ifdef HAVE_TK
  	    if (Tk_SetWindowVisual(pubvars->xtkwin,
  				   maxvip->visual,
  				   maxvip->depth,
  				   pubvars->cmap)) {
  		pubvars->depth = maxvip->depth;
  
  		bu_free(good, "dealloc good visuals");
  		return maxvip; /* success */
  	    } else
  #endif
  	    {
  		/* retry with lesser depth */
  		baddepth = maxvip->depth;
  		XFreeColormap(pubvars->dpy, pubvars->cmap);
  	    }
  	}
  
  	if (desire_trueColor) {
  	    desire_trueColor = 0;
  	} else {
  	    /* ran out of visuals, give up */
  	    break;
  	}
      }
  
      bu_free(good, "dealloc good visuals");
      return (XVisualInfo *)NULL; /* failure */
  }
  
  
  /*
   * Gracefully release the display.
   */
  HIDDEN int
  X_close(struct dm_internal *dmp)
  {
      struct dm_xvars *pubvars = (struct dm_xvars *)dmp->dm_vars.pub_vars;
      struct x_vars *privars = (struct x_vars *)dmp->dm_vars.priv_vars;
  
      if (pubvars->dpy) {
  	if (privars->gc)
  	    XFreeGC(pubvars->dpy,
  		    privars->gc);
  
  #ifdef HAVE_TK
  	if (privars->pix)
  	    Tk_FreePixmap(pubvars->dpy,
  			  privars->pix);
  #endif
  
  	/*XXX Possibly need to free the colormap */
  	if (pubvars->cmap)
  	    XFreeColormap(pubvars->dpy,
  			  pubvars->cmap);
  
  #ifdef HAVE_TK
  	if (pubvars->xtkwin)
  	    Tk_DestroyWindow(pubvars->xtkwin);
  #endif
  
      }
  
      bu_vls_free(&dmp->dm_pathName);
      bu_vls_free(&dmp->dm_tkName);
      bu_vls_free(&dmp->dm_dName);
      bu_free((void *)dmp->dm_vars.priv_vars, "X_close: x_vars");
      bu_free((void *)dmp->dm_vars.pub_vars, "X_close: dm_xvars");
      bu_free((void *)dmp, "X_close: dmp");
  
      return TCL_OK;
  }
  
  
  /*
   * Fire up the display manager, and the display processor.
   *
   */
  struct dm_internal *
  X_open_dm(Tcl_Interp *interp, int argc, char **argv)
  {
      static int count = 0;
      int make_square = -1;
      XGCValues gcv;
  #if USE_DIALS_AND_BUTTONS
      int j, k;
      int ndevices;
      int nclass = 0;
      int unused;
      XDeviceInfoPtr olist = NULL, list = NULL;
      XDevice *dev = NULL;
      XEventClass e_class[15];
      XInputClassInfo *cip = NULL;
  #endif
      struct bu_vls str = BU_VLS_INIT_ZERO;
      struct bu_vls init_proc_vls = BU_VLS_INIT_ZERO;
      struct dm_internal *dmp = (struct dm_internal *)NULL;
      Tk_Window tkwin = (Tk_Window)NULL;
      Screen *screen = (Screen *)NULL;
  
      struct dm_xvars *pubvars = NULL;
      struct x_vars *privars = NULL;
  
  #ifdef HAVE_TK
      if ((tkwin = Tk_MainWindow(interp)) == NULL) {
  	return DM_NULL;
      }
  #endif
  
      BU_ALLOC(dmp, struct dm_internal);
  
      *dmp = dm_X; /* struct copy */
      dmp->dm_interp = interp;
  
      BU_ALLOC(dmp->dm_vars.pub_vars, struct dm_xvars);
      pubvars = (struct dm_xvars *)dmp->dm_vars.pub_vars;
  
      BU_ALLOC(dmp->dm_vars.priv_vars, struct x_vars);
      privars = (struct x_vars *)dmp->dm_vars.priv_vars;
  
      bu_vls_init(&dmp->dm_pathName);
      bu_vls_init(&dmp->dm_tkName);
      bu_vls_init(&dmp->dm_dName);
  
      dm_processOptions(dmp, &init_proc_vls, --argc, ++argv);
  
      if (bu_vls_strlen(&dmp->dm_pathName) == 0)
  	bu_vls_printf(&dmp->dm_pathName, ".dm_X%d", count);
  
      ++count;
      if (bu_vls_strlen(&dmp->dm_dName) == 0) {
  	char *dp;
  
  	dp = getenv("DISPLAY");
  	if (dp)
  	    bu_vls_strcpy(&dmp->dm_dName, dp);
  	else
  	    bu_vls_strcpy(&dmp->dm_dName, ":0.0");
      }
      if (bu_vls_strlen(&init_proc_vls) == 0)
  	bu_vls_strcpy(&init_proc_vls, "bind_dm");
  
      /* initialize dm specific variables */
      pubvars->devmotionnotify = LASTEvent;
      pubvars->devbuttonpress = LASTEvent;
      pubvars->devbuttonrelease = LASTEvent;
      dmp->dm_aspect = 1.0;
  
      pubvars->fontstruct = NULL;
  
      if (dmp->dm_top) {
  #ifdef HAVE_TK
  	/* Make xtkwin a toplevel window */
  	pubvars->xtkwin = Tk_CreateWindowFromPath(interp, tkwin,
  						  bu_vls_addr(&dmp->dm_pathName),
  						  bu_vls_addr(&dmp->dm_dName));
  	pubvars->top = pubvars->xtkwin;
  #endif
      } else {
  	char *cp;
  
  	cp = strrchr(bu_vls_addr(&dmp->dm_pathName), (int)'.');
  	if (cp == bu_vls_addr(&dmp->dm_pathName)) {
  	    pubvars->top = tkwin;
  	} else {
  	    struct bu_vls top_vls = BU_VLS_INIT_ZERO;
  
  	    bu_vls_strncpy(&top_vls, (const char *)bu_vls_addr(&dmp->dm_pathName), cp - bu_vls_addr(&dmp->dm_pathName));
  
  #ifdef HAVE_TK
  	    pubvars->top =
  		Tk_NameToWindow(interp, bu_vls_addr(&top_vls), tkwin);
  #endif
  	    bu_vls_free(&top_vls);
  	}
  
  #ifdef HAVE_TK
  	/* Make xtkwin an embedded window */
  	pubvars->xtkwin =
  	    Tk_CreateWindow(interp, pubvars->top,
  			    cp + 1, (char *)NULL);
  #endif
      }
  
      if (pubvars->xtkwin == NULL) {
  	bu_log("X_open_dm: Failed to open %s\n", bu_vls_addr(&dmp->dm_pathName));
  	(void)X_close(dmp);
  	return DM_NULL;
      }
  
  #ifdef HAVE_TK
      bu_vls_printf(&dmp->dm_tkName, "%s",
  		  (char *)Tk_Name(pubvars->xtkwin));
  #endif
  
      bu_vls_printf(&str, "_init_dm %s %s\n",
  		  bu_vls_addr(&init_proc_vls),
  		  bu_vls_addr(&dmp->dm_pathName));
  
      if (Tcl_Eval(interp, bu_vls_addr(&str)) == TCL_ERROR) {
  	bu_vls_free(&str);
  	(void)X_close(dmp);
  	return DM_NULL;
      }
  
      bu_vls_free(&init_proc_vls);
      bu_vls_free(&str);
  
      pubvars->dpy = NULL;
  #ifdef HAVE_TK
      pubvars->dpy = Tk_Display(pubvars->top);
  #endif
  
      /* make sure there really is a display before proceeding. */
      if (!pubvars->dpy) {
  	bu_log("ERROR: Unable to attach to display (%s)\n", bu_vls_addr(&dmp->dm_pathName));
  	(void)X_close(dmp);
  	return DM_NULL;
      }
  
      screen = DefaultScreenOfDisplay(pubvars->dpy);
  
      if (!screen) {
  #ifdef HAVE_TK
  	/* failed to get a default screen, try harder */
  	screen = Tk_Screen(pubvars->top);
  #endif
      }
  
      /* make sure there really is a screen before processing. */
      if (!screen) {
  	bu_log("ERROR: Unable to attach to screen (%s)\n", bu_vls_addr(&dmp->dm_pathName));
  	(void)X_close(dmp);
  	return DM_NULL;
      }
  
      /* FIXME: Applications on Mac OS X linking against the system
       * Tcl/Tk frameworks may crash here due to dpy being a high (7+
       * digit) value and the screens[] array it indexes into being 0x0.
       * Might have better luck calling functions instead of macros.
       */
  
      if (dmp->dm_width == 0) {
  	dmp->dm_width =
  	    DisplayWidth(pubvars->dpy,
  			 DefaultScreen(pubvars->dpy)) - 30;
  	++make_square;
      }
  
      if (dmp->dm_height == 0) {
  	dmp->dm_height =
  	    DisplayHeight(pubvars->dpy,
  			  DefaultScreen(pubvars->dpy)) - 30;
  	++make_square;
      }
  
      if (make_square > 0) {
  	/* Make window square */
  	if (dmp->dm_height <
  	    dmp->dm_width)
  	    dmp->dm_width = dmp->dm_height;
  	else
  	    dmp->dm_height = dmp->dm_width;
      }
  
  #ifdef HAVE_TK
      Tk_GeometryRequest(pubvars->xtkwin,
  		       dmp->dm_width,
  		       dmp->dm_height);
  #endif
  
      /* must do this before MakeExist */
      if ((pubvars->vip = X_choose_visual(dmp)) == NULL) {
  	bu_log("X_open_dm: Can't get an appropriate visual.\n");
  	(void)X_close(dmp);
  	return DM_NULL;
      }
  
  #ifdef HAVE_TK
      Tk_MakeWindowExist(pubvars->xtkwin);
      pubvars->win = Tk_WindowId(pubvars->xtkwin);
      dmp->dm_id = pubvars->win;
      privars->pix =
  	Tk_GetPixmap(pubvars->dpy,
  		     pubvars->win,
  		     dmp->dm_width,
  		     dmp->dm_height,
  		     Tk_Depth(pubvars->xtkwin));
  #endif
  
      if (privars->is_trueColor) {
  	XColor fg, bg;
  
  	INIT_XCOLOR(&fg);
  	INIT_XCOLOR(&bg);
  
  	fg.red = 65535;
  	fg.green = fg.blue = 0;
  
  	get_color(pubvars->dpy,
  		  pubvars->cmap,
  		  &fg);
  
  	privars->fg = fg.pixel;
  
  	bg.red = bg.green = bg.blue = 0;
  	get_color(pubvars->dpy,
  		  pubvars->cmap,
  		  &bg);
  
  	privars->bg = bg.pixel;
      } else {
  	X_allocate_color_cube(pubvars->dpy,
  			      pubvars->cmap,
  			      privars->pixels,
  			      /* cube dimension, uses XStoreColor */
  			      6, CMAP_BASE, 1);
  
  	privars->bg =
  	    X_get_pixel(DM_BLACK,
  			privars->pixels,
  			CUBE_DIMENSION);
  	privars->fg =
  	    X_get_pixel(DM_RED,
  			privars->pixels,
  			CUBE_DIMENSION);
      }
  
      gcv.background = privars->bg;
      gcv.foreground = privars->fg;
      privars->gc = XCreateGC(pubvars->dpy,
  			    pubvars->win,
  			    (GCForeground|GCBackground), &gcv);
  
      /* First see if the server supports XInputExtension */
      {
  	int return_val;
  
  	if (!XQueryExtension(pubvars->dpy,
  			     "XInputExtension", &return_val, &return_val, &return_val))
  	    goto Skip_dials;
      }
  
  #if USE_DIALS_AND_BUTTONS
      /*
       * Take a look at the available input devices. We're looking for
       * "dial+buttons".
       */
      if (XQueryExtension(pubvars->dpy, "XInputExtension", &unused, &unused, &unused)) {
  	olist = list = (XDeviceInfoPtr)XListInputDevices(pubvars->dpy, &ndevices);
      }
  
      if (list == (XDeviceInfoPtr)NULL ||
  	list == (XDeviceInfoPtr)1) goto Done;
  
      for (j = 0; j < ndevices; ++j, list++) {
  	if (list->use == IsXExtensionDevice) {
  	    if (BU_STR_EQUAL(list->name, "dial+buttons")) {
  		if ((dev = XOpenDevice(pubvars->dpy,
  				       list->id)) == (XDevice *)NULL) {
  		    bu_log("X_open_dm: Couldn't open the dials+buttons\n");
  		    goto Done;
  		}
  
  		for (cip = dev->classes, k = 0; k < dev->num_classes;
  		     ++k, ++cip) {
  		    switch (cip->input_class) {
  #ifdef IR_BUTTONS
  			case ButtonClass:
  			    DeviceButtonPress(dev, pubvars->devbuttonpress,
  					      e_class[nclass]);
  			    ++nclass;
  			    DeviceButtonRelease(dev, pubvars->devbuttonrelease,
  						e_class[nclass]);
  			    ++nclass;
  			    break;
  #endif
  #ifdef IR_KNOBS
  			case ValuatorClass:
  			    DeviceMotionNotify(dev, pubvars->devmotionnotify,
  					       e_class[nclass]);
  			    ++nclass;
  			    break;
  #endif
  			default:
  			    break;
  		    }
  		}
  
  		XSelectExtensionEvent(pubvars->dpy,
  				      pubvars->win, e_class, nclass);
  		goto Done;
  	    }
  	}
      }
  Done:
      XFreeDeviceList(olist);
  #endif
  
  Skip_dials:
      (void)X_configureWin_guts(dmp, 1);
  
  #ifdef HAVE_TK
      Tk_SetWindowBackground(pubvars->xtkwin,
  			   privars->bg);
      Tk_MapWindow(pubvars->xtkwin);
  #endif
  
      MAT_IDN(privars->xmat);
  
      return dmp;
  }
  
  
  HIDDEN int
  X_drawBegin(struct dm_internal *dmp)
  {
      XGCValues gcv;
      struct dm_xvars *pubvars = (struct dm_xvars *)dmp->dm_vars.pub_vars;
      struct x_vars *privars = (struct x_vars *)dmp->dm_vars.priv_vars;
  
      if (dmp->dm_debugLevel)
  	bu_log("X_drawBegin()\n");
  
      /* clear pixmap */
      gcv.foreground = privars->bg;
      XChangeGC(pubvars->dpy,
  	      privars->gc,
  	      GCForeground, &gcv);
      XFillRectangle(pubvars->dpy,
  		   privars->pix,
  		   privars->gc, 0,
  		   0, dmp->dm_width + 1,
  		   dmp->dm_height + 1);
  
      /* reset foreground */
      gcv.foreground = privars->fg;
      XChangeGC(pubvars->dpy,
  	      privars->gc,
  	      GCForeground, &gcv);
  
      return TCL_OK;
  }
  
  
  HIDDEN int
  X_drawEnd(struct dm_internal *dmp)
  {
      struct dm_xvars *pubvars = (struct dm_xvars *)dmp->dm_vars.pub_vars;
      struct x_vars *privars = (struct x_vars *)dmp->dm_vars.priv_vars;
  
      if (dmp->dm_debugLevel)
  	bu_log("X_drawEnd()\n");
  
      XCopyArea(pubvars->dpy,
  	      privars->pix,
  	      pubvars->win,
  	      privars->gc,
  	      0, 0, dmp->dm_width,
  	      dmp->dm_height, 0, 0);
  
      /* Prevent lag between events and updates */
      XSync(pubvars->dpy, 0);
  
      return TCL_OK;
  }
  
  
  /*
   * Load a new transformation matrix.  This will be followed by many
   * calls to X_draw().
   */
  HIDDEN int
  X_loadMatrix(struct dm_internal *dmp, fastf_t *mat, int which_eye)
  {
      struct x_vars *privars = (struct x_vars *)dmp->dm_vars.priv_vars;
  
      if (dmp->dm_debugLevel) {
  	bu_log("X_loadMatrix()\n");
  
  	bu_log("which eye = %d\t", which_eye);
  	bu_log("transformation matrix = \n");
  
  	/* note the row/column ordering */
  	bu_log("%g %g %g %g\n", mat[0], mat[1], mat[2], mat[3]);
  	bu_log("%g %g %g %g\n", mat[4], mat[5], mat[6], mat[7]);
  	bu_log("%g %g %g %g\n", mat[8], mat[9], mat[10], mat[11]);
  	bu_log("%g %g %g %g\n", mat[12], mat[13], mat[14], mat[15]);
      }
  
      MAT_COPY(privars->xmat, mat);
      return TCL_OK;
  }
  
  
  HIDDEN int
  X_drawVList(struct dm_internal *dmp, struct bn_vlist *vp)
  {
      extern int vectorThreshold;	/* defined in libdm/tcl.c */
  
      static vect_t spnt, lpnt, pnt;
      struct bn_vlist *tvp;
      XSegment segbuf[1024];	/* XDrawSegments list */
      XSegment *segp;		/* current segment */
      int nseg;		        /* number of segments */
      fastf_t delta;
      point_t *pt_prev = NULL;
      fastf_t dist_prev=1.0;
      static int nvectors = 0;
      struct dm_xvars *pubvars = (struct dm_xvars *)dmp->dm_vars.pub_vars;
      struct x_vars *privars = (struct x_vars *)dmp->dm_vars.priv_vars;
      fastf_t pointSize = DM_X_DEFAULT_POINT_SIZE;
  
      if (dmp->dm_debugLevel) {
  	bu_log("X_drawVList()\n");
  	bu_log("vp - %lu, perspective - %d\n", vp, dmp->dm_perspective);
      }
  
      /* delta is used in clipping to insure clipped endpoint is
       * slightly in front of eye plane (perspective mode only).  This
       * value is a SWAG that seems to work OK.
       */
      delta = privars->xmat[15]*0.0001;
      if (delta < 0.0)
  	delta = -delta;
      if (delta < SQRT_SMALL_FASTF)
  	delta = SQRT_SMALL_FASTF;
  
      nseg = 0;
      segp = segbuf;
      for (BU_LIST_FOR(tvp, bn_vlist, &vp->l)) {
  	int i;
  	int nused = tvp->nused;
  	int *cmd = tvp->cmd;
  	point_t *pt = tvp->pt;
  	fastf_t dist;
  
  	/* Viewing region is from -1.0 to +1.0 */
  	/* 2^31 ~= 2e9 -- dynamic range of a long int */
  	/* 2^(31-11) = 2^20 ~= 1e6 */
  	/* Integerize and let the X server do the clipping */
  	for (i = 0; i < nused; i++, cmd++, pt++) {
  	    switch (*cmd) {
  		case BN_VLIST_POLY_START:
  		case BN_VLIST_POLY_VERTNORM:
  		case BN_VLIST_TRI_START:
  		case BN_VLIST_TRI_VERTNORM:
  		    continue;
  		case BN_VLIST_POLY_MOVE:
  		case BN_VLIST_LINE_MOVE:
  		case BN_VLIST_TRI_MOVE:
  		    /* Move, not draw */
  		    if (dmp->dm_debugLevel > 2) {
  			bu_log("before transformation:\n");
  			bu_log("pt - %lf %lf %lf\n", V3ARGS(*pt));
  		    }
  
  		    if (dmp->dm_perspective > 0) {
  			/* cannot apply perspective transformation to
  			 * points behind eye plane!!!!
  			 */
  			dist = VDOT(*pt, &privars->xmat[12]) + privars->xmat[15];
  			if (dist <= 0.0) {
  			    pt_prev = pt;
  			    dist_prev = dist;
  			    continue;
  			} else {
  			    MAT4X3PNT(lpnt, privars->xmat, *pt);
  			    dist_prev = dist;
  			    pt_prev = pt;
  			}
  		    } else {
  			MAT4X3PNT(lpnt, privars->xmat, *pt);
  		    }
  
  		    lpnt[0] *= 2047;
  		    lpnt[1] *= 2047 * dmp->dm_aspect;
  		    lpnt[2] *= 2047;
  		    continue;
  		case BN_VLIST_POLY_DRAW:
  		case BN_VLIST_POLY_END:
  		case BN_VLIST_LINE_DRAW:
  		case BN_VLIST_TRI_DRAW:
  		case BN_VLIST_TRI_END:
  		    /* draw */
  		    if (dmp->dm_debugLevel > 2) {
  			bu_log("before transformation:\n");
  			bu_log("pt - %lf %lf %lf\n", V3ARGS(*pt));
  		    }
  
  		    if (dmp->dm_perspective > 0) {
  			/* cannot apply perspective transformation to
  			 * points behind eye plane!!!!
  			 */
  			dist = VDOT(*pt, &privars->xmat[12]) + privars->xmat[15];
  			if (dmp->dm_debugLevel > 2)
  			    bu_log("dist=%g, dist_prev=%g\n", dist, dist_prev);
  			if (dist <= 0.0) {
  			    if (dist_prev <= 0.0) {
  				/* nothing to plot */
  				dist_prev = dist;
  				pt_prev = pt;
  				continue;
  			    } else {
  				if (pt_prev) {
  				fastf_t alpha;
  				vect_t diff;
  				point_t tmp_pt;
  
  				/* clip this end */
  				VSUB2(diff, *pt, *pt_prev);
  				alpha = (dist_prev - delta) / (dist_prev - dist);
  				VJOIN1(tmp_pt, *pt_prev, alpha, diff);
  				MAT4X3PNT(pnt, privars->xmat, tmp_pt);
  				}
  			    }
  			} else {
  			    if (dist_prev <= 0.0) {
  				if (pt_prev) {
  				fastf_t alpha;
  				vect_t diff;
  				point_t tmp_pt;
  
  				/* clip other end */
  				VSUB2(diff, *pt, *pt_prev);
  				alpha = (-dist_prev + delta) / (dist - dist_prev);
  				VJOIN1(tmp_pt, *pt_prev, alpha, diff);
  				MAT4X3PNT(lpnt, privars->xmat, tmp_pt);
  				lpnt[0] *= 2047;
  				lpnt[1] *= 2047 * dmp->dm_aspect;
  				lpnt[2] *= 2047;
  				MAT4X3PNT(pnt, privars->xmat, *pt);
  				}
  			    } else {
  				MAT4X3PNT(pnt, privars->xmat, *pt);
  			    }
  			}
  			dist_prev = dist;
  		    } else {
  			MAT4X3PNT(pnt, privars->xmat, *pt);
  		    }
  
  		    pnt[0] *= 2047;
  		    pnt[1] *= 2047 * dmp->dm_aspect;
  		    pnt[2] *= 2047;
  
  		    /* save pnt --- it might get changed by clip() */
  		    VMOVE(spnt, pnt);
  		    pt_prev = pt;
  
  		    if (dmp->dm_debugLevel > 2) {
  			bu_log("before clipping:\n");
  			bu_log("clipmin - %lf %lf %lf\n",
  			       dmp->dm_clipmin[X],
  			       dmp->dm_clipmin[Y],
  			       dmp->dm_clipmin[Z]);
  			bu_log("clipmax - %lf %lf %lf\n",
  			       dmp->dm_clipmax[X],
  			       dmp->dm_clipmax[Y],
  			       dmp->dm_clipmax[Z]);
  			bu_log("pt1 - %lf %lf %lf\n", lpnt[X], lpnt[Y], lpnt[Z]);
  			bu_log("pt2 - %lf %lf %lf\n", pnt[X], pnt[Y], pnt[Z]);
  		    }
  
  		    if (dmp->dm_zclip) {
  			if (vclip(lpnt, pnt,
  				  dmp->dm_clipmin,
  				  dmp->dm_clipmax) == 0) {
  			    VMOVE(lpnt, spnt);
  			    continue;
  			}
  		    } else {
  			fastf_t min_short = (fastf_t)SHRT_MIN;
  			fastf_t max_short = (fastf_t)SHRT_MAX;
  
  			/* Check to see if lpnt or pnt contain values
  			 * that exceed the capacity of a short (segbuf
  			 * is an array of XSegments which contain
  			 * shorts). If so, do clipping now. Otherwise,
  			 * let the X server do the clipping.
  			 */
  			if (lpnt[0] < min_short || max_short < lpnt[0] ||
  			    lpnt[1] < min_short || max_short < lpnt[1] ||
  			    pnt[0] < min_short || max_short < pnt[0] ||
  			    pnt[1] < min_short || max_short < pnt[1]) {
  			    /* if the entire line segment will not be
  			     * visible then ignore it.
  			     */
  			    if (clip(&lpnt[0], &lpnt[1], &pnt[0], &pnt[1]) == -1) {
  				VMOVE(lpnt, spnt);
  				continue;
  			    }
  			}
  		    }
  
  		    if (dmp->dm_debugLevel > 2) {
  			bu_log("after clipping:\n");
  			bu_log("pt1 - %lf %lf %lf\n", lpnt[X], lpnt[Y], lpnt[Z]);
  			bu_log("pt2 - %lf %lf %lf\n", pnt[X], pnt[Y], pnt[Z]);
  		    }
  
  		    /* convert to X window coordinates */
  		    segp->x1 = (short)GED_TO_Xx(dmp, lpnt[0]);
  		    segp->y1 = (short)GED_TO_Xy(dmp, lpnt[1]);
  		    segp->x2 = (short)GED_TO_Xx(dmp, pnt[0]);
  		    segp->y2 = (short)GED_TO_Xy(dmp, pnt[1]);
  
  		    nseg++;
  		    segp++;
  		    VMOVE(lpnt, spnt);
  
  		    if (nseg == 1024) {
  			XDrawSegments(pubvars->dpy,
  				      privars->pix,
  				      privars->gc, segbuf, nseg);
  
  			nseg = 0;
  			segp = segbuf;
  		    }
  		    break;
  		case BN_VLIST_POINT_DRAW:
  		    if (dmp->dm_debugLevel > 2) {
  			bu_log("before transformation:\n");
  			bu_log("pt - %lf %lf %lf\n", V3ARGS(*pt));
  		    }
  
  		    if (dmp->dm_perspective > 0) {
  			dist = VDOT(*pt, &privars->xmat[12]) + privars->xmat[15];
  
  			if (dist <= 0.0) {
  			    /* nothing to plot - point is behind eye plane */
  			    continue;
  			}
  		    }
  
  		    MAT4X3PNT(pnt, privars->xmat, *pt);
  
  		    pnt[0] *= 2047;
  		    pnt[1] *= 2047 * dmp->dm_aspect;
  		    pnt[2] *= 2047;
  		    VMOVE(lpnt, pnt);
  
  		    if (dmp->dm_debugLevel > 2) {
  			bu_log("after clipping:\n");
  			bu_log("pt - %lf %lf %lf\n", pnt[X], pnt[Y], pnt[Z]);
  		    }
  
  		    if (pointSize <= DM_X_DEFAULT_POINT_SIZE) {
  			/* draws a single pixel */
  			XDrawPoint(pubvars->dpy, privars->pix, privars->gc,
  				GED_TO_Xx(dmp, pnt[0]), GED_TO_Xy(dmp, pnt[1]));
  		    } else {
  			int upperLeft[2];
  
  			/* need the upper-left adjusted a half-pixel
  			 * so points with odd size are centered.
  			 */
  			upperLeft[X] = (double)GED_TO_Xx(dmp, pnt[0]) - (pointSize / 2.0) + 0.5;
  			upperLeft[Y] = (double)GED_TO_Xy(dmp, pnt[1]) - (pointSize / 2.0) + 0.5;
  
  			/* NOTE: in order to get smooth circles,
  			 * especially for small radii, X11 requires
  			 * that you draw both the interior and the
  			 * exterior.  only filling small radius
  			 * circles will otherwise render as a square.
  			 */
  			XDrawArc(pubvars->dpy, privars->pix, privars->gc, upperLeft[X], upperLeft[Y], pointSize, pointSize, 0, 360*64);
  			XFillArc(pubvars->dpy, privars->pix, privars->gc, upperLeft[X], upperLeft[Y], pointSize, pointSize, 0, 360*64);
  		    }
  		    break;
  		case BN_VLIST_POINT_SIZE:
  		    pointSize = (*pt)[0];
  		    if (pointSize < DM_X_DEFAULT_POINT_SIZE) {
  			pointSize = DM_X_DEFAULT_POINT_SIZE;
  		    }
  		    break;
  	    }
  	}
  
  	nvectors += nused;
  	if (nvectors >= vectorThreshold) {
  	    if (dmp->dm_debugLevel)
  		bu_log("X_drawVList(): handle Tcl events\n");
  
  	    nvectors = 0;
  
  	    /* Handle events in the queue */
  	    while (Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT));
  	}
      }
  
      if (nseg) {
  	XDrawSegments(pubvars->dpy,
  		      privars->pix,
  		      privars->gc, segbuf, nseg);
      }
  
      return TCL_OK;
  }
  
  
  HIDDEN int
  X_draw(struct dm_internal *dmp, struct bn_vlist *(*callback_function)(void *), void **data)
  {
      struct bn_vlist *vp;
      if (!callback_function) {
  	if (data) {
  	    vp = (struct bn_vlist *)data;
  	    X_drawVList(dmp, vp);
  	}
      } else {
  	if (!data) {
  	    return TCL_ERROR;
  	} else {
  	    (void)callback_function(data);
  	}
      }
      return TCL_OK;
  }
  
  
  /**
   * Restore the display processor to a normal mode of operation (i.e.,
   * not scaled, rotated, displaced, etc.).
   */
  HIDDEN int
  X_normal(struct dm_internal *dmp)
  {
      if (dmp->dm_debugLevel)
  	bu_log("X_normal()\n");
  
      return TCL_OK;
  }
  
  
  /**
   * Output a string into the displaylist.  The starting position of the
   * beam is as specified.
   */
  HIDDEN int
  X_drawString2D(struct dm_internal *dmp, const char *str, fastf_t x, fastf_t y, int size, int use_aspect)
  {
      int sx, sy;
      struct dm_xvars *pubvars = (struct dm_xvars *)dmp->dm_vars.pub_vars;
      struct x_vars *privars = (struct x_vars *)dmp->dm_vars.priv_vars;
  
      if (dmp->dm_debugLevel) {
  	bu_log("X_drawString2D():\n");
  	bu_log("\tstr - %s\n", str);
  	bu_log("\tx - %g\n", x);
  	bu_log("\ty - %g\n", y);
  	bu_log("\tsize - %d\n", size);
  	if (use_aspect) {
  	    bu_log("\tuse_aspect - %d\t\taspect ratio - %g\n", use_aspect, dmp->dm_aspect);
  	} else
  	    bu_log("\tuse_aspect - 0");
      }
  
      sx = dm_Normal2Xx(dmp, x);
      sy = dm_Normal2Xy(dmp, y, use_aspect);
  
      XDrawString(pubvars->dpy,
  		privars->pix,
  		privars->gc,
  		sx, sy, str, strlen(str));
  
      return TCL_OK;
  }
  
  
  HIDDEN int
  X_drawLine2D(struct dm_internal *dmp, fastf_t x_1, fastf_t y_1, fastf_t x_2, fastf_t y_2)
  {
      int sx1, sy1, sx2, sy2;
      struct dm_xvars *pubvars = (struct dm_xvars *)dmp->dm_vars.pub_vars;
      struct x_vars *privars = (struct x_vars *)dmp->dm_vars.priv_vars;
  
      sx1 = dm_Normal2Xx(dmp, x_1);
      sx2 = dm_Normal2Xx(dmp, x_2);
      sy1 = dm_Normal2Xy(dmp, y_1, 0);
      sy2 = dm_Normal2Xy(dmp, y_2, 0);
  
      if (dmp->dm_debugLevel) {
  	bu_log("X_drawLine2D()\n");
  	bu_log("x1 = %g, y1 = %g\n", x_1, y_1);
  	bu_log("x2 = %g, y2 = %g\n", x_2, y_2);
  	bu_log("sx1 = %d, sy1 = %d\n", sx1, sy1);
  	bu_log("sx2 = %d, sy2 = %d\n", sx2, sy2);
      }
  
      XDrawLine(pubvars->dpy,
  	      privars->pix,
  	      privars->gc,
  	      sx1, sy1, sx2, sy2);
  
      return TCL_OK;
  }
  
  
  HIDDEN int
  X_drawLine3D(struct dm_internal *dmp, point_t pt1, point_t pt2)
  {
      return draw_Line3D(dmp, pt1, pt2);
  }
  
  
  HIDDEN int
  X_drawLines3D(struct dm_internal *dmp, int npoints, point_t *points, int UNUSED(sflag))
  {
      if (!dmp || npoints < 0 || !points)
  	return TCL_ERROR;
  
      return TCL_OK;
  }
  
  
  HIDDEN int
  X_drawPoint2D(struct dm_internal *dmp, fastf_t x, fastf_t y)
  {
      int sx, sy;
      struct dm_xvars *pubvars = (struct dm_xvars *)dmp->dm_vars.pub_vars;
      struct x_vars *privars = (struct x_vars *)dmp->dm_vars.priv_vars;
  
      sx = dm_Normal2Xx(dmp, x);
      sy = dm_Normal2Xy(dmp, y, 0);
  
      if (dmp->dm_debugLevel) {
  	bu_log("X_drawPoint2D()\n");
  	bu_log("x = %g, y = %g\n", x, y);
  	bu_log("sx = %d, sy = %d\n", sx, sy);
      }
  
      XDrawPoint(pubvars->dpy,
  	       privars->pix,
  	       privars->gc, sx, sy);
  
      return TCL_OK;
  }
  
  
  HIDDEN int
  X_setFGColor(struct dm_internal *dmp, unsigned char r, unsigned char g, unsigned char b, int strict, fastf_t transparency)
  {
      XGCValues gcv;
      struct dm_xvars *pubvars = (struct dm_xvars *)dmp->dm_vars.pub_vars;
      struct x_vars *privars = (struct x_vars *)dmp->dm_vars.priv_vars;
  
      if (dmp->dm_debugLevel)
  	bu_log("X_setFGColor() rgb=[%d, %d, %d] strict=%d transparency=%f\n", r, g, b, strict, transparency);
  
      dmp->dm_fg[0] = r;
      dmp->dm_fg[1] = g;
      dmp->dm_fg[2] = b;
  
      if (privars->is_trueColor) {
  	XColor color;
  
  	INIT_XCOLOR(&color);
  
  	color.red = r << 8;
  	color.green = g << 8;
  	color.blue = b << 8;
  	get_color(pubvars->dpy,
  		  pubvars->cmap,
  		  &color);
  
  	gcv.foreground = color.pixel;
      } else {
  	gcv.foreground = X_get_pixel(r, g, b, privars->pixels, CUBE_DIMENSION);
      }
  
      /* save foreground pixel */
      privars->fg = gcv.foreground;
  
      XChangeGC(pubvars->dpy,
  	      privars->gc,
  	      GCForeground, &gcv);
  
      return TCL_OK;
  }
  
  
  HIDDEN int
  X_setBGColor(struct dm_internal *dmp, unsigned char r, unsigned char g, unsigned char b)
  {
      struct dm_xvars *pubvars = (struct dm_xvars *)dmp->dm_vars.pub_vars;
      struct x_vars *privars = (struct x_vars *)dmp->dm_vars.priv_vars;
  
      if (dmp->dm_debugLevel)
  	bu_log("X_setBGColor()\n");
  
      dmp->dm_bg[0] = r;
      dmp->dm_bg[1] = g;
      dmp->dm_bg[2] = b;
  
      if (privars->is_trueColor) {
  	XColor color;
  
  	INIT_XCOLOR(&color);
  
  	color.red = r << 8;
  	color.green = g << 8;
  	color.blue = b << 8;
  
  	get_color(pubvars->dpy,
  		  pubvars->cmap,
  		  &color);
  
  	privars->bg = color.pixel;
      } else
  	privars->bg =
  	    X_get_pixel(r, g, b, privars->pixels, CUBE_DIMENSION);
  
      return TCL_OK;
  }
  
  
  HIDDEN int
  X_setLineAttr(struct dm_internal *dmp, int width, int style)
  {
      int linestyle;
      struct dm_xvars *pubvars = (struct dm_xvars *)dmp->dm_vars.pub_vars;
      struct x_vars *privars = (struct x_vars *)dmp->dm_vars.priv_vars;
  
      if (dmp->dm_debugLevel)
  	bu_log("X_setLineAttr()\n");
  
      dmp->dm_lineWidth = width;
      dmp->dm_lineStyle = style;
  
      if (width <= 1)
  	width = 0;
  
      if (style == DM_DASHED_LINE)
  	linestyle = LineOnOffDash;
      else
  	linestyle = LineSolid;
  
      XSetLineAttributes(pubvars->dpy,
  		       privars->gc,
  		       width, linestyle, CapButt, JoinMiter);
  
      return TCL_OK;
  }
  
  
  HIDDEN int
  X_debug(struct dm_internal *dmp, int lvl)
  {
      dmp->dm_debugLevel = lvl;
  
      return TCL_OK;
  }
  
  
  HIDDEN int
  X_logfile(struct dm_internal *dmp, const char *filename)
  {
      bu_vls_sprintf(&dmp->dm_log, "%s", filename);
  
      return TCL_OK;
  }
  
  
  HIDDEN int
  X_setWinBounds(struct dm_internal *dmp, fastf_t *w)
  {
      if (dmp->dm_debugLevel)
  	bu_log("X_setWinBounds()\n");
  
      dmp->dm_clipmin[0] = w[0];
      dmp->dm_clipmin[1] = w[2];
      dmp->dm_clipmin[2] = w[4];
      dmp->dm_clipmax[0] = w[1];
      dmp->dm_clipmax[1] = w[3];
      dmp->dm_clipmax[2] = w[5];
  
      return TCL_OK;
  }
  
  
  HIDDEN int
  X_configureWin(struct dm_internal *dmp, int force)
  {
      /* don't force */
      return X_configureWin_guts(dmp, force);
  }
  
  
  HIDDEN int
  X_setLight(struct dm_internal *dmp, int light_on)
  {
      if (dmp->dm_debugLevel)
  	bu_log("X_setLight:\n");
  
      dmp->dm_light = light_on;
  
      return TCL_OK;
  }
  
  
  HIDDEN int
  X_setZBuffer(struct dm_internal *dmp, int zbuffer_on)
  {
      if (dmp->dm_debugLevel)
  	bu_log("X_setZBuffer:\n");
  
      dmp->dm_zbuffer = zbuffer_on;
  
      return TCL_OK;
  }
  
  
  HIDDEN int
  X_getDisplayImage(struct dm_internal *dmp, unsigned char **image)
  {
      XImage *ximage_p;
      unsigned char **rows;
      unsigned char *idata;
      unsigned char *irow;
      int bytes_per_pixel;
      int bytes_per_pixel_output = 3; /* limit for current bu_image raw pix usage */
      int bytes_per_line_output;
      int i, j, k;
      unsigned char *dbyte0, *dbyte1, *dbyte2;
      int red_shift;
      int green_shift;
      int blue_shift;
      int red_bits;
      int green_bits;
      int blue_bits;
  
  
      ximage_p = XGetImage(((struct dm_xvars *)dmp->dm_vars.pub_vars)->dpy,
  			 ((struct dm_xvars *)dmp->dm_vars.pub_vars)->win,
  			 0, 0,
  			 dmp->dm_width,
  			 dmp->dm_height,
  			 ~0, ZPixmap);
  
      if (!ximage_p) {
  	bu_log("png: could not get XImage\n", (char *)NULL);
  	return TCL_ERROR;
      }
  
      bytes_per_pixel = ximage_p->bytes_per_line / ximage_p->width;
  
      if (bytes_per_pixel == 4) {
  	unsigned long mask;
  	unsigned long tmask;
  
  	/* This section assumes 8 bits per channel */
  
  	mask = ximage_p->red_mask;
  	tmask = 1;
  	for (red_shift = 0; red_shift < 32; red_shift++) {
  	    if (tmask & mask)
  		break;
  	    tmask = tmask << 1;
  	}
  
  	mask = ximage_p->green_mask;
  	tmask = 1;
  	for (green_shift = 0; green_shift < 32; green_shift++) {
  	    if (tmask & mask)
  		break;
  	    tmask = tmask << 1;
  	}
  
  	mask = ximage_p->blue_mask;
  	tmask = 1;
  	for (blue_shift = 0; blue_shift < 32; blue_shift++) {
  	    if (tmask & mask)
  		break;
  	    tmask = tmask << 1;
  	}
  
  	/*
  	 * We need to reverse things if the image byte order
  	 * is different from the system's byte order.
  	 */
  	if (((bu_byteorder() == BU_BIG_ENDIAN) && (ximage_p->byte_order == LSBFirst)) ||
  	    ((bu_byteorder() == BU_LITTLE_ENDIAN) && (ximage_p->byte_order == MSBFirst))) {
  	    DM_REVERSE_COLOR_BYTE_ORDER(red_shift, ximage_p->red_mask);
  	    DM_REVERSE_COLOR_BYTE_ORDER(green_shift, ximage_p->green_mask);
  	    DM_REVERSE_COLOR_BYTE_ORDER(blue_shift, ximage_p->blue_mask);
  	}
  
      } else if (bytes_per_pixel == 2) {
  	unsigned long mask;
  	unsigned long tmask;
  	int bpb = 8;   /* bits per byte */
  
  	/*XXX
  	 * This section probably needs logic similar
  	 * to the previous section (i.e. bytes_per_pixel == 4).
  	 * That is we may need to reverse things depending on
  	 * the image byte order and the system's byte order.
  	 */
  
  	mask = ximage_p->red_mask;
  	tmask = 1;
  	for (red_shift = 0; red_shift < 16; red_shift++) {
  	    if (tmask & mask)
  		break;
  	    tmask = tmask << 1;
  	}
  	for (red_bits = red_shift; red_bits < 16; red_bits++) {
  	    if (!(tmask & mask))
  		break;
  	    tmask = tmask << 1;
  	}
  
  	red_bits = red_bits - red_shift;
  	if (red_shift == 0)
  	    red_shift = red_bits - bpb;
  	else
  	    red_shift = red_shift - (bpb - red_bits);
  
  	mask = ximage_p->green_mask;
  	tmask = 1;
  	for (green_shift = 0; green_shift < 16; green_shift++) {
  	    if (tmask & mask)
  		break;
  	    tmask = tmask << 1;
  	}
  	for (green_bits = green_shift; green_bits < 16; green_bits++) {
  	    if (!(tmask & mask))
  		break;
  	    tmask = tmask << 1;
  	}
  
  	green_bits = green_bits - green_shift;
  	green_shift = green_shift - (bpb - green_bits);
  
  	mask = ximage_p->blue_mask;
  	tmask = 1;
  	for (blue_shift = 0; blue_shift < 16; blue_shift++) {
  	    if (tmask & mask)
  		break;
  	    tmask = tmask << 1;
  	}
  	for (blue_bits = blue_shift; blue_bits < 16; blue_bits++) {
  	    if (!(tmask & mask))
  		break;
  	    tmask = tmask << 1;
  	}
  	blue_bits = blue_bits - blue_shift;
  
  	if (blue_shift == 0)
  	    blue_shift = blue_bits - bpb;
  	else
  	    blue_shift = blue_shift - (bpb - blue_bits);
      } else {
  	bu_log("png: %d bytes per pixel is not yet supported\n", bytes_per_pixel);
  	return TCL_ERROR;
      }
  
      rows = (unsigned char **)bu_calloc(ximage_p->height, sizeof(unsigned char *), "rows");
      idata = (unsigned char *)bu_calloc(ximage_p->height * ximage_p->width, bytes_per_pixel_output, "png data");
      *image = idata;
  
      /* for each scanline */
      bytes_per_line_output = ximage_p->width * bytes_per_pixel_output;
      for (i = ximage_p->height - 1, j = 0; 0 <= i; --i, ++j) {
  	/* irow points to the current scanline in ximage_p */
  	irow = (unsigned char *)(ximage_p->data + ((ximage_p->height-i-1)*ximage_p->bytes_per_line));
  
  	if (bytes_per_pixel == 4) {
  	    unsigned int pixel;
  
  	    /* rows[j] points to the current scanline in idata */
  	    rows[j] = (unsigned char *)(idata + ((ximage_p->height-i-1)*bytes_per_line_output));
  
  	    /* for each pixel in current scanline of ximage_p */
  	    for (k = 0; k < ximage_p->width; k++) {
  		pixel = *((unsigned int *)(irow + k*bytes_per_pixel));
  
  		dbyte0 = rows[j] + k*bytes_per_pixel_output;
  		dbyte1 = dbyte0 + 1;
  		dbyte2 = dbyte0 + 2;
  
  		*dbyte0 = (pixel & ximage_p->red_mask) >> red_shift;
  		*dbyte1 = (pixel & ximage_p->green_mask) >> green_shift;
  		*dbyte2 = (pixel & ximage_p->blue_mask) >> blue_shift;
  	    }
  	} else if (bytes_per_pixel == 2) {
  	    unsigned short spixel;
  	    unsigned long pixel;
  
  	    /* rows[j] points to the current scanline in idata */
  	    rows[j] = (unsigned char *)(idata + ((ximage_p->height-i-1)*bytes_per_line_output));
  
  	    /* for each pixel in current scanline of ximage_p */
  	    for (k = 0; k < ximage_p->width; k++) {
  		spixel = *((unsigned short *)(irow + k*bytes_per_pixel));
  		pixel = spixel;
  
  		dbyte0 = rows[j] + k*bytes_per_pixel_output;
  		dbyte1 = dbyte0 + 1;
  		dbyte2 = dbyte0 + 2;
  
  		if (0 <= red_shift)
  		    *dbyte0 = (pixel & ximage_p->red_mask) >> red_shift;
  		else
  		    *dbyte0 = (pixel & ximage_p->red_mask) << -red_shift;
  
  		*dbyte1 = (pixel & ximage_p->green_mask) >> green_shift;
  
  		if (0 <= blue_shift)
  		    *dbyte2 = (pixel & ximage_p->blue_mask) >> blue_shift;
  		else
  		    *dbyte2 = (pixel & ximage_p->blue_mask) << -blue_shift;
  	    }
  	} else {
  	    bu_free(rows, "rows");
  	    bu_free(idata, "image data");
  
  	    bu_log("png: not supported for this platform\n", (char *)NULL);
  	    return TCL_ERROR;
  	}
      }
  
      bu_free(rows, "rows");
      return TCL_OK;
  }
  
  int
  X_openFb(struct dm_internal *dmp)
  {
      struct fb_platform_specific *fb_ps;
      struct X24_fb_info *xfb_ps;
      struct dm_xvars *pubvars = (struct dm_xvars *)dmp->dm_vars.pub_vars;
      struct x_vars *privars = (struct x_vars *)dmp->dm_vars.priv_vars;
  
      fb_ps = fb_get_platform_specific(FB_X24_MAGIC);
      xfb_ps = (struct X24_fb_info *)fb_ps->data;
      xfb_ps->dpy = pubvars->dpy;
      xfb_ps->win = privars->pix;
      xfb_ps->cwinp = pubvars->win;
      xfb_ps->cmap = pubvars->cmap;
      xfb_ps->vip = pubvars->vip;
      xfb_ps->gc = privars->gc;
  
      dmp->fbp = fb_open_existing("X", dm_get_width(dmp), dm_get_height(dmp), fb_ps);
      fb_put_platform_specific(fb_ps);
      return 0;
  }
  
  struct bu_structparse X_vparse[] = {
      {"%g",  1, "bound",         DM_O(dm_bound),         dm_generic_hook, NULL, NULL},
      {"%d",  1, "useBound",      DM_O(dm_boundFlag),     dm_generic_hook, NULL, NULL},
      {"%d",  1, "zclip",         DM_O(dm_zclip),         dm_generic_hook, NULL, NULL},
      {"%d",  1, "debug",         DM_O(dm_debugLevel),    dm_generic_hook, NULL, NULL},
      {"",    0, (char *)0,       0,                      BU_STRUCTPARSE_FUNC_NULL, NULL, NULL}
  };
  
  /* Display Manager package interface */
  struct dm_internal dm_X = {
      X_close,
      X_drawBegin,
      X_drawEnd,
      X_normal,
      X_loadMatrix,
      null_loadPMatrix,
      X_drawString2D,
      X_drawLine2D,
      X_drawLine3D,
      X_drawLines3D,
      X_drawPoint2D,
      null_drawPoint3D,
      null_drawPoints3D,
      X_drawVList,
      X_drawVList,
      X_draw,
      X_setFGColor,
      X_setBGColor,
      X_setLineAttr,
      X_configureWin,
      X_setWinBounds,
      X_setLight,
      null_setTransparency,
      null_setDepthMask,
      X_setZBuffer,
      X_debug,
      X_logfile,
      null_beginDList,
      null_endDList,
      null_drawDList,
      null_freeDLists,
      null_genDLists,
      NULL,
      X_getDisplayImage, /* display to image function */
      X_reshape,
      null_makeCurrent,
      X_openFb,
      NULL,
      NULL,
      0,
      0,				/* no displaylist */
      0,                            /* no stereo */
      PLOTBOUND,			/* zoom-in limit */
      1,				/* bound flag */
      "X",
      "X Window System (X11)",
      DM_TYPE_X,
      1,
      0,
      0,
      0, /* bytes per pixel */
      0, /* bits per channel */
      0,
      0,
      1.0, /* aspect ratio */
      0,
      {0, 0},
      NULL,
      NULL,
      BU_VLS_INIT_ZERO,		/* bu_vls path name*/
      BU_VLS_INIT_ZERO,		/* bu_vls full name drawing window */
      BU_VLS_INIT_ZERO,		/* bu_vls short name drawing window */
      {0, 0, 0},			/* bg color */
      {0, 0, 0},			/* fg color */
      {GED_MIN, GED_MIN, GED_MIN},	/* clipmin */
      {GED_MAX, GED_MAX, GED_MAX},	/* clipmax */
      0,				/* no debugging */
      BU_VLS_INIT_ZERO,		/* bu_vls logfile */
      0,				/* no perspective */
      0,				/* no lighting */
      0,				/* no transparency */
      0,				/* depth buffer is not writable */
      0,				/* no zbuffer */
      0,				/* no zclipping */
      1,                          /* clear back buffer after drawing and swap */
      0,                          /* not overriding the auto font size */
      X_vparse,
      FB_NULL,
      0				/* Tcl interpreter */
  };
  
  /* Because class is actually used to access a struct
   * entry in this file, preserve our redefinition
   * of class for the benefit of avoiding C++ name
   * collisions until the end of this file */
  #undef class
  
  #endif /* DM_X */
  
  /*
   * Local Variables:
   * mode: C
   * tab-width: 8
   * indent-tabs-mode: t
   * c-file-style: "stroustrup"
   * End:
   * ex: shiftwidth=4 tabstop=8
   */
  