
/**************************************************************************
 *
 * SOURCE FILE NAME = VMEVENT.C
 *
 * DESCRIPTIVE NAME = Virtual Mouse Device Driver Event Services
 *
 * Copyright : COPYRIGHT IBM CORPORATION, 1991, 1992
 *             Copyright Microsoft Corporation, 1990
 *             LICENSED MATERIAL - PROGRAM PROPERTY OF IBM
 *             REFER TO COPYRIGHT INSTRUCTION FORM#G120-2083
 *             RESTRICTED MATERIALS OF IBM
 *             IBM CONFIDENTIAL
 *
 * VERSION =   V2.0
 *
 * DATE        01/10/92
 *
 * DESCRIPTION This module contains the interrupt-time notification procedure
 *             called by the PMD, as well other related event support.
 *
 * FUNCTIONS
 *
 *  vmInt33SetUserSub       Int 33h Set User Routine (function 12)
 *  vmInt33SwapUserSub      Int 33h Swap User Routine (function 20)
 *  vmFindAltUserSub        Find Alternate User Routine
 *  vmInt33SetAltUserSub    Int 33h Set Alternate User Routine (function 24)
 *  vmInt33QueryAltUserSub  Int 33h Query Alternate User Routine (function 25)
 *  VMEventProc             Process interrupt-time event notifications
 *  vmMickeysToPixels       Translate mickeys into pixels
 *  vmAddEvent              Add event to mouse event buffer
 *  vmSendEvent             Send mouse event to VDM
 *  vmClearEvent            Clear Mouse Event Buffer
 *  vmPeekEvent             Peek next event from the event buffer
 *  vmRemoveEvent           Remove next event from the event buffer
 *  vmNeedEvent             Determine whether VDM needs PDD event
 *  vmEvent2CallMask        Transform PDD Event Mask into Call Mask
 *  vmProcessEvent          Update INT 33h state info from PDD event data
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
 * CHANGE ACTIVITY =
 *  DATE      FLAG        APAR     CHANGE DESCRIPTION
 *  --------  ----------  -----    --------------------------------------
 *    mm/dd/yy  @Vr.mpppxx  xxxxx      xxxxxxx
 *1.1 07/14/92  @V2.0MAD01  FEA 25979  Added support for DPMI apps
 *    09/26/92  @V2.0WEB01  DEF 46593  Fixed check for call to
 *                                         VDHArmContextHook twice which will
 *                                         cause IPE.
 *    10/06/92  @V2.0WEB02  DEF 55422  Regression for DEF 46593 where
 *                                         variable needed to be in instance
 *                                         data instead of global data.
 *1.4 11/15/92  @V2.0MAD02  DEF 56786  Check if V86 or protect mode handler
 *    07/08/94  86565  Change Team     Change SendEvent logic to process only
 *                                     one event at a time rather than loop
 *                                     until there are no more events.  This
 *                                     ensures all events are sent to the vdm.
 *    12/01/94  92508  Change Team     Modified fix for 86565 which caused
 *                                     regressions in some dos games. This defect was originally fixed in
 *                                     R206 and is now being merged into r207
 *    08/03/96  160647 CN              Took out 92508 change.  This caused major
 *                                     regressions in win/os2 full screen.  Mouse
 *                                     virtually would not work.
 ****************************************************************************/

#define  INCL_MI                      /* @V2.0MAD02                          */
#include "vmdp.h"
#include <math.h>
#include <seldesc.h>                  /* @V2.0MAD02                          */

#ifdef   VDDSTRICT
MODNAME = __FILE__;
#endif


/*
**    External References
*/

extern HIRQ hirq;
extern MEVBUFF amevBuffer[]; /* ring buffer for mouse events                 */
extern PMEVBUFF pEventHead;  /* pointer to head of mouse events ring buffer  */
extern PMEVBUFF pEventTail;  /* pointer to tail of mouse events ring buffer  */
extern BOOL fMouseEnabled;
extern BOOL fEventsPending;
extern ULONG iSubActive;
extern HHOOK hhookSendEventHook;
extern LONG nxMickeysRemain;
extern LONG nyMickeysRemain;
extern USHORT nThreshold;
extern USHORT uxScale;
extern USHORT uyScale;
extern SHORT nxPixelsRemain;
extern SHORT nyPixelsRemain;
extern ULONG fsstate;
extern BOOL fContextHookFlag;         /* @V2.0WEB02                          */
#pragma  BEGIN_SWAP_CODE


/****************************************************************************
 *
 * FUNCTION NAME = vmInt33SetUserSub
 *
 * DESCRIPTION   = Int 33h Set User Routine (function 12)
 *
 *
 * INPUT         = (register PCRF pcrf)
 *
 * OUTPUT        = Exit  EMULATED
 *                           TRUE
 *                       NOT EMULATED
 *                           FALSE (pass control to next VDD and/or ROM)
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 *
 *  USES
 *      32-bit small-model PASCAL calling/register conventions
 *
 *  CONTEXT
 *      VDM Task-time
 *
 *  PSEUDO-CODE                                     REFERENCES
 *      clear shift state bits from call mask;
 *      remap center button mask from call12 format to standard format;
 *      save call mask;
 *      save function entry point;
 *
 **************************************************************************/


BOOL PRIVENTRY vmInt33SetUserSub( register PCRF pcrf )
{
  register ULONG fl;
  register ULONG ulacc;               /* @V2.0MAD02                         */

  fl = CX(pcrf)&CALL12MASK_ALLOWED;

  if (fl&CALL12MASK_CENTERDOWN)
  {
    fl |= CALLMASK_CENTERDOWN;
    fl &= ~CALL12MASK_CENTERDOWN;
  }
  if (fl&CALL12MASK_CENTERUP)
  {
    fl |= CALLMASK_CENTERUP;
    fl &= ~CALL12MASK_CENTERUP;
  }

/*
** If there is a DPMI app in the VDM, get the access rights for the code
** selector passed in to determine if it is a V86 or protect mode handler
*/

  if (flVdmStatus&VDM_STATUS_VPM_APP)
  {                                    /* @V2.0MAD02 begin                   */
    ulacc = (ULONG)ES(pcrf);

    _asm {
          lar  ulacc, ulacc            /* get the access rights              */
          jz   short VMI33Inv          /* ZF means valid selector            */
          xor  ulacc,ulacc             /* invalid, clear access rights       */
          VMI33Inv:
         }

  /*
  ** If this is a valid ring 3 code selector, set VPMHANDLER flag
  */

    if (((ulacc >> 8)&(D_PRIV|D_SEG|D_CODE)) == (D_DPL3|D_SEG|D_CODE))
    {
      fl |= CALLMASK_VPMHANDLER;
    }
  }                                   /* @V2.0MAD02 end                      */

  VDMData.mstates.subinfo[SUB_NORMAL].si_flCallMask = fl;
  SEGMENTOF32(VDMData.mstates.subinfo[SUB_NORMAL].si_SubAddr) = ES(pcrf);
  OFFSETOF32(VDMData.mstates.subinfo[SUB_NORMAL].si_SubAddr) = DX(pcrf);

  return  TRUE;

}                                      /* vmInt33SetUserSub                  */


/****************************************************************************
 *
 * FUNCTION NAME = vmInt33SwapUserSub
 *
 * DESCRIPTION   = Int 33h Swap User Routine (function 20)
 *
 *
 * INPUT         = (register PCRF pcrf)
 *
 * OUTPUT        = Exit EMULATED
 *                          TRUE
 *               = NONE NOT EMULATED
 *                          FALSE (pass control to next VDD and/or ROM)
 *
 *  USES
 *      32-bit small-model PASCAL calling/register conventions
 *
 *  CONTEXT
 *      VDM Task-time
 *
 *  PSEUDO-CODE                                     REFERENCES
 *      save old call mask and old sub. entry point;
 *      call vmInt33SetUserSub to set new user sub.;
 *      store old call mask into VDM CX;
 *      store old sub. segment into VDM BX;
 *      store old sub. offset into VDM DX;
 **************************************************************************/


BOOL PRIVENTRY vmInt33SwapUserSub( register PCRF pcrf )
{
  FPFN fpfnOldSubAddr;
  ULONG flMaskOld;

  fpfnOldSubAddr = VDMData.mstates.subinfo[SUB_NORMAL].si_SubAddr;
  flMaskOld = VDMData.mstates.subinfo[SUB_NORMAL].si_flCallMask;

  if (flMaskOld&CALLMASK_CENTERDOWN)
  {
    flMaskOld &= ~CALLMASK_CENTERDOWN;
    flMaskOld |= CALL12MASK_CENTERDOWN;
  }
  if (flMaskOld&CALLMASK_CENTERUP)
  {
    flMaskOld &= ~CALLMASK_CENTERUP;
    flMaskOld |= CALL12MASK_CENTERUP;
  }
  vmInt33SetUserSub(pcrf);
  CX(pcrf) = (WORD)flMaskOld;
  ES(pcrf) = (SHORT)SEGMENTOF32(fpfnOldSubAddr);

  if ((flVdmStatus&VDM_STATUS_VPM_EXEC)&(flVdmStatus&VDM_STATUS_VPM_32))
  {                                                /* @V2.0MAD01             */
    pcrf->crf_edx = OFFSETOF32(fpfnOldSubAddr);    /* @V2.0MAD01             */
  }
  else
  {
    DX(pcrf) = (SHORT)OFFSETOF32(fpfnOldSubAddr);
  }
  return  TRUE;
}                                      /* vmInt33SwapUserSub                 */


/****************************************************************************
 *
 * FUNCTION NAME = vmFindAltUserSub
 *
 * DESCRIPTION   = Find Alternate User Routine
 *
 *
 * INPUT         = (HVDM hvdm,ULONG flEvMask)
 *
 * OUTPUT        = Exit
 *            returns  SUB_ALT* if match found
 *            returns -SUB_ALT* as the empty slot if no match found
 *            returns  0 if no match found and no empty slot or invalid EvMask
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 *
 *  USES
 *      32-bit small-model PASCAL calling/register conventions
 *
 *  CONTEXT
 *      Task-time
 *
 *  PSEUDO-CODE                                     REFERENCES
 *      if a shift button event {
 *          search event mask in alt user sub table;
 *          if found
 *              return slot number;
 *          else if found empty slot;
 *              return -slot number;
 *          else
 *              return 0;
 *      } else
 *          return 0;
 ***************************************************************************/


LONG PRIVENTRY vmFindAltUserSub( HVDM hvdm, ULONG flEvMask )
{
  register INT i;
  INT iEmpty = 0;                     /* assume no empty slot                */

  register PVDMDATA pvd = pVDMData(hvdm);
  if (!(flEvMask&CALLMASK_BUTTONSHIFTS))
    return 0;
  for (i = SUB_ALT1; i < MAX_USERSUBS; i++)
  {
    if ( !( pvd->mstates.subinfo[i].si_flCallMask&CALLMASK_BUTTONSHIFTS ) )
      iEmpty = i;                     /* remember empty slot                 */
    else
      if ( (pvd->mstates.subinfo[i].si_flCallMask&CALLMASK_BUTTONSHIFTS ) ==
         ( flEvMask&CALLMASK_BUTTONSHIFTS ) )
        return  i;
  }

  return -iEmpty;
}                                      /* vmFindAltUserSub                   */


/****************************************************************************
 *
 * FUNCTION NAME = vmInt33SetAltUserSub
 *
 * DESCRIPTION   = Int 33h Set Alternate User Routine (function 24)
 *
 *
 * INPUT         = (register PCRF pcrf)
 *
 * OUTPUT        = Exit: EMULATED
 *                          TRUE
 *                      NOT EMULATED
 *                          FALSE (pass control to next VDD and/or ROM)
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 *
 *  USES
 *      32-bit small-model PASCAL calling/register conventions
 *
 *  CONTEXT
 *      VDM Task-time
 *
 *  PSEUDO-CODE                                     REFERENCES
 *      if cannot find event mask in alt user sub table
 *          set VDM AX to INT33_ERROR;
 *      else
 *       {
 *          store alt user sub mask and entry point;
 *          if entry point is NULL
 *              zero mask in the slot;
 *       }
 *
 **************************************************************************/


BOOL PRIVENTRY vmInt33SetAltUserSub( register PCRF pcrf )
{
  register INT iSub;

  if ( ( iSub = abs ( vmFindAltUserSub( CURRENT_VDM,(ULONG)CX(pcrf) )) ) == 0 )
    AX(pcrf) = INT33_ERROR;
  else
  {
    SEGMENTOF32(VDMData.mstates.subinfo[iSub].si_SubAddr) = ES(pcrf);
    OFFSETOF32(VDMData.mstates.subinfo[iSub].si_SubAddr) = DX(pcrf);
    VDMData.mstates.subinfo[iSub].si_flCallMask
        = VDMData.mstates.subinfo[iSub].si_SubAddr?(ULONG)CX(pcrf):0;
  }
  return  TRUE;
}                                      /* vmInt33SetAltUserSub               */



/****************************************************************************
 *
 * FUNCTION NAME = vmInt33QueryAltUserSub
 *
 * DESCRIPTION   = Int 33h Query Alternate User Routine (function 25)
 *
 *
 * INPUT         = (register PCRF pcrf)
 *
 * OUTPUT        = Exit
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = Exit : EMULATED  TRUE
 *                        NOT EMULATED  FALSE ( pass control to
 *                                          next VDD and /or ROM)
 *
 *
 *
 *
 *  USES
 *      32-bit small-model PASCAL calling/register conventions
 *
 *  CONTEXT
 *      VDM Task-time
 *
 *  PSEUDO-CODE                                     REFERENCES
 *      if cannot find event mask in alt user sub table {
 *          set VDM AX to INT33_ERROR;
 *          clear VDM BX, CX, DX;
 *      } else {
 *          store mask found in VDM CX;
 *          store alt user sub segment in VDM BX;
 *          store alt user sub offset in VDM DX;
 *      }
 *************************************************************************/



BOOL PRIVENTRY vmInt33QueryAltUserSub( register PCRF pcrf )
{
  register INT iSub;
  if ( ( iSub = vmFindAltUserSub( CURRENT_VDM, (ULONG)CX(pcrf) ) ) <= 0 )
  {
    AX(pcrf) = INT33_ERROR;
    BX(pcrf) = CX(pcrf) = DX(pcrf) = 0;
  }
  else
  {
    CX(pcrf) = (WORD)VDMData.mstates.subinfo[iSub].si_flCallMask;
    BX(pcrf) = SEGMENTOF32(VDMData.mstates.subinfo[iSub].si_SubAddr);
    DX(pcrf) = OFFSETOF32(VDMData.mstates.subinfo[iSub].si_SubAddr);
  }
  return  TRUE;
}                                      /* vmInt33QueryAltUserSub             */

#pragma  END_SWAP_CODE
#pragma  BEGIN_GLOBAL_CODE


/****************************************************************************
 *
 * FUNCTION NAME = VMEventProc
 *
 * DESCRIPTION   = Process interrupt-time event notifications
 *
 *
 * INPUT         = (ULONG ulFunc,F16PVOID f16p1,F16PVOID f16p2)
 *
 *                  uFunc == function code (VMDCMD_NOTIFYEVENT)
 *                  f16p1 -> MEVENT
 *                  f16p2 == undefined (reserved)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 *
 *  USES
 *      32-bit small-model PASCAL calling/register conventions
 *
 *  CONTEXT
 *      Interrupt-time - called by PMSE
 *
 *  PSEUDO-CODE                                     REFERENCES
 *      if NOTIFYEVENT command
 *
 *        if valid vdm handle AND
 *        if mouse is enabled for the VDM AND
 *        if this VDM session is ready to accept events
 *           add the mouse event to the event buffer of the VDM
 *************************************************************************/


VOID VDDENTRY VMEventProc( ULONG ulFunc, F16PVOID f16p1, F16PVOID f16p2 )
{

  PVOID p1;
  register HVDM hvdm;

  BOOL fNullEvent = FALSE;
  AssertTRUE(ulFunc == VMDCMD_NOTIFYEVENT);
  p1 = VDHQueryLin(f16p1);
  hvdm = VDHHandleFromSGID(((PMEV)p1)->mev_uSG);

  if ( hvdm && REFHVDM ( hvdm, BOOL, fMouseEnabled ) &&
          ( REFHVDM(hvdm, ULONG, fsstate )&SESSION_READY ) )
  {
    if ( ( ( (PMEV)p1 )->mev_mi.minfo_fs&MEVSTAT_ABSPIXEVENT ) == 0 )
      vmMickeysToPixels( (PMINFO)&( ( (PMEV)p1)->mev_mi) );

    vmAddEvent(hvdm, (PMINFO)&(((PMEV)p1)->mev_mi),
                      (ULONG)VDHQueryKeyShift(hvdm), TRUE);
  }
}                                      /* VMEventProc                        */


/****************************************************************************
 *
 * FUNCTION NAME = vmMickeysToPixels
 *
 * DESCRIPTION   = Translate mickeys into pixels
 *
 *
 * INPUT         = (PMINFO pmi)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 *
 * CONTEXT
 *          Interrupt-time
 *
 * PSEUDOCODE
 *          scale x mickeys to pixels;
 *          if raw x mickeys > threshold
 *              x pixels *= 2;
 *          scale y mickeys to pixels;
 *          if raw y mickeys > threshold
 *              y pixels *= 2;
 *************************************************************************/


VOID PRIVENTRY vmMickeysToPixels( PMINFO pmi )
{
  LONG nxPixels,nyPixels;

  pmi->nxMickeys = pmi->minfo_nx;     /* save delta x Mickeys                */
  pmi->nyMickeys = pmi->minfo_ny;     /* save delta y Mickeys                */
  if (abs(pmi->minfo_nx) > nThreshold)
    pmi->minfo_nx *= 2;
  nxPixels = pmi->minfo_nx*8 + nxMickeysRemain;
  nxMickeysRemain = nxPixels % (SHORT)uxScale;
  pmi->minfo_nx = nxPixels/(SHORT)uxScale;

  if (abs(pmi->minfo_ny) > nThreshold)
    pmi->minfo_ny *= 2;
  nyPixels = pmi->minfo_ny*8 + nyMickeysRemain;
  nyMickeysRemain = nyPixels % (SHORT)uyScale;
  pmi->minfo_ny = nyPixels/(SHORT)uyScale;
}                                      /* vmMickeysToPixels                  */


/****************************************************************************
 *
 * FUNCTION NAME = vmAddEvent
 *
 * DESCRIPTION   = Add event to mouse event buffer
 *
 *                 Adds a mouse event to the event buffer.  If the buffer is
 *                 full, drop the event.  Otherwise, add the mouse event.
 *                 Then check to see if an interrupt should be simulated now.
 *
 * INPUT         = (HVDM hvdm,PMINFO pmi,ULONG flKeyShift,BOOL fInterrupt)
 *
 *                 hvdm  = VDM handle
 *                 pmi = far pointer to the mouse info. packet
 *                 flKeyShift = keyboard shift states
 *                 fInterrupt = TRUE=>called at interrupt time
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 *
 *      CONTEXT
 *          Interrupt
 *
 * PSEUDOCODE
 *     if buffer is not full
 *         add mouse event to the buffer tail;
 *         advance buffer tail pointer;
 *     endif
 *     if no context hook pending
 *       arm a global context hook to delay processing until task time;
 *****************************************************************************/

VOID PRIVENTRY vmAddEvent(HVDM hvdm,PMINFO pmi,
                           ULONG flKeyShift,BOOL fInterrupt)
{
  PMEVBUFF pEvent,pNext;
  PPMEVBUFF ppEvent;
  ULONG ulFlags;

  ulFlags = SAVEFLAGS();
  DISABLE();

  ppEvent = pVDM(hvdm, PPMEVBUFF, &pEventTail);   /* ppEvent->pEventTail     */
  pNext = NEXTPTR(*ppEvent, MBUFFHEAD, MBUFFTAIL);/* pNext->next location    */

  if (pNext != REFHVDM(hvdm, PMEVBUFF, pEventHead))
  {                                               /* buffer is not full      */
    pEvent = pVDM(hvdm, PMEVBUFF, *ppEvent);      /* pEvent -> tail slot     */
    pEvent->mevb_mi = *pmi;
    pEvent->mevb_fsKeyShift = (USHORT)flKeyShift;
    *ppEvent = pNext;                 /* pEventTail points to next position  */
  }
  if (!fInterrupt)
    vmSendEvent((PHVDM)SSToDS(&hvdm), NULL);
  else
    if (!REFHVDM(hvdm, BOOL, fContextHookFlag))
    {                                                    /* @V2.0WEB02       */
      REFHVDM(hvdm, BOOL, fContextHookFlag) = TRUE;      /* @V2.0WEB02       */
      VDHArmContextHook(REFHVDM(hvdm, HHOOK, hhookSendEventHook), hvdm);
    }

  RESTOREFLAGS(ulFlags);
}                                      /* vmAddEvent                         */
#pragma  END_GLOBAL_CODE


#pragma  BEGIN_SWAP_CODE
/****************************************************************************
 *
 * FUNCTION NAME = vmSendEvent
 *
 * DESCRIPTION   = Send mouse event to VDM
 *                 Determines if a mouse event is available in the buffer,sends
 *                 it to the VDM by simulating an interrupt.
 *
 * INPUT         = (PHVDM phvdm,PCRF pcrf)
 *                  phvdm = pointer to VDM handle
 *                  pcrf  = pointer to client register frame (not used)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 *      CONTEXT
 *          Task-time (VDM task or global task)
 *
 *      PSEUDOCODE
 *          while there is still event in the buffer {
 *              yield after processing some amount of events;
 *              if user sub. needs this event {
 *                  if a user sub callout is already active {
 *                      indicate sendevent blocked by an active user sub.;
 *                      break;
 *                  } else if exceed max. int. nesting level {
 *                      indicate sendevent blocked by exceeding int. level;
 *                      break;
 *                  } else {
 *                      store which user sub. to invoke and the event mask;
 *                      increment interrupt nesting level;
 *                      simulate an interrupt to the VDM
 *                  }
 *              }
 *              update the virtual mouse state according to the event;
 *              removed the processed event from the buffer;
 *          }
 *          if sendevent was not blocked {
 *              enter critical section;
 *              if more mouse events are in the buffer
 *                  arm a global context hook to reactive this routine;
 *              else
 *                  indicate no more event is in process;
 *              exit critical section;
 *          }
 *****************************************************************************/



VOID HOOKENTRY vmSendEvent( PHVDM phvdm, PCRF pcrf )
{
  register INT i;
  ULONG nLoops = MAX_EVENTYIELD;
  register PVDMDATA pvd = pVDMData(*phvdm);
  ULONG flEvMask;
  MEVBUFF mevb;

    /*
    ** If a call to VDHArmContextHook has been made AND pcrf parm
    ** is not NULL, which means we have been called from the outside
    ** via the VDHArmContextHook registration of this routine.
    */

  if ( REFHVDM(*phvdm, BOOL, fContextHookFlag ) && pcrf != NULL )/*@V2.0WEB02*/
       REFHVDM(*phvdm, BOOL, fContextHookFlag) = FALSE;          /*@V2.0WEB02*/

  VDHWakeIdle(*phvdm);


   /* 92508 - If waiting for a return hook (see iSubActive), don't process    */
  /*          any more mouse events.  This ensures all events get simulated. */

  while ( REFHVDM(*phvdm, BOOL, fEventsPending ) =
                      vmPeekEvent( *phvdm, SSToDS ( &mevb ) ) )  /* && took out 92508 */
 /*         ( REFHVDM(*phvdm, ULONG, iSubActive ) == SUB_NONE ) )  */    /*92508*/
  {

     /*
     ** Yield after so many events
     */

    if (!nLoops--)
    {
      VDHYield(!VDH_YIELD_TIME_CRITICAL);
      nLoops = MAX_EVENTYIELD;
    }
    i = vmNeedEvent(*phvdm, (ULONG)mevb.mevb_mi.minfo_fs, (ULONG)
       mevb.mevb_fsKeyShift, SSToDS(&flEvMask));

    if ((ULONG)mevb.mevb_mi.minfo_fs & MEVSTAT_ABSPIXEVENT) {
       pvd->fAbsPt=TRUE;
    }
    else {
       pvd->fAbsPt=FALSE;
    }

        /*
        ** If event must be sent to a registered INT 33H user subroutine
        */

    if ( i != SUB_NONE )
    {
      if ( REFHVDM( *phvdm, ULONG, iSubActive ) == SUB_NONE )
      {
        REFHVDM(*phvdm, ULONG, iSubActive) = i;
        pvd->flEventMask = flEvMask;
        VDHSetVIRR(*phvdm, hirq);
      }
    }

        /*
        ** If no INT 33h user subroutines need this event, we will check
        ** to see if an INT 15H user subroutine needs to be called
        */

    else
      if (pvd->mstates.int15info.ii_SubAddr != NULL &&
         pvd->mstates.int15info.ii_fstatus&0x20)
      {
        if (REFHVDM(*phvdm, ULONG, iSubActive) == SUB_NONE)
        {
          REFHVDM(*phvdm, ULONG, iSubActive) = SUB_INT15;
//        pvd->flEventMask = (ULONG)mevb.mevb_mi.minfo_fs,
//                               VDHSetVIRR(*phvdm, hirq);
          pvd->flEventMask = (ULONG)mevb.mevb_mi.minfo_fs;
          VDHSetVIRR(*phvdm, hirq);
        }
      }

        /*
        ** we are done with it, get rid of it
        */

    AssertRC(vmRemoveEvent(*phvdm, SSToDS(&mevb)));

        /*
        ** Update INT 33h state info with the event just read
        */

    vmProcessEvent(*phvdm, SSToDS(&mevb), flEvMask);
  }
}                                      /* vmSendEvent                        */


/****************************************************************************
 *
 * FUNCTION NAME = vmClearEvent
 *
 * DESCRIPTION   = Clear Mouse Event Buffer
 *
 *                 Flushes the virtual mouse event buffer.  This is generally
 *                 done if the mouse is disabled for the VDM.
 *
 * INPUT         = (HVDM hvdm)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 *
 *      CONTEXT
 *          Task-time
 *
 *      PSEUDOCODE
 *          reset both pEventHead and pEventTail
 ****************************************************************************/

VOID PRIVENTRY vmClearEvent(HVDM hvdm)
{
  REFHVDM(hvdm, PMEVBUFF, pEventHead) = REFHVDM(hvdm, PMEVBUFF, pEventTail)
                                      = MBUFFHEAD;
}                                      /* vmClearEvent                       */


 /****************************************************************************
 * FUNCTION NAME = vmPeekEvent
 *
 * DESCRIPTION   = Peek next event from the event buffer
 *
 *                 Peek the next event from the event buffer.  If the buffer is
 *                 empty, an error is returned.
 *
 *                 Note that we don't have to enter critical section in
 *                 this procedure because interrupt time procedures will
 *                 only change the tail pointer but not the head pointer
 *                 and we are just read accessing the header pointer here.
 *                 If the caller is concerning the interrupt time routines
 *                 changing the tail pointer after this routine determines
 *                 the event buffer being empty, the caller should enter
 *                 critical section before calling this procedure.
 *
 * INPUT         = (HVDM hvdm,PMEVBUFF pmevb)
 *
 * OUTPUT        = EXIT
 *                   SUCCESS
 *                       returns TRUE and the contents of the event
 *                   FAILURE
 *                       returns FALSE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 *
 * CONTEXT
 *          Task-time
 *
 * PSEUDOCODE
 *          if buffer empty
 *            return FALSE              //buffer empty
 *          else
 *            if buffer pointer is not null
 *              return event contents
 *            return TRUE
 *          endif
 ****************************************************************************/


BOOL PRIVENTRY vmPeekEvent( HVDM hvdm, PMEVBUFF pmevb )
{
  PMEVBUFF pEvent;

  pEvent = REFHVDM(hvdm, PMEVBUFF, pEventHead);

  if ( pEvent == REFHVDM( hvdm, PMEVBUFF, pEventTail ) )
    return  FALSE;

  if ( pmevb )
    *pmevb = *pVDM(hvdm, PMEVBUFF, pEvent);

  return  TRUE;
}                                      /* vmPeekEvent                        */


/****************************************************************************
 *
 * FUNCTION NAME = vmRemoveEvent
 *
 * DESCRIPTION   = Remove next event from the event buffer
 *
 *                 Removes a event from the event buffer.  If the buffer is
 *                 empty, an error is returned.
 *
 * INPUT         = (HVDM hvdm,PMEVBUFF pmevb)
 *
 *
 * OUTPUT        = EXIT
 *                     SUCCESS
 *                         returns TRUE
 *                     FAILURE
 *                         returns FALSE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 *
 * CONTEXT
 *          Task-time
 *
 * PSEUDOCODE
 *          disable interrupts
 *          if buffer empty
 *            enable interrupts
 *            return FALSE              //buffer empty
 *          else
 *            advance head pointer
 *            enable interrupts
 *            return TRUE
 *          endif
 ****************************************************************************/


BOOL PRIVENTRY vmRemoveEvent ( HVDM hvdm, PMEVBUFF pmevb )
{
  PPMEVBUFF ppEvent;
  BOOL fSuccess = FALSE;              /* assume failure                      */
  ULONG ulFlags;
  ulFlags = SAVEFLAGS();
  DISABLE();

  ppEvent = pVDM(hvdm, PPMEVBUFF, &pEventHead);  /* ppEvent -> pEventHead    */

  if (*ppEvent != REFHVDM(hvdm, PMEVBUFF, pEventTail))
  {                                              /* buffer is not empty      */
    if (pmevb)
      *pmevb = *pVDM(hvdm, PMEVBUFF, *ppEvent);
    *ppEvent = NEXTPTR(*ppEvent, MBUFFHEAD, MBUFFTAIL);
    fSuccess = TRUE;
  }
  RESTOREFLAGS(ulFlags);
  return  fSuccess;
}                                      /* vmRemoveEvent                      */


/****************************************************************************
 *
 * FUNCTION NAME = vmNeedEvent
 *
 * DESCRIPTION   = Determine whether VDM needs PDD event
 *
 *
 * INPUT         = (HVDM hvdm,ULONG flPDD,ULONG flKeyShift,PULONG
 *                    pflEvMask)
 *
 *                  hvdm      -> VDM
 *                  flPDD     =  PDD mouse event
 *                  pflEvMask -> Event Mask
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 *
 *      CONTEXT
 *          Task-time
 *
 *      PSEUDOCODE
 *          disable interrupts
 *          if buffer empty
 *            enable interrupts
 *            return FALSE              //buffer empty
 *          else
 *            advance head pointer
 *            enable interrupts
 *            return TRUE
 *          endif
 ***************************************************************************/


ULONG PRIVENTRY vmNeedEvent( HVDM hvdm,ULONG flPDD,ULONG flKeyShift,PULONG
                             pflEvMask )
{
  register INT i;
  register PVDMDATA pvd = pVDMData(hvdm);

  *pflEvMask = vmEvent2CallMask(flPDD,
                                flKeyShift,
                                pvd->flButtons);

  i = vmFindAltUserSub(hvdm, *pflEvMask);

/*
**  vmFindAltUserSub only matches the shift state class but not the
**  mouse event states, so we have check further
*/

  if ( ( i > 0 ) && ( *pflEvMask&pvd->mstates.subinfo[i].si_flCallMask&~
     CALLMASK_BUTTONSHIFTS ) )
  {
    *pflEvMask &= pvd->mstates.subinfo[i].si_flCallMask;
    return  i;
  }

/*
** If an alternate user sub is not interested in this event we then
** check if the default user sub should get this event.
*/

  if ( ( *pflEvMask&pvd->mstates.subinfo[SUB_NORMAL].si_flCallMask ) )
  {
    *pflEvMask &= pvd->mstates.subinfo[SUB_NORMAL].si_flCallMask;
    return  SUB_NORMAL;
  }
  return  SUB_NONE;
}                                      /* vmNeedEvent                        */


/****************************************************************************
 *
 * FUNCTION NAME = vmEvent2CallMask
 *
 * DESCRIPTION   =  Transform PDD Event Mask into Call Mask
 *
 *
 * INPUT         = (ULONG flPDD,ULONG flKFlag,ULONG flButtons)
 *
 *                  flPDD     == PDD event mask
 *                  flKFlag   == VDM keyboard shift states
 *                  flButtons == VDM button states
 *
 * OUTPUT        = return call mask from PDD event mask
 *                 return event mask if VDM wanted event
 *                 return zero if VDM did not want the event
 *
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 *
 *  USES
 *      32-bit small-model PASCAL calling/register conventions
 *
 *  CONTEXT
 *      Task-time
 *
 *  PSEUDO-CODE                                     REFERENCES
 *      check PDD event mask with actual button and keyboard shift states;
 *      set corresponding call mask bits;
 ****************************************************************************/


ULONG PRIVENTRY vmEvent2CallMask(ULONG flPDD,ULONG flKFlag,ULONG flButtons)
{
   register ULONG  flAll;

   flAll =
       !!(flPDD & MEVSTAT_ALLMOTION) * CALLMASK_MOTION |
       (flPDD & (MEVSTAT_LEFTDOWN + MEVSTAT_MOTIONLEFT) &&
           !(flButtons & BUTBIT_LEFT)) * CALLMASK_LEFTDOWN |
       (!(flPDD & (MEVSTAT_LEFTDOWN + MEVSTAT_MOTIONLEFT)) &&
           flButtons & BUTBIT_LEFT) * CALLMASK_LEFTUP |
       (flPDD & (MEVSTAT_RIGHTDOWN + MEVSTAT_MOTIONRIGHT) &&
           !(flButtons & BUTBIT_RIGHT)) * CALLMASK_RIGHTDOWN |
       (!(flPDD & (MEVSTAT_RIGHTDOWN + MEVSTAT_MOTIONRIGHT)) &&
           flButtons & BUTBIT_RIGHT) * CALLMASK_RIGHTUP |
       (flPDD & (MEVSTAT_CENTERDOWN + MEVSTAT_MOTIONCENTER) &&
           !(flButtons & BUTBIT_CENTER)) * CALLMASK_CENTERDOWN |
       (!(flPDD & (MEVSTAT_CENTERDOWN + MEVSTAT_MOTIONCENTER)) &&
           flButtons & BUTBIT_CENTER) * CALLMASK_CENTERUP;

   if (flAll & (CALLMASK_LEFTDOWN   + CALLMASK_LEFTUP +
                CALLMASK_RIGHTDOWN  + CALLMASK_RIGHTUP +
                CALLMASK_CENTERDOWN + CALLMASK_CENTERUP))
       flAll |=
         !!(flKFlag & BIOSKFLAG_ALT) * CALLMASK_ALTBUTTON |
         !!(flKFlag & BIOSKFLAG_CTRL) * CALLMASK_CTRLBUTTON |
         !!(flKFlag & BIOSKFLAG_SHIFTMASK) * CALLMASK_SHIFTBUTTON;

   return flAll;

}                                      /* vmEvent2CallMask                   */




/****************************************************************************
 *
 * FUNCTION NAME = vmProcessEvent
 *
 * DESCRIPTION   = Update INT 33h state info from PDD event data
 *
 *
 * INPUT         = (HVDM hvdm,register PMEVBUFF pmevb,ULONG flEvent)
 *
 *                 hvdm    -> VDM
 *                 pmev    -> PDD event data
 *                 flEvent =  PDD event mask
 *
 * OUTPUT        = Appropriate instance data updated
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 *
 *  USES
 *      32-bit small-model PASCAL calling/register conventions
 *
 *  CONTEXT
 *      Task-time
 *
 *  PSEUDO-CODE                                     REFERENCES
 *      translate mouse PDD coordinates into virtual coordinates;
 *      calculate number of delta X/Y mickeys;
 *      translate mouse button event bits into button state bits;
 *      update button press/release info;
 *      if mouse motion event
 *          call vmSetPosition to reposition the new mouse pointer location;
 ****************************************************************************/


VOID PRIVENTRY vmProcessEvent(HVDM hvdm,register PMEVBUFF pmevb,ULONG flEvent)
{
  register PVDMDATA pvd = pVDMData(hvdm);
  ULONG xCur,yCur;
  if (pmevb->mevb_mi.minfo_fs&MEVSTAT_ABSPIXEVENT)
  {
    pmevb->mevb_mi.minfo_fs &= ~MEVSTAT_ABSPIXEVENT;
    pvd->mstates.nxMics += pmevb->mevb_mi.minfo_nx-pvd->mstates.xCur;
    pvd->mstates.nyMics += pmevb->mevb_mi.minfo_ny-pvd->mstates.yCur;
    xCur = vmBoundedX(hvdm, pmevb->mevb_mi.minfo_nx);
    yCur = vmBoundedY(hvdm, pmevb->mevb_mi.minfo_ny);
  }
  else
  {
    if (pvd->vmss.vmss_ulCellHeight != 1)
    {                                 /* text mode                           */
      pmevb->mevb_mi.minfo_nx += nxPixelsRemain;
      pmevb->mevb_mi.minfo_ny += nyPixelsRemain;
    }
    pvd->mstates.nxMics += pmevb->mevb_mi.nxMickeys;
    pvd->mstates.nyMics += pmevb->mevb_mi.nyMickeys;
    xCur = vmBoundedX(hvdm, pvd->mstates.xCur+pmevb->mevb_mi.minfo_nx);
    yCur = vmBoundedY(hvdm, pvd->mstates.yCur+pmevb->mevb_mi.minfo_ny);
    if (pvd->vmss.vmss_ulCellHeight != 1)
    {                                 /* text mode                           */
      nxPixelsRemain = (LONG)xCur%(LONG)pvd->vmss.vmss_ulCellWidth;
      nyPixelsRemain = (LONG)yCur%(LONG)pvd->vmss.vmss_ulCellHeight;
    }
  }

  pvd->flButtons =
  !!(pmevb->mevb_mi.minfo_fs & (MEVSTAT_LEFTDOWN   + MEVSTAT_MOTIONLEFT))  *
      BUTBIT_LEFT |
  !!(pmevb->mevb_mi.minfo_fs & (MEVSTAT_RIGHTDOWN  + MEVSTAT_MOTIONRIGHT)) *
      BUTBIT_RIGHT |
  !!(pmevb->mevb_mi.minfo_fs & (MEVSTAT_CENTERDOWN + MEVSTAT_MOTIONCENTER))*
      BUTBIT_CENTER;

  if (flEvent&CALLMASK_LEFTDOWN)
  {
    pvd->mstates.butinfo[BUTTON_LEFT].bi_nPresses++;
    pvd->mstates.butinfo[BUTTON_LEFT].bi_xLastPress = xCur;
    pvd->mstates.butinfo[BUTTON_LEFT].bi_yLastPress = yCur;
  }
  if (flEvent&CALLMASK_LEFTUP)
  {
    pvd->mstates.butinfo[BUTTON_LEFT].bi_nReleases++;
    pvd->mstates.butinfo[BUTTON_LEFT].bi_xLastRelease = xCur;
    pvd->mstates.butinfo[BUTTON_LEFT].bi_yLastRelease = yCur;
  }
  if (flEvent&CALLMASK_RIGHTDOWN)
  {
    pvd->mstates.butinfo[BUTTON_RIGHT].bi_nPresses++;
    pvd->mstates.butinfo[BUTTON_RIGHT].bi_xLastPress = xCur;
    pvd->mstates.butinfo[BUTTON_RIGHT].bi_yLastPress = yCur;
  }
  if (flEvent&CALLMASK_RIGHTUP)
  {
    pvd->mstates.butinfo[BUTTON_RIGHT].bi_nReleases++;
    pvd->mstates.butinfo[BUTTON_RIGHT].bi_xLastRelease = xCur;
    pvd->mstates.butinfo[BUTTON_RIGHT].bi_yLastRelease = yCur;
  }
  if (flEvent&CALLMASK_CENTERDOWN)
  {
    pvd->mstates.butinfo[BUTTON_CENTER].bi_nPresses++;
    pvd->mstates.butinfo[BUTTON_CENTER].bi_xLastPress = xCur;
    pvd->mstates.butinfo[BUTTON_CENTER].bi_yLastPress = yCur;
  }
  if (flEvent&CALLMASK_CENTERUP)
  {
    pvd->mstates.butinfo[BUTTON_CENTER].bi_nReleases++;
    pvd->mstates.butinfo[BUTTON_CENTER].bi_xLastRelease = xCur;
    pvd->mstates.butinfo[BUTTON_CENTER].bi_yLastRelease = yCur;
  }
  if (flEvent&CALLMASK_MOTION)
    vmSetPosition(hvdm, xCur, yCur, FALSE);
}                                      /* vmProcessEvent                     */
#pragma  END_SWAP_CODE

