/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Microsoft Corporation, 1989                                 */
/* 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 = VMHOOK.C
 *
 * DESCRIPTIVE NAME = Virtual Mouse Device Driver User-event Processing
 *
 *
 * VERSION     V2.0
 *
 * DATE        12/10/91
 *
 * DESCRIPTION This module contains the VMD's user-event handlers.
 *
 *
 * FUNCTIONS
 *     VMInt33Hook()        : Int 33h processing and dispatch
 *     VMUserSubIntHook()   :  Simulated hardware interrupt hook
 *     VMUserSubVPMIntHook(): Simulated hardware VPM interrupt hook
 *     VMUserSubReturnHook(): User-defined subroutine return processing
 *
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/
#include "vmdp.h"

#ifdef   VDDSTRICT
MODNAME = __FILE__;
#endif

/*
**    External References
*/

extern PFNINT33 apfnInt33Services[];
extern HIRQ hirq;
extern BOOL fEventsPending;
extern ULONG iSubActive;
extern HHOOK hhookUserSubReturnHook;   /* hook handle for VMUserSubReturnHook*/
extern HHOOK hhookSendEventHook;
BOOL VMCheckContext(VOID);             /*            */

#pragma  BEGIN_SWAP_CODE

 /****************************************************************************
 *
 * FUNCTION NAME  : VMInt33Hook()
 *
 * DESCRIPTION    : Int 33h processing and dispatch
 *
 *                  Verifies the given Int 33h request is for us, and then
 *                  dispatches to the appropriate worker routine.
 *
 *  INPUT         :
 *                  pcrf -> VDM register frame
 *
 *  OUTPUT        :
 *                  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 a valid function request
 *          route to the correpsonding handler;
 *
 ****************************************************************************/

BOOL HOOKENTRY
VMInt33Hook(register PCRF pcrf)
{
     AssertTRUE(VDMData.fScrInit);
     VDHPopInt();
     if (AX(pcrf) <= INT33_MAXFUNC) {
          if (*apfnInt33Services[AX(pcrf)] != NULL) {
               return((*apfnInt33Services[AX(pcrf)])(pcrf));
          }
     }
     return(FALSE);
}                                      /* VMInt33Hook                        */

 /****************************************************************************
 * FUNCTION NAME :  VMUserSubIntHook()
 *
 * DESCRIPTION   :  Simulated hardware interrupt hook
 *
 *  Intercepts simulated hardware interrupts before they start executing
 *  in VDM space, eats the interrupt (since there may or may not even be a
 *  valid interrupt handler installed in the VDM), tells VPIC to EOI the
 *  interrupt, saves all the VDM's registers on its own stack, and then sets
 *  VDM execution up for a far call to its "user-defined mouse event
 *  subroutine", with return hook.  There is such a subroutine, because
 *  otherwise an interrupt would not have been simulated.
 *
 *  The return hook will restore the VDM's registers, and return to the VDM,
 *  so that it may continue normal processing.  We may also request another
 *  interrupt to be simulated at that time, if another event is pending.
 *
 *  INPUT         :
 *                  pcrf -> VDM register frame
 *
 *  OUTPUT        :
 *                  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
 *      eat the interrupt to disallow chaining;
 *      clear interrupt;
 *      send virtual EOI;
 *      save all VDM registers;
 *      prepare VDM registers for user sub. callout;
 *      put callout address on the stack;
 *      arm return hook to regain control after callout;
 *      reset active sub. indicator;
 *      if sendevent was blocked because of an active sub {
 *          reset blocked flag;
 *          kick sendevent back into action;
 *      }
 *
 ****************************************************************************/

BOOL HOOKENTRY VMUserSubIntHook(PCRF pcrf)
{
  HVDM hvdm = CURRENT_VDM;
  BOOL rc = FALSE;
  USHORT ausInt15Words[4] = { 0,0,0,0 };

  if (VDHQueryVIRQ(hvdm, hirq)&VPICQ_REQUEST_PENDING)
  {

       /*
       ** Eat the interrupt
       */

    VDHPopInt();

       /*
       ** Clear the interrupt
       */

    VDHClearVIRR(CURRENT_VDM, hirq);

       /*
       ** EOI the interrupt
       */

    VDHSendVEOI(hirq);

    /*            - If we entered on the context of the last DPMI client to  */
    /* register with vmouse, process the interrupt.  Else, eat the interrupt */
    if ( VMCheckContext() )
    {
           /*
           ** Push all VDM's general-purpose registers
           */

        VDHPushRegs(VDHREG_GENERAL);
        if (iSubActive != SUB_INT15)
        {

              /*
              ** Set up VDM registers before calling out
              */

          AX(pcrf) = (WORD)VDMData.flEventMask;
          if ((iSubActive == SUB_NORMAL) &&
              VDMData.fPassAbs && VMData.fAbsPt) {
              AX(pcrf)|=MEVSTAT_ABSPIXEVENT;
          }
          BX(pcrf) = (WORD)VDMData.flButtons;

          CX(pcrf) = (WORD)(VDMData.mstates.xCur << VDMData.fShiftX);
          SI(pcrf) = (WORD)(VDMData.mstates.nxMics << VDMData.fShiftX);

          if (VDMData.vmss.vmss_ulCellHeight == 1)           /* graphics mode    */
            DX(pcrf) = (WORD)VDMData.mstates.yCur;
          else                                               /* text mode        */
            DX(pcrf) = (WORD)((VDMData.mstates.yCur *8)/
               VDMData.vmss.vmss_ulCellHeight);

          DI(pcrf) = (WORD)VDMData.mstates.nyMics;

              /*
              ** Set up return hook for call
              */

          VDHArmReturnHook(hhookUserSubReturnHook, VDHARH_CSEIP_HOOK);

              /*
              ** Prepare for call to activated user-defined subroutine
              */

          VDHPushFarCall(VDMData.mstates.subinfo[iSubActive].si_SubAddr);
        }
        else
        {
          if (VDMData.flEventMask&6)
            ausInt15Words[3] |= 1;        /* Left button Down                    */
          if (VDMData.flEventMask&0x18)   /* Right button down                   */
            ausInt15Words[3] |= 2;
          if (VDMData.mstates.nxMics < 0)
            ausInt15Words[3] |= 0x10;

       /*
       ** NOTE: The INT 15h interface takes point (0,0) to be the
       ** lower left hand corner of the screen, Unlike INT 33h and
       ** OS/2 APIs which take point (0,0) to be the upper left hand
       ** corner of the screen. Therefore a negative y mickey count
       ** is really a positive movement and vise versa.
       */

          if (VDMData.mstates.nyMics > 0)
            ausInt15Words[3] |= 0x20;

          ausInt15Words[2] = (BYTE)VDMData.mstates.nxMics;
          ausInt15Words[1] = (BYTE)(0-VDMData.mstates.nyMics);

          VDMData.mstates.nxMics = VDMData.mstates.nyMics = 0;
          VDHPushStack ( 8,SSToDS( &ausInt15Words[0] ) );

              /*
              ** Prepare for call to activated user-defined subroutine
              */

          VDHPushFarCall(VDMData.mstates.int15info.ii_SubAddr);

              /*
              ** Catch return from call
              */

          VDHArmReturnHook(hhookUserSubReturnHook, VDHARH_NORMAL_RET);
        }
    /*            - START */
    }
    else
    {
        /* Since the VMUserSubReturnHook return hook is not armed, process */
        /* any pending events now */

        iSubActive = SUB_NONE;
        if (fEventsPending)
          vmSendEvent((PHVDM)SSToDS(&hvdm), NULL);
    }
    rc = TRUE;
    /*            - END */
  }
  return (rc);
}                                      /* VMUserSubIntHook                   */

 /***************************************************************************
 *
 * FUNCTION NAME  : VMUserSubVPMIntHook()           
 *
 * DESCRIPTION    : Simulated hardware VPM interrupt hook
 *
 *  Intercepts the simulated hardware interrupts in VPM mode, and calls
 *  calls VMUserSubIntHook to process the interrupt.
 *
 *  INPUT         :
 *                   pcrf -> VDM register frame
 *                   p    ->
 *  OUTPUT        :
 *                   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
 *      call VMUserSubIntHook
 *      if IRQ not for mouse, chain to next VPM handler
 *
 ***************************************************************************/

BOOL HOOKENTRY VMUserSubVPMIntHook(PVOID p,PCRF pcrf)          /*            */
{
  ULONG ulrc;
  if ((!(VDMData.mstates.subinfo[SUB_NORMAL].si_flCallMask&    /*            */
     CALLMASK_VPMHANDLER)) || !(ulrc = VMUserSubIntHook(pcrf)))
  {                                                            /*            */

    /*
    ** IRQ isn't for mouse, so chain to next interrupt handler
    */

    pcrf->crf_cs = SEGMENTOF32(VDMData.pappdata->pad_fpfnOriginalVPMIRQ);
    if (flVdmStatus&VDM_STATUS_VPM_32)
    {
      pcrf->crf_eip = OFFSETOF32(VDMData.pappdata->pad_fpfnOriginalVPMIRQ);
    }
    else
    {
      IP(pcrf) = (WORD)OFFSETOF32(VDMData.pappdata->pad_fpfnOriginalVPMIRQ);
    }
  }
  return (ulrc);
}

 /****************************************************************************
 *  FUNCTION NAME : VMUserSubReturnHook()
 *
 *  DESCRIPTION   : User-defined subroutine return processing
 *
 *  Restores the VDM's registers, and returns to the VDM, so that it may
 *  continue normal processing.  We may also request another interrupt to be
 *  simulated at this time, if more events are pending.
 *
 *  INPUT         :
 *                   p      -> undefined (not used)
 *                   pcrf   -> VDM register frame
 *
 *  OUTPUT        :  NONE.
 *
 *  USES          :
 *                   32-bit small-model PASCAL calling/register conventions
 *
 *  CONTEXT       :
 *                   VDM Task-time
 *
 *  PSEUDO-CODE   :                                 REFERENCES
 *      restore VDM registers prior callout;
 *      decrement interrupt nesting level;
 *      if sendevent was blocked because of exceeding max. int. level nesting {
 *          reset blocked flag;
 *          kick sendevent back into action;
 *      }
 *
 ****************************************************************************/

VOID HOOKENTRY VMUserSubReturnHook(PVOID p,PCRF pcrf)
{
  HVDM hvdm = CURRENT_VDM;
  USHORT ausInt15Words[4];

  if (iSubActive == SUB_INT15)
    VDHPopStack(8, SSToDS(&ausInt15Words[0]));

    /*
    ** Restore the VDM's registers
    */

  VDHPopRegs(VDHREG_GENERAL);

    /*
    ** Reset active subroutine indicator
    */

  iSubActive = SUB_NONE;
  if (fEventsPending)
    vmSendEvent((PHVDM)SSToDS(&hvdm), NULL);
}                                                /* VMUserSubReturnHook      */
#pragma  END_SWAP_CODE
