/*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 = VMDEVRQ.C
 *
 * DESCRIPTIVE NAME = Virtual Mouse Device Request Processing
 *
 *
 * VERSION =   V2.0
 *
 * DATE        02/20/92
 *
 * DESCRIPTION Virtual Mouse Device Request Processing
 *
 *             This module contains the VMD's VDHRequestVDD support,
 *             requests from which come in two flavors:
 *
 *              1. Register pointer functions
 *              2. Set new video mode/screen size
 *
 * FUNCTIONS
 *
 * VMDeviceReqProc()          VDHRequestVDD router
 *  vmRegisterVVD()            Record entry points for Video VDD
 *  vmSetScreenSize()          Accept new screen dimensions
 *  vmPMDNotification()        Notify Physical mouse driver about screen changes
 *  vmSeamlessNotification()   SEAMLESS NOTIFICATION
 *  vmProtInt33Entry()         Handle protect mode int 33 requests
 *  vmDPMITask()               DPMI Task begin/end notification
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#include "vmdp.h"

#ifdef   VDDSTRICT
MODNAME = __FILE__;
#endif

/*
**    External References
*/

extern FPFNPDD fpfnPMRequestProc;
extern PVVINFO pvvinfoHead;
extern PVVINFO pvvinfoTail;
extern SHORT nxPixelsRemain;
extern SHORT nyPixelsRemain;

extern ULONG fsstate;
extern USHORT usMouExclusive;
extern HHOOK hhookUserSubIntHook;/*hook handlefor VMUserSubIntHook           */
extern PFNINT33 apfnInt33Services[];  /*                                     */
extern MHARDWARE mhwInstalled;        /*                                     */
extern ULONG iSubActive;                                         /*          */

BOOL VMCheckContext(VOID)   ;                                    /*          */

#pragma BEGIN_SWAP_INSTANCE  /*            - START */

PDHD   pdhdData;             /* Last Current DPMI task data structure */
extern PDHD pdhdHostData;    /* Current DPMI task data structure */

#pragma END_SWAP_INSTANCE    /*            - END   */

#pragma  BEGIN_SWAP_CODE

/**************************************************************************
* FUNCTION NAME  :  VMDeviceReqProc()
*
* DESCRIPTION    :  VDHRequestVDD router
*
*            This subroutine is registered during VDD initialization via
*            VDHRegisterVDD, and receives requests from video VDDs.
*            See VMD.H for a description of the input/output buffers, if any.
*
*  INPUT         :
*                   hvdm    -> VDM
*                   ulFunc  == function code
*                   pbufIn  -> input buffer
*                   pbufOut -> output buffer (not used here)
*
*  OUTPUT        :
*                   SUCCESS
*                       TRUE
*                   FAILURE
*                       FALSE (ie, VDD not owner, function not supported, etc)
*  USES          :
*                  32-bit small-model PASCAL calling/register conventions
*
*  CONTEXT       :
*                    Task-time
*
*  PSEUDO-CODE   :                                  REFERENCES
*      based on function code                          ulFunc
*        route to appropriate worker function          vmRegisterVVD()
*                                                      vmSetScreenSize()
*
*****************************************************************************/

BOOL EXPENTRY VMDeviceReqProc(HVDM hvdm, ULONG ulFunc,
                               PVOID pbufIn, PVOID pbufOut)
{

  if (ulFunc == VMDEVREQ_REGISTER)
    return  vmRegisterVVD(hvdm, pbufIn, pbufOut);

  else if (ulFunc == VMDEVREQ_SETSIZE)
      return  vmSetScreenSize(hvdm, pbufIn);

  else if (ulFunc == VMDEVREQ_SEAMLESS)
      return  vmSeamlessNotification(hvdm, pbufIn);

  else if ((ulFunc == VMDEVREQ_PROTINT33) && hhookUserSubIntHook)/*          */
      return  vmProtInt33Entry(hvdm, pbufIn);                    /*          */

  else if (ulFunc == VMDEVREQ_DPMITASK)                          /*          */
      return  vmDPMITask(hvdm, (BOOL)pbufIn);                    /*          */

  else
      return  FALSE;

}                                      /* VMDeviceReqProc                    */

/**************************************************************************
 *  FUNCTION NAME : vmRegisterVVD()
 *
 *  DESCRIPTION   : Record entry points for Video VDD
 *
 *  This subroutine processes the VMDEVREQ_REGISTER request.  There is no
 *  defined limit on the number of VDDs that may register services.  For a
 *  given service, we simply walk the function list in the order registered,
 *  calling services until a non-zero return code is received.  Entries
 *  for any functions not registered must be zeroed (a null pointer may also
 *  be passed if no entries are to be registered).
 *
 *  Video VDDs should not attempt to register until the first VDM creation,
 *  since there is no guarantee of when the virtual mouse driver will be
 *  loaded and initialized.  Because of this requirement, no "de-registration"
 *  service is provided.
 *
 *  INPUT         :
 *
 *                     hvdm    -> VDM
 *                     pvmreg  -> video function registration packet
 *                     pvmfunc -> mouse function return packet
 *
 *  OUTPUT        :
 *      SUCCESS
 *          TRUE (registered)
 *      FAILURE
 *          FALSE (not registered -- too many video VDDs/not enough memory)
 *
 *  USES          :
 *                  32-bit small-model PASCAL calling/register conventions
 *
 *  CONTEXT       :
 *                    Task-time
 *
 *  PSEUDO-CODE                                     REFERENCES
 *      if video driver passed anything in {
 *          allocate memory for the new video info;
 *          chain it to the end of the video info list;
 *          copy the new video info into the new structure;
 *      }
 *      if video driver is able to issue light pen query request
 *          give it the address;
 ****************************************************************************/

BOOL PRIVENTRY vmRegisterVVD( HVDM hvdm,
                              register PVMREG pvmreg,
                              register PVMFUNC pvmfunc )
{
  PVOID p;

    /*
    ** If video driver passed anything in, record it
    */

  if (pvmreg)
  {
    p = VDHAllocMem(sizeof(VVINFO), VDHAM_SWAPPABLE);
    if (!p)
      return  FALSE;
    if (!pvvinfoTail)
      pvvinfoHead = pvvinfoTail = p;
    else
    {
      pvvinfoTail->vvi_pvvinfoNext = p;
      pvvinfoTail = p;
    }
    pvvinfoTail->vvi_vmreg = *pvmreg;
    pvvinfoTail->vvi_pvvinfoNext = NULL;
  }

    /*
    ** If video driver can accept it, give light pen request address
    */

  if ( pvmfunc )
    if ( pvmfunc->vmfunc_nb >= sizeof(VMFUNC) )
      pvmfunc->vmfunc_pfnQueryStatus = VMQueryStatus;
  return  TRUE;
}                                      /* vmRegisterVVD                      */

 /************************************************************************
 * FUNCTION NAME  : vmSetScreenSize()
 *
 * DESCRIPTION    : Accept new screen dimensions
 *
 *  This subroutine processes the VMDEVREQ_SETSIZE request.  It is
 *  generally only called by the video driver, following the completion
 *  an Int 10h reset in the current VDM.  However, it uses the given HVDM
 *  to make the interface more flexible.
 *
 *  INPUT        :
 *                    hvdm --> VDM
 *                    pvmsiz -> screen mode/size packet
 *
 *  OUTPUT       :
 *                           SUCCESS
 *                             TRUE
 *  USES         :
 *                 32-bit small-model PASCAL calling/register conventions
 *
 *  CONTEXT      :
 *                 Task-time
 *
 *  PSEUDO-CODE  :                                   REFERENCES
 *
 *      store new video information;
 *      if screen width is 320
 *          set fShiftX;
 *      else
 *          clear fShiftX;
 *      inform the mouse PDD about the new screen size;
 *      if this is the first time we set screen size on this VDM {
 *          indicate we have set the screen size at least once;
 *          do a software reset;
 *      } else {
 *          re-initialize some mode-related int33 state info.;
 *          set mouse position to the default place;
 *      }
 *****************************************************************************/

BOOL PRIVENTRY vmSetScreenSize(register HVDM hvdm, register PVMSSIZE pvmss)
{
  register PVDMDATA pvd = pVDMData(hvdm);
  ULONG ulOldWidth,ulOldHeight;
  MSSIZE mss;

  ulOldWidth = pvd->vmss.vmss_ulWidth;
  ulOldHeight = pvd->vmss.vmss_ulHeight;

    /*
    ** Stash a copy of the structure in our private instance data
    */

  pvd->vmss = *pvmss;

    /*
    ** Re-init other mode-related Int 33h state info
    */

    if (pvd->vmss.vmss_ulWidth == 320)
      pvd->fShiftX = 1;
    else
      pvd->fShiftX = FALSE;
  nxPixelsRemain = nyPixelsRemain = 0;

    /*
    ** Notify the physical mouse driver of the mode change
    */

  vmPMDNotification(hvdm);             /* PenPM EMI change                   */
  if (!pvd->fScrInit)
  {
    pvd->fScrInit++;

        /*
        ** Reset everything, including masks
        */

    vmSoftReset(hvdm);
  }
  else
  {

        /*
        ** The video driver turned the pointer off for us, so make a note
        */

    pvd->mstates.fPtrHidden = PTR_HIDDEN;
    pvd->mstates.xMin = pvd->mstates.yMin = 0;
    pvd->mstates.xMax = pvd->vmss.vmss_ulWidth-1;
    pvd->mstates.yMax = pvd->vmss.vmss_ulHeight-1;

    /*
    ** Reset the graphics masks only, and notify video driver
    */

    vmResetMasks(hvdm, FALSE);
    vmSetPosition(hvdm,
                  (pvd->mstates.xCur *pvd->vmss.vmss_ulWidth)/ulOldWidth,
                  (pvd->mstates.yCur *pvd->vmss.vmss_ulHeight)/ulOldHeight,
                  TRUE);
  }
  REFHVDM(hvdm, ULONG, fsstate) |= SESSION_READY;
  return  TRUE;
}                                      /* vmSetScreenSize                    */


/*
** Start of New code for PenPM EMI change
*/

 /***************************************************************************
 * FUNCTION NAME  : vmPMDNotification()
 *
 * DESCRIPTION    : Notify Physical mouse driver about screen changes
 *
 *  INPUT         :
 *                     hvdm   -> VDM
 *  OUTPUT        :
 *                  SUCCESS
 *                   TRUE
 *  USES          :
 *                 32-bit small-model PASCAL calling/register conventions
 *
 *  CONTEXT       :
 *                  Task-time
 *
 *  PSEUDO-CODE   :                                  REFERENCES
 *
 *   inform the mouse PDD about the new screen size in a VIO_SETMODE packet
 *****************************************************************************/

BOOL PRIVENTRY vmPMDNotification(register HVDM hvdm)
{
  register PVDMDATA pvd = pVDMData(hvdm);
  MSSIZE mss;

    /*
    ** Notify the physical mouse driver of the mode change
    */

  mss.mss_nb = sizeof(mss);
  mss.mss_gcol_res = (USHORT)(pvd->vmss.vmss_ulWidth);
  mss.mss_grow_res = (USHORT)(pvd->vmss.vmss_ulHeight);
  mss.mss_tcol_res = (USHORT)(pvd->vmss.vmss_ulWidth /
                              pvd->vmss.vmss_ulCellWidth );
  mss.mss_trow_res = (USHORT)(pvd->vmss.vmss_ulHeight/
                              pvd->vmss.vmss_ulCellHeight);

  switch (pvd->vmss.vmss_lMode)
  {
    case 0x00000000 :
    case 0x00000002 :
      {
        mss.mss_type = (UCHAR)5;
        mss.mss_color = (UCHAR)4;
        break;
      }
    case 0x00000001 :
    case 0x00000003 :
      {
        mss.mss_type = (UCHAR)1;
        mss.mss_color = (UCHAR)4;
        break;
      }
    case 0x00000006 :
    case 0x00000011 :
      {
        mss.mss_type = (UCHAR)3;
        mss.mss_color = (UCHAR)1;
        break;
      }
    case 0x0000000D :
    case 0x0000000E :
    case 0x00000010 :
    case 0x00000012 :
      {
        mss.mss_type = (UCHAR)3;
        mss.mss_color = (UCHAR)4;
        break;
      }
    case 0x00000004 :
      {
        mss.mss_type = (UCHAR)3;
        mss.mss_color = (UCHAR)2;
        break;
      }
    case 0x00000005 :
      {
        mss.mss_type = (UCHAR)7;
        mss.mss_color = (UCHAR)2;
        break;
      }
    case 0x00000007 :
      {
        mss.mss_type = (UCHAR)0;
        mss.mss_color = (UCHAR)0;
        break;
      }
    case 0x0000000F :
      {
        mss.mss_type = (UCHAR)2;
        mss.mss_color = (UCHAR)0;
        break;
      }
    case 0x00000013 :
      {
        mss.mss_type = (UCHAR)3;
        mss.mss_color = (UCHAR)8;
        break;
      }
    default  :
      {
        mss.mss_type = (UCHAR)3;
        mss.mss_color = (UCHAR)4;
        break;
      }
  }
  AssertRC( (*fpfnPMRequestProc)(PMDCMD_SETSCREENSIZE,
                                F16PFROMSSP(&mss),
                                NULL) );

  return  TRUE;
}                                      /* vmPMDNotification                  */

/*
** End of New code for PenPM EMI change
*/


 /**************************************************************************
 * FUNCTION NAME  : vmSeamlessNotification()
 *
 * DESCRIPTION    : SEAMLESS NOTIFICATION
 *
 *  This subroutine processes the VMDEVREQ_SEAMLESS message.  It is
 *  generally only called by the VWIN driver, to inform us that this
 *  VDM is in seamless mode. The reason for this call is so that we
 *  never put this VDM into X Mouse Mode.
 *
 *  INPUT         :
 *                   hvdm       -> VDM
 *                   *fSeamless -> TRUE  - this VDM is seamless
 *                    FALSE - this VDM is seamless
 *
 *  OUTPUT        :    SUCESS TRUE
 *
 *
 *  USES          :
 *      32-bit small-model PASCAL calling/register conventions
 *
 *  CONTEXT       :
 *      Task-time
 *
 ****************************************************************************/

BOOL PRIVENTRY vmSeamlessNotification(HVDM hvdm,PBOOL fSeamless)
{
  if (*fSeamless)
  {
    if (usMouExclusive)
      VMGrabAccess(hvdm, FALSE);       /* Turn X Mouse Mode OFF              */
    REFHVDM(hvdm, ULONG, fsstate) |= SESSION_SEAMLESS;
    REFHVDM(hvdm, ULONG, fsstate) &= ~SESSION_EXCMOUSE;
  }
  else
    REFHVDM(hvdm, ULONG, fsstate) &= ~SESSION_SEAMLESS;
  return  TRUE;
}


 /**************************************************************************
 *  FUNCTION NAME : vmProtInt33Entry()
 *
 *  DESCRIPTION   : Handle protect mode int 33 requests
 *
 *  This subroutine routes Int 33 requests from applications that issue
 *  Int 33's in protect mode.
 *
 *  INPUT         :
 *                  hvdm   -> VDM
 *                  pcrf   -> pointer to client register frame
 *  OUTPUT
 *                : SUCCESS
 *                   TRUE
 *  USES          :
 *                  32-bit small-model PASCAL calling/register conventions
 *
 *  NOTES         :
 *                   The mouse calls that are supported for DPMI applications are:
 *                    x0 - Reset mouse and get status
 *                    x1 - Show mouse pointer
 *                    x2 - Hide mouse pointer
 *                    x3 - Get mouse position and button status
 *                    x4 - Set mouse pointer position
 *                    x5 - Get button press information
 *                    x6 - Get button release information
 *                    x7 - Set horizontal limits for pointer
 *                    x8 - Set vertical limits for pointer
 *                    x9 - Set pointer shape
 *                    xA - Set text pointer type
 *                    xB - Read mouse motion counters
 *                    xC - Set user-defined mouse event handler
 *                    xD - Turn on light pen emulation
 *                    xE - Turn off light pen emulation
 *                    xF - Set mickeys to pixels ratio
 *                   x10 - Set mouse pointer exclusion area
 *                   x13 - Set double speed threshold
 *                   x14 - Exchange user-defined mouse event handler
 *                   x15 - Get mouse save state buffer size
 *                   x16 - Save mouse driver state
 *                   x17 - Restore mouse driver state
 *                   x1A - Set mouse sensitivity
 *                   x1B - Get mouse sensitivity
 *                   x1C - Set mouse interrupt rate
 *                   x1D - Select pointer page rate
 *                   x1E - Get pointer page
 *                   x20 - Enable mouse
 *                   x21 - Reset mouse driver
 *                   x22 - Set language for mouse driver
 *                   x23 - Get language numberuse driver
 *                   x24 - Get mouse information
 *
 *  CONTEXT       :
 *                   Task-time
 *
 *  PSEUDO-CODE   :                                  REFERENCES
 *      if supported mouse service
 *         call appropriate Int 33 worker based on client's AX
 *
 ****************************************************************************/

BOOL PRIVENTRY
vmProtInt33Entry(HVDM hvdm,PCRF pcrf)
{
     if (AX(pcrf) <= INT33_MAXFUNC) {
          if (*apfnInt33Services[AX(pcrf)] != NULL) {
               return((*apfnInt33Services[AX(pcrf)])(pcrf));
          }
     }
     return(FALSE);
}

/****************************************************************************
 *
 * FUNCTION NAME  :  vmDPMITask()
 *
 * DESCRIPTION    :  DPMI Task begin/end notification
 *
 *                   This subroutine will be called when a dpmi task begins
 *                   or stops execution in a VDM.
 *
 *  INPUT         :
 *                   hvdm   -> VDM
 *                   fStart -> TRUE == task start, FALSE == task end
 *  OUTPUT        :
 *                   SUCCESS
 *                       TRUE
 *  USES          :
 *                   32-bit small-model PASCAL calling/register conventions
 *
 *  CONTEXT       :
 *                   Task-time
 *
 *  NOTES         :
 *                    New for release R205
 *
 *  PSEUDO-CODE   :                                  REFERENCES
 *
 *      if fStart
 *         hook mouse event irq in protect mode
 *
 *****************************************************************************/

BOOL PRIVENTRY vmDPMITask( HVDM hvdm, BOOL fStart )
{
  PAPPDATA pappdata;

  /* DPMI task starting */
  if ( fStart )
  {
    /* First DPMI task in VDM is starting, allocate and arm the int hook */
    if (VDMData.ulcVPMapps++ == 0) {                            /*            */
      if ((hhookUserSubIntHook = VDHAllocHook(VDH_BP_HOOK,      /*            */
                                 (PFNARM)VMUserSubVPMIntHook,   /*            */
                                  0)) == NULL)
      {                                                         /*            */
        return  FALSE;                                          /*            */
      }                                                         /*            */
      VDMData.fpfnVPMBP = VDHArmVPMBPHook(hhookUserSubIntHook); /*            */
    }

    /* Another DPMI task is starting, save away mouse event handler info */
    else                                                        /*            */
    {                                                           /*            */
      memcpy(&(VDMData.pappdata->pad_subinfo),                  /*            */
             VDMData.mstates.subinfo,                           /*            */
             MAX_USERSUBS*sizeof(SUBINFO));                     /*            */
      VDMData.pappdata->pad_ulsubactive = iSubActive;           /*            */
      VDMData.pappdata->pad_pdhdData = pdhdData;                /*            */
      iSubActive = SUB_NONE;                                    /*            */
    }                                                           /*            */

    /* Allocate a new per app data area for the new task */
    pappdata = (PAPPDATA)VDHAllocMem(sizeof(PERAPPDATA),        /*            */
                                     VDHAM_SWAPPABLE);          /*            */

    /* If we didn't get the memory, or the breakpoint isn't armed, return */
    if (!pappdata || !VDMData.fpfnVPMBP)                        /*            */
    {                                                           /*            */
      return(FALSE);                                            /*            */
    }                                                           /*            */

    /* Otherwise, add this data area to the front of per app data area's */
    else                                                        /*            */
    {                                                           /*            */
      pappdata->pad_pappdataPrev = VDMData.pappdata;            /*            */
      VDMData.pappdata = pappdata;                              /*            */
      pdhdData = pdhdHostData;                                  /*            */
    }                                                           /*            */

    /* Get the original int vector, and hook the mouse IRQ in protect mode */
    VDHGetVPMIntVector(INTFROMIRQ(mhwInstalled.mhw_irq),        /*            */
                        &(VDMData.pappdata->pad_fpfnOriginalVPMIRQ));  /*               */
    VDHSetVPMIntVector(INTFROMIRQ(mhwInstalled.mhw_irq), VDMData.fpfnVPMBP);
  }

  /* DPMI task exiting */
  else if (VDMData.ulcVPMapps != 0)                             /*            */
  {                                                             /*            */
    /* Restore previous VPM app data */
    pappdata = VDMData.pappdata;                                /*            */
    if (pappdata != NULL) {                                     /*            */
       VDMData.pappdata = pappdata->pad_pappdataPrev;           /*            */
       VDHFreeMem(pappdata);                                    /*            */
    }                                                           /*            */

    /* last DPMI task in VDM exited, free hook                                */
    if (--VDMData.ulcVPMapps == 0)                              /*            */
    {
      if ( hhookUserSubIntHook )
      {                                                         /*            */
        VDHFreeHook(hhookUserSubIntHook);                       /*            */
        hhookUserSubIntHook = 0;                                /*            */
        pdhdData = 0;                                           /*            */
      }                                                         /*            */

    }

    /* Not the last DPMI task, restore this tasks mouse event handler info */
    else                                                        /*            */
    {                                                           /*            */
      memcpy(VDMData.mstates.subinfo,                           /*            */
             &(VDMData.pappdata->pad_subinfo),                  /*            */
             MAX_USERSUBS*sizeof(SUBINFO));                     /*            */
      iSubActive = VDMData.pappdata->pad_ulsubactive;           /*            */
      pdhdData = VDMData.pappdata->pad_pdhdData;                /*            */
    }                                                           /*            */

  }                                                             /*            */
  return (TRUE);
}

/*            - START */
/***   VMCheckContext
 *
 *     Check to make sure the current dpmi task is correct.
 *
 *     NOTES
 *         New for defect 73183
 *
 *     ENTRY
 *         none
 *
 *     EXIT
 *         TRUE  - VDM is in the context of the correct dpmi client
 *         FALSE - VDM is in the context of the       dpmi client
 */

BOOL VMCheckContext()
{
    BOOL rc = TRUE;

    if (pdhdData != pdhdHostData)
    {
       rc = FALSE;
    }

    return(rc);
}
/*            - END */

#pragma  END_SWAP_CODE
