/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file: dxtools.c                                                          */
/*                                                                          */
/*                                                                          */
/* description: interface to IBM Data Explorer                              */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  authors:   Oliver Kriessl                                               */
/*             Institut fuer Mathematik                                     */
/*             Universitaet Augsburg                                        */
/*             Universitaetsstr. 14                                         */
/*             D-86159 Augsburg, Germany                                    */
/*                                                                          */
/*             Daniel Koester                                               */
/*             Institut fuer Mathematik                                     */
/*             Universitaet Augsburg                                        */
/*             Universitaetsstr. 14                                         */
/*             D-86159 Augsburg, Germany                                    */
/*                                                                          */
/*  http://www.mathematik.uni-freiburg.de/IAM/ALBERTA                       */
/*                                                                          */
/*  (c) by A. Schmidt and K.G. Siebert (1996-2005)                          */
/*  (c) by D. Koester and O.Kriessl    (2001-2005)                          */
/*                                                                          */
/* WARNING: The DX library is not thread-safe. We need to make              */
/* certain that the second thread started in open_dxtools_window() never    */
/* accesses DX functions concurrently with the main thread.                 */
/*                                                                          */
/*--------------------------------------------------------------------------*/

#ifdef HAVE_CONFIG_H
# include "config.h" /* this is really needed here */
#endif

#include "alberta_intern.h"
#include "alberta.h"

#include <unistd.h>
#include <errno.h>
#include <Xm/Xm.h> 
#include <Xm/Form.h> 
#include <Xm/DrawingA.h>
#include <pthread.h>

#define Screen dxScreen
#define String dxString
#define Object dxObject

#undef PACKAGE
#undef PACKAGE_BUGREPORT
#undef PACKAGE_NAME
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
#undef PACKAGE_VERSION
#undef VERSION

#include <dxconfig.h>
#include <dx/dx.h>
#undef String
#undef Screen
#undef Object 

/*--------------------------------------------------------------------------*/
/* Type definitions used for the dxtools.                                   */
/*--------------------------------------------------------------------------*/

typedef enum {DXTOOLS_NONE, DXTOOLS_MESH, DXTOOLS_FEVECTOR} DXOBJECT_TYPE;

#define DXTOOLS_MAX_WINDOWS 20 
#define DXTOOLS_KEY_HELP               'h'
#define DXTOOLS_KEY_QUIT               'q'
#define DXTOOLS_KEY_BLOCK              'b'
#define DXTOOLS_KEY_PROJECTION         'p'
#define DXTOOLS_KEY_HARDWARE           'f'
#define DXTOOLS_KEY_LEGEND             'l'
#define DXTOOLS_KEY_VIDEO              'v'
#define DXTOOLS_KEY_RESET              'r'
#define DXTOOLS_KEY_WRITE              'w'
#define DXTOOLS_KEY_AXES               'a'
#define DXTOOLS_KEY_BOX                'k'
#define DXTOOLS_KEY_CUTPLANE           'c'
#define DXTOOLS_KEY_CUTPLANE_PLUS      '+'
#define DXTOOLS_KEY_CUTPLANE_MINUS     '-'
#define DXTOOLS_KEY_CUTPLANE_PPLUS     '>'
#define DXTOOLS_KEY_CUTPLANE_MMINUS    '<'
#define DXTOOLS_KEY_CUTPLANE_X         'x'
#define DXTOOLS_KEY_CUTPLANE_Y         'y'
#define DXTOOLS_KEY_CUTPLANE_Z         'z'
#define DXTOOLS_KEY_MIDDLE_BUTTON_MODE ' '


typedef struct dxtools_context DXTOOLS_CONTEXT;

struct dxtools_window {
  /* Pointer to the context */
  DXTOOLS_CONTEXT *dxc;

  /* Internal number of this window */
  int number;

  /* data for window layout */
  char *title;
  int width, height;
  int offsetx, offsety;

  /* data of the drawing area */
  int draw_width, draw_height;

  Widget me;
  Window drawingarea;

  int use_hardware;
  int use_perspective;
  int use_legend;
  int use_inverse_video;
  int use_bounding_box;
  int use_axes;
  int use_cutplane;
  int middle_button_mode;

  /* what is being displayed (mesh, vector, leaf data, etc.) */
  int dim;
  DXOBJECT_TYPE type;

  /* current window object for display */
  dxObject displayobject;

  /* a colormap for displaying legends */
  Field colormap;

  /* a camera object to store the initial view */
  dxObject initial_camera;

  /* cutplane settings */
  int cutplane_direction;
  float cutplane_level;

  /* entries for communication from the main thread */
  /* window contents should also be dumped to a file. */
  int save_me;

  /* window contents need to be redrawn. */
  int redraw_me;

  /* camera view should be reset. */
  int reset_me;

  /* this window must be opened. */
  int open_me;

  /* should this window be closed? */
  int close_me;

};


struct dxtools_context {
  /* lock for thread_data */
  pthread_mutex_t tlock;
  /* Condition to signal readiness of windows to the main thread */
  pthread_cond_t windows_ready;
  /* Condition to block the main thread if desired */
  pthread_cond_t block_context;

  /* wait for signal that windows are ready if this flag is set. */
  int check_block_context;

  /* window list */
  DXTOOLS_WINDOW *win[DXTOOLS_MAX_WINDOWS];

  /* number of open windows */
  int n_open_windows;

  /* is the thread running? */
  int thread_running;

  XtAppContext    app;
  Widget          toplevel;
};




/*               code to override the DX                                    */
/*       standard UserInteractor. These functions are based on the DX file  */
/*              <prefix>/src/exec/dxmods/definter.c                         */
/*                                                                          */
/*              NOTE: Future versions of DX could break this code.          */

#define N_USER_INTERACTORS 1

static void *RPZInitMode(dxObject, int, int, int *);
static void  RPZEndMode(void *);
static void  RPZSetCamera(void *, float *, float *,
		       float *, int, float, float);
static int   RPZGetCamera(void *, float *, float *,
		       float *, int *, float *, float *);
static void  RPZSetRenderable(void *, dxObject);
static int   RPZGetRenderable(void *, dxObject *);
static void  RPZEventHandler(void *, DXEvent *);


static UserInteractor _userInteractionTable[N_USER_INTERACTORS];

int DXDefaultUserInteractors(int *n, void *t)
{
    _userInteractionTable[0].InitMode      = RPZInitMode;
    _userInteractionTable[0].EndMode       = RPZEndMode;
    _userInteractionTable[0].SetCamera     = RPZSetCamera;
    _userInteractionTable[0].GetCamera     = RPZGetCamera;
    _userInteractionTable[0].SetRenderable = RPZSetRenderable;
    _userInteractionTable[0].GetRenderable = RPZGetRenderable;
    _userInteractionTable[0].EventHandler  = RPZEventHandler;

    *n = N_USER_INTERACTORS;
    *(void **)t = (void *)_userInteractionTable;
    return 1;
}
     
/***** Control similar to GMV *****/

static void StartStroke(void *, DXMouseEvent *);
static void RotateEndStroke(void *, DXMouseEvent *);
static void PanEndStroke(void *, DXMouseEvent *);
static void TiltEndStroke(void *, DXMouseEvent *);
static void ZoomEndStroke(void *, DXMouseEvent *);


typedef struct RPZData
{
  dxObject args;	/* Interactor arguments		*/
  int w, h;		/* Width and height of window 	*/
  dxObject obj;		/* Current object		*/

  DXTOOLS_WINDOW *me; /* This window */

  float to[3];	/* Current camera parameters	*/
  float from[3];
  float up[3];
  int p;
  float fov;
  float width;
  
  float grad_r;
  float grad_p;
  float init_width;
  
  struct
  {
    int x, y;
  } mousePosition;
  int buttonStates[3];

} RPZData;



static void *
RPZInitMode(dxObject args, int w, int h, int *mask)
{
    RPZData *rpzdata = (RPZData *)DXAllocateZero(sizeof(struct RPZData));
    if (! rpzdata)
	return NULL;

    /* Retrieve the window pointer. */
    rpzdata->me = (DXTOOLS_WINDOW *)DXGetPrivateData((Private)args);
    
    /* 
     * Grab the window size 
     */
    rpzdata->w = w;
    rpzdata->h = h;

    if (w > h) 
	rpzdata->grad_r = rpzdata->grad_p = h / 2.0;
    else
	rpzdata->grad_r = rpzdata->grad_p = w / 2.0;

    rpzdata->buttonStates[0] = BUTTON_UP;
    rpzdata->buttonStates[1] = BUTTON_UP;
    rpzdata->buttonStates[2] = BUTTON_UP;

    *mask = DXEVENT_KEYPRESS | DXEVENT_LEFT | DXEVENT_MIDDLE | DXEVENT_RIGHT;
	
    return (void *)rpzdata;
}

static void
RPZEndMode(void *data)
{
    RPZData *rpzdata = (RPZData *)data;
    if (rpzdata)
    {
	if (rpzdata->args)
	    DXDelete(rpzdata->args);
	
	if (rpzdata->obj)
	    DXDelete(rpzdata->obj);
    }

    DXFree(data);
}

static void 
RPZSetCamera(void *data, float to[3], float from[3], float up[3],
		    int proj, float fov, float width)
{
    int i;
    RPZData *rpzdata = (RPZData *)data;

    /*
     * Grab the camera
     */
    for (i = 0; i < 3; i++)
	rpzdata->to[i]   = to[i],
	rpzdata->from[i] = from[i],
	rpzdata->up[i]   = up[i];

    rpzdata->p     = proj;
    rpzdata->fov   = fov;
    rpzdata->width = rpzdata->init_width = width;


    if (proj) {
      float dx = to[0] - from[0];
      float dy = to[1] - from[1];
      float dz = to[2] - from[2];
      float vd = sqrtf(dx*dx + dy*dy + dz*dz);

	rpzdata->grad_p = (fov * vd) / rpzdata->w;
    }
    else
	rpzdata->grad_p = width / rpzdata->w;

}

static int 
RPZGetCamera(void *data, float to[3], float from[3], float up[3],
		    int *proj, float *fov, float *width)
{
    int i;
    RPZData *rpzdata = (RPZData *)data;

    /*
     * Return the camera 
     */
    for (i = 0; i < 3; i++)
	to[i]   = rpzdata->to[i],
	from[i] = rpzdata->from[i],
	up[i]   = rpzdata->up[i];

    *proj  = rpzdata->p;
    *fov   = rpzdata->fov;
    *width = rpzdata->width;

    return 1;
}

static void
RPZSetRenderable(void *data, dxObject obj)
{
    RPZData *rpzdata = (RPZData *)data;
    DXReference(obj);
    if (rpzdata->obj)
	DXDelete(rpzdata->obj);
    rpzdata->obj = obj;
}
    

static int
RPZGetRenderable(void *data, dxObject *obj)
{
    RPZData *rpzdata = (RPZData *)data;
    if (rpzdata->obj)
    {
	*obj = rpzdata->obj;
	return 1;
    }
    else
    {
	*obj = NULL;
	return 0;
    }
}

static void
RPZEventHandler(void *data, DXEvent *event)
{
  RPZData *rpzdata = (RPZData *)data;
  DXTOOLS_WINDOW *win = rpzdata->me;
  int b = WHICH_BUTTON(event);

  if(!data)
    return;

  switch(event->any.event)
    {
    case DXEVENT_LEFT:
      
      switch (event->mouse.state)
	{
	case BUTTON_DOWN:
	  StartStroke(data, (DXMouseEvent *)event);
	  rpzdata->buttonStates[b] = BUTTON_DOWN;
	  break;
	  
	case BUTTON_MOTION:
	  if (rpzdata->buttonStates[b] == BUTTON_UP)
	    StartStroke(data, (DXMouseEvent *)event);
	  else
	    RotateEndStroke(data, (DXMouseEvent *)event);
	  rpzdata->buttonStates[b] = BUTTON_DOWN;
	  break;
	  
	case BUTTON_UP:
	  RotateEndStroke(data, (DXMouseEvent *)event);
	  rpzdata->buttonStates[b] = BUTTON_UP;
	  break;
	  
	default:
	  break;
	}
      
      break;
    case DXEVENT_MIDDLE:
      
      switch (event->mouse.state)
	{
	case BUTTON_DOWN:
	  StartStroke(data, (DXMouseEvent *)event);
	  rpzdata->buttonStates[b] = BUTTON_DOWN;
	  break;
	  
	case BUTTON_MOTION:
	  if (rpzdata->buttonStates[b] == BUTTON_UP)
	    StartStroke(data, (DXMouseEvent *)event);
	  else {
	    if(win->middle_button_mode == 0)
	      PanEndStroke(data, (DXMouseEvent *)event);
	    else
	      TiltEndStroke(data, (DXMouseEvent *)event);
	  }
	  rpzdata->buttonStates[b] = BUTTON_DOWN;
	  break;
	  
	case BUTTON_UP:
	  if(win->middle_button_mode == 0)
	    PanEndStroke(data, (DXMouseEvent *)event);
	  else
	    TiltEndStroke(data, (DXMouseEvent *)event);
	  rpzdata->buttonStates[b] = BUTTON_UP;
	  break;
	  
	default:
	  break;
	}
      
      break;
    case DXEVENT_RIGHT:
      
      switch (event->mouse.state)
	{
	case BUTTON_DOWN:
	  
	  StartStroke(data, (DXMouseEvent *)event);
	  rpzdata->buttonStates[b] = BUTTON_DOWN;
	  break;
	  
	case BUTTON_MOTION:
	  if (rpzdata->buttonStates[b] == BUTTON_UP)
	    StartStroke(data, (DXMouseEvent *)event);
	  else
	    ZoomEndStroke(data, (DXMouseEvent *)event);
	  rpzdata->buttonStates[b] = BUTTON_DOWN;
	  break;
	  
	case BUTTON_UP:
	  ZoomEndStroke(data, (DXMouseEvent *)event);
	  rpzdata->buttonStates[b] = BUTTON_UP;
	  break;
	  
	default:
	  break;
	}
      
      break;
    case DXEVENT_KEYPRESS:
      
      switch(event->keypress.key)
	{
	  int n_error;

	case DXTOOLS_KEY_HELP:
	  printf("****************************************\n");
	  printf("Online help for DXTOOLS:\n");
	  printf("****************************************\n");
	  printf("* Mouse controls:\n");
	  printf("Left button:   rotate\n");
	  printf("Middle button: pan or tilt, see below\n");
	  printf("Right button:  zoom\n\n");
	  printf("* Key controls:\n");
	  printf("'%c': this help blurb\n",
		 DXTOOLS_KEY_HELP);
	  printf("'%c': close window\n",
		 DXTOOLS_KEY_QUIT);
	  printf("'%c': block the simulation from providing new input\n",
		 DXTOOLS_KEY_BLOCK);
	  printf("'%c': toggle display of legend\n",
		 DXTOOLS_KEY_LEGEND);
	  printf("'%c': toggle orthographic/perspective projection\n",
		 DXTOOLS_KEY_PROJECTION);
	  printf("'%c': toggle hardware rendering\n",
		 DXTOOLS_KEY_HARDWARE);
	  printf("'%c': toggle background color black/white\n",
		 DXTOOLS_KEY_VIDEO);
	  printf("'%c': toggle display of bounding box\n",
		 DXTOOLS_KEY_BOX);
	  printf("'%c': toggle display of axes box\n",
		 DXTOOLS_KEY_AXES);
	  printf("'%c': reset camera view\n",
		 DXTOOLS_KEY_RESET);
	  printf("'%c': write image to disk\n",
		 DXTOOLS_KEY_WRITE);
	  printf("'%c': toggle middle mouse button mode between pan/tilt\n",
		 DXTOOLS_KEY_MIDDLE_BUTTON_MODE);

	  break;
	case DXTOOLS_KEY_QUIT:
	  win->close_me = true;
	  break;
	case DXTOOLS_KEY_WRITE:
	  win->save_me = true;
	  break;
	case DXTOOLS_KEY_BLOCK:
	  if(win->dxc->check_block_context) {
	    win->dxc->check_block_context = false;
	    printf("*** DXTOOLS: input UNBLOCKED\n");
	    n_error = pthread_cond_broadcast(&win->dxc->block_context);
	    if(n_error)
	      fprintf(stderr, "Signalling of condition failed!\n");
	  }
	  else {
	    win->dxc->check_block_context = true;
	    printf("*** DXTOOLS: input BLOCKED\n");
	  }
	  break;
	case DXTOOLS_KEY_VIDEO:
	  if(win->use_inverse_video) {
	    win->use_inverse_video = false;
	    printf("*** %s: background color is BLACK\n", win->title);	    
	  }
	  else {
	    win->use_inverse_video = true;
	    printf("*** %s: background color is WHITE\n", win->title);	    
	  }

	  win->redraw_me = true;
	  break;
	case DXTOOLS_KEY_RESET:
	  win->reset_me = true;
	  break;
	case DXTOOLS_KEY_LEGEND:
	  if (win->use_legend) {
	    win->use_legend = false;
	    printf("*** %s: legend display is OFF\n", win->title);

	    win->redraw_me = true;
	  }
	  else {
	    if(win->type != DXTOOLS_FEVECTOR) {
	      printf("*** %s: WARNING: No legend available for mesh display.\n", win->title);
	    }
	    else {
	      win->use_legend = true;
	      printf("*** %s: legend display is ON\n", win->title);	    

	      win->redraw_me = true;
	    }
	  }

	  break;
	case DXTOOLS_KEY_PROJECTION:
	  if (win->use_perspective) {
	    win->use_perspective = false;
	    printf("*** %s: orthographic projection ON\n", win->title);

	    win->reset_me = true;
	  }
	  else {
	    win->use_perspective = true;
	    printf("*** %s: perspective projection ON\n", win->title);
	    
	    win->reset_me = true;
	  }

	  break;
	case DXTOOLS_KEY_HARDWARE:
	  if (win->use_hardware) {
	    win->use_hardware = false;
	    printf("*** %s: hardware rendering OFF\n", win->title);
	  }
	  else {
	    win->use_hardware = true;
	    printf("*** %s: hardware rendering ON\n", win->title);
	  }
	  win->redraw_me = true;

	  break;
	case DXTOOLS_KEY_BOX:
	  if(win->use_bounding_box) {
	    win->use_bounding_box = false;
	    printf("*** %s: bounding box OFF\n", win->title);	    
	  }
	  else {
	    win->use_bounding_box = true;
	    printf("*** %s: bounding box ON\n", win->title);	    
	  }

	  win->redraw_me = true;
	  break;
	case DXTOOLS_KEY_AXES:
	  if(win->use_axes) {
	    win->use_axes = false;
	    printf("*** %s: axis display OFF\n", win->title);	    
	  }
	  else {
	    win->use_axes = true;
	    printf("*** %s: axis display ON\n", win->title);	    
	  }

	  win->redraw_me = true;
	  break;
#if DIM_OF_WORLD == 3
	case DXTOOLS_KEY_CUTPLANE:
	  if(win->use_cutplane) {
	    win->use_cutplane = false;
	    printf("*** %s: cutplane display OFF\n", win->title);	    
	    win->redraw_me = true;
	  }
	  else {
 	    if(win->type != DXTOOLS_FEVECTOR) {
	      printf("*** %s: WARNING: cut plane only works for FE data. Operation cancelled.\n", win->title);
	    }
	    else {
	      win->use_cutplane = true;
	      printf("*** %s: cutplane display ON\n", win->title);	    
	      win->redraw_me = true;
	    }
	  }

	  break;
	case DXTOOLS_KEY_CUTPLANE_X:
	  printf("*** %s: cutplane normal is (1,0,0)\n", win->title);
	  win->cutplane_direction = 0;
	  win->redraw_me = true;
	  break;
	case DXTOOLS_KEY_CUTPLANE_Y:
	  printf("*** %s: cutplane normal is (0,1,0)\n", win->title);
	  win->cutplane_direction = 1;
	  win->redraw_me = true;
	  break;
	case DXTOOLS_KEY_CUTPLANE_Z:
	  printf("*** %s: cutplane normal is (0,0,1)\n", win->title);
	  win->cutplane_direction = 2;
	  win->redraw_me = true;
	  break;
	case DXTOOLS_KEY_CUTPLANE_PLUS:
	  win->cutplane_level += 0.1f;
	  win->cutplane_level = MIN(win->cutplane_level, 1.0f);

	  printf("*** %s: cutplane level at %+.2f\n", win->title, 
		 win->cutplane_level);
	  win->redraw_me = true;
	  break;
	case DXTOOLS_KEY_CUTPLANE_MINUS:
	  win->cutplane_level -= 0.1f;
	  win->cutplane_level = MAX(win->cutplane_level, -1.0f);

	  printf("*** %s: cutplane level at %+.2f\n", win->title, 
		 win->cutplane_level);
	  win->redraw_me = true;
	  break;
	case DXTOOLS_KEY_CUTPLANE_PPLUS:
	  win->cutplane_level += 0.01f;
	  win->cutplane_level = MIN(win->cutplane_level, 1.0f);

	  printf("*** %s: cutplane level at %+.2f\n", win->title, 
		 win->cutplane_level);
	  win->redraw_me = true;
	  break;
	case DXTOOLS_KEY_CUTPLANE_MMINUS:
	  win->cutplane_level -= 0.01f;
	  win->cutplane_level = MAX(win->cutplane_level, -1.0f);

	  printf("*** %s: cutplane level at %+.2f\n", win->title, 
		 win->cutplane_level);
	  win->redraw_me = true;
	  break;
#endif
	case DXTOOLS_KEY_MIDDLE_BUTTON_MODE:
	  if(win->middle_button_mode == 1) {
	    win->middle_button_mode = 0;
	    printf("*** %s: middle mouse button does PAN\n", win->title);    
	  }
	  else {
	    win->middle_button_mode = 1;
	    printf("*** %s: middle mouse button does TILT\n", win->title);    
	  }

	  break;
	default:
	  break;
	}
      

      break;
    default:
      break;
    }
}


static void StartStroke(void *data, DXMouseEvent *event)
{
    RPZData *rdata = (RPZData *)data;

    rdata->mousePosition.x = event->x;
    rdata->mousePosition.y = event->y;

    return;
}

static void
RotateEndStroke(void *data, DXMouseEvent *event)
{
    RPZData *rdata = (RPZData *)data;
    float ov[3], nv[3], rot[4][4];
    float rt[3], up[3];
    float stroke[3], axis[3], d;
    float a, dx, dy;
    float t, s, c;

    dx = event->x - rdata->mousePosition.x;
    dy = event->y - rdata->mousePosition.y;

    if (dx || dy)
      {
	
	ov[0] = rdata->from[0] - rdata->to[0];
	ov[1] = rdata->from[1] - rdata->to[1];
	ov[2] = rdata->from[2] - rdata->to[2];
	
	rt[0] = ov[1]*rdata->up[2] - ov[2]*rdata->up[1];
	rt[1] = ov[2]*rdata->up[0] - ov[0]*rdata->up[2];
	rt[2] = ov[0]*rdata->up[1] - ov[1]*rdata->up[0];
	
	d = sqrtf(rt[0]*rt[0] + rt[1]*rt[1] + rt[2]*rt[2]);
	rt[0] /= d;
	rt[1] /= d;
	rt[2] /= d;
	
	up[0] = rt[1]*ov[2] - rt[2]*ov[1];
	up[1] = rt[2]*ov[0] - rt[0]*ov[2];
	up[2] = rt[0]*ov[1] - rt[1]*ov[0];
	d = sqrtf(up[0]*up[0] + up[1]*up[1] + up[2]*up[2]);
	rdata->up[0] = up[0] /= d;
	rdata->up[1] = up[1] /= d;
	rdata->up[2] = up[2] /= d;
	
	dx /= rdata->grad_r;
	dy /= rdata->grad_r;
	
	stroke[0] = dx*rt[0] + dy*up[0];
	stroke[1] = dx*rt[1] + dy*up[1];
	stroke[2] = dx*rt[2] + dy*up[2];
	
	axis[0] = ov[1]*stroke[2] - ov[2]*stroke[1];
	axis[1] = ov[2]*stroke[0] - ov[0]*stroke[2];
	axis[2] = ov[0]*stroke[1] - ov[1]*stroke[0];
	d = sqrtf(axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2]);
	axis[0] /= d;
	axis[1] /= d;
	axis[2] /= d;
	
	a = sqrt(dx*dx + dy*dy);
	s = sinf(a);
	c = cosf(a);
	t = 1.0 - c;
	
	ROTXYZ(rot, axis[0], axis[1], axis[2], s, c, t);
	
	nv[0] = rot[0][0]*ov[0] +
	  rot[1][0]*ov[1] +
	  rot[2][0]*ov[2];
	
	nv[1] = rot[0][1]*ov[0] +
	  rot[1][1]*ov[1] +
	  rot[2][1]*ov[2];
	
	nv[2] = rot[0][2]*ov[0] +
	  rot[1][2]*ov[1] +
	  rot[2][2]*ov[2];
	
	rdata->from[0] = rdata->to[0] + nv[0];
	rdata->from[1] = rdata->to[1] + nv[1];
	rdata->from[2] = rdata->to[2] + nv[2];
      }


    rdata->mousePosition.x = event->x;
    rdata->mousePosition.y = event->y;


    return;

}


static void PanEndStroke(void *data, DXMouseEvent *event)
{
    float a, u[3], r[3], v[3];
    float dx, dy;

    RPZData *pdata = (RPZData *)data;


    if (event->x != pdata->mousePosition.x ||
	event->y != pdata->mousePosition.y)
    {

	dx = event->x - pdata->mousePosition.x;
	dy = event->y - pdata->mousePosition.y;

	a = sqrtf(pdata->up[0]*pdata->up[0] +
		 pdata->up[1]*pdata->up[1] +
		 pdata->up[2]*pdata->up[2]);
	    
	u[0] = pdata->up[0]/a;
	u[1] = pdata->up[1]/a;
	u[2] = pdata->up[2]/a;

	v[0] = pdata->from[0] - pdata->to[0];
	v[1] = pdata->from[1] - pdata->to[1];
	v[2] = pdata->from[2] - pdata->to[2];

	r[0] = v[2]*pdata->up[1] - v[1]*pdata->up[2];
	r[1] = v[0]*pdata->up[2] - v[2]*pdata->up[0];
	r[2] = v[1]*pdata->up[0] - v[0]*pdata->up[1];

	a = sqrtf(r[0]*r[0] + r[1]*r[1]+ r[2]*r[2]);
	r[0] /= a;
	r[1] /= a;
	r[2] /= a;

	v[0] = (-dx*r[0] + dy*u[0]) * pdata->grad_p;
	v[1] = (-dx*r[1] + dy*u[1]) * pdata->grad_p;
	v[2] = (-dx*r[2] + dy*u[2]) * pdata->grad_p;

	pdata->to[0] += v[0];
	pdata->to[1] += v[1];
	pdata->to[2] += v[2];

	pdata->from[0] += v[0];
	pdata->from[1] += v[1];
	pdata->from[2] += v[2];
    }

    pdata->mousePosition.x = event->x;
    pdata->mousePosition.y = event->y;

    return;

}

static void TiltEndStroke(void *data, DXMouseEvent *event)
{
  RPZData *tdata = (RPZData *)data;
  float v[3], rot[4][4];
  float n[3], d;
  float a, a0, a1;
  float x0, y0;
  float x1, y1;
  float t, s, c;
  int cx = tdata->w / 2;
  int cy = tdata->h / 2;


  if (event->x != tdata->mousePosition.x ||
      event->y != tdata->mousePosition.y) {
    x0 = event->x - cx;
    y0 = event->y - cy;
    
    if (x0 == 0 && y0 == 0)
      x0 = 1.0f;

    d = sqrtf(x0*x0 + y0*y0);
    x0 /= d;
    y0 /= d;
    if (y0 < 0)
      a0 = acosf(x0);
    else 
      a0 = (2*3.1415926) - acos(x0);
    
    x1 = tdata->mousePosition.x - cx;
    y1 = tdata->mousePosition.y - cy;

    if (x1 == 0 && y1 == 0)
      x1 = 1.0f;

    d = sqrtf(x1*x1 + y1*y1);
    x1 /= d;
    y1 /= d;
    if (y1 < 0)
      a1 = acosf(x1);
    else 
      a1 = (2*3.1415926) - acos(x1);

    v[0] = tdata->from[0] - tdata->to[0];
    v[1] = tdata->from[1] - tdata->to[1];
    v[2] = tdata->from[2] - tdata->to[2];
    d = sqrtf(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
    v[0] /= d;
    v[1] /= d;
    v[2] /= d;

    a = a1 - a0;

    s = sinf(a);
    c = cosf(a);
    t = 1.0f - c;

    ROTXYZ(rot, v[0], v[1], v[2], s, c, t);

    n[0] = rot[0][0]*tdata->up[0] +
      rot[1][0]*tdata->up[1] +
      rot[2][0]*tdata->up[2];

    n[1] = rot[0][1]*tdata->up[0] +
      rot[1][1]*tdata->up[1] +
      rot[2][1]*tdata->up[2];
    
    n[2] = rot[0][2]*tdata->up[0] +
      rot[1][2]*tdata->up[1] +
      rot[2][2]*tdata->up[2];
    
    tdata->up[0] = n[0];
    tdata->up[1] = n[1];
    tdata->up[2] = n[2];
  }
  
  tdata->mousePosition.x = event->x;
  tdata->mousePosition.y = event->y;

  return;
}


static void ZoomEndStroke(void *data, DXMouseEvent *event)
{
    float dy;
    RPZData *zdata = (RPZData *)data;



    dy = (event->y - zdata->mousePosition.y) /
			((float) zdata->h);

    if (event->x != zdata->mousePosition.x ||
	event->y != zdata->mousePosition.y)
    {
	if (zdata->p)
	{
	    zdata->from[0] -= 0.9*dy*(zdata->to[0] - zdata->from[0]);
	    zdata->from[1] -= 0.9*dy*(zdata->to[1] - zdata->from[1]);
	    zdata->from[2] -= 0.9*dy*(zdata->to[2] - zdata->from[2]);
	}
	else
	{
	    zdata->width += dy * zdata->init_width;
	}
    }

    zdata->mousePosition.x = event->x;
    zdata->mousePosition.y = event->y;

    return;

}







static void display_error(int n_error)
{
  FUNCNAME("display_error");

  switch(n_error) {
  case EFAULT:
    WARNING("Bad pointer. (EFAULT)\n");
    break;
  case EAGAIN: 
    WARNING("System lacks resources. (EAGAIN)\n");
    break;
  case ENOMEM:
    WARNING("Insufficient memory. (ENOMEM)\n");
    break;
  case EINVAL:
    WARNING("Invalid attributes? (EINVAL)\n");
    break;
  case EPERM:
    WARNING("Caller lacks permission. (EPERM)\n");
    break;
  case EBUSY:
    WARNING("Object already initialized or busy. (EBUSY)\n");
    break;
  case EDEADLK:
    WARNING("Current thread already owns mutex. (EDEADLK)\n");
    break;
  case ETIMEDOUT:
    WARNING("Timed out while waiting for signal. (ETIMEDOUT)\n");
    break;
  default:
    WARNING("Unknown error %d.\n", n_error);
  }

  return;
}

static void display_dx_error()
{
  FUNCNAME("display_dx_error");

  ERROR("DX error code: %d\n", DXGetError());
  ERROR("DX error message: %s\n", DXGetErrorMessage());

#if ALBERTA_DEBUG
  /* Paranoid error behavior. */
  ERROR_EXIT("Exiting...\n");
#endif  

  return;
}

/* return true on error. */

static int DX_update(DXTOOLS_WINDOW *win)
{

  ModuleInput  min[10];
  ModuleOutput mout[10];
  dxObject tmpobj, tmpcam, tmpwhere;
  dxObject events, size, displayobject, where, camera;
  dxObject colorbar = NULL, boundingbox = NULL;
  Private  winobj;
  Array windowsize;
  RGBColor color = { 0, };
  int *windowsize_ptr;
  const int supervise_mode = 0;

  if(!win->displayobject)
    return false;

  if(!(windowsize = DXNewArray(TYPE_INT,CATEGORY_REAL,1, 2))) {
    display_dx_error();
    return true;
  }
  if(!DXAddArrayData(windowsize,0,1,NULL)) {
    display_dx_error();
    return true;
  }

  windowsize_ptr = (int *)DXGetArrayData(windowsize);
  windowsize_ptr[0]=win->draw_width;
  windowsize_ptr[1]=win->draw_height;

  /* call SuperviseWindow and check for events */

  DXModSetObjectInput(&min[0], "size", (dxObject)windowsize);
  DXModSetIntegerInput(&min[1], "parent", win->drawingarea);
  DXModSetIntegerInput(&min[2], "sizeFlag",1);
  DXModSetStringInput(&min[3], "name",win->title);
  DXModSetObjectOutput(&mout[0], "where", &where);
  DXModSetObjectOutput(&mout[1], "size", &size);
  DXModSetObjectOutput(&mout[2], "events", &events);
  if(!DXCallModule("SuperviseWindow",4,min,3,mout)) {
    display_dx_error();
    return true;
  }

  /* No events? Camera view not reset? Then leave routine. */
  if(!events && !win->reset_me && !win->redraw_me && !win->save_me) {
    if(!DXDelete(where))
      display_dx_error();
    if(!DXDelete(size))
      display_dx_error();
    if(!DXDelete(events))
      display_dx_error();
     
    return false; 
  }

  /* No initial camera set? Do so now. */
  if (win->reset_me || !win->initial_camera) {
    if(win->initial_camera && !DXDelete(win->initial_camera))
      display_dx_error();


    if(!DXReference(win->displayobject)) {
      display_dx_error();
      return true;
    }
    DXModSetObjectInput(&min[0], "object", win->displayobject);
    DXModSetIntegerInput(&min[1], "perspective", win->use_perspective);
    if(win->use_inverse_video)
      DXModSetStringInput(&min[2], "background", "white");
    else
      DXModSetStringInput(&min[2], "background", "black");
    DXModSetObjectOutput(&mout[0], "camera", &win->initial_camera);
    if(!DXCallModule("AutoCamera",3,min,1,mout)) {
      display_dx_error();
      return true;
    }
  }
  
  if(!(winobj = DXNewPrivate((Pointer)win, NULL))) {
    display_dx_error();
    return true;
  }

  if(!DXReference(win->displayobject)) {
    display_dx_error();
    return true;
  }

  if(!DXReference(win->initial_camera)) {
    display_dx_error();
    return true;
  }
      
  DXModSetObjectInput(&min[0], "where",where);
  DXModSetObjectInput(&min[1], "defaultCamera", win->initial_camera);
  DXModSetIntegerInput(&min[2], "resetCamera", win->reset_me ? 1: 0);
  win->reset_me = false;

  DXModSetObjectInput(&min[3], "object",win->displayobject);
  DXModSetIntegerInput(&min[4], "resetObject",1);
  DXModSetObjectInput(&min[5], "size",size);
  DXModSetObjectInput(&min[6], "events",events);
  DXModSetIntegerInput(&min[7], "mode",supervise_mode);
  DXModSetObjectInput(&min[8], "args",(dxObject)winobj);
  DXModSetObjectOutput(&mout[0], "object",&tmpobj);
  DXModSetObjectOutput(&mout[1], "camera",&tmpcam);
  DXModSetObjectOutput(&mout[2], "where",&tmpwhere);
  if(!DXCallModule("SuperviseState",9,min,3,mout)) {
    display_dx_error();
    return true;
  }

  where         = tmpwhere;
  displayobject = tmpobj;
  camera        = tmpcam;
 

  /* Don't try to to 3D perspective volume rendering - DX can't do it. */
#if DIM_OF_WORLD == 3
  if((win->type == DXTOOLS_FEVECTOR)
     && !win->use_hardware
     && win->use_perspective
     && !win->use_cutplane 
     && win->dim == 3) {
    printf("*** %s: WARNING: DX currently does not support perspective volume rendering. Perspective projection OFF.\n", win->title);
    win->use_perspective = false;
    win->reset_me = true;

    if(!DXDelete(where))
      display_dx_error();
     
    return false;
  }
#endif


  /* Adjust settings of camera, if necessary. */
  if(win->use_inverse_video) {
    color.r = 1.0f; color.g = 1.0f; color.b = 1.0f;
  }
  else {
    color.r = 0.0f; color.g = 0.0f; color.b = 0.0;
  }
  camera = (dxObject)DXSetBackgroundColor((Camera)camera, color);


  /* If we have a colormap, then display of a legend is possible. */

  if(win->use_legend && win->colormap) {
    if(!DXReference((dxObject)win->colormap)) {
      display_dx_error();
      return true;
    }
    
    DXModSetObjectInput(&min[0], "colormap", (dxObject)win->colormap);
    if(win->use_inverse_video) {
      DXModSetStringInput(&min[1], "colors", "black");
      DXModSetStringInput(&min[2], "annotation", "labels");
    }
    else {
      DXModSetStringInput(&min[1], "colors", "white");
      DXModSetStringInput(&min[2], "annotation", "labels");
    }
    DXModSetObjectOutput(&mout[0], "colorbar", &colorbar);

    if(!DXCallModule("ColorBar",3,min,1,mout)) {
      display_dx_error();
      return true;
    }
  }


  /* Show a bounding box if desired. */
  /* for display of a cutplane with axes box, we also compute the */
  /* bounding box to use it as corners input of AutoAxes. */

  if(win->use_bounding_box || (win->use_axes && win->use_cutplane)) {
    if(!DXReference(displayobject)) {
      display_dx_error();
      return true;
    }

    DXModSetObjectInput(&min[0], "input", displayobject);
    DXModSetObjectOutput(&mout[0], "box", &boundingbox);

    if(!DXCallModule("ShowBox",1,min,1,mout)) {
      display_dx_error();
      return true;
    }
  }

  /* build a cutplane, if desired. Do this after bounding box */
  /* so that these are built around the original data. */
  if(win->use_cutplane) {
    float *array_normal_data;
    Array  array_normal;
    float *array_center_data;
    Array  array_center;
    Point  bbox[8] = { { 0,}, };
    float  cx[3], dx[3];
    int i;

    if(!(array_normal = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 1, 3))) {
      display_dx_error();
      return true;
    }

    if(!DXAddArrayData(array_normal, 0, 1, NULL)) {
      display_dx_error();
      return true;
    }

    if(!(array_normal_data = (float *)DXGetArrayData(array_normal))) {
      display_dx_error();
      return true;
    }


    displayobject = DXBoundingBox(displayobject, bbox);

    cx[0] = (bbox[0].x + bbox[7].x)/2.0f;
    cx[1] = (bbox[0].y + bbox[7].y)/2.0f;
    cx[2] = (bbox[0].z + bbox[7].z)/2.0f;

    dx[0] = (bbox[7].x - bbox[0].x)/2.0f;
    dx[1] = (bbox[7].y - bbox[0].y)/2.0f;
    dx[2] = (bbox[7].z - bbox[0].z)/2.0f;

    if(!(array_center = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 1, 3))) {
      display_dx_error();
      return true;
    }

    if(!DXAddArrayData(array_center, 0, 1, NULL)) {
      display_dx_error();
      return true;
    }

    if(!(array_center_data = (float *)DXGetArrayData(array_center))) {
      display_dx_error();
      return true;
    }

    for(i = 0; i < 3; i++)
      array_center_data[i] = cx[i];
    i = win->cutplane_direction;
    array_center_data[i] += win->cutplane_level * dx[i];

    array_normal_data[i] = 1.0f;


    DXModSetObjectInput(&min[0], "data", displayobject);
    DXModSetObjectInput(&min[1], "normal", (dxObject)array_normal);
    DXModSetObjectInput(&min[2], "point", (dxObject)array_center);
    DXModSetObjectOutput(&mout[0], "plane", &tmpobj);

    if(!DXCallModule("MapToPlane", 3, min, 1, mout)) {
      display_dx_error();
      return true;
    }
    displayobject = tmpobj;

    /* remove the normals component as advised in the DX documentation */
    /* since the shading is not that useful. */

    DXModSetObjectInput(&min[0], "input", displayobject);
    DXModSetStringInput(&min[1], "name", "normals");
    DXModSetObjectOutput(&mout[0], "output", &tmpobj);

    if(!DXCallModule("Remove",2,min,1,mout)) {
      display_dx_error();
      return true;
    }
    displayobject = tmpobj;
  }

  /* Show an axes box if desired. */
  if(win->use_axes) {
    int n_in = 0;

    if(!DXReference(displayobject)) {
      display_dx_error();
      return true;
    }
    if(!DXReference(camera)) {
      display_dx_error();
      return true;
    }

    DXModSetObjectInput(&min[n_in++], "input", displayobject);
    DXModSetObjectInput(&min[n_in++], "camera", camera);
    if(win->use_cutplane) {
      /* Reference the bounding box since it might also be displayed. Sigh. */
      if(win->use_bounding_box) {
	if(!DXReference(boundingbox)) {
	  display_dx_error();
	  return true;
	}
      }
      DXModSetObjectInput(&min[n_in++], "corners", boundingbox);
    }
    DXModSetObjectOutput(&mout[0], "axes", &tmpobj);

    if(!DXCallModule("AutoAxes",n_in, min, 1, mout)) {
      display_dx_error();
      return true;
    }
    displayobject = tmpobj;
  }


  /* Collect the colorbar, bounding box, and any other objects. */
  
  if(win->use_bounding_box || colorbar) {
    int n_in = 0;

    DXModSetObjectInput(&min[n_in++],NULL, displayobject);
    if(colorbar) 
      DXModSetObjectInput(&min[n_in++],NULL, colorbar);
    if(win->use_bounding_box) 
      DXModSetObjectInput(&min[n_in++],NULL, boundingbox);

    DXModSetObjectOutput(&mout[0], "group", &tmpobj);
    
    if(!DXCallModule("Collect",n_in, min, 1, mout)) {
      display_dx_error();
      return true;
    }
    
    displayobject = tmpobj;
  }

  /* Now output the display object with Display. */
  if (win->use_hardware) {
    DXModSetObjectInput(&min[0], "input", displayobject);
    DXModSetStringInput(&min[1], "attribute", "rendering mode");
    DXModSetStringInput(&min[2], "value", "hardware");
    DXModSetObjectOutput(&mout[0], "output",&tmpobj);
    if(!DXCallModule("Options",3,min,1,mout)) {
      display_dx_error();
      return true;
    }
      
    displayobject = tmpobj;
  }



  if (win->save_me) {
    dxObject image;
    static int counter = 0;
    char filename[1024] = { '\0', };
    char format[1024] = "eps color";


    GET_PARAMETER(0, "dxtools saving file format", "%s", format);

    DXModSetObjectInput(&min[0], "object", displayobject);
    DXModSetObjectInput(&min[1], "camera", camera);
    DXModSetObjectOutput(&mout[0], "image", &image);
    if(!DXCallModule("Render",2,min,1,mout)) {
      display_dx_error();
      return true;
    }

    if(!DXReference(image)) {
      display_dx_error();
      return true;
    }

    snprintf(filename, 1024, "%s.%.6d", win->title, counter);

    DXModSetObjectInput(&min[0], "image", image);
    DXModSetStringInput(&min[1], "format", format);
    DXModSetStringInput(&min[2], "name", filename);
    if(!DXCallModule("WriteImage",3,min,0,mout)) {
      display_dx_error();
      return true;
    }
    printf("*** Image saved as '%s'.\n", filename);

    DXModSetObjectInput(&min[0], "object", image);
    DXModSetObjectInput(&min[1], "where", where);
    if(!DXCallModule("Display",2,min,0,mout)) {
      display_dx_error();
      return true;
    }

    win->save_me = false;
    counter++;
  }
  else {
    DXModSetObjectInput(&min[0], "object", displayobject);
    DXModSetObjectInput(&min[1], "camera", camera);
    DXModSetObjectInput(&min[2], "where", where);
    if(!DXCallModule("Display",3,min,0,mout)) {
      display_dx_error();
      return true;
    }
  }

  win->redraw_me = false;

  return false;
}



void destroyCB(Widget w, XtPointer xp1, XtPointer xp2)
{
  DXTOOLS_WINDOW *win = (DXTOOLS_WINDOW *)xp1;

  win->close_me = true;

  return;
}

/*
 * the following is the resize callback for the drawing area. If the
 * drawing area is resized, we need to send the new size to SuperviseWindow
 */

void drawing_resizeCB(Widget w, XtPointer xp1, XtPointer xp2)
{
  DXTOOLS_WINDOW *win = (DXTOOLS_WINDOW *)xp1;
  Dimension       width, height;

  XtVaGetValues(w, 
		XtNwidth, &width, 
		XtNheight, &height,
		NULL);
  
  win->draw_width = (int)width;
  win->draw_height = (int)height;

  win->redraw_me = true;

  return;
}


static void create_window(DXTOOLS_WINDOW *win)
{
  Widget          form, drawingarea;


  win->me = XtVaCreatePopupShell("DXTools",
	         topLevelShellWidgetClass, win->dxc->toplevel,
				 XmNtitle, win->title,
		 XmNwidth,    win->width,
		 XmNheight,   win->height,
				    XmNx, win->offsetx,
				    XmNy, win->offsety,
                 NULL);

  form = XtVaCreateManagedWidget("form",
				 xmFormWidgetClass, win->me,
				 XmNfractionBase, 5,
				 NULL);

  drawingarea = XtVaCreateManagedWidget("drawingarea",
                 xmDrawingAreaWidgetClass,
                 form,
                 XmNtopAttachment,   XmATTACH_FORM,
                 XmNtopOffset,  5,
                 XmNleftAttachment,  XmATTACH_FORM,
                 XmNleftOffset,  5,
                 XmNrightAttachment,  XmATTACH_FORM,
                 XmNrightOffset,  5,
                 XmNbottomAttachment,  XmATTACH_FORM,
                 XmNbottomOffset,  5,
		 XmNwidth,    win->draw_width,
		 XmNheight,   win->draw_height,
                 NULL); 



  /* add a deletion callback for the drawing area */
  XtAddCallback(drawingarea, XtNdestroyCallback,
		(XtCallbackProc)destroyCB,
		(XtPointer)win);

  /* add a resize callback for the drawing area */
  XtAddCallback(drawingarea, XmNresizeCallback,
		(XtCallbackProc)drawing_resizeCB,
		(XtPointer)win);

  /* Draw the window! */
  XtPopup(win->me, XtGrabNonexclusive);

  /* get the window id of the drawing area */
  /* we'll need this to pass to SuperviseWindow */
  win->drawingarea = XtWindow(drawingarea); 
  
  win->open_me = false;
  
  return;
}


static void delete_window(DXTOOLS_WINDOW *win)
{
  int n_error, i;
  DXTOOLS_CONTEXT *dxc = win->dxc;

  XtDestroyWidget(win->me);

  win->drawingarea = 0;

  if(win->displayobject) {
    if(!DXDelete(win->displayobject))
      display_dx_error();
    win->displayobject = NULL;
  }

  if(win->colormap) {
    if(!DXDelete((dxObject)win->colormap))
      display_dx_error();
    win->colormap = NULL;
  }

/* If this was the last window, send unblocking signal to the main thread.*/
  if(dxc->check_block_context) {
    for(i = 0; i < DXTOOLS_MAX_WINDOWS; i++)
      if(dxc->win[i] && dxc->win[i]->displayobject)
	break;
    
    if(i == DXTOOLS_MAX_WINDOWS) {
      printf("*** No more blocking content, sending unblocking signal!\n");
      dxc->check_block_context = false;
      n_error = pthread_cond_broadcast(&win->dxc->block_context);
      if(n_error)
	fprintf(stderr, "Signalling of condition failed!\n");
    }
  }

  win->close_me = false;

  return;
}


Boolean XCheckRIH(void *data)
{
  FUNCNAME("XCheckRIH");
  DXTOOLS_CONTEXT *dxc = (DXTOOLS_CONTEXT *)data;
  int    i, n_error;
  static struct timespec time_req = {0, 10000};

  if(!dxc) { /* Really bad error */
    ERROR("Lost DXTOOLS_CONTEXT object! Giving up.\n");
    return True;
  }

  /* Lock the context manager. */
  n_error = pthread_mutex_lock(&dxc->tlock);
  if(n_error){
    ERROR("Locking of thread failed!\n");
    display_error(n_error);
    return True;
  }

  /* Let DX handle its X events. */
  
  DXCheckRIH(0);
  
  /* Loop through all open windows. */
  for(i = 0; i < DXTOOLS_MAX_WINDOWS; i++) {
    if(dxc->win[i]) {
      /* First check if a window needs to be opened or closed. */
      if(dxc->win[i]->open_me)
	create_window(dxc->win[i]);

      if(dxc->win[i]->close_me) {
	delete_window(dxc->win[i]);
	continue;
      }

      /* Now let DX check for events in drawing areas. */
      if(DX_update(dxc->win[i])) {
	ERROR("Updating DX data failed!\n");
	return True;
      }
    }
  }

  /* Now let the main thread know that some windows might ready. */

  n_error = pthread_cond_broadcast(&dxc->windows_ready);
  if(n_error){
    ERROR("Signalling of condition failed!\n");
    display_error(n_error);
    return True;
  }

  /* Unlock the context manager. */
  n_error = pthread_mutex_unlock(&dxc->tlock);
  if(n_error){
    ERROR("Unlocking of thread failed!\n");
    display_error(n_error);
    return True;
  }

  /* Put the thread to sleep for a while to avoid hogging resources. */
  nanosleep(&time_req, NULL);
    
  return False;
}




/* windows_thread(): Entry point for the thread managing windows. */

/* return true on error or false otherwise. */

static void *windows_thread(void *data)
{
  FUNCNAME("windows_thread");
  DXTOOLS_CONTEXT *dxc = (DXTOOLS_CONTEXT *)data;
  static int trv = true;
  static int zero = 0;
  int    i, n_error = 0;

#if ALBERTA_DEBUG
  /* Paranoid error behavior. */
  DXSetErrorExit(2);
#endif


  /* Perform necessary initialization of the X Toolkit Intrinsics. */
  if(XtToolkitThreadInitialize() != True) {
    WARNING("X Toolkit Intrinsics do not seem to support multi-threading.\n");
    return &trv;
  }

  /* Lock the context manager. */
  n_error = pthread_mutex_lock(&dxc->tlock);
  if(n_error){
    ERROR("Locking of thread failed!\n");
    display_error(n_error);
    return &trv;
  }

  dxc->toplevel = XtVaAppInitialize (&dxc->app, "Alberta", 
				     NULL, 0, &zero, NULL, 
				     NULL, NULL);

  /* Unlock the context manager. */
  n_error = pthread_mutex_unlock(&dxc->tlock);
  if(n_error){
    ERROR("Unlocking of thread failed!\n");
    display_error(n_error);
    return &trv;
  }

  /* Add our event handler and start the event loop. This loop should */
  /* not exit, the thread should keep running.  */

  XtAppAddWorkProc(dxc->app, (XtWorkProc)XCheckRIH, dxc);

  XtAppMainLoop(dxc->app);

  /* This should not happen normally, see context_manager() below. */

  WARNING("Fell out of the X application loop! Cleaning up.\n");

  /* Try to clean up. */

  /* Lock the context manager. */
  n_error = pthread_mutex_lock(&dxc->tlock);
  if(n_error){
    ERROR("Locking of thread failed!\n");
    display_error(n_error);
    return &trv;
  }

  for(i = 0; i < DXTOOLS_MAX_WINDOWS; i++)
    if(dxc->win[i])
      delete_window(dxc->win[i]);

  XtDestroyApplicationContext(dxc->app);

  dxc->n_open_windows = 0;
  dxc->thread_running = false;

  /* Now unlock context data */
  n_error = pthread_mutex_unlock(&dxc->tlock);
  if(n_error){
    ERROR("Unlocking of thread failed!\n");
    display_error(n_error);
  }

  return &trv;
}



static DXTOOLS_CONTEXT *context_manager(void)
{
  FUNCNAME("contex_manager");
  int n_error;
  static pthread_t *thread = NULL;
  static DXTOOLS_CONTEXT *dxc = NULL;

  if(dxc) { 
    /* Perform some checks before returning the context handle. */

    if(dxc->thread_running)
      return dxc;
    else
      WARNING("Something went wrong, thread no longer running.\n");

    if(thread)
      MEM_FREE(thread, 1, pthread_t);    
    else
      ERROR("Thread object missing!\n");


    /* Now, after complaining, just initialize again. */
    /* This behavior could be changed, e.g. the thread might exit normally */
    /* after closing the last window. However, I do not expect the */
    /* running thread to consume many resources if no window is open. */

    MEM_FREE(dxc, 1, DXTOOLS_CONTEXT);
    dxc = NULL;
  }

  thread = MEM_CALLOC(1, pthread_t);
  if(!thread) {
    WARNING("Could not allocate thread object!\n");
    return NULL;
  }
  
  /* Perform necessary initialization of DX library. */
  
  DXInitModules();
  
  /* Create a context object. */
  dxc = MEM_CALLOC(1, DXTOOLS_CONTEXT);
  if(!dxc) {
    WARNING("Could not allocate context object!\n");
    return NULL;
  }
  
  
  n_error = pthread_mutex_init(&dxc->tlock, NULL);
  if(n_error) {
    WARNING("Initialization of mutex failed!\n");
    goto error_out;    
  }
  
  n_error = pthread_cond_init(&dxc->windows_ready, NULL);
  if(n_error) {
    WARNING("Initialization of condition variable failed!\n");
    display_error(n_error);
    goto error_out;
  }

  n_error = pthread_cond_init(&dxc->block_context, NULL);
  if(n_error) {
    WARNING("Initialization of condition variable failed!\n");
    display_error(n_error);
    goto error_out;
  }
  
  /* Create a new thread for the context. Capture possible errors. */
  
  dxc->thread_running = true;
  n_error = pthread_create(thread, NULL, windows_thread, dxc);

  if(n_error) {
    WARNING("Creation of thread failed!\n");
    display_error(n_error);
    goto error_out;
  }
  
  return dxc;

 error_out:
  
  MEM_FREE(thread, 1, pthread_t);
  MEM_FREE(dxc, 1, DXTOOLS_CONTEXT);
  dxc = NULL;
  thread = NULL;
  return NULL;
}


static void block_windows(DXTOOLS_CONTEXT *dxc)
{
  FUNCNAME("block_windows");
  int n_error;

  if(dxc->check_block_context) {
    MSG("*** Simulation blocked. Hit '%c' in any DXTOOLS window to unblock.\n",
	DXTOOLS_KEY_BLOCK);

    n_error = pthread_cond_wait(&dxc->block_context, &dxc->tlock);

    if(n_error) {
      ERROR("Error occured while waiting for blocking window thread.\n");
      display_error(n_error);
    }
  }

  return;
}



/****************************************************************************/
/* USER INTERFACE:                                                          */
/****************************************************************************/


extern DXTOOLS_WINDOW *open_dxtools_window(const char *title,
					   const char *geometry)
{
  FUNCNAME("open_dxtools_window");
  DXTOOLS_CONTEXT     *dxc;
  DXTOOLS_WINDOW      *win = NULL;
  int    i, n_error;
  char   wtitle[170];
  struct timespec abstime = { 0, };

  /* Get a context manager and start the graphics thread. */
  if(!(dxc = context_manager()))
    return NULL;

  /* Lock the context manager. */
  n_error = pthread_mutex_lock(&dxc->tlock);
  if(n_error){
    ERROR("Locking of thread failed!\n");
    display_error(n_error);
    return NULL;
  }


  if(dxc->n_open_windows >= DXTOOLS_MAX_WINDOWS) {
    WARNING("Sorry, only %d DXTOOLS windows available!\n", 
	    DXTOOLS_MAX_WINDOWS);
    return(NULL);
  }

  /* Create a new DXTOOLS_WINDOW. */

  for(i = 0; i < DXTOOLS_MAX_WINDOWS; i++) {
    if(!dxc->win[i]) {
      dxc->win[i] = win = MEM_CALLOC(1, DXTOOLS_WINDOW);

      if(!win) {
	WARNING("Unable to allocate DXTOOLS_WINDOW object!\n");
	return(NULL);
      }

      dxc->n_open_windows++;

      break;
    }
  }
  DEBUG_TEST_EXIT(i < DXTOOLS_MAX_WINDOWS, "Inconsistency detected: n_open_windows was apparently small than the number of windows in use!\n");

  

  /* set window data */

  win->dxc    = dxc;
  win->number = i;
  
  GET_PARAMETER(0, "dxtools window use perspective", "%d", 
		&win->use_perspective);
  GET_PARAMETER(0, "dxtools window use hardware rendering", "%d", 
		&win->use_hardware);

  if (geometry)  {
    int w, h, x = 0, y = 0, size = 1;
    char *s;

    if (strchr(geometry, 'x')) {
      if ((s = strchr(geometry, '+'))) {
	/*  "wwxhh+xx+yy"   */
	if (strchr(s+1, '+')) sscanf(geometry, "%dx%d+%d+%d", &w, &h, &x, &y);
	/*  "wwxhh+xx"      */
	else {
	  sscanf(geometry, "%dx%d+%d", &w, &h, &x);
	  y = x;
	}
      }
      /*  "wwxhh"         */
      else {
	sscanf(geometry, "%dx%d", &w, &h);
      }
    }
    else {
      size = 0;
      if ((s = strchr(geometry, '+'))) {
	/*  "xx+yy"         */
	if (strchr(s+1, '+')) sscanf(s, "+%d+%d", &x, &y);
	/*  "wwxhh+xx"      */
	else {
	  sscanf(s, "+%d", &x);
	  y = x;
	}
      }
    }
    if (size) {
      win->width = w;
      win->height = h;
    }
    win->offsetx = x;
    win->offsety = y;
  }
  else win->width = win->height = 500;

  win->draw_width = win->width;
  win->draw_height = win->height;

  /* create unique title */
  if (title) 
    snprintf(wtitle, 170, "%s", title);     /* cut too long titles */
  else 
    snprintf(wtitle, 170, "DXTOOLS Window [%d]", i+1);

  win->title = strdup(wtitle);

  /* trigger opening of this window. */
  win->open_me = true;

  /* Now wait until the window is ready. */
  /* Wait at most 60 seconds (time is money for numerical simulations) */
  n_error = clock_gettime(CLOCK_REALTIME, &abstime);
  if(n_error) {
    ERROR("Could not get absolute system time!\n");
    display_error(n_error);
    return NULL;
  }

  abstime.tv_sec += 60;
  while (win->open_me == true) {
    n_error = pthread_cond_timedwait(&dxc->windows_ready, &dxc->tlock,
				     &abstime);
    if(n_error) {
      ERROR("Waiting for window opening failed!\n");
      display_error(n_error);
      return NULL;
    }
  }

  /* Now unlock context data */
  n_error = pthread_mutex_unlock(&dxc->tlock);
  if(n_error){
    ERROR("Unlocking of thread failed!\n");
    display_error(n_error);
  }

  return((DXTOOLS_WINDOW *) win);
}


extern void close_dxtools_window(DXTOOLS_WINDOW *win)
{
  FUNCNAME("close_dxtools_window");
  DXTOOLS_CONTEXT     *dxc;
  int                  i, n_error;
  struct timespec      abstime = { 0, };


  if (!win) return;

  dxc = win->dxc;

  /* Lock the context manager. */
  n_error = pthread_mutex_lock(&dxc->tlock);
  if(n_error){
    ERROR("Locking of thread failed!\n");
    display_error(n_error);
    return;
  }

  /* first check if the context is blocked. */
  block_windows(dxc);

  /* trigger thread to close the window */
  win->close_me = true;

  /* Now wait until the window is ready. */
  /* Wait at most 60 seconds (time is money for numerical simulations) */
  n_error = clock_gettime(CLOCK_REALTIME, &abstime);
  if(n_error) {
    ERROR("Could not get absolute system time!\n");
    display_error(n_error);
  }

  abstime.tv_sec += 60;
  while (win->close_me == true) {
    n_error = pthread_cond_timedwait(&dxc->windows_ready, &dxc->tlock,
				     &abstime);
    if(n_error) {
      ERROR("Error occured while waiting for window to close.\n");
      display_error(n_error);

      /* We can not safely deallocate the DXTOOLS_WINDOW object */
      /* at this point, this could cause the graphics backround routines to */
      /* crash. We just set the dxc->win[i] pointer to NULL which will */
      /* cause this window to be ignored in the event loop. */
    }
  }

  /* Now update entries in the context. */

  for(i = 0; i < DXTOOLS_MAX_WINDOWS; i++) {
    if(win == dxc->win[i]) {
      if(!n_error) {
	free(win->title);
	MEM_FREE(win, 1, DXTOOLS_WINDOW);
      }
      dxc->win[i] = NULL;
      dxc->n_open_windows--;

      break;
    }
  }
  DEBUG_TEST_EXIT(i < DXTOOLS_MAX_WINDOWS, "Did not find window in list!\n");

  /* Unlock the context manager. */
  n_error = pthread_mutex_unlock(&dxc->tlock);
  if(n_error){
    ERROR("Unlocking of thread failed!\n");
    display_error(n_error);
  }

  return;
}




static int create_field_components(MESH *mesh, const DOF_REAL_VEC *drv,
				   const DOF_REAL_D_VEC *drdv,
				   Array *coordinates, Array *connections,
				   Array *data)
{
  FUNCNAME("create_field_components");
  TRAVERSE_STACK *stack = NULL;
  static const REAL_B vertex_bary[N_VERTICES_LIMIT] = 
    {INIT_BARY_3D(1.0, 0.0, 0.0, 0.0),
     INIT_BARY_3D(0.0, 1.0, 0.0, 0.0),
     INIT_BARY_3D(0.0, 0.0, 1.0, 0.0),
     INIT_BARY_3D(0.0, 0.0, 0.0, 1.0)};
  float            coord[DIM_OF_WORLD], value;
  int              conn[N_VERTICES_MAX];
  const DOF_ADMIN *admin = NULL;
  FLAGS            adm_flags = ADM_FLAGS_DFLT;
  const EL_INFO   *el_info = NULL;
  DOF_INT_VEC     *dof_vert_ind = NULL;
  const FE_SPACE  *fe_space;
  PARAMETRIC      *parametric = NULL;
  int              dim, n0, nv, ne, i, j, *vert_ind = NULL;
  REAL            *data_vec = NULL;
  DOF              dof;

  /* Dimension */
  dim = mesh->dim;

  /* Create Arrays for coordinates, connections, and data */
  if(!(*coordinates = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 1,DIM_OF_WORLD))) {
    display_dx_error();
    goto error_out;
  }
  if(!(*connections = DXNewArray(TYPE_INT, CATEGORY_REAL, 1, dim+1))) {
    display_dx_error();
    goto error_out;
  }  
  if(data) {
    if(drv)
      *data = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 1, 1);
    else
      *data = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 1, DIM_OF_WORLD);
    if(!*data) {
      display_dx_error();
      goto error_out;
    }
  }

  /* allocate memory for coordinates, connections, and data */
  if(!DXAddArrayData(*coordinates, 0, mesh->n_vertices, NULL)) {
    display_dx_error();
    goto error_out;
  }

  if(!DXAddArrayData(*connections, 0, mesh->n_elements, NULL)) {
    display_dx_error();
    goto error_out;
  }

  if(data) {
    if(!DXAddArrayData(*data, 0, mesh->n_vertices, NULL)) {
      display_dx_error();
      goto error_out;
    }
    if(drv)
      data_vec = drv->vec;
    else if(drdv)
      data_vec = (REAL *)drdv->vec;
    else 
      goto error_out;

    if(!data_vec)
      goto error_out;
  }

  if(drv)
    adm_flags = drv->fe_space->admin->flags;
  else if(drdv)
    adm_flags = drdv->fe_space->admin->flags;

  adm_flags &= ~ADM_PERIODIC;

  /* get dof_admin for vertex count */
  admin = get_vertex_admin(mesh, adm_flags);
  fe_space = get_dof_space(mesh, "vertex fe_space", admin->n_dof, admin->flags);

  /* get offset for the dofs */
  n0 = admin->n0_dof[VERTEX];

  /* pointer for parametric mesh */
  parametric = mesh->parametric;

  /* get dof vector for vertex indices and initialize with -1 */
  dof_vert_ind = get_dof_int_vec("vertex indices", fe_space);
  GET_DOF_VEC(vert_ind, dof_vert_ind);
  FOR_ALL_DOFS(admin, vert_ind[dof] = -1);

  nv = ne = 0;
  stack = get_traverse_stack();

  /*------------------------------------------------------------------------*/
  /* The first pass counts elements and vertices, checks these against the  */
  /* entries of mesh->n_elements, mesh->n_vertices, and fills coordinates   */
  /* and data.                                                              */
  /*------------------------------------------------------------------------*/
  for(el_info = traverse_first(stack, mesh, -1, CALL_LEAF_EL | FILL_COORDS);
      el_info;
      el_info = traverse_next(stack, el_info)) {

    if (parametric) {
      parametric->init_element(el_info, parametric);
      parametric->coord_to_world(el_info, NULL, N_VERTICES(dim),
				 vertex_bary, (REAL_D *)el_info->coord);
    }

    for (i = 0; i < N_VERTICES(dim); i++) {
      dof = el_info->el->dof[i][n0];
      if (vert_ind[dof] == -1) {

	/* assign a global index to each vertex */
        vert_ind[dof] = nv;    

	/* insert coordinate into the field (ATTENTION: must be float!) */
	for (j = 0; j < DIM_OF_WORLD; j++) 
	  coord[j] = (float)el_info->coord[i][j];
	if(!DXAddArrayData(*coordinates, nv, 1, coord)) {
	  display_dx_error();
	  goto error_out;
	}

	if(data_vec){
	  if(drv)
	    value = (float)data_vec[dof];
	  else
	    value = (float)data_vec[dof*DIM_OF_WORLD];

	  if(!DXAddArrayData(*data, nv, 1, &value)) {
	    display_dx_error();
	    goto error_out;
	  }
	}

        nv++;

        if (nv>mesh->n_vertices) 
	  ERROR_EXIT("mesh %s: n_vertices (==%d) is too small!\n",
		     mesh->name, mesh->n_vertices);
      }        
    }

    ne++;

    if (ne>mesh->n_elements) 
      ERROR_EXIT("mesh %s: n_elements (==%d) is too small!\n", 
		 mesh->name, mesh->n_elements);
  }

  if (ne<mesh->n_elements) 
    ERROR_EXIT("mesh %s: n_elements (==%d) is too large!\n", 
	       mesh->name, mesh->n_elements, ne);

  if (nv<mesh->n_vertices)
    ERROR_EXIT("mesh %s: n_vertices (==%d) is too large\n", 
	       mesh->name, mesh->n_vertices, nv);


  ne = 0;
  
  /*------------------------------------------------------------------------*/
  /* The second pass fills connections                                      */
  /*------------------------------------------------------------------------*/
  for(el_info = traverse_first(stack, mesh, -1, CALL_LEAF_EL);
      el_info;
      el_info = traverse_next(stack, el_info)) {

    for (i = 0; i < N_VERTICES(dim); i++)
      conn[i] = vert_ind[el_info->el->dof[i][n0]];    
    if(!DXAddArrayData(*connections, ne, 1, conn)) {
      display_dx_error();
      goto error_out;
    }

    ne++;
  }

  free_dof_int_vec(dof_vert_ind);
  free_fe_space(fe_space);
  free_traverse_stack(stack);

  return false;

 error_out:
  if(dof_vert_ind) {
    free_dof_int_vec(dof_vert_ind);
    free_fe_space(fe_space);
  }
  if(stack)
    free_traverse_stack(stack);

  return true;
}





extern void dxtools_mesh(DXTOOLS_WINDOW *win, MESH *mesh)
{
  FUNCNAME("dxtool_mesh");
  DXTOOLS_CONTEXT *dxc = win->dxc;
  int              dim, n_error = 0;
  REAL             tube_size_real = 0.0;
  float            tube_size = 0.0;
  Array            coordinates = NULL, connections = NULL;
  Field            mf = NULL;
  ModuleInput      minput[10];
  ModuleOutput     moutput[10];
  dxObject         connectionsobject = NULL, displayobject = NULL;


  if (!mesh || !win)  return;

  /* Set the tube size */
  GET_PARAMETER(0, "dxtools mesh tube size", "%f", &tube_size_real);
  tube_size = MAX(0.0, (float)tube_size_real);

  /* Dimension */
  dim = mesh->dim;

  /* lock thread data */
  n_error = pthread_mutex_lock(&dxc->tlock);
  if(n_error){
    ERROR("Locking of thread failed!\n");
    display_error(n_error);
  }

  /* first check if the context is blocked. */
  block_windows(dxc);

  if(create_field_components(mesh, NULL, NULL, &coordinates, &connections, NULL))
    goto error_out;

  /* Create a new field and insert the Array Data */
  if(!(mf = DXNewField())) {
    display_dx_error();
    goto error_out;
  }

  DXSetComponentValue(mf, "positions", (dxObject)coordinates);
  DXSetComponentValue(mf, "connections", (dxObject)connections);

  switch(dim) {
  case 1:
    DXSetComponentAttribute(mf, "connections", "element type",
			    (dxObject)DXNewString("lines"));
    break;
  case 2:
    DXSetComponentAttribute(mf, "connections", "element type",
			    (dxObject)DXNewString("triangles"));
    break;
  case 3:
    DXSetComponentAttribute(mf, "connections", "element type",
			    (dxObject)DXNewString("tetrahedra"));
    break;
  default:
    ERROR_EXIT("Bad dimension?\n");
  }

  if(!DXEndField(mf)) {
    display_dx_error();
    goto error_out;
  }




  /* Create Connections of the field data */
  DXModSetObjectInput(&minput[0], "input", (dxObject)mf);
  DXModSetObjectOutput(&moutput[0], "output", &connectionsobject);
  if(!DXCallModule("ShowConnections", 1, minput, 1, moutput))
    goto error_out;


  if(tube_size > 0.0) {
    /* Draw connections as a tube */
    DXModSetObjectInput(&minput[0], "line", connectionsobject);
    DXModSetFloatInput(&minput[1], "diameter", tube_size);
    DXModSetObjectOutput(&moutput[0], "tube", &displayobject);
    if(!DXCallModule("Tube", 2, minput, 1, moutput))
      goto error_out;
  }
  else {
    /* Draw connections as plain lines */
    displayobject = connectionsobject;
  }

  /* Delete possible colormap objects. */
  if(win->colormap) {
    if(!DXDelete((dxObject)win->colormap))
      display_dx_error();
    win->colormap = NULL;
  }
    
  /* Setting new object for the window */
  if(win->displayobject && !DXDelete(win->displayobject))
    display_dx_error();
  win->displayobject = displayobject;

  win->type = DXTOOLS_MESH;
  win->dim  = dim;
  win->redraw_me = true;

  /* Unlock thread data */
  pthread_mutex_unlock(&dxc->tlock);
  if(n_error){
    ERROR("Unlocking of thread failed!\n");
    display_error(n_error);
  }

  return;

 error_out:
  WARNING("Mesh not displayed.\n");
  return;
}



extern void dxtools_drv(DXTOOLS_WINDOW *win, const DOF_REAL_VEC *u)
{
  FUNCNAME("dxtools_drv");
  DXTOOLS_CONTEXT *dxc = win->dxc;
  MESH            *mesh;
  int              mode_rubber_sheet = 0, mode_color = 1, dim, n_error = 0;
  int              mode_glyph = 0;
  Array            coordinates = NULL, connections = NULL;
  Array            data = NULL;
  Field            colormap = NULL, mf = NULL, tmp_mf = NULL;
  ModuleInput      minput[10];
  ModuleOutput     moutput[10];


  if (!u || !win)  return;

  if(!strstr(u->fe_space->bas_fcts->name, "lagrange")) {
    WARNING("Only implemented for Lagrange Finite Elements!\n");
    goto error_out;
  }


  /* Dimension */
  mesh = u->fe_space->mesh;
  dim = mesh->dim;

  /* lock thread data */
  n_error = pthread_mutex_lock(&dxc->tlock);
  if(n_error){
    ERROR("Locking of thread failed!\n");
    display_error(n_error);
  }

  /* first check if the context is blocked. */
  block_windows(dxc);

  if(create_field_components(mesh, u, NULL,
			     &coordinates, &connections, &data))
    goto error_out;


  /* Create a new field and insert the Array Data */
  if(!(mf = DXNewField())) {
    display_dx_error();
    goto error_out;
  }

  DXSetComponentValue(mf, "positions", (dxObject)coordinates);
  DXSetComponentValue(mf, "connections", (dxObject)connections);
  DXSetComponentValue(mf, "data", (dxObject)data);

  switch(dim) {
  case 1:
    DXSetComponentAttribute(mf, "connections", "element type",
			    (dxObject)DXNewString("lines"));
    break;
  case 2:
    DXSetComponentAttribute(mf, "connections", "element type",
			    (dxObject)DXNewString("triangles"));
    break;
  case 3:
    DXSetComponentAttribute(mf, "connections", "element type",
			    (dxObject)DXNewString("tetrahedra"));
    break;
  default:
    ERROR_EXIT("Bad dimension?\n");
  }

  if(!DXEndField(mf)) {
    display_dx_error();
    goto error_out;
  }


  GET_PARAMETER(0, "dxtools data use RubberSheet", "%d", &mode_rubber_sheet);
  GET_PARAMETER(0, "dxtools data use AutoColor", "%d", &mode_color);
  GET_PARAMETER(0, "dxtools data use AutoGlyph", "%d", &mode_glyph);

  /* Enable the DX "rubber sheet" effect, which is basically just a */
  /* 1/2D plot of data values as height over the mesh. */
  
  if (mode_rubber_sheet == 1) {
    if(dim == 3) {
      WARNING("Rubber sheet display mode is only available for 2D meshes.\n");
      mode_rubber_sheet = 0;
    }
    else {
      DXModSetObjectInput(&minput[0], "data", (dxObject)mf);
      DXModSetFloatInput(&minput[1], "scale", 1.0f);
      DXModSetObjectOutput(&moutput[0], "graph", (dxObject *)(void *)&tmp_mf);
      if(!DXCallModule("RubberSheet", 2, minput, 1, moutput)) {
	display_dx_error();
	goto error_out;
      }
      mf = tmp_mf;
    }
  }

  /* The rubber sheet and glyph effects add color, so AutoColor is not */
  /* always needed. */
  if((mode_color == 1) || (mode_rubber_sheet == 0)) {
    DXModSetObjectInput(&minput[0], "data", (dxObject)mf);
    DXModSetObjectOutput(&moutput[0], "mapped", (dxObject *)(void *)&tmp_mf);
    DXModSetObjectOutput(&moutput[1], "colormap", (dxObject *)(void *)&colormap);
    if(!DXCallModule("AutoColor", 1, minput, 2, moutput)) {
      display_dx_error();
      goto error_out;
    }
    mf = tmp_mf;
  }

  /* Apply glyphs, if desired. This includes arrows, textual display of */
  /* values, etc. */
  if(mode_glyph) {
    int  n_in = 0;
    char glyph_type[20] = { 0, };
    GET_PARAMETER(0, "dxtools data AutoGlyph type", "%s", glyph_type);


    DXModSetObjectInput(&minput[n_in++], "data", (dxObject)mf);
    if(strlen(glyph_type)) {
      if(strcmp(glyph_type, "colortext"))
	DXModSetStringInput(&minput[n_in++], "type", glyph_type);
      else
	DXModSetStringInput(&minput[n_in++], "type", "colored text");
    }

    DXModSetObjectOutput(&moutput[0], "glyphs", (dxObject *)(void *)&tmp_mf);
    if(!DXCallModule("AutoGlyph", n_in, minput, 1, moutput)) {
      display_dx_error();
      goto error_out;
    }
    mf = tmp_mf;
  }


  /* Set new colormap for the object. */
  if(colormap) {
    if(win->colormap && !DXDelete((dxObject)win->colormap))
      display_dx_error();
    win->colormap = colormap;
  }

  /* Setting new object for the window */
  if(win->displayobject && !DXDelete(win->displayobject))
    display_dx_error();
  win->displayobject = (dxObject)mf;

  win->type = DXTOOLS_FEVECTOR;
  win->dim  = dim;
  win->redraw_me = true;

  /* Unlock thread data */
  n_error = pthread_mutex_unlock(&dxc->tlock);
  if(n_error){
    ERROR("Unlocking of thread failed!\n");
    display_error(n_error);
  }

  return;

 error_out:
  WARNING("Vector not displayed.\n");
  return;


}

extern void dxtools_drdv(DXTOOLS_WINDOW *win, const DOF_REAL_D_VEC *u)
{
  FUNCNAME("dxtools_drdv");
  DXTOOLS_CONTEXT *dxc = win->dxc;
  MESH            *mesh;
  int              mode_rubber_sheet = 0, mode_color = 1, dim, n_error = 0;
  int              mode_glyph = 0;
  Array            coordinates = NULL, connections = NULL;
  Array            data = NULL;
  Field            colormap = NULL, mf = NULL, tmp_mf = NULL;
  ModuleInput      minput[10];
  ModuleOutput     moutput[10];


  if (!u || !win)  return;

  if(!strstr(u->fe_space->bas_fcts->name, "lagrange")) {
    WARNING("Only implemented for Lagrange Finite Elements!\n");
    goto error_out;
  }


  /* Dimension */
  mesh = u->fe_space->mesh;
  dim = mesh->dim;

  /* lock thread data */
  n_error = pthread_mutex_lock(&dxc->tlock);
  if(n_error){
    ERROR("Locking of thread failed!\n");
    display_error(n_error);
  }

  /* first check if the context is blocked. */
  block_windows(dxc);

  if(create_field_components(mesh, NULL, u,
			     &coordinates, &connections, &data))
    goto error_out;


  /* Create a new field and insert the Array Data */
  if(!(mf = DXNewField())) {
    display_dx_error();
    goto error_out;
  }

  DXSetComponentValue(mf, "positions", (dxObject)coordinates);
  DXSetComponentValue(mf, "connections", (dxObject)connections);
  DXSetComponentValue(mf, "data", (dxObject)data);

  switch(dim) {
  case 1:
    DXSetComponentAttribute(mf, "connections", "element type",
			    (dxObject)DXNewString("lines"));
    break;
  case 2:
    DXSetComponentAttribute(mf, "connections", "element type",
			    (dxObject)DXNewString("triangles"));
    break;
  case 3:
    DXSetComponentAttribute(mf, "connections", "element type",
			    (dxObject)DXNewString("tetrahedra"));
    break;
  default:
    ERROR_EXIT("Bad dimension?\n");
  }

  if(!DXEndField(mf)) {
    display_dx_error();
    goto error_out;
  }


  GET_PARAMETER(0, "dxtools data use RubberSheet", "%d", &mode_rubber_sheet);
  GET_PARAMETER(0, "dxtools data use AutoColor", "%d", &mode_color);
  GET_PARAMETER(0, "dxtools data use AutoGlyph", "%d", &mode_glyph);

  /* Enable the DX "rubber sheet" effect, which is basically just a */
  /* 1/2D plot of data values as height over the mesh. */
  
  if (mode_rubber_sheet == 1) {
    if(dim == 3) {
      WARNING("Rubber sheet display mode is only available for 2D meshes.\n");
      mode_rubber_sheet = 0;
    }
    else {
      DXModSetObjectInput(&minput[0], "data", (dxObject)mf);
      DXModSetFloatInput(&minput[1], "scale", 1.0f);
      DXModSetObjectOutput(&moutput[0], "graph", (dxObject *)(void *)&tmp_mf);
      if(!DXCallModule("RubberSheet", 2, minput, 1, moutput)) {
	display_dx_error();
	goto error_out;
      }
      mf = tmp_mf;
    }
  }

  /* The rubber sheet and glyph effects add color, so AutoColor is not */
  /* always needed. */
  if((mode_color == 1) || (mode_rubber_sheet == 0)) {
    DXModSetObjectInput(&minput[0], "data", (dxObject)mf);
    DXModSetObjectOutput(&moutput[0], "mapped", (dxObject *)(void *)&tmp_mf);
    DXModSetObjectOutput(&moutput[1], "colormap", (dxObject *)(void *)&colormap);
    if(!DXCallModule("AutoColor", 1, minput, 2, moutput)) {
      display_dx_error();
      goto error_out;
    }
    mf = tmp_mf;
  }

  /* Apply glyphs, if desired. This includes arrows, textual display of */
  /* values, etc. */
  if(mode_glyph) {
    int  n_in = 0;
    char glyph_type[20] = { 0, };
    GET_PARAMETER(0, "dxtools data AutoGlyph type", "%s", glyph_type);


    DXModSetObjectInput(&minput[n_in++], "data", (dxObject)mf);
    if(strlen(glyph_type)) {
      if(strcmp(glyph_type, "colortext"))
	DXModSetStringInput(&minput[n_in++], "type", glyph_type);
      else
	DXModSetStringInput(&minput[n_in++], "type", "colored text");
    }

    DXModSetObjectOutput(&moutput[0], "glyphs", (dxObject *)(void *)&tmp_mf);
    if(!DXCallModule("AutoGlyph", n_in, minput, 1, moutput)) {
      display_dx_error();
      goto error_out;
    }
    mf = tmp_mf;
  }


  /* Set new colormap for the object. */
  if(colormap) {
    if(win->colormap && !DXDelete((dxObject)win->colormap))
      display_dx_error();
    win->colormap = colormap;
  }

  /* Setting new object for the window */
  if(win->displayobject && !DXDelete(win->displayobject))
    display_dx_error();
  win->displayobject = (dxObject)mf;

  win->type = DXTOOLS_FEVECTOR;
  win->dim  = dim;
  win->redraw_me = true;

  /* Unlock thread data */
  n_error = pthread_mutex_unlock(&dxc->tlock);
  if(n_error){
    ERROR("Unlocking of thread failed!\n");
    display_error(n_error);
  }

  return;

 error_out:
  WARNING("Vector not displayed.\n");
  return;


}
