/***********************************************************************/
/* Open Visualization Data Explorer                                    */
/* (C) Copyright IBM Corp. 1989,1999                                   */
/* ALL RIGHTS RESERVED                                                 */
/* This code licensed under the                                        */
/*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
/***********************************************************************/

#include <dxconfig.h>


#ifndef lint
static char rcsid[] = "$Header: /home/gda/dxcvs/dx/src/exec/hwrender/hwInteractor.c,v 1.3 1999/05/10 15:45:34 gda Exp $"  ;
#endif

/*---------------------------------------------------------------------------*\
 $Source: /home/gda/dxcvs/dx/src/exec/hwrender/hwInteractor.c,v $
    
  This file contains core utilities for managing direct interactors.

\*---------------------------------------------------------------------------*/


#include <math.h>
#include "hwDeclarations.h"
#ifndef STANDALONE
#include "hwMemory.h"
#endif

#include "hwDebug.h"

/*
 *  Allocation utils
 */

static tdmInteractor _allocateInteractorArray (tdmInteractorWin W) ;
static tdmInteractor _allocateMoreInteractors (tdmInteractorWin W) ;

/* interactor storage is allocated in multiples of ARRAYSIZE */
#define ARRAYSIZE 16

/* pointer to array of `ARRAYSIZE' interactors */
typedef tdmInteractorT (*InteractorArray)[ARRAYSIZE] ; 


tdmInteractorWin
_dxfInitInteractors (void *portHandle, void *stack)
{
  tdmInteractorWin W ;

  ENTRY(("_dxfInitInteractors(0x%x, 0x%x)", portHandle, stack));

  /* allocate interactor common data associated with image window */
  if (! (W = (tdmInteractorWin) tdmAllocateLocal (sizeof(tdmInteractorWinT))))
    {
      /* !!!!! No warning message set???? */
      EXIT(("ERROR:out of memory"));
      return (tdmInteractorWin) 0 ;
    }

  W->Interactors = (tdmInteractor *) 0 ;
  W->extents = (void *) 0 ;
  W->numUsed = 0 ;
  W->numAllocated = 0 ;

  /* initialize camera stack */
  W->Cam = (tdmInteractorCam) 0 ;
  W->redo = (tdmInteractorCam) 0 ;
  _dxfPushInteractorCamera(W) ;

  W->Cam->view_state++ ;
  W->Cam->cursor_speed = tdmMEDIUM_CURSOR ;

  /* copy handles to graphics API context and software transformation stack */
  W->Cam->phP = portHandle ;
  W->Cam->stack = stack ;

  EXIT(("W = 0x%x", W));
  return W ;
}

void
_dxfDestroyAllInteractors (tdmInteractorWin W)
{
  int i ;
  tdmInteractorCam curr, next ;

  ENTRY(("_dxfDestroyAllInteractors(0x%x)", W));

  if (! W)
    {
      EXIT(("ERROR: bad window"));
      return ;
    }

  /* destroy all used interactors */
  if (W->Interactors)
    {
      for (i = 0 ; i < W->numAllocated ; i++)
          if (IS_USED(W->Interactors[i]))
              /*
               *  Invoke the interactor's destroy method.  It must call
               *  _dxfDeallocateInteractor() itself at some point. 
               */
              tdmDestroyInteractor(W->Interactors[i]) ;

      tdmFree((void *) W->Interactors) ;
      W->Interactors = (tdmInteractor *) 0 ;
    }

  /* destroy all allocated interactor data */
  if (W->extents)
    {
      InteractorArray *array = (InteractorArray *) W->extents ;
      int numInteractorArrays = W->numAllocated / ARRAYSIZE ;

      for (i = 0 ; i < numInteractorArrays ; i++)
        {
          tdmFree((void *) array[i]) ;
          array[i] = (InteractorArray) 0 ;
        }

      tdmFree(W->extents) ;
      W->extents = (void *) 0 ;
    }

  /* destroy all camera info on undo and redo stacks */
  curr = W->Cam ;
  W->Cam = (tdmInteractorCam) 0 ;
  while (curr)
    {
      next = curr->next ;
      curr->next = (tdmInteractorCam) 0 ;

      tdmFree ((void *) curr) ;

      if (next)
          curr = next ;
      else
        {
          curr = W->redo ;
          W->redo = (tdmInteractorCam) 0 ;
        }
    }

  tdmFree((void *) W) ;
  EXIT((""));
}

tdmInteractor
_dxfAllocateInteractor (tdmInteractorWin W, int size)
{
  int i ;
  tdmInteractor I = (tdmInteractor) 0 ;

  ENTRY(("_dxfAllocateInteractor(0x%x, %d)", W, size));

  if (! W) goto error ;

  if (W->numUsed == W->numAllocated)
      /* create more interactors */
      I = _allocateMoreInteractors(W) ;
  else
      /* find an unused interactor */
      for (i = 0 ; i < W->numAllocated ; i++)
          if (! IS_USED(W->Interactors[i]))
            {
              I = W->Interactors[i] ;
              break ;
            }

  if (! I) goto error ;
  
  if (size)
      /* allocate interactor private data */
      if (! (PRIVATE(I) = tdmAllocateLocal(size)))
          goto error ;
      else
          bzero ((char *) PRIVATE(I), size) ;

  WINDOW(I) = W ;
  AUX(I) = (tdmInteractor) 0 ;
  IS_AUX(I) = 0 ;
  IS_GROUP(I) = 0 ;
  IS_USED(I) = 1 ;
  W->numUsed++ ;

  /*
   * Default event mask
   */
  I->eventMask =  DXEVENT_LEFT | DXEVENT_MIDDLE | DXEVENT_RIGHT;

  EXIT(("I = 0x%x", I));
  return I ;

 error:

  EXIT(("ERROR"));
  return (tdmInteractor) 0 ;
}

void
_dxfDeallocateInteractor (tdmInteractor interactor)
{
  tdmInteractorWin W ;

  ENTRY(("_dxfDeallocateInteractor(0x%x)", interactor));

  if (! interactor || ! (W = WINDOW(interactor)))
    {
      EXIT(("ERROR"));
      return ;
    }
      
  if (PRIVATE(interactor))
      tdmFree((void *) PRIVATE(interactor)) ;

  bzero ((char *) interactor, sizeof(tdmInteractorT)) ;
  IS_USED(interactor) = 0 ;
  W->numUsed-- ;

  EXIT((""));
}
          

/*
 *  Here is a picture of some of the lower level details implemented below.
 *  Boxes represent contiguous arrays.  A downward arrow is a pointer to
 *  the first element of a contiguous array.  Left arrows are pointers to
 *  interactor objects.
 *
 *      W.extents                                 W.Interactors
 *          |                                           |
 *          v                                           |
 *  +---------------+                                   |
 *  |InteractorArray|------------------------.          |
 *  |InteractorArray|-------------.          |          |
 *  |       .       |             |          v          v   
 *  |       .       |             |    +----------+   +---+ 
 *  |InteractorArray|--.          |    |interactor|<--|ptr| -+        
 *  +---------------+  |          |    |interactor|<--|ptr|  |        
 *                     |          |    |     .    |<--| . |  ARRAYSIZE
 *                     |          |    |     .    |<--| . |  |        
 *                     |          v    |interactor|<--|ptr| -+        
 *                     |    +----------+----------+   |   |           
 *                     |    |interactor|<-------------|ptr| -+        
 *                     |    |interactor|<-------------|ptr|  |        
 *                     |    |     .    |<-------------| . |  ARRAYSIZE
 *                     |    |     .    |<-------------| . |  |        
 *                     v    |interactor|<-------------|ptr| -+        
 *               +----------+----------+              |   |           
 *               |interactor|<------------------------|ptr| -+        
 *               |interactor|<------------------------|ptr|  |        
 *               |     .    |<------------------------| . |  ARRAYSIZE
 *               |     .    |<------------------------| . |  |        
 *               |interactor|<------------------------|ptr| -+        
 *               +----------+                         +---+
 */

static tdmInteractor
_allocateMoreInteractors (tdmInteractorWin W)
{
  int i, j ;
  tdmInteractor newArray, *tmp ;

  ENTRY(("_allocateMoreInteractors(0x%x)", W));

  /* allocate ARRAYSIZE more interactor pointers */
  tmp = (tdmInteractor *)
        tdmAllocateLocal((W->numUsed + ARRAYSIZE) * sizeof(tdmInteractor)) ;

  if (! tmp) goto error ;

  /* allocate a new storage array with space for ARRAYSIZE interactors */
  newArray = _allocateInteractorArray(W) ;

  if (! newArray)
    {
      tdmFree((void *) tmp) ;
      goto error ;
    }
  else
      W->numAllocated = W->numUsed + ARRAYSIZE ;
  
  /* copy exiting pointers */
  for (i = 0 ; i < W->numUsed ; i++)
      tmp[i] = W->Interactors[i] ;

  /* init new pointers */
  for (i = W->numUsed, j = 0 ; i < W->numAllocated ; i++, j++)
      tmp[i] = &newArray[j] ;

  if (W->Interactors) tdmFree((void *) W->Interactors) ;
  W->Interactors = tmp ;

  EXIT(("OK"));
  return W->Interactors[W->numUsed] ;

 error:

  EXIT(("ERROR"));
  return (tdmInteractor) 0 ;
}

static tdmInteractor
_allocateInteractorArray (tdmInteractorWin W)
{
  InteractorArray *tmp ;
  int i, numInteractorArrays ;

  ENTRY(("_allocateInteractorArray(0x%x)", W));
  
  numInteractorArrays = W->numAllocated / ARRAYSIZE ;

  /* get a bigger array of array pointers */
  tmp = (InteractorArray *)
        tdmAllocateLocal((numInteractorArrays + 1) * sizeof(InteractorArray)) ;

  if (! tmp) {
    EXIT(("ERROR"));
    return (tdmInteractor) 0 ;
  }
  
  /* allocate a new InteractorArray of size ARRAYSIZE */
  tmp[numInteractorArrays] =
      (InteractorArray) tdmAllocateLocal(ARRAYSIZE * sizeof(tdmInteractorT)) ;

  if (! tmp[numInteractorArrays])
    {
      tdmFree((void *) tmp) ;
      EXIT(("ERROR"));
      return (tdmInteractor) 0 ;
    }
  else
      bzero ((char *) tmp[numInteractorArrays],
             ARRAYSIZE * sizeof(tdmInteractorT)) ;
  
  /* copy existing array pointers */
  for (i = 0 ; i < numInteractorArrays ; i++)
      tmp[i] = ((InteractorArray *) W->extents)[i] ;

  /* free old array pointer array */
  if (W->extents) tdmFree(W->extents) ;

  /* copy new */
  W->extents = (void *) tmp ;

  EXIT((""));
  return *tmp[numInteractorArrays] ;
}


/*
 *  Camera stack utils:
 *
 *  Each window contains two stacks: the undo stack (W->Cam) and the redo
 *  stack (W->redo).  The top of the undo stack is the current camera in
 *  use.  When camera info is popped off the undo stack, it is pushed onto
 *  the redo stack and vice versa.
 *
 *  Note that these stacks are entirely separate from the stack managed by
 *  the routines in hwMatrix.c.  The latter routines are used to maintain
 *  the state of the transformations used during application data
 *  structure traversal, while the routines in this file maintain a
 *  history of view transforms and other important information. 
 *
 *  The undo and redo stacks are limited in size to STACKLIMIT, which if
 *  exceeded frees the bottom stack entry.
 */

#define STACKLIMIT 10

static int
_PushInteractorCamera (tdmInteractorCam *stack, tdmInteractorCam new)
{
  register int i ;
  register tdmInteractorCam p ;

  ENTRY(("_PushInteractorCamera(0x%x, 0x%x)", stack, new));

  if (! stack)
    {
      EXIT(("bad camera stack reference"));
      return 0 ;
    }

  /* Chop tail.  This is crude.  Not worth improving for small stack limit. */
  for (p= *stack, i=0 ; p && i<STACKLIMIT-1 ; p=p->next, i++) ;
  if (p && p->next)
    {
      tdmFree((void *)p->next) ;
      p->next = (tdmInteractorCam) 0 ;
    }

  if (! new)
      /* allocate new camera */
      if (new = (tdmInteractorCam) tdmAllocateLocal(sizeof(tdmInteractorCamT)))
          if (*stack)
              /* copy current top */
              memcpy ((void *)new, (void *)*stack, sizeof(tdmInteractorCamT)) ;
          else
              /* init new camera */
              bzero ((char *)new, sizeof(tdmInteractorCamT)) ;
      else
        {
          EXIT(("out of memory"));
          return 0 ;
        }

  new->next = *stack ;
  *stack = new ;

  EXIT(("OK"));
  return 1 ;
}

int 
_dxfPushInteractorCamera (tdmInteractorWin W)
{

  ENTRY(("_dxfPushInteractorCamera(0x%x)", W));

  if (! W)
    {
      EXIT(("bad window"));
      return 0 ;
    }
  if (_PushInteractorCamera (&W->Cam, 0))
    {
      EXIT(("OK"));
      return 1 ;
    }
  else
    {
      EXIT(("error"));
      return 0 ;
    }
}

static int
_MoveInteractorCamera (tdmInteractorCam *dest, tdmInteractorCam *source)
{
  /* pop source stack and push onto destination stack */
  tdmInteractorCam top ;

  ENTRY(("_MoveInteractorCamera(0x%x, 0x%x)", dest, source));
  
  if (!source || !*source)
    {
      EXIT(("empty source stack"));
      return 0 ;
    }

  top = *source ;
  *source = top->next ;
  top->next = 0 ;
  if (*source)
    (*source)->view_state++ ;

  if (! _PushInteractorCamera (dest, top))
    {
      EXIT(("ERROR"));
      return 0 ;
    }

  EXIT(("OK"));
  return 1 ;
}

int
_dxfPopInteractorCamera (tdmInteractorWin W)
{
  tdmInteractorCam top ;

  ENTRY(("_dxfPopInteractorCamera(0x%x)", W));
  
  /* there must always be at least one camera in W->Cam stack */
  if (! W->Cam->next)
    {
      EXIT(("tried to pop last camera"));
      return 0 ;
    }

  /* pop current camera stack and push onto redo stack */
  if (! _MoveInteractorCamera (&W->redo, &W->Cam))
    {
      EXIT(("ERROR"));
      return 0 ;
    }
  
  /* indicate if OK to pop further */
  if (W->Cam->next)
    {
      EXIT(("OK"));
      return 1 ;
    }
  else
    {
      EXIT(("last undoable camera"));
      return -1 ;
    }
}
  
int
_dxfRedoInteractorCamera (tdmInteractorWin W)
{
  tdmInteractorCam top ;

  ENTRY(("_dxfRedoInteractorCamera(0x%x)", W));

  /* pop redo stack and push onto current camera stack */
  if (! _MoveInteractorCamera (&W->Cam, &W->redo))
    {
      EXIT(("ERROR"));
      return 0 ;
    }
  
  /* indicate if anything left on redo stack */
  if (W->redo)
    {
      EXIT((""));
      return 1 ;
    }
  else
    {
      EXIT(("popped last camera"));
      return -1 ;
    }
}
  

/*
 *  General interactor utils
 */

void
_dxfRedrawInteractorEchos (tdmInteractorWin W)
{
  int i ;

  /* redraw all appropriate interactor echos in this window */
  ENTRY(("_dxfRedrawInteractorEchos(0x%x)", W));

  if (! W)
    {
      EXIT(("bad window"));
      return ;
    }

  for (i = 0 ; i < W->numAllocated ; i++)
      if (IS_USED(W->Interactors[i]) &&
	  !IS_AUX(W->Interactors[i]) && !IS_GROUP(W->Interactors[i]))
          tdmResumeEcho (W->Interactors[i], tdmBothBufferDraw) ;

  EXIT((""));
  return ;
}

void
_dxfAssociateInteractorEcho (tdmInteractor T, tdmInteractor A)
{
  ENTRY(("_dxfAssociateInteractorEcho(0x%x, 0x%x)", T, A));

  /*
   *  Associate the echo of A to T.  An associated interactor need not be
   *  directly manipulated since it is driven by the interactor it is
   *  associated with.
   *
   *  Associations are stored in a list.  AUX(T) is the head of the list
   *  for interactor T.  If T is a rotation interactor and is redrawn with
   *  tdmResumeEcho(T), it first checks to see if it is visible, redraws
   *  itself, and then calls tdmResumeEcho(AUX(T)).  AUX(T) may also be
   *  the head of its own sublist, in which case it calls
   *  tdmResumeEcho(AUX(AUX(T))) recursively.
   *
   *  This is not a general facility.  One major restriction is that if an
   *  interactor uses either the globe or the gnomon as its echo, then all
   *  its associated interactors succeeding it in the list must also use
   *  echo either globes or gnomons.  This is because configuring the
   *  frame buffer for a globe or gnomon is something that can only be
   *  done once per redraw.
   */

  if (T) AUX(T) = A ;
  if (A) IS_AUX(A) = 1 ;

  EXIT((""));
}

void
_dxfDisassociateInteractorEcho (tdmInteractorWin W, tdmInteractor A)
{
  int i ;

  ENTRY(("_dxfDisassociateInteractorEcho(0x%x, 0x%x)", W, A));

  /* break all associations with aux interactor A */
  if (!W || !A || !IS_AUX(A)) {
    EXIT((""));
    return ;
  }

  for (i = 0 ; i < W->numAllocated ; i++)
      if (IS_USED(W->Interactors[i]) && AUX(W->Interactors[i]) == A)
	  AUX(W->Interactors[i]) = (tdmInteractor) 0 ;

  IS_AUX(A) = 0 ;

  EXIT((""));
}

void
_dxfSetInteractorBox (tdmInteractorWin W, float box[8][3])
{
  int i ;
  tdmInteractorCam C ;

  ENTRY(("_dxfSetInteractorBox(0x%x, 0x%x)", W, box));

  /* set up global box for all cursors in this window */
  if (!W || !(C = W->Cam)) goto error ;

  for (i = 0 ; i < 8 ; i++)
    {
      VPRINT(box[i]);
      C->box[i][0] = box[i][0] ;
      C->box[i][1] = box[i][1] ;
      C->box[i][2] = box[i][2] ;
    }

  C->box_state++ ;
  EXIT((""));
  return ;

 error:

  EXIT(("error"));
  return;
}

void
_dxfSetInteractorViewInfo (tdmInteractorWin W,
                          float *f, float *t, float *u, float dist,
                          float fov, float width, float aspect, int proj,
			  float Near, float Far)
{
  tdmInteractorCam C ;

  ENTRY(("_dxfSetInteractorViewInfo"
	 "(0x%x, 0x%x, 0x%x, 0x%x, %f, %f, %f, %f, %d, %f, %f)",
	 W, f, t, u, dist, fov, width, aspect, proj, Near, Far));

  if (!W || !(C = W->Cam)) goto error ;

  C->from[0] = f[0] ; C->from[1] = f[1] ; C->from[2] = f[2] ;
  C->  to[0] = t[0] ; C->  to[1] = t[1] ; C->  to[2] = t[2] ;
  C->  up[0] = u[0] ; C->  up[1] = u[1] ; C->  up[2] = u[2] ;

  if (dist != 0)
      C->vdist = dist ;
  else
      C->vdist = (float) sqrt ((double)((f[0]-t[0])*(f[0]-t[0]) +
                                        (f[1]-t[1])*(f[1]-t[1]) +
                                        (f[2]-t[2])*(f[2]-t[2]))) ;

  /*  !!!!!!! This is very questionable coding style if it's correct !!!!! */
  if (C->projection = proj)
    {
      /* perspective */
      C->fov = fov ;
      PRINT(("perspective projection fov %f", fov));
    }
  else
    {
      /* orthogonal width in world coordinates */
      C->width = width ;
      PRINT(("orthogonal projection width %f", width));
    }

  C->aspect = aspect ;
  C->view_coords_set = 1 ;
  C->Near = Near ;
  C->Far = Far ;
  C->view_state++ ;

  PRINT(("look-to point:"));
  VPRINT(C->to);
  PRINT(("look-from point, dist %f", C->vdist));
  VPRINT(C->from) ;
  PRINT(("up vector, length %f", LENGTH(C->up)));
  VPRINT(C->up) ;
  PRINT(("Near clip: %f", C->Near));
  PRINT((" Far clip: %f", C->Far));
  EXIT((""));
  return ;

 error:
  EXIT(("ERROR"));
  return;
}

void
_dxfSetInteractorWindowSize (tdmInteractorWin W, int width, int height)
{
  tdmInteractorCam C ;

  ENTRY(("_dxfSetInteractorWindowSize(0x%x, %d, %d)", W, width, height));

  /* record size of this window in pixels */
  if (!W || !(C = W->Cam)) goto error ;

  C->w = width ; C->h = height ;
  C->aspect = (float)height/(float)width ;
  PRINT(("width: %d height: %d aspect: %f", C->w, C->h, C->aspect));

  _dxfInteractorViewChanged(W) ;
  EXIT((""));
  return ;

 error:
  EXIT(("ERROR"));
  return;
}

void
_dxfSetInteractorViewDepth (tdmInteractorWin W, int depth)
{
  tdmInteractorCam C ;

  ENTRY(("_dxfSetInteractorViewDepth(0x%x, %d)", W, depth));

  /* record range of Z buffer for this window */
  if (!W || !(C = W->Cam)) goto error ;

  C->d = depth ;
  PRINT(("depth: %d", C->d));

  _dxfInteractorViewChanged(W) ;
  EXIT((""));
  return ;

 error:
  EXIT(("ERROR"));
  return;
}

void
_dxfGetInteractorViewInfo (tdmInteractorWin W,
                          float *f, float *t, float *u, float *dist,
                          float *fov, float *width, float *aspect,
			  int *projection, float *Near, float *Far,
			  int *pixwidth)
{
  tdmInteractorCam C ;

  ENTRY(("_dxfGetInteractorViewInfo(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, "
	 "0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)",
	 W, f, t, u, dist, fov, width, aspect,
	 projection, Near, Far, pixwidth));
  
  if (!W || !(C = W->Cam) || !C->view_coords_set) goto error ;

  f[0] = C->from[0] ; f[1] = C->from[1] ; f[2] = C->from[2] ;
  t[0] = C->  to[0] ; t[1] = C->  to[1] ; t[2] = C->  to[2] ;
  u[0] = C->  up[0] ; u[1] = C->  up[1] ; u[2] = C->  up[2] ;

  /* !!!!! should we check for NULL?? */
  *dist = C->vdist ;
  *fov = C->fov ;
  *width = C->width ;
  *aspect = C->aspect ;
  *projection = C->projection ;
  *Near = C->Near ;
  *Far = C->Far ;
  *pixwidth = C->w ;
  
  PRINT(("look-to point"));
  VPRINT(C->to) ;
  PRINT(("look-from point"));
  VPRINT(C->from) ;
  PRINT(("to-from dist: %12f" , C->vdist));
  PRINT(("up vector, length %f", LENGTH(C->up)));
  VPRINT(C->up) ;
  PRINT(("width in pixels: %d aspect: %f", C->w, C->aspect));
  PRINT(("%s", C->projection ? "perspective fov " : "ortho width "));
  PRINT(("%f", C->projection ? C->fov : C->width));
  PRINT(("Near clip: %f Far clip: %f", C->Near, C->Far)); 

  EXIT((""));
  return ;

 error:
  EXIT(("ERROR"));
  return;
}

void
_dxfInteractorViewChanged(tdmInteractorWin W)
{
  tdmInteractorCam C ;

  ENTRY(("_dxfInteractorViewChanged(0x%x)", W));

  if (!W || !(C = W->Cam)) goto error ;

  C->view_state++ ;

  EXIT((""));
  return ;

 error:
  EXIT(("ERROR"));
  return;
}

void
_dxfSetInteractorImage (tdmInteractorWin W, int w, int h, void *image)
{
  tdmInteractorCam C ;

  ENTRY(("_dxfSetInteractorImage(0x%x, %d, %d, 0x%x)", W, w, h, image));

  /* copy pointer to application window background image */
  if (!W || !(C = W->Cam)) goto error ;

  C->iw = w ;
  C->ih = h ;
  C->image = image ;

  EXIT((""));
  return ;

 error:
  EXIT(("ERROR"));
  return;
}

void
_dxfSetCursorSpeed (tdmInteractorWin W, int speed)
{
  tdmInteractorCam C ;

  ENTRY(("_dxfSetCursorSpeed(0x%x, %d)", W, speed));

  /* set speed global to all cursors in this window */
  if (!W || !(C = W->Cam)) goto error ;

  C->cursor_speed = speed ;

  EXIT((""));
  return ;

 error:
  EXIT(("ERROR"));
  return;
}

void
_dxfSetCursorConstraint (tdmInteractorWin W, int constraint)
{
  tdmInteractorCam C ;

  ENTRY(("_dxfSetCursorConstraint(0x%x, %d)", W, constraint));

  /* set constraints global to all cursors in this window */
  if (!W || !(C = W->Cam)) goto error ;

  C->cursor_constraint = constraint ;
  _dxfRedrawInteractorEchos(W) ;

  EXIT((""));
  return ;

 error:
  EXIT(("ERROR"));
  return;
}

/*
 *  Null functions for interactor implementations to use.
 */

void
_dxfNullDoubleClick (tdmInteractor I, int x, int y, tdmInteractorReturn *R)
{
  R->change = 0 ;
}

void
_dxfNullStartStroke (tdmInteractor I, int x, int y, int btn, int s)
{
}

void 
_dxfNullStrokePoint (tdmInteractor I, int a, int b, int type, int s)
{
}

void
_dxfNullEndStroke (tdmInteractor I, tdmInteractorReturn *R)
{
  R->change = 0 ;
}

void 
_dxfNullResumeEcho (tdmInteractor I, tdmInteractorRedrawMode redrawMode)
{
}

void
_dxfNullKeyStruck(tdmInteractor I, int x, int y, char c, int s)
{
}
