/**************************************************************************
 *
 * SOURCE FILE NAME = vkbdreq.c
 *
 * DESCRIPTIVE NAME = Virtual Keyboard System Request Processing
 *
 * 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
 *
 * DESCRIPTION
 *
 *      This module contains the VKBD's DosRequestVDD support.
 *
 * FUNCTIONS  VKSysReqProc
 *            vkSysSetAccess
 *            vkSysAddExpandScan
 *            vkSysXferExpandScan
 *            vkSysSyncShift
 *            vkSysSetFocus
 *            vkSysEndPaste
 *            vkSysPostScan
 *            vkSysSimCtrlAltUps
 *            vkSysAddNumScans
 *            vkSysReverseXlate
 *            vkSysPostChar
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
 * CHANGE ACTIVITY =
 *  DATE      FLAG        APAR     CHANGE DESCRIPTION
 *  --------  ----------  -----    --------------------------------------
 *  mm/dd/yy  @Vr.mpppxx  xxxxx    xxxxxxx
 *  07/10/89                       JTP  Created.
 *  02/17/93  @V2.0DDK01  DCR1195
 *  02/17/93  @V2.0DDK02  b730591
 *  09/19/94  70207       70207    ChgTeam - send alt-break-key if switching
 *                                 to a fullscreen VDM session.
 *  01/26/95  110258               ChangeTeam - Changed the check for NULL
 *                                 usCodePage in vkSysPostChar to force
 *                                 update of the reverse translate table.
 *************************************************************************/

#include <mvdm.h>
#define  INCL_VIDEODEVREQ
#include <vvd.h>
#define  INCL_DOSERRORS
#include <bseerr.h>
#include "vkbdp.h"

#ifdef   VDDSTRICT
MODNAME = __FILE__;
#endif
#define  INVALID_HVDM  ((HVDM)-2)
#pragma  BEGIN_INSTANCE_DATA
extern USHORT fsKeyShift;
extern PKEYPKT pkpScanHead;
extern PKEYPKT pkpScanTail;
#pragma  END_INSTANCE_DATA
#pragma  BEGIN_SWAP_DATA
extern FPFNPDD fpfnPKVDDProc;
extern PFNSYSRQ apfnSysReq[];
extern USHORT fsPMShift;
extern PCPENTRY pRXDefault;
#pragma  END_SWAP_DATA
#pragma  BEGIN_SWAP_INSTANCE
extern FLAGS flVDM;

/*
** @V2.0DDK01
*/
extern FLAGS flhkVDM;
extern ULONG nInt16Pauses;
extern HVDD hvddVideo;
extern UCHAR achExpandScan[];
extern ULONG nExpandScans;
extern USHORT fsPrePasteShift;
extern USHORT fsPreExpandShift;
extern USHORT fsPasteShift;
extern PCPENTRY pRXVDM;
extern KXLT xltBuff;
#pragma  END_SWAP_INSTANCE
#pragma  BEGIN_SWAP_CODE

/****************************************************************************
 *
 * FUNCTION NAME = VKSysReqProc
 *
 * DESCRIPTION   =
 *
 *  This subroutine is registered during VDD initialization via
 *  VDHRegisterVDD, and receives requests from OS/2 applications.
 *
 * INPUT         = (SGID sgid,ULONG ulFunc,ULONG nbIn,PVOID pIn,ULONG
 *                    nbOut,PVOID pOut)
 *
 *  ENTRY
 *      sgid   == screen group
 *      ulFunc == function code
 *      nbIn   -> input buffer size (0 if none)
 *      pIn    -> input buffer
 *      nbOut  -> output buffer size (0 if none)
 *      pOut   -> output buffer
 *  EXIT
 *      SUCCESS
 *          Zero
 *      FAILURE
 *          Error code (ie, invalid function, 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          apfnSysReq[]
 *
 ****************************************************************************/

LONG EXPENTRY VKSysReqProc(SGID sgid,ULONG ulFunc,ULONG nbIn,PVOID pIn,ULONG
                            nbOut,PVOID pOut)
{
  HVDM hvdm = INVALID_HVDM;
  if (sgid)
  {
    hvdm = VDHHandleFromSGID(sgid);
    if (!hvdm)
      hvdm = INVALID_HVDM;
  }
  if (ulFunc > VKBDSYSREQ_MAX)
    return  ERROR_INVALID_FUNCTION;
  else
  {
    return (apfnSysReq[--ulFunc])(hvdm, nbIn, (PVOID)pIn, nbOut, (PVOID)pOut);
  }
}                                      /* VKSysReqProc                       */

/****************************************************************************
 *
 * FUNCTION NAME = vkSysSetAccess
 *
 * DESCRIPTION   =
 *
 *  This subroutine processes the VKBDSYSREQ_SETACCESS function, and
 *  establishes (or relinquishes) exclusive access for the caller to
 *  any and all keyboard events posted for the specified VDM.
 *
 * INPUT         = (HVDM hvdm,BOOL fSet,PVOID p1,ULONG ul2,PVOID p2)
 *
 *  ENTRY
 *      hvdm -> VDM
 *      fSet == TRUE to request access, FALSE to release
 *  EXIT
 *      SUCCESS
 *          Zero
 *      FAILURE
 *          Error code (ie, access denied, etc)
 *  USES
 *      32-bit small-model PASCAL calling/register conventions
 *
 *  CONTEXT
 *      Task-time
 *
 *  PSEUDO-CODE                                     REFERENCES
 *
 ****************************************************************************/

LONG PRIVENTRY vkSysSetAccess(HVDM hvdm,BOOL fSet,PVOID p1,ULONG ul2,PVOID p2)
{
   KEYPKT  keyPacket   ;                                   // 70207
   PKEYPKT pkeyPacket  ;                                   // 70207
   UCHAR   aucScan[3] = { SCAN_BREAKMASK | EXTSCAN_ALT,    // 70207
                          SCAN_E0PREFIX               ,    // 70207
                          SCAN_BREAKMASK | EXTSCAN_ALT } ; // 70207
   ULONG   i ;                                             // 70207

  /*
  ** If the handle is invalid, we let it slide, because this
  ** call could well have been issued for a VDM that is now dead
  */

  if (hvdm != INVALID_HVDM)
  {
    if (fSet)
      REFHVDM(hvdm, FLAGS, flVDM) |= VDM_WINDOWED;
    else
    {
      if (REFHVDM(hvdm, FLAGS, flVDM)&VDM_WINDOWED)
        REFHVDM(hvdm, FLAGS, flVDM) &= ~VDM_WINDOWED;
    }

        /* @IBM BUGBUG JOECELI - If we are changing types we will clear*/
        /* @IBM out the shift states.  2/14/92 -  PTR SM09710*/

    vkSysSyncShift(hvdm, fsPMShift, (fsPMShift&~KEYSTATE_ALLSHIFTS));
    REFHVDM(hvdm, USHORT, fsKeyShift) &= ~KEYSTATE_ALLSHIFTS;
    pVDMBase(hvdm)->rb_fbKFlag &= ~BIOSKFLAG_ALT;
    pVDMBase(hvdm)->rb_fbKFlag1 &= ~BIOSKFLAG1_LEFTALT;
    pVDMBase(hvdm)->rb_fbKFlag3 &= ~BIOSKFLAG3_RIGHTALT;

    // 70207  Send break code for left and right alt keys in case ALT-HOME's
    // 70207  alt-key-break was lost in the process.  PMWIN does not process
    // 70207  the altkey-break even though PMDD writes it into the queue.  This
    // 70207  is a renegade fix but it should not make a diff since the VDM
    // 70207  should expect to get these.
    if (!(fSet))           // switching to fullscreen session            70207
    {                                                                  //70207
      pkeyPacket = SSToDS(&keyPacket);                                 //70207
      memset(SSToDS(&keyPacket), 0, sizeof(keyPacket));                //70207
      for ( i=0 ; i < ( sizeof(aucScan)/sizeof(UCHAR) ) ; i++ )        //70207
      {                                                                //70207
         keyPacket.kp_Scan      = aucScan[i]                   ;       //70207
         VKAddScan(hvdm, pkeyPacket, ADDSCAN_TASK|ADDSCAN_PASTEINPUT );//70207
      }                                                                //70207
    }                                                                  //70207
  }
  return  NO_ERROR;
}                                      /* vkSysSetAccess                     */

/****************************************************************************
 *
 * FUNCTION NAME = vkSysAddExpandScan
 *
 * DESCRIPTION   =
 *
 *  This subroutine add scan code to the paste expansion scan buffer.
 *
 * INPUT         = (HVDM hvdm,UCHAR chScan)
 *
 *  ENTRY
 *      hvdm   ->VDM
 *      chScan = paste scan code
 *  EXIT
 *      None
 *  USES
 *      32-bit small-model PASCAL calling/register conventions
 *
 *  CONTEXT
 *      Task-time
 *
 *  PSEUDO-CODE                                     REFERENCES
 *
 ****************************************************************************/

VOID PRIVENTRY vkSysAddExpandScan(HVDM hvdm,UCHAR chScan)
{
  PULONG pnExpand = pVDM(hvdm, PULONG, &nExpandScans);
  AssertTRUE(*pnExpand < MAX_PASTEEXPAND);
  REFHVDM(hvdm, UCHAR, achExpandScan[(*pnExpand)++]) = chScan;
}                                      /* vkSysAddExpandScan                 */

/****************************************************************************
 *
 * FUNCTION NAME = vkSysXferExpandScan
 *
 * DESCRIPTION   =
 *
 *  This subroutine transfers scan codes from the expand buffer to
 *  the scan code buffer.
 *
 *  ENTRY
 *      hvdm -> VDM
 *  EXIT
 *      None
 *  USES
 *      32-bit small-model PASCAL calling/register conventions
 *
 *  CONTEXT
 *      Task-time
 *
 *  PSEUDO-CODE                                     REFERENCES
 *
 ****************************************************************************/

VOID PRIVENTRY vkSysXferExpandScan(HVDM hvdm)
{
  PULONG pnExpandScans = pVDM(hvdm, PULONG, &nExpandScans);
  KEYPKT kp;
  PKEYPKT pkp;
  PUCHAR pScan;
  if (*pnExpandScans)
  {
    pkp = SSToDS(&kp);
    memset(SSToDS(&kp), 0, sizeof(kp));
    for (pScan = pVDM(hvdm, PUCHAR, &achExpandScan[0]); *pnExpandScans;
       (*pnExpandScans)--, pScan++)
    {
      kp.kp_Scan = *pScan;
      VKAddScan(hvdm, pkp, ADDSCAN_TASK|ADDSCAN_PASTEINPUT);
    }
  }
}                                      /* vkSysXferExpandScan                */

/****************************************************************************
 *
 * FUNCTION NAME = vkSysSyncShift
 *
 * DESCRIPTION   =
 *
 *  This subroutine synchronizes the shift states of the VDM to PM shift
 *  states when coming into focus.
 *
 *  ENTRY
 *      hvdm -> VDM
 *      fsSrcShift = current shift states
 *      fsDestShift = shift states to be sync'd to
 *  EXIT
 *      None
 *  USES
 *      32-bit small-model PASCAL calling/register conventions
 *
 *  CONTEXT
 *      Task-time
 *
 *  PSEUDO-CODE                                     REFERENCES
 *
 ****************************************************************************/

VOID PRIVENTRY vkSysSyncShift(HVDM hvdm,USHORT fsSrcShift,USHORT fsDstShift)
{
  USHORT fsShiftDiff;
  register INT i;
  AssertZERO(REFHVDM(hvdm, ULONG, nExpandScans));
  fsDstShift &= KEYSTATE_LRSHIFTS;
  fsShiftDiff = (USHORT)((fsSrcShift&KEYSTATE_LRSHIFTS)^fsDstShift);
  if (fsShiftDiff)
  {
    for (i = 0; i < NUM_SHENTRIES; i++)
      if (fsShiftDiff&pRXDefault->cp_ShTable[i].sh_fsShift)
      {
        if (pRXDefault->cp_ShTable[i].sh_scanPrefix)
          vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[i].sh_scanPrefix);
        if (fsDstShift&pRXDefault->cp_ShTable[i].sh_fsShift)/* make          */
          vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[i].sh_scanShift);
        else                          /* send break                          */
          vkSysAddExpandScan(hvdm, (UCHAR)(pRXDefault->cp_ShTable[i].
             sh_scanShift+SCAN_BREAKMASK));
      }
    vkSysXferExpandScan(hvdm);
  }
}                                      /* vkSysSyncShift                     */

/****************************************************************************
 *
 * FUNCTION NAME = vkSysSetFocus
 *
 * DESCRIPTION   =
 *
 *  This subroutine processes the VKBDSYSREQ_SETFOCUS function, and
 *  sets or clears focus flag for the specified VDM.
 *
 * ENTRY
 *
 *     hvdm -> VDM
 *
 *     ul1  ->
 *
 *     sp   -> shift states packet (defined below)
 *
 *          USHORT  fSetFocus; // TRUE to set focus, FALSE to clear focus
 *                             // TRUE = 1 and FALSE = 0
 *          USHORT  fsShift;   // PM shift states during set focus
 *                             //   undefined during clear focus
 *
 *     ul2  ->
 *
 *     p2   ->
 *
 * EXIT
 *     SUCCESS
 *         Zero
 *     FAILURE
 *         Error code (ie, lock overflow, etc)
 * USES
 *     32-bit small-model PASCAL calling/register conventions
 *
 * CONTEXT
 *     Task-time
 *
 * PSEUDO-CODE                                     REFERENCES
 *
 ****************************************************************************/

LONG PRIVENTRY vkSysSetFocus(HVDM hvdm,ULONG ul1,PSHIFTPKT sp,ULONG ul2,PVOID
                              p2)
{
  KLEDS kleds;
  PULONG pnExpandScans;
  PUCHAR pScan;
  KEYPKT kp;
  PKEYPKT pkp;
  KFOCUS kfocus;

  /*
  ** @V2.0DDK01
  */
  KHOTKEYS khotkeys;
  /* @IBM BUGBUG - Verify caller has access */
  if (hvdm == INVALID_HVDM)
    return  ERROR_INVALID_PARAMETER;
  else
  {
    if (sp->fSetFocus && !(REFHVDM(hvdm, FLAGS, flVDM)&VDM_FOCUS))
    {
      REFHVDM(hvdm, FLAGS, flVDM) |= VDM_FOCUS;
      kfocus.kf_len = sizeof(KFOCUS);
      kfocus.kf_fsFocus = sp->fSetFocus;
      (*fpfnPKVDDProc)(PKBDCMD_KFOCUS, F16PFROMSSP(&kfocus), F16PNULL);

      /*
      ** @V2.0DDK01 Getting the focus so Bypass any specified Hot Keys
      */
      khotkeys.khk_len = sizeof(KHOTKEYS);
      khotkeys.khk_action = 1;        /* Bypass any specified Hot Keys       */

      /*
      ** @V2.0DDK02
      */
      khotkeys.khk_hotkeys = REFHVDM(hvdm, FLAGS, flhkVDM);
      (*fpfnPKVDDProc)(PKBDCMD_HOTKEY, F16PFROMSSP(&khotkeys), F16PNULL);
      VKSetFgnd(hvdm);

      /*
      ** expand buffer not empty or scan buffer not empty
      */

      pnExpandScans = pVDM(hvdm, PULONG, &nExpandScans);
      if (*pnExpandScans || (REFHVDM(hvdm, PKEYPKT, pkpScanHead) != REFHVDM
         (hvdm, PKEYPKT, pkpScanTail)))
        return  ERROR_BUSY;           /* @IBM BUGBUG MTS: what should we do?      */
      else
      {
        vkSysSyncShift(hvdm, VKGetEnhancedShift(hvdm), sp->fsShift);
        fsPMShift = sp->fsShift;
      }
    }
    else
      if (!sp->fSetFocus && (REFHVDM(hvdm, FLAGS, flVDM)&VDM_FOCUS))
      {
        REFHVDM(hvdm, FLAGS, flVDM) &= ~VDM_FOCUS;
        VKSetBgnd(hvdm);
        kfocus.kf_len = sizeof(KFOCUS);
        kfocus.kf_fsFocus = sp->fSetFocus;
        (*fpfnPKVDDProc)(PKBDCMD_KFOCUS, F16PFROMSSP(&kfocus), F16PNULL);

        /*
        ** @V2.0DDK01 Losing the Focus so Enable ALL the Hot Keys
        */
        khotkeys.khk_len = sizeof(KHOTKEYS);
        khotkeys.khk_action = 0;      /* Enable all the control keys         */
        khotkeys.khk_hotkeys = 0;     /* Indicate ALL to be enabled          */
        (*fpfnPKVDDProc)(PKBDCMD_HOTKEY, F16PFROMSSP(&khotkeys), F16PNULL);
      }
    return  NO_ERROR;
  }
}                                      /* vkSysSetFocus                      */

/****************************************************************************
 *
 * FUNCTION NAME = vkSysEndPaste
 *
 * DESCRIPTION   =
 *
 *  This subroutine terminates the pasting mode and restore the
 *  shift states of the VDM prior to pasting.
 *
 * ENTRY
 *     hvdm -> VDM
 * EXIT
 *     None
 * USES
 *     32-bit small-model PASCAL calling/register conventions
 *
 * CONTEXT
 *     Task-time
 *
 * PSEUDO-CODE                                     REFERENCES
 *
 ****************************************************************************/

VOID PRIVENTRY vkSysEndPaste(HVDM hvdm)
{
  VDHRequestVDD(hvddVideo, hvdm, VVDDEVREQ_POSTPASTE, (PVOID)FALSE, NULL);

  /*
  ** If pasting was manually terminated, flush everything
  */

  if (REFHVDM(hvdm, BOOL, flVDM)&VDM_ENDPASTING)
  {
    VKClearScan(hvdm);
    VKClearKeys(hvdm);
  }

  REFHVDM(hvdm, BOOL, flVDM) &= ~(VDM_PASTING|VDM_ENDPASTING|VDM_PASTEINIT|
     VDM_PASTEPAUSE|VDM_PASTEDONE);

  if (REFHVDM(hvdm, ULONG, nExpandScans))
  {
    REFHVDM(hvdm, ULONG, nExpandScans) = 0;/* flush expand buffer            */
    vkSysSyncShift(hvdm, REFHVDM(hvdm, USHORT, fsPreExpandShift), REFHVDM(hvdm,
       USHORT, fsPrePasteShift));
  }
  else     /* @IBM BUGBUG MTS: whatif scan buffer doesn't have enough space? */
    vkSysSyncShift(hvdm, REFHVDM(hvdm, USHORT, fsPasteShift), REFHVDM(hvdm,
       USHORT, fsPrePasteShift));
  VDHNoIdle(hvdm, 0);
}                                      /* vkSysEndPaste                      */

/****************************************************************************
 *
 * FUNCTION NAME = vkSysPostScan
 *
 * DESCRIPTION   =
 *
 *  This subroutine processes the VKBDSYSREQ_POSTSCAN function.
 *  (Post a scan-code to the VDM )
 *
 * ENTRY
 *     hvdm -> VDM
 *     nkp  == repeat count for packet
 *     pkp  => PKEYPKT  (where PKEYPKT is defined below)
 *
 *      typedef struct keypkt_s {
 *         UCHAR       kp_Scan;
 *         UCHAR       kp_Char;
 *         USHORT      kp_fsState;
 *         USHORT      kp_fsDDFlags;
 *         USHORT      kp_fsKey;
 *      } KEYPKT;
 *      typedef KEYPKT *PKEYPKT;
 *
 *     ul   == size of return storage (should be sizeof(ULONG))
 *     pnkp -> return storage for # packets processed (under
 *             normal conditions, the value returned will equal nkp)
 *
 * EXIT
 *     SUCCESS
 *         Zero
 *     FAILURE
 *         Error code (ie, etc)
 * USES
 *     32-bit small-model PASCAL calling/register conventions
 *
 * CONTEXT
 *     Task-time
 *
 * PSEUDO-CODE                                     REFERENCES
 *
 ****************************************************************************/

LONG PRIVENTRY vkSysPostScan(HVDM hvdm,ULONG nkp,PKEYPKT pkp,ULONG ul,PULONG
                              pnkp)
{
  /* @IBM BUGBUG - Validate pointers/lock memory! */
  if (ul < sizeof(ULONG) || hvdm == INVALID_HVDM)
    return  ERROR_INVALID_PARAMETER;

  /*
  ** @IBM BUGBUG JOECELI
  ** - PM is sending us the make of the HOME key
  ** during the ALT-HOME hotkey sequence. When this HOME make
  ** gets into the VDM it causes some weird side effects. Also,
  ** it was waking up VDMs that were in a paused state.
  ** NOTE: In trying to narrow this special case down to only
  ** when we really need to throw away the HOME key, we check
  ** that the number of keypackets == 2 (because we only use
  ** the extended HOME key for the hotkey). We also must make
  ** sure that either ALT key is down and we check the scan that
  ** resides in keypacket 2.
  ** @IBM PTR B737271 - 2/24/92
  */

  if (!(REFHVDM(hvdm, FLAGS, flhkVDM)&VDM_ALTHOME) && nkp == 2 && pkp[1].
     kp_fsState&BIOSKFLAG_ALT)
    if (pkp[1].kp_Scan == 0x47 || pkp[1].kp_Scan == 0xC7)
      return  NO_ERROR;
  for (*pnkp = 0; *pnkp < nkp; (*pnkp)++)
    if (!VKAddScan(hvdm, &pkp[*pnkp], ADDSCAN_TASK|ADDSCAN_INPUT))
      return  ERROR_BUFFER_OVERFLOW;
  return  NO_ERROR;
}                                      /* vkSysPostScan                      */

/****************************************************************************
 *
 * FUNCTION NAME = vkSysSimCtrlAltUps
 *
 * DESCRIPTION   =
 *
 *  This subroutine simulates the ctrl and alt up scans if the VDM
 *  has those keys down.
 *
 * ENTRY
 *     hvdm    -> VDM
 *     pfsShift-> VDM's shift states
 * EXIT
 *     fsShift updated
 * USES
 *     32-bit small-model PASCAL calling/register conventions
 *
 * CONTEXT
 *     Task-time
 *
 * PSEUDO-CODE                                     REFERENCES
 *
 ****************************************************************************/

VOID PRIVENTRY vkSysSimCtrlAltUps(HVDM hvdm,PUSHORT pfsShift)
{
  if (*pfsShift&KEYSTATE_LEFTCTRL)
    vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_LCTRL].sh_scanShift+
       SCAN_BREAKMASK);
  if (*pfsShift&KEYSTATE_RIGHTCTRL)
  {
    vkSysAddExpandScan(hvdm, SCAN_E0PREFIX);
    vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_RCTRL].sh_scanShift+
       SCAN_BREAKMASK);
  }
  if (*pfsShift&KEYSTATE_LEFTALT)
    vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_LALT].sh_scanShift+
       SCAN_BREAKMASK);
  if (*pfsShift&KEYSTATE_RIGHTALT)
  {
    vkSysAddExpandScan(hvdm, SCAN_E0PREFIX);
    vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_RALT].sh_scanShift+
       SCAN_BREAKMASK);
  }
  *pfsShift &= ~(KEYSTATE_LEFTCTRL+KEYSTATE_RIGHTCTRL+KEYSTATE_LEFTALT+
     KEYSTATE_RIGHTALT);
}                                      /* vkSysSimCtrlAltUps                 */

/****************************************************************************
 *
 * FUNCTION NAME = vkSysAddNumScans
 *
 * DESCRIPTION   =
 *
 *  This subroutine translates the given character into a sequence of
 *  NumPad scans which correspond the ascii code of the character.
 *
 * ENTRY
 *     hvdm    -> VDM
 *     chChar  -  character to be reverse translated
 * EXIT
 *     None
 * USES
 *     32-bit small-model PASCAL calling/register conventions
 *
 * CONTEXT
 *     Task-time
 *
 * PSEUDO-CODE                                     REFERENCES
 *
 ****************************************************************************/

VOID PRIVENTRY vkSysAddNumScans(HVDM hvdm,UCHAR chChar)
{
  UCHAR achCode[3];
  register INT i;
  UCHAR n;

  if (chChar == 0)
    vkSysAddExpandScan(hvdm, pRXDefault->cp_NumTable[0]);
  else
  {
    for (i = 0; chChar && (i < 3); i++)
    {
      achCode[i] = pRXDefault->cp_NumTable[chChar%10];
      chChar /= 10;
    }
    for (--i; i >= 0; i--)
    {
      vkSysAddExpandScan(hvdm, achCode[i]);
      vkSysAddExpandScan(hvdm, (UCHAR)(achCode[i]+SCAN_BREAKMASK));
    }
  }
}                                      /* vkSysAddNumScans                   */

/****************************************************************************
 *
 * FUNCTION NAME = vkSysReverseXlate
 *
 * DESCRIPTION   =
 *
 *  This subroutine reverse translate a character back into scan codes
 *  for supporting pasting.
 *
 * ENTRY
 *     hvdm    -> VDM
 *     chChar  -  character to be reverse translated
 *     pfsShift-> VDM's shift states
 * EXIT
 *     fsShift updated
 * USES
 *     32-bit small-model PASCAL calling/register conventions
 *
 * CONTEXT
 *     Task-time
 *
 * PSEUDO-CODE                                     REFERENCES
 *
 ****************************************************************************/

VOID PRIVENTRY vkSysReverseXlate(HVDM hvdm,UCHAR chChar,PUSHORT pfsShift)
{
  UCHAR fbFlags;
  PCPENTRY pcp = REFHVDM(hvdm, PCPENTRY, pRXVDM);
  fbFlags = pcp->cp_RXTable[chChar].rx_fbRXFlags;

  switch (fbFlags)
  {
    case  RXFLAGS_NOSHIFT :
      vkSysSimCtrlAltUps(hvdm, pfsShift);

      if (*pfsShift&KEYSTATE_LEFTSHIFT)
        vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_LSHIFT].
           sh_scanShift+SCAN_BREAKMASK);

      if (*pfsShift&KEYSTATE_RIGHTSHIFT)
        vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_RSHIFT].
           sh_scanShift+SCAN_BREAKMASK);
      *pfsShift &= ~(KEYSTATE_LEFTSHIFT+KEYSTATE_RIGHTSHIFT);
      break;

    case  RXFLAGS_SHIFT :
      vkSysSimCtrlAltUps(hvdm, pfsShift);
      if (!(*pfsShift&(KEYSTATE_LEFTSHIFT+KEYSTATE_RIGHTSHIFT)))
      {
        vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_LSHIFT].
           sh_scanShift);
        *pfsShift |= KEYSTATE_LEFTSHIFT;
      }
      break;

    case  RXFLAGS_LOWCASE :
      vkSysSimCtrlAltUps(hvdm, pfsShift);
      if (*pfsShift&KEYSTATE_CAPSLOCK)
      {
        if (!(*pfsShift&(KEYSTATE_LEFTSHIFT+KEYSTATE_RIGHTSHIFT)))
        {
          vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_LSHIFT].
             sh_scanShift);
          *pfsShift |= KEYSTATE_LEFTSHIFT;
        }
      }
      else
      {
        if (*pfsShift&KEYSTATE_LEFTSHIFT)
          vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_LSHIFT].
             sh_scanShift+SCAN_BREAKMASK);
        if (*pfsShift&KEYSTATE_RIGHTSHIFT)
          vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_RSHIFT].
             sh_scanShift+SCAN_BREAKMASK);
        *pfsShift &= ~(KEYSTATE_LEFTSHIFT+KEYSTATE_RIGHTSHIFT);
      }
      break;

    case  RXFLAGS_UPCASE :
      vkSysSimCtrlAltUps(hvdm, pfsShift);
      if (*pfsShift&KEYSTATE_CAPSLOCK)
      {
        if (*pfsShift&KEYSTATE_LEFTSHIFT)
          vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_LSHIFT].
             sh_scanShift+SCAN_BREAKMASK);
        if (*pfsShift&KEYSTATE_RIGHTSHIFT)
          vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_RSHIFT].
             sh_scanShift+SCAN_BREAKMASK);
        *pfsShift &= ~(KEYSTATE_LEFTSHIFT+KEYSTATE_RIGHTSHIFT);
      }
      else
      {
        if (!(*pfsShift&(KEYSTATE_LEFTSHIFT+KEYSTATE_RIGHTSHIFT)))
        {
          vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_LSHIFT].
             sh_scanShift);
          *pfsShift |= KEYSTATE_LEFTSHIFT;
        }
      }
      break;

    case  RXFLAGS_CTRL :
      if (*pfsShift&KEYSTATE_LEFTSHIFT)
        vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_LSHIFT].
           sh_scanShift+SCAN_BREAKMASK);
      if (*pfsShift&KEYSTATE_RIGHTSHIFT)
        vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_RSHIFT].
           sh_scanShift+SCAN_BREAKMASK);
      if (*pfsShift&KEYSTATE_LEFTALT)
        vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_LALT].
           sh_scanShift+SCAN_BREAKMASK);
      if (*pfsShift&KEYSTATE_RIGHTALT)
      {
        vkSysAddExpandScan(hvdm, SCAN_E0PREFIX);
        vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_RALT].
           sh_scanShift+SCAN_BREAKMASK);
      }
      *pfsShift &= ~(KEYSTATE_LEFTSHIFT+KEYSTATE_RIGHTSHIFT+KEYSTATE_LEFTALT+
         KEYSTATE_RIGHTALT);
      if (!(*pfsShift&(KEYSTATE_LEFTCTRL+KEYSTATE_RIGHTCTRL)))
      {
        vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_LCTRL].
           sh_scanShift);
        *pfsShift |= KEYSTATE_LEFTCTRL;
      }
      break;

    case  RXFLAGS_NONKEY :
      if (!(*pfsShift&(KEYSTATE_LEFTALT+KEYSTATE_RIGHTALT)))
      {
        vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_LALT].
           sh_scanShift);
        *pfsShift |= KEYSTATE_LEFTALT;
      }
      else
        if ((*pfsShift&(KEYSTATE_LEFTALT+KEYSTATE_RIGHTALT)) ==
           (KEYSTATE_LEFTALT+KEYSTATE_RIGHTALT))
        {
          vkSysAddExpandScan(hvdm, SCAN_E0PREFIX);
          vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_RALT].
             sh_scanShift+SCAN_BREAKMASK);
          *pfsShift &= ~KEYSTATE_RIGHTALT;
        }
      vkSysAddNumScans(hvdm, chChar);
      if (*pfsShift&KEYSTATE_LEFTALT)
        vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_LALT].
           sh_scanShift+SCAN_BREAKMASK);
      else
        if (*pfsShift&KEYSTATE_RIGHTALT)
        {
          vkSysAddExpandScan(hvdm, SCAN_E0PREFIX);
          vkSysAddExpandScan(hvdm, pRXDefault->cp_ShTable[SHINDX_RALT].
             sh_scanShift+SCAN_BREAKMASK);
        }
      *pfsShift &= ~(KEYSTATE_LEFTALT+KEYSTATE_RIGHTALT);
  }
  if (fbFlags != RXFLAGS_NONKEY)
  {
    vkSysAddExpandScan(hvdm, pcp->cp_RXTable[chChar].rx_chScan);
    vkSysAddExpandScan(hvdm, (UCHAR)(pcp->cp_RXTable[chChar].rx_chScan+
       SCAN_BREAKMASK));
  }
}                                      /* vkSysReverseXlate                  */

/****************************************************************************
 *
 * FUNCTION NAME = vkSysPostChar
 *
 * DESCRIPTION   =
 *
 *  This subroutine processes the VKBDSYSREQ_POSTCHAR function.  This
 *  is the service used for pasting.  If the Shield passes a zero count,
 *  it implies that the paste data has been exhausted, in which case we
 *  set our PASTEDONE flag and simply return.
 *
 * ENTRY
 *     hvdm -> VDM
 *     nch  == # of characters to paste
 *     pch  => pointer to characters
 *     ul   == size of return storage (should be sizeof(ULONG))
 *     pnch -> return storage for # characters processed (under
 *             normal conditions, the value returned will equal nch)
 * EXIT
 *     SUCCESS
 *         Zero
 *     FAILURE
 *         Error code (ie, etc)
 * USES
 *     32-bit small-model PASCAL calling/register conventions
 *
 * CONTEXT
 *     Task-time
 *
 * NOTE
 *     At the time of pasting, there may still be some key scans in
 *     in the scan code buffer which may contain some shift scans.
 *     In order to reverse translate the given character with correct
 *     shift make/break scans, we must wait until the scan code buffer
 *     is empty (i.e. all shift scans in the scan buffer take effect)
 *     before doing any reverse translation.
 *
 * PSEUDO-CODE                                     REFERENCES
 *
 ****************************************************************************/

LONG PRIVENTRY vkSysPostChar(HVDM hvdm,ULONG nch,register PUCHAR pch,ULONG ul,
                              PULONG pnch)
{
  LONG rc;
  CHAR chScan;
  LONG nFreeSpace;
  PPCPENTRY ppcp;

  /* @IBM BUGBUG - Validate pointers/lock memory! */
  if (hvdm == INVALID_HVDM || ul < sizeof(ULONG))
    return  ERROR_INVALID_PARAMETER;
  *pnch = 0;

  /*
  ** See if Shield must be told that user terminated pasting
  */

  if (REFHVDM(hvdm, BOOL, flVDM)&VDM_ENDPASTING)
  {
    vkSysEndPaste(hvdm);
    return  NO_ERROR;
  }

  /*
  ** See if Shield is trying to tell us that pasting is complete
  */

  if (nch == 0)
  {
    REFHVDM(hvdm, BOOL, flVDM) |= VDM_PASTEDONE;
    VKPeekScan(hvdm);
    return  NO_ERROR;
  }

  /*
  ** None of the above;  Shield is initiating pasting
  */

  REFHVDM(hvdm, ULONG, nInt16Pauses) = 0;
  REFHVDM(hvdm, BOOL, flVDM) |= VDM_PASTING|VDM_PASTEDATA;

  /*
  ** If scan buffer not empty, don't allow pasting until it is;
  ** VKRemoveScan will take care of getting things rolling again
  */

  if (!(REFHVDM(hvdm, BOOL, flVDM)&VDM_PASTEINIT))
  {
    if (REFHVDM(hvdm, PKEYPKT, pkpScanHead) != REFHVDM(hvdm, PKEYPKT,
       pkpScanTail))
      return  ERROR_BUSY;
    REFHVDM(hvdm, USHORT, fsPrePasteShift) = REFHVDM(hvdm, USHORT, fsPasteShift
       ) = VKGetEnhancedShift(hvdm);
    REFHVDM(hvdm, BOOL, flVDM) |= VDM_PASTEINIT;
    if (*(ppcp = pVDM(hvdm, PPCPENTRY, &pRXVDM)) == NULL)
    {
      AssertRC(vkCreateRX(0, ppcp));
      pRXDefault = *ppcp;
      (*ppcp)->cp_cReference++;
    }
    else
/*    if ((*ppcp)->cp_usCodePage == 0)   RXTable is invalid   110258  */
      if ((*ppcp)->cp_usCodePage ^= 0)  /*  RXTable not invalid 110258 */
        AssertRC(vkCreateRX(REFHVDM(hvdm, USHORT, xltBuff.kxlt_usCodePage),
           ppcp));
    VDHNoIdle(hvdm, 600000);
  }
  rc = NO_ERROR;

  while (nch)
  {
    if (REFHVDM(hvdm, FLAGS, flVDM)&VDM_FASTPASTE)
    {
      chScan = REFHVDM(hvdm, PCPENTRY, pRXVDM)->cp_RXTable[*pch].rx_chScan;
      if (VKAddKey(hvdm, *pch, chScan))
      {
        pch++;
        (*pnch)++;
        nch--;
      }
      else
      {
        rc = ERROR_BUFFER_OVERFLOW;
        break;
      }
    }
    else
    {
      if (REFHVDM(hvdm, ULONG, nExpandScans) == 0)
      {
        REFHVDM(hvdm, USHORT, fsPreExpandShift) = REFHVDM(hvdm, USHORT,
           fsPasteShift);
        vkSysReverseXlate(hvdm, *pch++, pVDM(hvdm, PUSHORT, &fsPasteShift));
        (*pnch)++;
        nch--;
      }
      nFreeSpace = REFHVDM(hvdm, PKEYPKT, pkpScanHead)-REFHVDM(hvdm, PKEYPKT,
         pkpScanTail)-1;
      if (nFreeSpace < 0)
        nFreeSpace += MAX_SCANS;

      /*
      ** check if we have enough free space
      */
      if (nFreeSpace >= REFHVDM(hvdm, ULONG, nExpandScans))
        vkSysXferExpandScan(hvdm);
      else
      {
        VKTransferScan(hvdm, 0);
        rc = ERROR_BUFFER_OVERFLOW;
        break;
      }
    }
  }
  return  rc;
}                                      /* vkSysPostChar                      */

#pragma  END_SWAP_CODE
