/**************************************************************************
 *
 * SOURCE FILE NAME = vkbd.c
 *
 * DESCRIPTIVE NAME = Virtual Keyboard Device Driver (VKBD) main module
 *
 * 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 all the VKBD's initialization
 *          procedures.
 *
 *      RATIONALE
 *          1. Why is the VDD responsible for updating the LEDs when
 *             a VDM comes foreground?  The PDD manages this for other
 *             screen groups already.
 *
 *             Part of the answer is that the PDD is expected to have
 *             little or no per-screen group data for VDMs.  Between the
 *             information that the VDD has to track and the VDM's own ROM
 *             BIOS Data Area, there is little point in having the PDD
 *             track VDM state information in yet another place.
 *
 *             The rest of the answer is that the PDD cannot be told to
 *             change LED states while a VDM is background (to do so would
 *             involve it in the "virtualization" of a VDM).  The PDD is
 *             still used to do the actual LED hardware operation, so
 *             the separation of hardware-dependent operations remains
 *             strong.  This solution simply insures that the separation
 *             of "virtualization" between PDD and VDD remains strong also.
 *
 * FUNCTIONS       fpfnPKVDDPro
 *                 pfnScreenPro
 *                 VDDInit
 *                 VKExit
 *                 VKCreateVDM
 *                 VKgetTheValue
 *                 VKTerminateVDM
 *                 VKSetFgnd
 *                 VKSetBgnd
 *                 VKSetCodePage
 *                 vkCreateRX
 *                 vkDestroyRX
 *                 VKSetExtraKeys
 *                 VKSetFastPaste
 *                 VKSetKbdRestrict
 *                 VKAltHome
 *                 VKHotKeyBypass
 *                 VDHRegisterScreenProc
 *                 VDHQueryKeyShift
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
 * CHANGE ACTIVITY =
 *  DATE      FLAG        APAR     CHANGE DESCRIPTION
 *  --------  ----------  -----    --------------------------------------
 *  mm/dd/yy  @Vr.mpppxx  xxxxx    xxxxxxx
 *  11/21/88                       MTS Created.
 *  02/16/93  @V2.0DDK01  DCR1195
 *  02/16/93  @V2.0DDK02  DCR1388
 *  02/16/93  @V2.0DDK03  DCR1131
 *  03/30/95  116919      116919   ChgTeam - Changed line in VKCreateVDM which
 *                                 saves default code page. Prior to change, it
 *                                 was being set to 0 & wrong rxtable was used
 *                                 when pasting from NLS OS/2 window into VMB.
 ****************************************************************************/

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

#ifdef   VDDSTRICT
MODNAME = __FILE__;
#endif

/*
**    VKBD Global Data
*/

#pragma  BEGIN_GLOBAL_DATA

#ifdef   VDDSTRICT
CHAR szAssertMsg[] = "VKBD assertion failure in %s, line %d\n";
#endif

#ifdef   VDDDEBUG
CHAR szModule[] = "VKBD: ";
#endif
HHOOK hhookHotPlug;
#pragma  END_GLOBAL_DATA
#pragma  BEGIN_SWAP_DATA

/*
** @V2.0DDK01
*/

/*
** list of hot keys which can be bypassed
*/
SZ szHotKeyList =
{
  HOT_KEY_LIST
} ;

/*
** The following values correspond to the szHotKeyList (NONE, ALT+ESC, CTRL+ESC)
*/
ULONG aulHotKeyList[] =
{
  0x00000, 0x00001, 0x00002
} ;

TLS atls[2] =
{
  {
    szHotKeyList, aulHotKeyList
  } ,
  {
    0, 0
  }
} ;

PCHAR pchHotKeys = szHotKeyList;

/****************************************************************************
 *
 * FUNCTION NAME = fpfnPKVDDPro
 *
 * DESCRIPTION   =
 *
 * INPUT         = (FPFNPDD)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

FPFNPDD fpfnPKVDDProc = (FPFNPDD)
  0;                                  /* address of PDD entry point          */
  USHORT usKbdHWID;                   /* keyboard hardware ID                */
  HIRQ hirq = NULL;                   /* handle for keyboard IRQ interrupt   */

/****************************************************************************
 *
 * FUNCTION NAME = pfnScreenPro
 *
 * DESCRIPTION   =
 *
 * INPUT         = (PFNSCRNP)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

  PFNSCRNP pfnScreenProc = (PFNSCRNP)
  0;                                  /* video "screen-enable" entry point   */
  USHORT fsPMShift;                   /* shift states of the PM session      */
  BOOL fMarker = FALSE;               /* @V2.0DDK02                          */

  /*
  ** Port Hooking Data
  */
  IOH iotVKBuffer =
{
  VKReadBuff,VKWriteBuff,NULL,NULL,NULL
}

;
IOH iotVKCmd =
{
  VKReadCmd, VKWriteCmd, NULL, NULL, NULL
} ;

HVDD hvddVideo;                       /* handle to Video VDD                 */

/*
** Define array of function addresses for DosRequestVDD services
*/
PFNSYSRQ apfnSysReq[VKBDSYSREQ_MAX] =
{
  vkSysSetAccess, vkSysSetFocus, vkSysPostScan, vkSysPostChar,
} ;

CHAR szPropExtraKeys[] = PROP_NAME_EXTRA_KEYS;
CHAR szPropFastPaste[] = PROP_NAME_FAST_PASTE;
CHAR szPropKbdRestrict[] = PROP_NAME_KBD_RESTRICT;

/*
** @V2.0DDK01
*/
CHAR szPropAltHome[] = PROP_NAME_KBD_ALTHOM_BP;
CHAR szPropCtrlBypass[] = PROP_NAME_KBD_CTRL_BP;
PCPENTRY pRXDefault = NULL;
PCPENTRY pRXHead = NULL;
#pragma  END_SWAP_DATA

/*
**    VKBD Instance Data
*/

#pragma  BEGIN_INSTANCE_DATA

/*
** VDM states
*/
FLAGS flVDM;

/*
** @IBM @V2.0DDK01
*/
FLAGS flhkVDM;
ULONG nInt16Pauses;               /* # paste pauses at the INT 16h level  */

/*
** Ring buffer for this VDM's hardware events (scan codes)
*/
KEYPKT akpScans[MAX_SCANS];

/*
** Ring buffer head (advanced at task-time) and tail (advanced at
** interrupt-time)
*/
PKEYPKT pkpScanHead = & akpScans[0];
PKEYPKT pkpScanTail = & akpScans[0];
HHOOK hhookTransferScanHook;          /* handle for VKTransferScanHook       */
USHORT fsKeyShift;                    /* keyboard shift states               */
KEYPKT kpCur;                         /* last key packet removed             */
BYTE iKPPopup;                        /* @V2.0DDK03 KP warning popup switch  */
BYTE virtmem1f;                       /* virtual 8042 memory 1F location     */

#ifdef   MYDEBUG
INT iNextDebugEntry = 0;
DEBUGENTRY adeDebug[MAX_DEBUG_ENTRIES];
#endif
#pragma  END_INSTANCE_DATA
#pragma  BEGIN_SWAP_INSTANCE

/*
** initialization required for the following:
*/
UINT uPortbuffstate = PORTBUFF_KBD;/* index selects Kbd, Command, or Output  */
BYTE virtCommand = COMMAND_INTENABLE|COMMAND_SYSFLAG|COMMAND_XLTMODE;

/*
** current virtual Command byte
*/
BYTE virtTestInput = ~TESTINPUT_T0;   /* current virtual Test-Input byte     */
BYTE virtKbdEnable = 1;               /* virtual keyboard enable state       */
BYTE virtOldKbdEnable;      /* store KbdEnable state during set LED and RPT  */
BYTE virtStatus = STATUS_SYSFLAG|STATUS_NOTINHIBITED;
BYTE virtInput = INPUT_NOJUMPER|INPUT_NOTINHIBITED|INPUT_RESERVED;
BYTE virtKbdLEDs;                     /* virtual keyboard LED states         */
BYTE virtScanSet = 2;                 /* virtual scan code set               */
/*no initialization required for the following:*/
BYTE virtKbd;                         /* current virtual Kbd byte            */
BYTE virtOutput = OUTPUT_CPURESET;    /* current virtual Output byte         */
BYTE virtBuff;                        /* current virtual Buffer byte         */
BYTE virtKbdLastScan;            /* virtual keyboard last scan (for RESEND)  */
KXLT xltBuff;                         /* translation of current Buffer byte  */
KKEY xltKey1;                         /* translation key packet # 1          */
KKEY xltKey2;                         /* translation key packet # 2          */
KXF xltKxf;                           /* translation flags                   */

HHOOK hhookInt09PostInt15;            /* hook handle for VKInt09PostInt15    */
HHOOK hhookInt09PostBreak;            /* hook handle for VKInt09PostBreak    */
HHOOK hhookInt09PostPrtSc;            /* hook handle for VKInt09PostPrtSc    */
HHOOK hhookInt09PostSysReq;           /* hook handle for VKInt09PostSysReq   */
HHOOK hhookInt09PauseReturn;          /* hook handle for VKInt09PauseReturn  */
HHOOK hhookInt09PostIntReturn;        /* hook handl for VKInt09IntReturn     */
HHOOK hhookInt16ProcReturn;           /* hook handle for VKInt16ProcReturn   */
HHOOK hhookPasteTimeout;              /* hook handle for VKPasteTimeout      */
UCHAR achExpandScan[MAX_PASTEEXPAND]; /* paste scan expansion buffer         */
ULONG nExpandScans = 0;               /* number of expanded scan codes       */
USHORT fsPrePasteShift;           /* shift states of the VDM before pasting  */
USHORT fsPreExpandShift;   /* shift states of the VDM before scan expansion  */
USHORT fsPasteShift;              /* shift states of the VDM during pasting  */
UCHAR achExtraKeys[MAX_EXTRAKEYS *2];
ULONG iExtraKeyHead = 0;
ULONG iExtraKeyTail = 0;
PCPENTRY pRXVDM;
#pragma  END_SWAP_INSTANCE

/*
**    VKBD Initialization Code
*/

#pragma  BEGIN_INIT_CODE

/****************************************************************************
 *
 * FUNCTION NAME = VDDInit
 *
 * DESCRIPTION   =
 *
 *      VKBD initialization routine
 *      See VDD Manager for complete semantics.
 *
 *      This dynamically exported subroutine is called during
 *      system initialization.
 *
 * ENTRY
 *     psz - pointer to configuration strings (not used)
 *
 * EXIT
 *     SUCCESS
 *         return TRUE
 *     FAILURE
 *         return FALSE - unable to open PDD (PDD absent)
 *                      - unable to open keyboard VIRQ
 *
 * CONTEXT
 *     Init-time
 *
 * PSEUDOCODE
 *     install exit handler;
 *     get entry point from keyboard PDD (fpfnPKVDDProc);
 *     give VKPDDProc entry point to PDD;
 *     query keyboard type information from PDD;
 *     get handle for keyboard IRQ;
 *     specify EOI and IRET hooks for IRQ;
 *     define user event hooks (VKCreateVDM, VKTerminateVDM, etc.);
 *
 ****************************************************************************/

BOOL EXPENTRY VDDInit(PSZ psz)
{
  KID kid;
  VDHInstallUserHook(VDD_EXIT, VKExit);

  /*
  ** Register with PDD
  */
  if ((fpfnPKVDDProc = VDHOpenPDD(KBD_NAME, VKPDDProc)) == (FPVOID)NULL)
    return  FALSE;
  kid.kid_len = sizeof(KID);
  AssertRC((*fpfnPKVDDProc)(PKBDCMD_QUERYID, F16PNULL, F16PFROMSSP(&kid)));
  usKbdHWID = kid.kid_ID;
  AssertRC(vkCreateRX(0, &pRXDefault));

#ifdef   STUB_VPIC
  hirq = 0;

#else

  /*
  ** register EOI,IRET handlers
  */
  if ((hirq = VDHOpenVIRQ(KBD_IRQ, VKEOIProc, VKIRETProc, IRET_TIMEOUT,
  !VPIC_SHARE_IRQ)                    /* no interrupt sharing                */
  ) == 0)
    return  FALSE;
#endif
  VDHInstallUserHook(VDM_CREATE, VKCreateVDM);
  VDHInstallUserHook(VDM_TERMINATE, VKTerminateVDM);
  VDHInstallUserHook(VDM_FOREGROUND, VKSetFgnd);
  VDHInstallUserHook(VDM_BACKGROUND, VKSetBgnd);
  VDHInstallUserHook(VDM_CODEPAGE_CHANGE, VKSetCodePage);
  VDHRegisterVDD(VKBD_NAME, VKSysReqProc, NULL);
  VPLInit();                          /* initialize polling detection        */
  VDHRegisterProperty(szPropExtraKeys, /* property name                      */
     NULL,                            /* no help file                        */
     0L,                              /* no help id                          */
     VDMP_BOOL,                       /* type                                */
     VDMP_ORD_OTHER,                  /* no ordinal                          */
     0L,                              /* flags                               */
     (VOID *)TRUE,                    /* default value                       */
     NULL,                            /* validation info                     */
     VKSetExtraKeys);                 /* function                            */
  VDHRegisterProperty(szPropFastPaste, /* property name                      */
     NULL,                            /* no help file                        */
     0L,                              /* no help id                          */
     VDMP_BOOL,                       /* type                                */
     VDMP_ORD_OTHER,                  /* no ordinal                          */
     0L,                              /* flags                               */
     (VOID *)FALSE,                   /* default value                       */
     NULL,                            /* validation info                     */
     VKSetFastPaste);                 /* function                            */
  VDHRegisterProperty(szPropKbdRestrict, /* property name                    */
     NULL,                            /* no help file                        */
     0L,                              /* no help id                          */
     VDMP_BOOL,                       /* type                                */
     VDMP_ORD_OTHER,                  /* no ordinal                          */
     0,                               /* flags                               */
     (VOID *)FALSE,                   /* default value                       */
     NULL,                            /* validation info                     */
     VKSetKbdRestrict);               /* function                            */

  /*
  ** @V2.0DDK01
  */
  VDHRegisterProperty(szPropCtrlBypass, /* property name                     */
     NULL,                            /* no help file                        */
     0L,                              /* no help id                          */
     VDMP_ENUM,                       /* type                                */
     VDMP_ORD_OTHER,                  /* no ordinal                          */
     0,                               /* can't reset                         */
     pchHotKeys,                      /* default                             */
     szHotKeyList,                    /* validation info                     */
     VKHotKeyBypass);                 /* function                            */
  VDHRegisterProperty(szPropAltHome,  /* property name                       */
     NULL,                            /* no help file                        */
     0L,                              /* no help id                          */
     VDMP_BOOL,                       /* type                                */
     VDMP_ORD_OTHER,                  /* no ordinal                          */
     0,                               /* flags                               */
     (VOID *)FALSE,                   /* default value                       */
     NULL,                            /* validation info                     */
     VKAltHome);                      /* function                            */
  hhookHotPlug = VDHAllocHook(VDH_CONTEXT_HOOK, (PFNARM)VKHotPlugEvent, 0);
  return  TRUE;
}                                      /* VDDInit                            */
#pragma  END_INIT_CODE
#pragma  BEGIN_SWAP_CODE

/****************************************************************************
 *
 * FUNCTION NAME = VKExit
 *
 * DESCRIPTION   =
 *
 *      VKBD Exit Handler
 *      See VDHInstallUserHook for complete semantics.
 *
 *      This registered subroutine is called when MVDM is going to
 *      be shut down.  This allows the keyboard VDD to clean up
 *      resources that it has allocated.
 *
 * ENTRY
 *     None
 *
 * EXIT
 *     return TRUE
 *
 * CONTEXT
 *     Initialization
 *     VDM Task-time
 *
 * PSEUDOCODE
 *     if register with PKBD
 *         call PDD to do de-registration;
 *     if VIRQ has been opened
 *         call VDHCloseVIRQ;
 *
 ****************************************************************************/

BOOL HOOKENTRY VKExit(VOID)
{
  if (fpfnPKVDDProc)
    AssertRC((*fpfnPKVDDProc)(PKBDCMD_DEREGISTER, F16PNULL, F16PNULL));
  if (hirq)
    VDHCloseVIRQ(hirq);
  return  TRUE;
}                                      /* VKExit                             */

/****************************************************************************
 *
 * FUNCTION NAME = VKCreateVDM
 *
 * DESCRIPTION   =
 *
 *      VDM creation notification
 *      See VDHInstallUserHook for complete semantics.
 *
 *      This registered subroutine is called each time a new VDM
 *      is created.  Note that not a lot of initialization should be
 *      needed (ie, static initialization for most of the instance
 *      data suffices).
 *
 * ENTRY
 *     hvdm - handle of VDM
 *
 * EXIT
 *     SUCCESS
 *         return TRUE
 *     FAILURE
 *         return FALSE - failed to install I/O hooks
 *
 * CONTEXT
 *     VDM Task-time
 *
 * PSEUDOCODE
 *     define I/O port hooks;
 *     define Int 09h and Int 16h interrupt vector hooks;
 *     initialize VDM instance data;
 *     if kbd id is unknown or is enhanced kbd
 *         initialize BIOS variable to show default enhanced kbd;
 *     call VKUpdateLEDs to initialize virtKbdLEDs and some BIOS variables;
 *     set keyboard idle timer;
 *     call (*fpfnPKVDDProc)(PKBDCMD_VDMCREATED);
 *       to allow type-ahead and future keystrokes to flow into the VDD
 *     if proper model, type, and BIOS level
 *       Indicate to use f8h marker
 *     else
 *       Indicate to NOT use f8h marker
 *     endif
 *
 ****************************************************************************/

BOOL HOOKENTRY VKCreateVDM(HVDM hvdm)
{
  KSGID ksgid;
  PSYS_CONFIG_TABLE pConfig;    /* @V2.0DDK02 pointer to machine information */

  /*
  ** @V2.0DDK01
  */
  PCHAR pchUser = NULL;
  ULONG ulAddr = 0;

#ifndef  STUB_VPIC
  /*
  ** register IO hook for current VDM
  */
  if ((VDHInstallIOHook(hvdm, PORT_BUFFER, 1, /* one port                    */
&iotVKBuffer, !VDH_ASM_HOOK) == 0) || (VDHInstallIOHook(hvdm, PORT_CMD, 1,
                                       /* one port                           */
  &iotVKCmd, !VDH_ASM_HOOK) == 0))
    return  FALSE;
  VDHInstallIntHook(hvdm, BIOSINT_KEYINTERRUPT, VKInt09Proc, !VDH_ASM_HOOK);
  VDHInstallIntHook(hvdm, BIOSINT_KEYBOARD, VKInt16Proc, !VDH_ASM_HOOK);
#endif
  if ((hhookTransferScanHook = VDHAllocHook(VDH_CONTEXT_HOOK,
     VKTransferScanHook, sizeof(HVDM))) == NULL)
  {
    return  FALSE;
  }
  if ((hhookInt09PostInt15 = VDHAllocHook(VDH_RETURN_HOOK, VKInt09PostInt15, 0)
     ) == NULL)
  {
    return  FALSE;
  }
  if ((hhookInt09PostBreak = VDHAllocHook(VDH_RETURN_HOOK, VKInt09PostBreak, 0)
     ) == NULL)
  {
    return  FALSE;
  }
  if ((hhookInt09PostPrtSc = VDHAllocHook(VDH_RETURN_HOOK, VKInt09PostPrtSc, 0)
     ) == NULL)
  {
    return  FALSE;
  }
  if ((hhookInt09PostSysReq = VDHAllocHook(VDH_RETURN_HOOK, VKInt09PostSysReq,
     0)) == NULL)
  {
    return  FALSE;
  }
  if ((hhookInt09PostIntReturn = VDHAllocHook(VDH_RETURN_HOOK,
     VKInt09PostIntReturn, 0)) == NULL)
  {
    return  FALSE;
  }
  if ((hhookInt09PauseReturn = VDHAllocHook(VDH_WAITVIRRS_HOOK,
     VKInt09PauseReturn, 0)) == NULL)
  {
    return  FALSE;
  }
  if ((hhookInt16ProcReturn = VDHAllocHook(VDH_WAITVIRRS_HOOK,
     VKInt16ProcReturn, 0)) == NULL)
  {
    return  FALSE;
  }
  if ((hhookPasteTimeout = VDHAllocHook(VDH_TIMER_HOOK, VKPasteTimeout, 0)) ==
     NULL)
  {
    return  FALSE;
  }
  if ((BOOL)VDHQueryProperty(szPropExtraKeys))
    flVDM |= VDM_EXTRAKEYS;
  if ((BOOL)VDHQueryProperty(szPropFastPaste))
    flVDM |= VDM_FASTPASTE;
  if ((BOOL)VDHQueryProperty(szPropKbdRestrict))
    flVDM |= VDM_KBDRESTRICT;

  /*
  ** @V2.0DDK01
  */

  /*
  ** Query property to get ALT+HOME BYPASS DATA.
  */
  if ((BOOL)VDHQueryProperty(szPropAltHome))
    flhkVDM |= VDM_ALTHOME;
  else
    flhkVDM &= ~VDM_ALTHOME;

  /*
  ** Query property to get CONTROL BYPASS DATA.  The user can choose
  ** NONE, ALT+ESC or CTRL+ESC to be bypassed. (only 1 choice allowed)
  */
  if ((pchUser = (PCHAR)VDHQueryProperty(szPropCtrlBypass)) && (VKgetTheValue
     (pchUser, 0, SSToDS(&ulAddr))))
  {

    /*
    ** If NONE are selected to be bypassed, the two cases below will
    ** clear the bits.
    */
    if (ulAddr == 0x0001)             /* If Alt+Esc is to be bypassed        */
      flhkVDM |= VDM_ALTESC;          /* bit 0004h                           */
    else
      flhkVDM &= ~VDM_ALTESC;

    if (ulAddr == 0x0002)             /* If Ctrl+Esc is to be bypassed       */
      flhkVDM |= VDM_CTRLESC;         /* bit 0008h                           */
    else
      flhkVDM &= ~VDM_CTRLESC;
  }                                   /* endif                               */

  /*
  ** Free memory allocated by VDHQueryProperty
  */
  if (pchUser)
    VDHFreeMem(pchUser);

  /*
  ** initialize VDM instance data
  */
  iKPPopup = TRUE ;

  if ((usKbdHWID == KBDID_ENHANCED) || (usKbdHWID == KBDID_UNKNOWN))
      VDMBase.rb_fbKFlag3 |= BIOSKFLAG3_KBX;  //default to enhanced kbd

  pVDMBase(hvdm)->rb_fbKFlag = 0;     /* Shift byte at 40:17                 */
  pVDMBase(hvdm)->rb_fbKFlag1 = 0;    /* Shift byte at 40:18                 */

  fsKeyShift = 0;                     /* Init shift state to zero            */

  pRXVDM = pRXDefault;
  pRXVDM->cp_cReference++;

  xltBuff.kxlt_len = KXLT_PKT_LEN;
  xltKxf.kxf_len = KXF_PKT_LEN;
  xltKxf.kxf_fbMode = KXFMODE_BINARY;

  xltBuff.kxlt_f16pKey1 = F16PFROMP(&xltKey1);
  xltBuff.kxlt_f16pKey2 = F16PFROMP(&xltKey2);
  xltBuff.kxlt_f16pKxf = F16PFROMP(&xltKxf);

  xltBuff.kxlt_usCodePage = pRXVDM->cp_usCodePage;                  /*116919*/

  pVDMBase(hvdm)->rb_fbKFlag2 &= (!BIOSKFLAG2_CYR);  /* Set to LATIN layer */

  ksgid.ksgid_len = sizeof(KSGID);

  ksgid.ksgid_sgid = (SGID)VDHQuerySysValue(hvdm, VDHLSV_SESSIONID);
  AssertRC((*fpfnPKVDDProc)(PKBDCMD_VDMCREATED, F16PFROMSSP(&ksgid),
                                                F16PNULL));

  /*
  ** @V2.0DDK02
  */
  pConfig = VDHQuerySysValue(hvdm, VDHGSV_MACHINEINFO);
  if ((pConfig->Model == 0xf8) && (pConfig->Submodel == 0x19) &&
     (pConfig->BIOS_revision <= 5))
  {
    fMarker = FALSE;               /* don't use the marker                   */
  }
  else
    fMarker = TRUE;               /* put an f8h marker into the char field   */
                                  /* for ext ext scans*/

  /*
  ** Get handle to Video VDD so that we can post events for windowed VDMs
  */

  if (!hvddVideo)
    hvddVideo = VDHOpenVDD(VVD_NAME1);
  return  VPLCreate(hvdm);            /* initialize idle detection           */
}                                     /* VKCreateVDM                         */

/*
** @V2.0DDK01
*/

/****************************************************************************
 *
 * FUNCTION NAME = VKgetTheValue
 *
 * DESCRIPTION   =   Compares input string to list of hot keys and
 *                   returns the ULONG bit value which corresponds
 *                   to the string.
 *
 *      ENTRY   pchUser - pointer to input string
 *              itls    - index of test list to compare to
 *              pulAddr - return value corresponding to string
 *
 *      EXIT    SUCCESS returns pointer to asciiz string in test list
 *              *pulAddr filled with bit value corresponding to string
 *              FAILURE returns NULL
 *
 *      CONTEXT OS/2 task context (usually the Shield layer)
 *
 *      METHOD
 *      get comparison strings and values from atls[itls]
 *      scan comparison strings until match or not found
 *      if found
 *          set return value to start of match in string
 *          set *pulAddr
 *      endif
 *
 ****************************************************************************/

PCHAR PASCAL VKgetTheValue(PCHAR pchUser,ULONG itls,PULONG pulAddr)
{
  ULONG cb;
  PULONG pul;
  PCHAR pchar,pchRet = NULL;
  pchar = atls[itls].tls_pchList;
  pul = atls[itls].tls_pulVal;
  for (; *pchar; pchar += cb+1, pul++)
  {
    cb = strlen(pchar);
    if (!memcmp(pchUser, pchar, cb))
    {
      pchRet = pchar;
      *pulAddr = *pul;
      break;                          /* matched                             */
    }                                 /* endif                               */
  }                                   /* endfor                              */
  return (pchRet);
}                                     /* end VKgetTheValue                   */

/****************************************************************************
 *
 * FUNCTION NAME = VKTerminateVDM
 *
 * DESCRIPTION   =
 *
 *     VDM destruction notification
 *     See VDHInstallUserHook for complete semantics.
 *
 *     This registered subroutine is called each time a VDM
 *     is destroyed.  This handler deallocates the per VDM
 *     resources used by the VDM being destroyed.
 *
 * ENTRY
 *     hvdm - handle of VDM
 *
 * EXIT
 *     return TRUE
 *
 * CONTEXT
 *     VDM Task-time
 *
 * PSEUDOCODE
 *     deallocate I/O port hooks;
 *     call PDD to disable this VDM;
 *
 ****************************************************************************/

BOOL HOOKENTRY VKTerminateVDM(HVDM hvdm)
{
  KSGID ksgid;
  PHVDM phvdm;
  KFOCUS kfocus;

  /*
  ** @V2.0DDK01
  */
  KHOTKEYS khotkeys;
  pRXVDM->cp_cReference--;
  ksgid.ksgid_len = sizeof(KSGID);
  ksgid.ksgid_sgid = (SGID)VDHQuerySysValue(hvdm, VDHLSV_SESSIONID);
  AssertRC((*fpfnPKVDDProc)(PKBDCMD_VDMTERMINATED, F16PFROMSSP(&ksgid),
     F16PNULL));

  /*
  ** Make sure the hook was allocated before trying to query it
  */
  if (hhookTransferScanHook)
  {
    if (*(phvdm = (PHVDM)VDHQueryHookData(hhookTransferScanHook)) == hvdm)
      *phvdm = 0;
  }
  kfocus.kf_len = sizeof(KFOCUS);
  kfocus.kf_fsFocus = FALSE;
  (*fpfnPKVDDProc)(PKBDCMD_KFOCUS, F16PFROMSSP(&kfocus), F16PNULL);

  /*
  ** @V2.0DDK01
  */
  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  TRUE;
}                                      /* VKTerminateVDM                     */

/****************************************************************************
 *
 * FUNCTION NAME = VKSetFgnd
 *
 * DESCRIPTION   =
 *
 *      VDM foreground notification
 *      (Virtual keyboard de-virtualization )
 *      See VDHInstallUserHook for complete semantics.
 *
 *      This registered subroutine is called each time a VDM
 *      is switched to foreground.  It adjusts the level of virtualization
 *      so that I/O commands are directed to the PDD instead of being
 *      virtualized.
 *
 * ENTRY
 *     hvdm - VDM handle
 *
 * EXIT
 *     returns TRUE
 *
 * CONTEXT
 *     Task-time
 *
 * PSEUDOCODE
 *     set VDM_FGND
 *     call VKUpdateLEDs
 *
 ****************************************************************************/

BOOL HOOKENTRY VKSetFgnd(HVDM hvdm)
{
  REFHVDM(hvdm, BOOL, flVDM) |= VDM_FGND;
  VKUpdateLEDs(hvdm);
  return  TRUE;
}                                      /* VKSetFgnd                          */

/****************************************************************************
 *
 * FUNCTION NAME = VKSetBgnd
 *
 * DESCRIPTION   =
 *
 *      VDM background notification
 *      (Virtual keyboard virtualization )
 *      See VDHInstallUserHook for complete semantics.
 *
 *      This registered subroutine is called each time a VDM
 *      is switched to background.  It adjusts the level of virtualization
 *      so that I/O commands are virtualized instead of being directed
 *      to the PDD.
 *
 * ENTRY
 *     hvdm - VDM handle
 *
 * EXIT
 *     return TRUE
 *
 * CONTEXT
 *     Task-time
 *
 * PSEUDOCODE
 *     clear VDM_FGND
 *
 ****************************************************************************/

BOOL HOOKENTRY VKSetBgnd(HVDM hvdm)
{
  REFHVDM(hvdm, BOOL, flVDM) &= ~VDM_FGND;
  return  TRUE;
}                                      /* VKSetBgnd                          */

/****************************************************************************
 *
 * FUNCTION NAME = VKSetCodePage
 *
 * DESCRIPTION   =
 *
 *      VDM change code page notification
 *      See VDHInstallUserHook for complete semantics.
 *
 *      This registered subroutine is called each time a VDM
 *      switches a codepage value.
 *
 * ENTRY
 *     ulCodePage - new code page value
 *
 * EXIT-SUCCESS
 *     return TRUE
 * EXIT-FAILURE
 *     return FALSE
 *
 * CONTEXT
 *     VDM Task-time
 *
 * PSEUDOCODE
 *
 ****************************************************************************/

BOOL HOOKENTRY VKSetCodePage(ULONG ulCodePage)
{
  PCPENTRY pcp;
  if ((--pRXVDM->cp_cReference == 0) && /* nobody references it-             */
     (pRXVDM != pRXDefault) &&        /* cannot destroy default RX           */
     (pRXVDM->cp_usCodePage == 0))
  {                                   /* invalidated CPENTRY                 */
    vkDestroyRX(pRXVDM);
    pRXVDM = NULL;
  }
  for (pcp = pRXHead; pcp != NULL; pcp = pcp->cp_pcpNext)
    if ((ULONG)pcp->cp_usCodePage == ulCodePage)
      break;
  if (pcp)
  {                                   /* codepage found                      */
    pcp->cp_cReference++;
    pRXVDM = pcp;
    xltBuff.kxlt_usCodePage = (USHORT)ulCodePage;

    VDMBase.rb_fbKFlag2 &= (!BIOSKFLAG2_CYR);  /* Set to LATIN layer */

    return  TRUE;
  }
  else
    if (vkCreateRX(ulCodePage, SSToDS(&pcp)))
    {                                 /* create a new code page              */
      pcp->cp_cReference++;
      if (pcp->cp_pcpNext = pRXHead)
        pRXHead->cp_pcpPrev = pcp;
      pRXHead = pRXVDM = pcp;
      xltBuff.kxlt_usCodePage = (USHORT)ulCodePage;

      VDMBase.rb_fbKFlag2 &= (!BIOSKFLAG2_CYR);  /* Set to LATIN layer */

      return  TRUE;
    }
    else
      return  FALSE;
}                                      /* VKSetCodePage                      */

/****************************************************************************
 *
 * FUNCTION NAME = vkCreateRX
 *
 * DESCRIPTION   = create reverse translation table
 *
 * ENTRY
 *     ulCodePage - new code page value
 *     ppcp -> pointer to CPENTRY
 *         *ppcp == NULL if new CPENTRY is to be allocated
 *         *ppcp != NULL to use the given CPENTRY
 *
 * EXIT-SUCCESS
 *     return TRUE
 *     *ppcp -> newly allocated CPENTRY
 * EXIT-FAILURE
 *     return FALSE
 *
 * CONTEXT
 *     VDM Task-time
 *
 * PSEUDOCODE
 *
 ****************************************************************************/

BOOL PRIVENTRY vkCreateRX(ULONG ulCodePage,PPCPENTRY ppcp)
{
  KQRX kqrx;
  SEL selcp;
  BOOL rc = TRUE,fNew = FALSE;
  if (*ppcp == NULL)
  {
    *ppcp = (PCPENTRY)VDHAllocMem(sizeof(CPENTRY), VDHAM_SWAPPABLE);
    fNew = TRUE;
    AssertNONZERO(*ppcp);
  }
  memset(*ppcp, 0, sizeof(CPENTRY));
  (*ppcp)->cp_ShTable[SHINDX_LSHIFT].sh_fsShift = KEYSTATE_LEFTSHIFT;
  (*ppcp)->cp_ShTable[SHINDX_LCTRL].sh_fsShift = KEYSTATE_EITHERCTRL+
     KEYSTATE_LEFTCTRL;
  (*ppcp)->cp_ShTable[SHINDX_LALT].sh_fsShift = KEYSTATE_EITHERALT+
     KEYSTATE_LEFTALT;
  (*ppcp)->cp_ShTable[SHINDX_RSHIFT].sh_fsShift = KEYSTATE_RIGHTSHIFT;
  (*ppcp)->cp_ShTable[SHINDX_RCTRL].sh_fsShift = KEYSTATE_EITHERCTRL+
     KEYSTATE_RIGHTCTRL;
  (*ppcp)->cp_ShTable[SHINDX_RCTRL].sh_scanPrefix = SCAN_E0PREFIX;
  (*ppcp)->cp_ShTable[SHINDX_RALT].sh_fsShift = KEYSTATE_EITHERALT+
     KEYSTATE_RIGHTALT;
  (*ppcp)->cp_ShTable[SHINDX_RALT].sh_scanPrefix = SCAN_E0PREFIX;
  selcp = VDHCreateSel(*ppcp, sizeof(CPENTRY));
  kqrx.kqrx_len = sizeof(KQRX);
  kqrx.kqrx_usCodePage = (USHORT)ulCodePage;
  kqrx.kqrx_f16pRXTable = VPFROMVADDR(selcp, ((PBYTE)&(*ppcp)->cp_RXTable-
     (PBYTE)(*ppcp)));
  kqrx.kqrx_f16pShTable = VPFROMVADDR(selcp, ((PBYTE)&(*ppcp)->cp_ShTable-
     (PBYTE)(*ppcp)));
  kqrx.kqrx_f16pNumTable = VPFROMVADDR(selcp, ((PBYTE)&(*ppcp)->cp_NumTable-
     (PBYTE)(*ppcp)));
  if ((*fpfnPKVDDProc)(PKBDCMD_QUERYREVXLATE, F16PFROMSSP(&kqrx), F16PNULL))
  {
    (*ppcp)->cp_usCodePage = kqrx.kqrx_usCodePage;
    (*ppcp)->cp_hCodePage = kqrx.kqrx_hCodePage;/* store table handle        */
  }
  else
    if (fNew)
    {                                /* only dispose newly allocated memory  */
      VDHFreeMem(*ppcp);
      *ppcp = NULL;
      rc = FALSE;
    }
  VDHDestroySel(selcp);
  return  rc;
}                                      /* vkCreateRX                         */

/****************************************************************************
 *
 * FUNCTION NAME = vkDestroyRX
 *
 * DESCRIPTION   = destroy reverse translation table
 *
 * ENTRY
 *     pcp-> CPENTRY in the linked list
 * EXIT
 *     NONE
 *
 * CONTEXT
 *     VDM Task-time
 *
 * PSEUDOCODE
 *
 ****************************************************************************/

VOID PRIVENTRY vkDestroyRX(PCPENTRY pcp)
{
  AssertTRUE(pcp != pRXDefault);
  if (pcp->cp_pcpPrev != NULL)
    pcp->cp_pcpPrev->cp_pcpNext = pcp->cp_pcpNext;
  if (pcp->cp_pcpNext != NULL)
    pcp->cp_pcpNext->cp_pcpPrev = pcp->cp_pcpPrev;
  if (pRXHead == pcp)
    pRXHead = pcp->cp_pcpNext;
  VDHFreeMem(pcp);
}                                      /* vkDestroyRX                        */

/****************************************************************************
 *
 * FUNCTION NAME = VKSetExtraKeys
 *
 * DESCRIPTION   = set extra key buffering
 *
 * ENTRY
 *     op - operation to perform (set)
 *     hvdm - target VDM
 *     cb - length of value
 *     pch - ptr to BOOL value
 *
 * EXIT
 *     returns 0 (no error)
 *
 * CONTEXT OS/2 task context (usually the Shield layer)
 *
 ****************************************************************************/


ULONG EXPENTRY VKSetExtraKeys(ULONG op,HVDM hvdm,ULONG cb,CHAR *pch)
{
  if ((BOOL)pch)
  {
    if (!(REFHVDM(hvdm, FLAGS, flVDM)&VDM_EXTRAKEYS))
    {
      REFHVDM(hvdm, FLAGS, flVDM) |= VDM_EXTRAKEYS;
      REFHVDM(hvdm, ULONG, iExtraKeyHead) = REFHVDM(hvdm, ULONG, iExtraKeyTail)
         = 0;
    }
  }
  else
    REFHVDM(hvdm, FLAGS, flVDM) &= ~VDM_EXTRAKEYS;
  return 0;
}

/****************************************************************************
 *
 * FUNCTION NAME = VKSetFastPaste
 *
 * DESCRIPTION   = set fast paste mode
 *
 * ENTRY
 *     op - operation to perform (set)
 *     hvdm - target VDM
 *     cb - length of value
 *     pch - ptr to BOOL value
 *
 * EXIT
 *     returns 0 (no error)
 *
 * CONTEXT OS/2 task context (usually the Shield layer)
 *
 ****************************************************************************/

ULONG EXPENTRY VKSetFastPaste(ULONG op,HVDM hvdm,ULONG cb,CHAR *pch)
{
  if (REFHVDM(hvdm, FLAGS, flVDM)&VDM_PASTING)
    return  ERROR_BUSY;
  if ((BOOL)pch)
    REFHVDM(hvdm, FLAGS, flVDM) |= VDM_FASTPASTE;
  else
    REFHVDM(hvdm, FLAGS, flVDM) &= ~VDM_FASTPASTE;
  return 0;
}

/****************************************************************************
 *
 * FUNCTION NAME = VKSetKbdRestrict
 *
 * DESCRIPTION   = set kbd restrictions
 *
 * ENTRY
 *     op - operation to perform (set)
 *     hvdm - target VDM
 *     cb - length of value
 *     pch - ptr to BOOL value
 *
 * EXIT
 *     returns 0 (no error)
 *
 * CONTEXT OS/2 task context (usually the Shield layer)
 *
 ****************************************************************************/

ULONG EXPENTRY VKSetKbdRestrict(ULONG op,HVDM hvdm,ULONG cb,CHAR *pch)
{
  if ((BOOL)pch)
    REFHVDM(hvdm, FLAGS, flVDM) |= VDM_KBDRESTRICT;
  else
    REFHVDM(hvdm, FLAGS, flVDM) &= ~VDM_KBDRESTRICT;
  return 0;
}

/****************************************************************************
 *
 * FUNCTION NAME = VKAltHome
 *
 * DESCRIPTION   = Enable/Bypass the Alt+Home
 *
 * ENTRY
 *     op - operation to perform (set)
 *     hvdm - target VDM
 *     cb - length of value
 *     pch - ptr to BOOL value
 *
 * EXIT
 *     returns 0 (no error)
 *
 * CONTEXT OS/2 task context (usually the Shield layer)
 *
 ****************************************************************************/

/*
** @V2.0DDK01
*/

/*
** VKAltHome : Entry point registered by VDHRegisterProperty call.
*/

ULONG EXPENTRY VKAltHome(ULONG op,HVDM hvdm,ULONG cb,CHAR *pch)
{
  KHOTKEYS khotkeys;
  if ((BOOL)pch)                      /* If TRUE                             */
    REFHVDM(hvdm, FLAGS, flhkVDM) |= VDM_ALTHOME;/* bit 0004h                */
  else
    REFHVDM(hvdm, FLAGS, flhkVDM) &= ~VDM_ALTHOME;
  khotkeys.khk_len = sizeof(KHOTKEYS);
  if (khotkeys.khk_hotkeys = (REFHVDM(hvdm, FLAGS, flhkVDM)&VDM_ALTHOME))
    khotkeys.khk_action = 1;          /* Bypass the Alt+Home                 */
  else
    khotkeys.khk_action = 0;          /* Enable the Alt+Home                 */
  (*fpfnPKVDDProc)(PKBDCMD_HOTKEY, F16PFROMSSP(&khotkeys), F16PNULL);
  return 0;
}

/****************************************************************************
 *
 * FUNCTION NAME = VKHotKeyBypass
 *
 * DESCRIPTION   = Enable/Bypass Alt+Esc OR Ctrl+Esc
 *
 * ENTRY
 *     op - operation to perform (set)
 *     hvdm - target VDM
 *     cb - length of value
 *     pch - ptr to STRING value
 *
 * EXIT
 *     returns 0 (no error)
 *
 * CONTEXT OS/2 task context (usually the Shield layer)
 *
 ****************************************************************************/

/*
** @V2.0DDK01
*/

/*
** VKHotKeyBypass : Entry point registered by VDHRegisterProperty call.
*/

ULONG EXPENTRY VKHotKeyBypass(ULONG op,HVDM hvdm,ULONG cb,CHAR *pch)
{
  ULONG ulAddr = 0;
  KHOTKEYS khotkeys;
  VKgetTheValue(pch, 0, SSToDS(&ulAddr));

  /*
  ** If NONE are selected to be bypassed, both cases below will
  ** clear any bits that may be set.  flhkVDM can NOT be zero'd out
  ** because Alt+Home bit may be set. NONE only pertains to the
  ** Alt+Esc and Ctrl+Esc bits.
  */
  if (ulAddr == 0x0001)               /* If Alt+Esc is to be bypassed        */
    REFHVDM(hvdm, FLAGS, flhkVDM) |= VDM_ALTESC;/* 0004                      */
  else
    REFHVDM(hvdm, FLAGS, flhkVDM) &= ~VDM_ALTESC;
  if (ulAddr == 0x0002)               /* If Ctrl+Esc is to be bypassed       */
    REFHVDM(hvdm, FLAGS, flhkVDM) |= VDM_CTRLESC;/* 0008                     */
  else
    REFHVDM(hvdm, FLAGS, flhkVDM) &= ~VDM_CTRLESC;
  khotkeys.khk_len = sizeof(KHOTKEYS);
  if (REFHVDM(hvdm, FLAGS, flhkVDM)&(VDM_CTRLESC+VDM_ALTESC))
  {

    /*
    ** One of the above bits are set, enable the other one since it
    ** may have been previously set.
    */
    khotkeys.khk_action = 0;          /* Indicate to ENABLE                  */
    if (REFHVDM(hvdm, FLAGS, flhkVDM)&VDM_CTRLESC)
    {
      khotkeys.khk_hotkeys = VDM_ALTESC;/* Enable Alt+Esc                    */
      (*fpfnPKVDDProc)(PKBDCMD_HOTKEY, F16PFROMSSP(&khotkeys), F16PNULL);
    }
    else
    {
      khotkeys.khk_hotkeys = VDM_CTRLESC;/* Enable Ctrl+Esc                  */
      (*fpfnPKVDDProc)(PKBDCMD_HOTKEY, F16PFROMSSP(&khotkeys), F16PNULL);
    }

    /*
    ** Bypass the Hot Key associated with the bit that is set
    ** NOTE this is done after ENABLE to prevent setting both bits.
    */
    khotkeys.khk_action = 1;          /* Indicate to BYPASS                  */
    khotkeys.khk_hotkeys = REFHVDM(hvdm, FLAGS, flhkVDM);
    (*fpfnPKVDDProc)(PKBDCMD_HOTKEY, F16PFROMSSP(&khotkeys), F16PNULL);
  }
  else
  {                  /* Else the user selected NONE, enable both Hot Keys.   */
    khotkeys.khk_hotkeys = (VDM_CTRLESC+VDM_ALTESC);
    khotkeys.khk_action = 0;          /* Enable ALL control keys             */
    (*fpfnPKVDDProc)(PKBDCMD_HOTKEY, F16PFROMSSP(&khotkeys), F16PNULL);
  }
  return 0;
}

/****************************************************************************
 *
 * FUNCTION NAME = VDHRegisterScreenProc
 *
 * DESCRIPTION   = Video-enable registration
 *
 *      This routine is called by whichever virtual video device driver is
 *      acting on behalf of the primary adapter, to establish the address
 *      of a routine to call in the event the user PAUSES a VDM, allowing the
 *      virtual video driver to re-enable the video signal if need be.  If
 *      no service is ever registered, or if a NULL address is registered,
 *      it is assumed that screen-enabling is unnecessary.
 *
 * ENTRY
 *     pfn     -> VVD entry point to re-enable video signal
 *
 * EXIT
 *     None
 *
 * CONTEXT
 *     Task-time
 *
 * PSEUDOCODE
 *     save pfn in pfnScreenProc
 *
 ****************************************************************************/

VOID VDHENTRY VDHRegisterScreenProc(PFNSCRNP pfn)
{
  pfnScreenProc = pfn;
}                                      /* VDHRegisterScreenProc              */

#pragma  END_SWAP_CODE
#pragma  BEGIN_GLOBAL_CODE

/****************************************************************************
 *
 * FUNCTION NAME = VDHQueryKeyShift
 *
 * DESCRIPTION   = Query keyboard shift states
 *
 *      This routine is called by the virtual mouse driver to query the
 *      keyboard shift states of a VDM.
 *
 * ENTRY
 *     hvdm -> VDM
 *
 * EXIT
 *     None
 *
 * CONTEXT
 *     Interrupt
 *
 * PSEUDOCODE
 *     return the key shift states of the VDM;
 *
 ****************************************************************************/

USHORT VDHENTRY VDHQueryKeyShift(HVDM hvdm)
{
  return  REFHVDM(hvdm, USHORT, fsKeyShift);
}                                      /* VDHQueryKeyShift                   */
#pragma  END_GLOBAL_CODE
