/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = VTEVENT.C
 *
 * DESCRIPTIVE NAME = Virtual Touch Device Driver Event Processing
 *
 *
 * VERSION = V2.0
 *
 * DATE        09/20/91
 *
 * DESCRIPTION
 *             This module contains the VTD's event processing code.
 *
 * FUNCTIONS   VTEventProc,      vtNotifyEmulState,   vtNotifyExclusiveState,
 *             vtAddEvent,       vtUpdateXYZData,     vtUpdateOTData,
 *             vtUpdOneTouch,    vtFindStablePoint,   vtCheckSPData,
 *             vtUpdSPBuffer,    vtUpdStablePoint,    vtUpdateSDData,
 *             vtGoodEvent,      vtRemapXYZ,          vtFilterEvent,
 *             vtSendEvent,      vtPeekEvent,         vtRemoveEvent,
 *             vtNeedEvent,      vtProcessEvent.
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#include "vtdef.h"

/*
** Externals
*/

extern FPFNPDD        fpfnPTDRequestProc;
extern PQEVENT        pEventHead,pEventTail;
extern QEVENT         atevBuffer[];
extern HHOOK          hhookSendEventHook;
extern HHOOK          hhookUserSubReturnHook;
extern HIRQ           hirq;
extern ULONG          ulSstate;
extern ULONG          ulSG;
extern BOOL           fTouchEnabled;
extern BOOL           fEventsPending;
extern BOOL           fSubActive;
extern TOUDEVICESTATE touDeviceState;
extern ULONG          ulExclusiveSG;

#pragma  BEGIN_GLOBAL_CODE


/****************************************************************************
 *
 * FUNCTION NAME = VTEventProc
 *
 * DESCRIPTION   = Process event packets from PDD.
 *   PSEUDO-CODE -
 *      if handle and pointer are valid and touch is enabled,
 *          add the event to the VDM's buffer.
 *
 *
 * INPUT         = ulFunc -  function code (VTDCMD_REPORT_EVENT)
 *                 f16p1  -> MONREC
 *                 f16p2  -  Not used.
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID VDDENTRY VTEventProc( ULONG ulFunc, F16PVOID f16p1, F16PVOID f16p2 )
{
  PMONREC pMonRec;
  register HVDM hvdm;

  switch( ulFunc )
  {
    case  VTDCMD_REPORT_EVENT :
      pMonRec = (PMONREC)VDHQueryLin( f16p1 );
      if( pMonRec->SGId == PM_SCREENGROUP )
        hvdm = VDHHandleFromSGID( (USHORT)ulExclusiveSG );
      else
        hvdm = VDHHandleFromSGID( pMonRec->SGId );
      if( hvdm && REFHVDM( hvdm, BOOL, fTouchEnabled ) )
        vtAddEvent( hvdm, &pMonRec->QEvent );
      break;
    default  :
      Assert( TRUE );
  }
}


/****************************************************************************
 *
 * FUNCTION NAME = vtNotifyEmulState
 *
 * DESCRIPTION   = Notify DIDD of change in emulation state.
 *   PSEUDO-CODE -
 *      if current VDM is not in foreground
 *          return, nothing to do
 *      otherwise
 *          inform DIDD of emulation state
 *
 *
 * INPUT         = hvdm - VDM handle
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID PRIVENTRY vtNotifyEmulState( HVDM hvdm )
{
  TOUEMULSTATE touEmulState =  {
    sizeof( TOUEMULSTATE ),(BYTE)0
  };

  if( REFHVDM( hvdm, ULONG, ulSstate ) == SESSION_FOREGROUND )
  {
    if( pVDMData( hvdm )->fEmulationOn )
      switch( pVDMData( hvdm )->EmulState.usEmulType )
      {
        case  EMULTYPE_ABSOLUTE :
          touEmulState.fEmulState = 1;
          break;
        case  EMULTYPE_RELATIVE :
        case  EMULTYPE_GLASSMOUSE :
          touEmulState.fEmulState = 2;
          break;
      }
    else
      touEmulState.fEmulState = 0;

    (*fpfnPTDRequestProc)( PTDREQ_SETEMULSTATE, F16PFROMSSP( &touEmulState ),
                           NULL );
  }
}


/****************************************************************************
 *
 * FUNCTION NAME = vtNotifyExclusiveState
 *
 * DESCRIPTION   = Notify PDD of change in exclusive access state.
 *   PSEUDO-CODE -
 *      set/clear relevant VDM's exclusive access state
 *      record exclusive access VDM's screen group id
 *      inform DIDD of emulation state for screen group
 *
 *
 * INPUT         = hvdm      - VDM handle
 *                 ExclState - new state to set/clear
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID PRIVENTRY vtNotifyExclusiveState( HVDM hvdm, BOOL ExclState )
{
  TOUEXCLACCESS touExclAccess =  {
    sizeof( TOUEXCLACCESS ),(BYTE)0
  };

  if( ExclState )
  {
    REFHVDM( hvdm, ULONG, ulSstate ) |= SESSION_EXCLUSIVE;
    ulExclusiveSG = REFHVDM( hvdm, ULONG, ulSG );
  }
  else
  {
    REFHVDM( hvdm, ULONG, ulSstate ) &= ~SESSION_EXCLUSIVE;
    ulExclusiveSG = 0;
  }
  touExclAccess.fExclAccess = (BYTE)ExclState;

  (*fpfnPTDRequestProc)( PTDREQ_EXCLUSIVEACCESS, F16PFROMSSP( &touExclAccess ),
                         NULL );
}


/****************************************************************************
 *
 * FUNCTION NAME = vtAddEvent
 *
 * DESCRIPTION   = Add a touch event to a VDM's event queue.
 *   PSEUDO-CODE -
 *      Add touch event to VDM event buffer (at head, circular queue)
 *          Queue implemented as:
 *              Queue is empty when head == tail
 *              Add events at head,
 *                  advance head pointer
 *                  if head == tail, advance tail (ie. discard oldest data)
 *              Remove from tail,
 *                  advance tail pointer
 *
 *      Arm context hook to delay processing until VDM task time (local
 *      context) and therefore avoid spending too much interrupt-time here.
 *
 *
 * INPUT         = hvdm    -  VDM handle
 *                 pQEvent -> Event packet.
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID PRIVENTRY vtAddEvent( HVDM hvdm, PQEVENT pQEvent )
{
  ULONG flFlags;
  PQEVENT pTmp;
  PPQEVENT ppQHead, ppQTail;

  flFlags = SAVEFLAGS();
  DISABLE( );                           /* Enter critical-section       */
  if( vtFilterEvent( hvdm, pQEvent ) )  /* TRUE if we keep it           */
  {
    ppQTail = pVDM( hvdm, PPQEVENT, &pEventTail );
    ppQHead = pVDM( hvdm, PPQEVENT, &pEventHead );
    pTmp = pVDM( hvdm, PQEVENT, *ppQHead );
    pTmp->x = pQEvent->x;
    pTmp->y = pQEvent->y;
    pTmp->z = pQEvent->z;
    pTmp->Status = pQEvent->Status;
    pTmp->TimeStamp = pQEvent->TimeStamp;
    *ppQHead = NEXTQPTR( *ppQHead );
    if( *ppQHead == *ppQTail )
      *ppQTail = NEXTQPTR( *ppQTail );
  }
  RESTOREFLAGS( flFlags );              /* Exit critical-section        */
  if( !( REFHVDM( hvdm, BOOL, fEventsPending ) ||
         REFHVDM( hvdm, BOOL, fSubActive ) ) )
  {
    REFHVDM( hvdm, BOOL, fEventsPending ) = TRUE; /* ie. hook is armed  */
    VDHArmContextHook( REFHVDM( hvdm, HHOOK, hhookSendEventHook ), hvdm );
  }
}


/****************************************************************************
 *
 * FUNCTION NAME = vtUpdateXYZData
 *
 * DESCRIPTION   = Transfer event packet to local XYZ data structure.
 *   PSEUDO-CODE -
 *      Transfer event packet to local XYZ data structure.
 *
 *
 * INPUT         = pxyz    -> XYZ data structure.
 *                 pQEvent -> Event packet.
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID PRIVENTRY vtUpdateXYZData( PXYZDATA pxyz, PQEVENT p )
{
  pxyz->x = p->x;
  pxyz->y = p->y;
  pxyz->z = p->z;
  pxyz->Status = p->Status;
  pxyz->Count++;
}


/****************************************************************************
 *
 * FUNCTION NAME = vtUpdateOTData
 *
 * DESCRIPTION   =
 *   PSEUDO-CODE -
 *      if data not already sent (only one select between TD and LO)
 *         AND X and Y within tolerance {
 *          save averaged X, Y
 *          if method is not lift-off AND count packets were within tolerance...
 *              Set the detected flag.
 *      } else {
 *          reset counter
 *          save this X, Y as new start for averaging
 *      }
 *      Update stray SingleTouch elements, Z and Status.
 *
 *
 * INPUT         = pvdmdata -> VDM data.
 *                 pQEvent  -> Event packet.
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID PRIVENTRY vtUpdateOTData( register PVDMDATA pvdmdata, PQEVENT pQEvent )
{
  if( !( pvdmdata->smData.usFlag & SM_FLAG_SENT ) &&
      ( abs( pQEvent->x - pvdmdata->xyzSingleTouch.x ) <=
        pvdmdata->smData.usTolerance ) &&
      ( abs( pQEvent->y - pvdmdata->xyzSingleTouch.y ) <=
        pvdmdata->smData.usTolerance ) )
  {
    pvdmdata->xyzSingleTouch.x += pQEvent->x;
    pvdmdata->xyzSingleTouch.x /= 2;
    pvdmdata->xyzSingleTouch.y += pQEvent->y;
    pvdmdata->xyzSingleTouch.y /= 2;
    if( !( pvdmdata->smData.usMethod & SM_LIFT_OFF ) &&
        !( --pvdmdata->smData.usOTCount ) )
      pvdmdata->smData.usFlag |= SM_FLAG_DETECTED;
  }
  else
  {
    pvdmdata->smData.usOTCount = pvdmdata->smData.usCount;
    pvdmdata->xyzSingleTouch.x = pQEvent->x;
    pvdmdata->xyzSingleTouch.y = pQEvent->y;
  }
  pvdmdata->xyzSingleTouch.z = pQEvent->z;
  pvdmdata->xyzSingleTouch.Status = pQEvent->Status;
}


/****************************************************************************
 *
 * FUNCTION NAME = vtUpdOneTouch
 *
 * DESCRIPTION   = Update selection bit using One-Touch algorithm
 *   PSEUDO-CODE -
 *      Note: Only one event between touch-down and lift-off
 *            will be sent with the selection bit set.
 *      Ignore off-screen events.
 *      If event is a touch-down, save away X, Y data, clear flags, counters.
 *      If event is lift-off and method is by lift-off
 *          Update single-touch structure.
 *          Force event to be sent with selection bit set.
 *      If event is on-screen
 *          Update single-touch structure.
 *      If selection criteria have been met (SM_FLAG_DETECTED)
 *         AND event hasn't already been sent with selection bit set {
 *              Update current event with SingleTouch X, Y
 *              Set selection bit.
 *              Flag that an event has been sent.
 *      }
 *
 *
 * INPUT         = hvdm    -  VDM handle
 *                 pQEvent -> Event packet.
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID PRIVENTRY vtUpdOneTouch( HVDM hvdm, PQEVENT pQEvent )
{
  register PVDMDATA pvdmdata = pVDMData( hvdm );

  switch ( pQEvent->Status & STATUS_ON_SCREEN )
  {
    case  STATUS_OFF_SCREEN :
      break;
    case  STATUS_LIFT_OFF :    /* if this is lift-off possibly return data  */
      if( pvdmdata->smData.usMethod & SM_LIFT_OFF )
      {
        vtUpdateOTData( pvdmdata, pQEvent );
        pvdmdata->smData.usFlag |= SM_FLAG_DETECTED; /* force it to be sent */
      }
      break;
    case  STATUS_TOUCH_DOWN :                /* set up data for first point */
      vtUpdateXYZData( &pvdmdata->xyzSingleTouch, pQEvent );
      pvdmdata->smData.usFlag = 0;
      pvdmdata->smData.usOTCount = pvdmdata->smData.usCount;
      break;
    case  STATUS_ON_SCREEN :
      vtUpdateOTData( pvdmdata, pQEvent );
      break;
  }
  if( !( pvdmdata->smData.usFlag & SM_FLAG_SENT ) &&
       ( pvdmdata->smData.usFlag & SM_FLAG_DETECTED ) )
  {
    pQEvent->x = pvdmdata->xyzSingleTouch.x;
    pQEvent->y = pvdmdata->xyzSingleTouch.y;
    pQEvent->Status |= STATUS_SELECT_DETECTED;
    pvdmdata->smData.usFlag |= SM_FLAG_SENT;
  }
}


/****************************************************************************
 *
 * FUNCTION NAME = vtFindStablePoint
 *
 * DESCRIPTION   = Search buffer for stable point.
 *   PSEUDO-CODE -
 *      Note:   Buffer should already contain at least one co-ord
 *              pair before reaching here as vtUpdSPBuffer() is
 *              called before vtCheckSPData() which got us this far.
 *
 *      Set buffer 'pointers' to most recent data.
 *      Use incoming event X, Y as reference.
 *      Begin searching buffer *backwards* for stable point.
 *      Search starts at current buffer position (SPBufferHead has
 *        already been adjusted) and continues until 'Limit' points
 *        have been processed, the buffer is exhausted, or a stable
 *        region is found.
 *      Update StablePoint structure if found.
 *      Return whether found or not.
 *
 *
 * INPUT         = pvdmdata -> VDM data.
 *                 pQEvent  -> Event packet.
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = TRUE  - Stable region found
 * RETURN-ERROR  = FALSE - not found
 *
 ****************************************************************************/

#define  NEXTSPPTR(p)  ((USHORT)((p>=(SPBUFFERMAX-1)) ? (0) : (p+1)))
#define  PREVSPPTR(p)  ((USHORT)((p) ? (p-1) : (SPBUFFERMAX-1)))
BOOL PRIVENTRY vtFindStablePoint( register PVDMDATA pvdmdata, PQEVENT pQEvent )
{
  BOOL rc = FALSE;
  USHORT SPBuffHead, SPBuffTail;
  ULONG ulX = 0, ulY = 0;
  USHORT i = 0, x, y, count;

  SPBuffHead = PREVSPPTR( pvdmdata->smData.usSPBuffHead );
  SPBuffTail = pvdmdata->smData.usSPBuffTail;
  if( SPBuffHead == SPBuffTail )
    SPBuffTail = PREVSPPTR( SPBuffTail );
  pvdmdata->xyzStablePoint.x = pQEvent->x;      /* 1st reference point  */
  pvdmdata->xyzStablePoint.y = pQEvent->y;
  count = pvdmdata->smData.usCount;
  while( (i++ < pvdmdata->smData.usLimit) && (SPBuffHead != SPBuffTail) )
  {
    x = pvdmdata->smData.SPBuffer[SPBuffHead].x;
    y = pvdmdata->smData.SPBuffer[SPBuffHead].y;
    if( (abs(x-pvdmdata->xyzStablePoint.x) <= pvdmdata->smData.usTolerance) &&
        (abs(y-pvdmdata->xyzStablePoint.y) <= pvdmdata->smData.usTolerance) )
    {
      if( !--count )
      {
        count = pvdmdata->smData.usCount;
        for( i = 0; i < count; i++ )
        {
          ulX += pvdmdata->smData.SPBuffer[SPBuffHead].x;
          ulY += pvdmdata->smData.SPBuffer[SPBuffHead].y;
          SPBuffHead = NEXTSPPTR(SPBuffHead);
        }
        pvdmdata->xyzStablePoint.x = (USHORT)( ulX/count );
        pvdmdata->xyzStablePoint.y = (USHORT)( ulY/count );
        rc = TRUE;
        break;
      }
    }
    else
    {
      count = pvdmdata->smData.usCount;
      pvdmdata->xyzStablePoint.x = x;
      pvdmdata->xyzStablePoint.y = y;
    }
    SPBuffHead = PREVSPPTR( SPBuffHead );
  }
  return( rc );
}


/****************************************************************************
 *
 * FUNCTION NAME = vtCheckSPData
 *
 * DESCRIPTION   = Update current event if stable point found.
 *   PSEUDO-CODE -
 *      Update current event (pQEvent) with new data
 *        if stable region found in buffer.
 *
 *
 * INPUT         = pvdmdata -> VDM data.
 *                 pQEvent  -> Event packet.
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID PRIVENTRY vtCheckSPData( register PVDMDATA pvdmdata, PQEVENT pQEvent )
{
  if( !( pvdmdata->smData.usFlag & SM_FLAG_SENT ) )
  {
    if( vtFindStablePoint( pvdmdata, pQEvent ) )
    {
      pQEvent->x = pvdmdata->xyzStablePoint.x;
      pQEvent->y = pvdmdata->xyzStablePoint.y;
      pQEvent->Status |= STATUS_SELECT_DETECTED;
      pvdmdata->smData.usFlag |= SM_FLAG_SENT;
    }
  }
}


/****************************************************************************
 *
 * FUNCTION NAME = vtUpdSPBuffer
 *
 * DESCRIPTION   = Add X, Y co-ordinate pair to Stable Point buffer
 *   PSEUDO-CODE -
 *      Add the incoming X, Y co-ordinates to stable point buffer
 *      at the position indicated by SPBufferHead.
 *      Advance buffer pointers.
 *
 *
 * INPUT         = pvdmdata -> VDM data
 *                 usX      -  X incoming co-ordinate
 *                 usY      -  Y incoming co-ordinate
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID PRIVENTRY vtUpdSPBuffer( PVDMDATA pvdmdata, USHORT usX, USHORT usY )
{
  USHORT SPBuffHead, SPBuffTail;

  SPBuffHead = pvdmdata->smData.usSPBuffHead;
  SPBuffTail = pvdmdata->smData.usSPBuffTail;
  pvdmdata->smData.SPBuffer[SPBuffHead].x = usX;
  pvdmdata->smData.SPBuffer[SPBuffHead].y = usY;
  if( ++SPBuffHead >= SPBUFFERMAX )
    SPBuffHead = 0;
  if( SPBuffTail == SPBuffHead )        /* overrun, advance tail        */
  {
    if( ++SPBuffTail >= SPBUFFERMAX )
      SPBuffTail = 0;
  }
  pvdmdata->smData.usSPBuffHead = SPBuffHead;
  pvdmdata->smData.usSPBuffTail = SPBuffTail;
}


/****************************************************************************
 *
 * FUNCTION NAME = vtUpdStablePoint
 *
 * DESCRIPTION   = Update selection bit using Stable Point algorithm
 *   PSEUDO-CODE -
 *      if event is off-screen, ignore.
 *      if event is touch-down, reset flags, flush queue.
 *      if event is lift-off and method is lift-off
 *          Add event data to stable point buffer head (X and Y)
 *          Look for stable point.
 *      if event is on-screen {
 *          Add event data to stable point buffer head (X and Y)
 *          if selection method is not lift-off and Z > push threshold
 *              Look for stable point.
 *      }
 *
 *
 * INPUT         = hvdm    -  VDM handle
 *                 pQEvent -> Event packet.
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID PRIVENTRY vtUpdStablePoint( HVDM hvdm, PQEVENT pQEvent )
{
  register PVDMDATA pvdmdata = pVDMData( hvdm );

  switch( pQEvent->Status & STATUS_ON_SCREEN )
  {
    case  STATUS_OFF_SCREEN :
      break;
    case  STATUS_TOUCH_DOWN :
      pvdmdata->smData.usFlag = 0;       /* reset flag                  */
      pvdmdata->smData.usSPBuffHead = 0; /* clear queue                 */
      pvdmdata->smData.usSPBuffTail = 0;
      break;
    case  STATUS_ON_SCREEN :             /* add event to buffer         */
      vtUpdSPBuffer( pvdmdata, pQEvent->x, pQEvent->y );
      if( !( pvdmdata->smData.usMethod & SM_LIFT_OFF ) &&
           ( pQEvent->z > pvdmdata->thData.usPushSelection ) )
        vtCheckSPData( pvdmdata, pQEvent );
      break;
    case  STATUS_LIFT_OFF :
      if( pvdmdata->smData.usMethod & SM_LIFT_OFF )
      {
        vtUpdSPBuffer( pvdmdata, pQEvent->x, pQEvent->y );
        vtCheckSPData( pvdmdata, pQEvent );
      }
      break;
  }
}


/****************************************************************************
 *
 * FUNCTION NAME = vtUpdateSDData
 *
 * DESCRIPTION   = Update selection bit using appropriate algorithm
 *   PSEUDO-CODE - NONE
 *
 *
 * INPUT         = hvdm    -  VDM handle
 *                 pQEvent -> Event packet.
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID PRIVENTRY vtUpdateSDData( HVDM hvdm, PQEVENT pQEvent )
{
  register PVDMDATA pvdmdata = pVDMData( hvdm );

  pQEvent->Status &= ~STATUS_SELECT_DETECTED;   /* clear SD bit         */
  switch( pvdmdata->smData.usMethod )
  {
    case  SM_PUSH_HARDER :
      if( ( ( pQEvent->Status & STATUS_ON_SCREEN ) == STATUS_ON_SCREEN ) &&
          ( ( pQEvent->z > pvdmdata->thData.usPushSelection ) ||
            ( ( pQEvent->z > pvdmdata->thData.usPushHysteresis ) &&
              ( pvdmdata->xyzCurrent.Status & STATUS_SELECT_DETECTED ) ) ) )
        pQEvent->Status |= STATUS_SELECT_DETECTED;
      break;
    case  SM_LIFT_OFF :
      if( ( pQEvent->Status & STATUS_ON_SCREEN ) == STATUS_LIFT_OFF )
        pQEvent->Status |= STATUS_SELECT_DETECTED;
      break;
    case  SM_ONETOUCH | SM_PUSH_HARDER :
    case  SM_ONETOUCH | SM_LIFT_OFF :
      vtUpdOneTouch( hvdm, pQEvent );
      break;
    case  SM_STABLE_POINT | SM_PUSH_HARDER :
    case  SM_STABLE_POINT | SM_LIFT_OFF :
      vtUpdStablePoint( hvdm, pQEvent );
      break;
    default  :
      break;
  }
}


/****************************************************************************
 *
 * FUNCTION NAME = vtGoodEvent
 *
 * DESCRIPTION   = Check for     data
 *   PSEUDO-CODE -
 *      if block mask 'all data' bit set
 *          don't allow any data at all.
 *      else if data is pegged & block mask 'pegged data' bit is set
 *          allow data.
 *
 *
 * INPUT         = hvdm    -  VDM handle
 *                 pQEvent -> Event packet.
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

BOOL PRIVENTRY vtGoodEvent( HVDM hvdm, PQEVENT pQEvent )
{
  BOOL rc = FALSE;

  if( !( pVDMData( hvdm )->flBlockMask & BLOCKMASK_ALL_DATA ) )
    rc = !( ( pVDMData( hvdm )->flBlockMask & BLOCKMASK_PEGGED_DATA ) &&
            ( pQEvent->Status & STATUS_CHANNEL_PEGGED ) );
  return( rc );
}


/****************************************************************************
 *
 * FUNCTION NAME = vtRemapXYZ
 *
 * DESCRIPTION   =
 *
 * INPUT         = hvdm    -  VDM handle
 *                 pQEvent -> Event packet.
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID PRIVENTRY vtRemapXYZ( HVDM hvdm, PQEVENT pQEvent )
{
  register PVDMDATA pvdmdata = pVDMData( hvdm );

  pQEvent->x = MAPCOORD( pQEvent->x, pvdmdata->coData.usXMin,
                         pvdmdata->coData.usXMax, MAX_XY );
  pQEvent->y = MAPCOORD( pQEvent->y, pvdmdata->coData.usYMin,
                         pvdmdata->coData.usYMax, MAX_XY );
  pQEvent->z = MAPCOORD( pQEvent->z, pvdmdata->coData.usZMin,
                         pvdmdata->coData.usZMax, MAX_Z );
  if( !pvdmdata->fOriginTopLeft )
    pQEvent->y = pvdmdata->coData.usYMax - pQEvent->y + pvdmdata->coData.usYMin;
}


/****************************************************************************
 *
 * FUNCTION NAME = vtFilterEvent
 *
 * DESCRIPTION   = Filter current event for selections/bad data
 *   PSEUDO-CODE -
 *      Remap current X, Y and Z to current ranges.
 *      Translate Y for screen origin.
 *      if Z value is on-screen
 *          Apply selection detection algorithm if any.
 *          return TRUE
 *      otherwise
 *          return FALSE
 *
 *
 * INPUT         = hvdm    -  VDM handle
 *                 pQEvent -> Event packet.
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = TRUE
 * RETURN-ERROR  = FALSE
 *
 ****************************************************************************/

BOOL PRIVENTRY vtFilterEvent( HVDM hvdm, PQEVENT pQEvent )
{
  register PVDMDATA pvdmdata = pVDMData( hvdm );
  BOOL rc = FALSE;

  if( REFHVDM( hvdm, BOOL, fTouchEnabled ) &&
      ( REFHVDM( hvdm, ULONG, ulSstate ) &
        ( SESSION_FOREGROUND | SESSION_EXCLUSIVE ) ) &&
      vtGoodEvent( hvdm, pQEvent ) )    /* not pegged data etc.         */
  {
    vtRemapXYZ( hvdm, pQEvent );        /* translate co-ords            */
    if( pQEvent->z >= pvdmdata->thData.usOnScreen )
    {
      vtUpdateSDData( hvdm, pQEvent );  /* apply SD algorithms          */
      rc = TRUE;
    }
  }
  return( rc );
}

#pragma  END_GLOBAL_CODE

#pragma  BEGIN_SWAP_CODE


/****************************************************************************
 *
 * FUNCTION NAME = vtSendEvent
 *
 * DESCRIPTION   = Send a touch event to a VDM by simulating an interrupt.
 *   PSEUDO-CODE -
 *      while there are touch events to send and subroutine not active {
 *          if event must be sent to user sub {
 *              set up data to send;
 *              flag user subroutine active;
 *              simulate an interrupt to the VDM;
 *          }
 *          remove event from queue;
 *          update appropriate per-VDM data from event data;
 *      }
 *      set flag for more events pending
 *
 *
 * INPUT         = pRefdata -> VDM handle
 *                 pcrf     -> VDM registers
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID HOOKENTRY vtSendEvent( PHVDM phvdm, PCRF pcrf )
{
  ULONG flFlags, ConditionMask;
  register PVDMDATA pvdmdata = pVDMData( *phvdm );
  QEVENT q;

  while( !REFHVDM( *phvdm, BOOL, fSubActive ) &&
         vtPeekEvent( *phvdm, SSToDS( &q ) ) )
  {
    if( pvdmdata->fEmulationOn && pvdmdata->EmulState.fEmulBeep &&
        ( touDeviceState.fMouState & MOUSTATE_BEEPALLOWED ) )
    {
      if( ( ( q.Status ^ pvdmdata->xyzCurrent.Status ) &
            STATUS_SELECT_DETECTED ) &&
          ( q.Status & STATUS_SELECT_DETECTED ) )    /* beep SD -> ON   */
        VDHDevBeep( EMULBEEP_FREQUENCY, EMULBEEP_DURATION );
    }
    else
      if( vtNeedEvent( *phvdm, SSToDS( &q ), SSToDS( &ConditionMask ) ) )
      {
        pvdmdata->UserSubStack.x = q.x;
        pvdmdata->UserSubStack.y = q.y;
        pvdmdata->UserSubStack.z = q.z;
        pvdmdata->UserSubStack.Status = q.Status;
        pvdmdata->UserSubStack.ConditionMask = (USHORT)ConditionMask;
        REFHVDM( *phvdm, BOOL, fSubActive ) = TRUE;
        VDHSetVIRR( *phvdm, hirq );
      }

    vtRemoveEvent( *phvdm, SSToDS( &q ) );
    vtProcessEvent( *phvdm, SSToDS( &q ) );
  }
  flFlags = SAVEFLAGS( );
  DISABLE( );                           /* enter critical-section       */
  REFHVDM( *phvdm, BOOL, fEventsPending ) = vtPeekEvent( *phvdm, NULL );
  RESTOREFLAGS( flFlags );              /* exit critical-section        */
}


/****************************************************************************
 *
 * FUNCTION NAME = vtPeekEvent
 *
 * DESCRIPTION   = Return whether events to send and if so its contents.
 *   PSEUDO-CODE -
 *      Enter critical section
 *      if queue is empty
 *          return FALSE
 *      else
 *          return TRUE and a copy of the event to send
 *      Exit critical section
 *
 *
 * INPUT         = hvdm    -  VDM handle
 *                 pQEvent -> Event packet.
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = TRUE  - Queue has data, copy
 * RETURN-ERROR  = FALSE - Queue is empty.
 *
 ****************************************************************************/

BOOL PRIVENTRY vtPeekEvent( HVDM hvdm, PQEVENT p )
{
  BOOL rc = FALSE;
  PQEVENT pTail;
  ULONG flFlags;

  flFlags = SAVEFLAGS( );
  DISABLE( );                           /* enter critical-section       */
  pTail = REFHVDM( hvdm, PQEVENT, pEventTail );
  if( pTail != REFHVDM( hvdm, PQEVENT, pEventHead ) )
  {
    if( p )
      *p = *pVDM( hvdm, PQEVENT, pTail ); /* Queue is not empty so return   */
    rc = TRUE;                          /* contents of current 'tail' event */
  }
  RESTOREFLAGS( flFlags );              /* exit critical-section        */
  return( rc );
}


/****************************************************************************
 *
 * FUNCTION NAME = vtRemoveEvent
 *
 * DESCRIPTION   = Remove an event from the event-queue.
 *   PSEUDO-CODE -
 *      Enter critical section
 *      if event queue head != tail {
 *          if tail->timestamp == pqevent->timestamp
 *              advance the tail pointer (ie. remove the event)
 *          else
 *              event has been overwritten (ie. do nothing)
 *      }
 *      Exit critical section
 *
 *
 * INPUT         = hvdm    -  VDM handle
 *                 pQEvent -> Event packet.
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID PRIVENTRY vtRemoveEvent( HVDM hvdm, PQEVENT pQEvent )
{
  ULONG flFlags;
  PPQEVENT ppQTail;

  flFlags = SAVEFLAGS( );
  DISABLE( );
  ppQTail = pVDM( hvdm, PPQEVENT, &pEventTail );
  if( ( *ppQTail != *pVDM( hvdm, PPQEVENT, &pEventHead ) ) &&
      !memcmp( pVDM( hvdm, PQEVENT, *ppQTail ), pQEvent, sizeof( QEVENT ) ) )
    *ppQTail = NEXTQPTR( *ppQTail );
  RESTOREFLAGS( flFlags );
}


/****************************************************************************
 *
 * FUNCTION NAME = vtNeedEvent
 *
 * DESCRIPTION   = Determine whether user-sub must be called for this event.
 *   PSEUDO-CODE -
 *      form up condition mask from current status;
 *      ( The structure 'xyzCurrent' contains the previous
 *        event's data sent to the VDM and only gets updated
 *        by vtProcessEvent after the user-sub call. 'pQEvent'
 *        points to the current event that may be sent. )
 *      if condition mask satisfies callmask && user-sub has been hooked
 *         AND the event doesn't have off-screen status
 *          return TRUE and the condition mask;
 *      else
 *          return FALSE;
 *
 *
 * INPUT         = hvdm      -  VDM handle
 *                 pQEvent   -> Event packet to be sent.
 *                 pCondMask -> Return condition mask.
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = TRUE  - Event can be sent
 * RETURN-ERROR  = FALSE - Nothing to send
 *
 ****************************************************************************/

BOOL PRIVENTRY vtNeedEvent( HVDM hvdm, PQEVENT pQEvent, PULONG CondMask )
{
  BOOL rc = FALSE;
  /* NICKY if ( pVDMData( hvdm )->vpUserSub != NULL ) { */

  if( pVDMData( hvdm )->fpfnUserSub != NULL )
  {
    *CondMask = 0;
    switch( pQEvent->Status & STATUS_ON_SCREEN )
    {
      case  STATUS_OFF_SCREEN :
        return( rc );                   /* explicitly return FALSE      */
      case  STATUS_LIFT_OFF :
        *CondMask |= CALLMASK_LIFT_OFF;
        break;
      case  STATUS_TOUCH_DOWN :
        *CondMask |= CALLMASK_TOUCH_DOWN;
        break;
      default  :
        break;
    }
    if( ( pQEvent->x != pVDMData( hvdm )->xyzCurrent.x ) ||
        ( pQEvent->y != pVDMData( hvdm )->xyzCurrent.y ) )
      *CondMask |= CALLMASK_XY_CHANGED;
    if( pQEvent->z != pVDMData( hvdm )->xyzCurrent.z )
      *CondMask |= CALLMASK_Z_CHANGED;

    /*            - If status is different AND selection has changed */
    if( (pQEvent->Status ^ pVDMData( hvdm )->xyzCurrent.Status) &
        STATUS_SELECT_DETECTED )
    {
      if( pQEvent->Status & STATUS_SELECT_DETECTED )
        *CondMask |= CALLMASK_SELECT_TO_ON;
      else
        *CondMask |= CALLMASK_SELECT_TO_OFF;
    }
    rc = !!( pVDMData( hvdm )->flCallMask & *CondMask );
  }
  return( rc );
}


/****************************************************************************
 *
 * FUNCTION NAME = vtProcessEvent
 *
 * DESCRIPTION   = Update per-VDM data with event data
 *   PSEUDO-CODE - NONE
 *
 *
 * INPUT         = hvdm    -  VDM handle
 *                 PQEVENT -> event data
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID PRIVENTRY vtProcessEvent( HVDM hvdm, PQEVENT p )
{
  register PVDMDATA pvdmdata = pVDMData( hvdm );

  switch( p->Status & STATUS_ON_SCREEN )
  {
    case  STATUS_TOUCH_DOWN :
      vtUpdateXYZData( &pvdmdata->xyzLastTouchDown, p );
      break;
    case  STATUS_LIFT_OFF :
      vtUpdateXYZData( &pvdmdata->xyzLastLiftOff, p );
      break;
    default  :
      break;
  }
  if( ( p->Status ^ pvdmdata->xyzCurrent.Status ) & STATUS_SELECT_DETECTED )
  {
    if (p->Status & STATUS_SELECT_DETECTED )
      vtUpdateXYZData( &pvdmdata->xyzLastSelectOn, p );
    else
      vtUpdateXYZData( &pvdmdata->xyzLastSelectOff, p );
  }
  vtUpdateXYZData( &pvdmdata->xyzCurrent, p );
}

#pragma  END_SWAP_CODE
