/** @file
  Virtural Keyboard Layout Engine

  Copyright (c) 2012 - 2020, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "VirtualKeyboard.h"

//
// 1.FullKeyboardBody
// Follow below design
//   (1).\KeyboardLayout\CapitalLetterKeyboard.bmp
//   (2).\KeyboardLayout\DigitKeyboard.bmp
// Based on keyboard's (startX, startY)
//
// Page0Font
// +---+---+---+---+---+---+---+---+---+---+---+
// | q | w | e | r | t | y | u | i | o | p |<X|| Line 0
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | | a | s | d | f | g | h | j | k | l | F2| | Line 1
// +-+---+---+---+---+---+---+---+---+---+---+-+
// |Caps | z | x | c | v | b | n | m |aU |Enter| Line 2
// +-----+---+---+---+---+---+---+---+---+---+-+
// | Esc |12#|       Space       |aL |aD |aR | | Line 3
// +-----+---+-------------------+---+---+---+-+
//
// Page1Font
// +---+---+---+---+---+---+---+---+---+---+---+
// | Q | W | E | R | T | Y | U | I | O | P |<X|| Line 0
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | | A | S | D | F | G | H | J | K | L | F2| | Line 1
// +-+---+---+---+---+---+---+---+---+---+---+-+
// |Caps | Z | X | C | V | B | N | M |aU |Enter| Line 2
// +-----+---+---+---+---+---+---+---+---+---+-+
// | Esc |12#|       Space       |aL |aD |aR | | Line 3
// +-----+---+-------------------+---+---+---+-+
//
// Page2Font
// +---+---+---+---+---+---+---+---+---+---+---+
// | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 |<X|| Line 0
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |F1 |F2 |F3 |F4 |F5 |F6 |F7 |F8 |F9 |F10| | Line 1
// +-+---+---+---+---+---+---+---+---+---+---+-+
// |Shift| . | ; | ' | , | . | / |F11|F12|Enter| Line 2
// +-----+---+---+---+---+---+---+---+---+---+-+
// | Esc |12#|       Space       | \ | - | = | | Line 3
// +-----+---+-------------------+---+---+---+-+
//
// Page3Font
// +---+---+---+---+---+---+---+---+---+---+---+
// | ! | @ | # | $ | % | ^ | & | * | ( | ) |<X|| Line 0
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |F1 |F2 |F3 |F4 |F5 |F6 |F7 |F8 |F9 |F10| | Line 1
// +-+---+---+---+---+---+---+---+---+---+---+-+
// |Shift| ~ | : | " | < | > | ? |F11|F12|Enter| Line 2
// +-----+---+---+---+---+---+---+---+---+---+-+
// | Esc |12#|       Space       | | | _ | + | | Line 3
// +-----+---+-------------------+---+---+---+-+
//
// 2.Screen Corner
// Follow below design
//   (1).\KeyboardLayout\SimpleIcon.bmp # Screen Corner A/B
//   (2).\KeyboardLayout\FullIcon.bmp   # Screen Corner C/D
// +-+------------------------------------+-+
// |A|                                    |C|
// +-+                                    +-+
// |                                        |
// |                                        |
// |                                        |
// |                                        |
// |                                        |
// |                                        |
// +-+                                    +-+
// |B|                                    |D|
// +-+------------------------------------+-+
//
VK_STRUCT mFullKeyboardBody[] = {
  // StartX  StartY  EndX    EndY     Page0Font            Page1Font            Page2Font            Page3Font
  // Line 0
  {  0x0000, 0x0000, 0x0032, 0x0032, {L'q',                L'Q',                L'1',                L'!'            }},
  {  0x0032, 0x0000, 0x0064, 0x0032, {L'w',                L'W',                L'2',                L'@'            }},
  {  0x0064, 0x0000, 0x0096, 0x0032, {L'e',                L'E',                L'3',                L'#'            }},
  {  0x0096, 0x0000, 0x00C8, 0x0032, {L'r',                L'R',                L'4',                L'$'            }},
  {  0x00C8, 0x0000, 0x00FA, 0x0032, {L't',                L'T',                L'5',                L'%'            }},
  {  0x00FA, 0x0000, 0x012C, 0x0032, {L'y',                L'Y',                L'6',                L'^'            }},
  {  0x012C, 0x0000, 0x015E, 0x0032, {L'u',                L'U',                L'7',                L'&'            }},
  {  0x015E, 0x0000, 0x0190, 0x0032, {L'i',                L'I',                L'8',                L'*'            }},
  {  0x0190, 0x0000, 0x01C2, 0x0032, {L'o',                L'O',                L'9',                L'('            }},
  {  0x01C2, 0x0000, 0x01F4, 0x0032, {L'p',                L'P',                L'0',                L')'            }},
  {  0x01F4, 0x0000, 0x0226, 0x0032, {VkKeyBackspace,      VkKeyBackspace,      VkKeyBackspace,      VkKeyBackspace  }},
  // Line 1
  {  0x0000, 0x0032, 0x0019, 0x0064, {VkKeyNull,           VkKeyNull,           VkKeyNull,           VkKeyNull       }},
  {  0x0019, 0x0032, 0x004B, 0x0064, {L'a',                L'A',                VkKeyF1,             VkKeyF1         }},
  {  0x004B, 0x0032, 0x007D, 0x0064, {L's',                L'S',                VkKeyF2,             VkKeyF2         }},
  {  0x007D, 0x0032, 0x00AF, 0x0064, {L'd',                L'D',                VkKeyF3,             VkKeyF3         }},
  {  0x00AF, 0x0032, 0x00E1, 0x0064, {L'f',                L'F',                VkKeyF4,             VkKeyF4         }},
  {  0x00E1, 0x0032, 0x0113, 0x0064, {L'g',                L'G',                VkKeyF5,             VkKeyF5         }},
  {  0x0113, 0x0032, 0x0145, 0x0064, {L'h',                L'H',                VkKeyF6,             VkKeyF6         }},
  {  0x0145, 0x0032, 0x0177, 0x0064, {L'j',                L'J',                VkKeyF7,             VkKeyF7         }},
  {  0x0177, 0x0032, 0x01A9, 0x0064, {L'k',                L'K',                VkKeyF8,             VkKeyF8         }},
  {  0x01A9, 0x0032, 0x01DB, 0x0064, {L'l',                L'L',                VkKeyF9,             VkKeyF9         }},
  {  0x01DB, 0x0032, 0x020D, 0x0064, {VkKeyF2,             VkKeyF2,             VkKeyF10,            VkKeyF10        }},
  // Line 2
  {  0x0000, 0x0064, 0x004B, 0x0096, {VkKeyCapslock,       VkKeyCapslock,       VkKeyShift,          VkKeyShift      }},
  {  0x004B, 0x0064, 0x007D, 0x0096, {L'z',                L'Z',                L'`',                L'~'            }},
  {  0x007D, 0x0064, 0x00AF, 0x0096, {L'x',                L'X',                L';',                L':'            }},
  {  0x00AF, 0x0064, 0x00E1, 0x0096, {L'c',                L'C',                L'\'',               L'"'            }},
  {  0x00E1, 0x0064, 0x0113, 0x0096, {L'v',                L'V',                L',',                L'<'            }},
  {  0x0113, 0x0064, 0x0145, 0x0096, {L'b',                L'B',                L'.',                L'>'            }},
  {  0x0145, 0x0064, 0x0177, 0x0096, {L'n',                L'N',                L'/',                L'?'            }},
  {  0x0177, 0x0064, 0x01A9, 0x0096, {L'm',                L'M',                VkKeyF11,            VkKeyF11        }},
  {  0x01A9, 0x0064, 0x01DB, 0x0096, {VkKeyUp,             VkKeyUp,             VkKeyF12,            VkKeyF12        }},
  {  0x01DB, 0x0064, 0x0226, 0x0096, {VkKeyEnter,          VkKeyEnter,          VkKeyEnter,          VkKeyEnter      }},
  // Line 3
  {  0x0000, 0x0096, 0x004B, 0x00C8, {VkKeyEsc,            VkKeyEsc,            VkKeyEsc,            VkKeyEsc        }},
  {  0x004B, 0x0096, 0x007D, 0x00C8, {VkKeyTwoPage,        VkKeyTwoPage,        VkKeyTwoPage,        VkKeyTwoPage    }},
  {  0x007D, 0x0096, 0x0177, 0x00C8, {L' ',                L' ',                L' ',                L' '            }},
  {  0x0177, 0x0096, 0x01A9, 0x00C8, {VkKeyLeft,           VkKeyLeft,           L'\\',               L'|'            }},
  {  0x01A9, 0x0096, 0x01DB, 0x00C8, {VkKeyDown,           VkKeyDown,           L'-',                L'_'            }},
  {  0x01DB, 0x0096, 0x020D, 0x00C8, {VkKeyRight,          VkKeyRight,          L'=',                L'+'            }},
  // Screen Corner A
  {  0x0000, 0x0000, 0x001E, 0x001E, {VkKeyTypeMaximum,    VkKeyTypeMaximum,    VkKeyTypeMaximum,    VkKeyTypeMaximum}},
  // Screen Corner B
  {  0x0000, 0x023A, 0x001E, 0x0258, {VkKeyTypeMaximum,    VkKeyTypeMaximum,    VkKeyTypeMaximum,    VkKeyTypeMaximum}},
  // Screen Corner C
  {  0x02E4, 0x0000, 0x0320, 0x001E, {VkKeyTypeMaximum,    VkKeyTypeMaximum,    VkKeyTypeMaximum,    VkKeyTypeMaximum}},
  // Screen Corner D
  {  0x02E4, 0x023A, 0x0320, 0x0258, {VkKeyTypeMaximum,    VkKeyTypeMaximum,    VkKeyTypeMaximum,    VkKeyTypeMaximum}}
};

//
// 1.SimpleKeyboardBody
// Follow below design
//   (1).\KeyboardLayout\SimpleKeyboard.bmp
// Based on keyboard's (startX, startY)
// +-----+-----+
// | Esc |Enter| Line 0
// +-----+-----+
// | Up  | Down| Line 1
// +-----+-----+
//
// 2.Screen Corner
// Follow below design
//   (1).\KeyboardLayout\SimpleIcon.bmp # Screen Corner A/B
//   (2).\KeyboardLayout\FullIcon.bmp   # Screen Corner C/D
// +-+------------------------------------+-+
// |A|                                    |C|
// +-+                                    +-+
// |                                        |
// |                                        |
// |                                        |
// |                                        |
// |                                        |
// |                                        |
// +-+                                    +-+
// |B|                                    |D|
// +-+------------------------------------+-+
//
VK_STRUCT mSimpleKeyboardBody[] = {
  // StartX  StartY  EndX    EndY     Page0Font            Page1Font            Page2Font            Page3Font
  // Line 0
  {  0x0000, 0x0000, 0x0032, 0x0032, {VkKeyEsc,            VkKeyEsc,            VkKeyEsc,            VkKeyEsc        }},
  {  0x0032, 0x0000, 0x0064, 0x0032, {VkKeyEnter,          VkKeyEnter,          VkKeyEnter,          VkKeyEnter      }},
  // Line 1
  {  0x0000, 0x0032, 0x0032, 0x0064, {VkKeyUp,             VkKeyUp,             VkKeyUp,             VkKeyUp         }},
  {  0x0032, 0x0032, 0x0064, 0x0064, {VkKeyDown,           VkKeyDown,           VkKeyDown,           VkKeyDown       }},
  // Screen Corner A
  {  0x0000, 0x0000, 0x001E, 0x001E, {VkKeyTypeMaximum,    VkKeyTypeMaximum,    VkKeyTypeMaximum,    VkKeyTypeMaximum}},
  // Screen Corner B
  {  0x0000, 0x023A, 0x001E, 0x0258, {VkKeyTypeMaximum,    VkKeyTypeMaximum,    VkKeyTypeMaximum,    VkKeyTypeMaximum}},
  // Screen Corner C
  {  0x02E4, 0x0000, 0x0320, 0x001E, {VkKeyTypeMaximum,    VkKeyTypeMaximum,    VkKeyTypeMaximum,    VkKeyTypeMaximum}},
  // Screen Corner D
  {  0x02E4, 0x023A, 0x0320, 0x0258, {VkKeyTypeMaximum,    VkKeyTypeMaximum,    VkKeyTypeMaximum,    VkKeyTypeMaximum}}
};

/**
  Modify the color of key if Shift or CapsLock is pressed.

  @param[in]       VkContext   Address of an VK_CONTEXT structure.
  @param[in, out]  BltBuffer   Address of a blt buffer.

  @retval EFI_SUCCESS          Success for the function.

**/
EFI_STATUS
ModifyShiftKeyColor (
  IN     VK_CONTEXT                    *VkContext,
  IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL **BltBuffer
  )
{
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *TempBltBuffer;
  UINTN                         BltSize;
  BOOLEAN                       IsPressed;

  TempBltBuffer = *BltBuffer;
  BltSize       = VkContext->VkBodyBltHeight * VkContext->VkBodyBltWidth;
  IsPressed     = VkContext->PageNumber <= VkPage1 ?
                  VkContext->IsCapsLockFlag :
                  VkContext->IsShiftKeyFlag;

  while (BltSize-- != 0) {
    //
    // Color gradient issue
    //
    if (((TempBltBuffer->Red - TempBltBuffer->Green) > 0x20) &&
        ((TempBltBuffer->Red - TempBltBuffer->Blue)  > 0x20)) {
      if (IsPressed) {
        TempBltBuffer->Red   = 0;
        TempBltBuffer->Green = 255;
        TempBltBuffer->Blue  = 255;
      } else {
        TempBltBuffer->Red   = 255;
        TempBltBuffer->Green = 255;
        TempBltBuffer->Blue  = 255;
      }
    }
    TempBltBuffer++;
  }

  return EFI_SUCCESS;
}

/**
  Make the keyboard transparent.

  @param[in]      VkContext      Address of an VK_CONTEXT structure.
  @param[in]      IsTransparent  TRUE for set keyboard transparent.
  @param[in]      BltIn          Address of keyboard blt buffer.
  @param[in, out] BltOut         Address of output blt buffer.
                                 NULL will allocate it.

  @retval EFI_SUCCESS            Success for the function.
  @retval EFI_OUT_OF_RESOURCES   Allocate memory failed.

**/
EFI_STATUS
MakeKeyboardTransparent (
  IN     VK_CONTEXT                    *VkContext,
  IN     BOOLEAN                       IsTransparent,
  IN     EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltIn,
  IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL **BltOut
  )
{
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Compound;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Keyboard;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Background;
  UINTN                         BltSize;

  if (*BltOut == NULL) {
    *BltOut = AllocateZeroPool (VkContext->VkBodyBltSize);
    if (*BltOut == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }
  }
  Compound   = *BltOut;
  Keyboard   = BltIn;
  Background = VkContext->VkBodyBackgroundBltBuffer;
  BltSize    = VkContext->VkBodyBltHeight * VkContext->VkBodyBltWidth;
  while (BltSize-- != 0) {
    if (IsTransparent) {
      Compound->Red   = (Keyboard->Red * TRANSPARENCY_WEIGHT) / 100 + (Background->Red * (100 - TRANSPARENCY_WEIGHT)) / 100;
      Compound->Green = (Keyboard->Green * TRANSPARENCY_WEIGHT) / 100 + (Background->Green * (100 - TRANSPARENCY_WEIGHT)) / 100;
      Compound->Blue  = (Keyboard->Blue * TRANSPARENCY_WEIGHT) / 100 + (Background->Blue * (100 - TRANSPARENCY_WEIGHT)) / 100;
    } else {
      *Compound = *Keyboard;
    }
    Compound++;
    Keyboard++;
    Background++;
  }

  return EFI_SUCCESS;
}

/**
  Save the background blt buffer.

  @param[in] VkContext           Address of an VK_CONTEXT structure.
  @param[in] BltSize             Size of blt.

  @retval EFI_SUCCESS            Success for the function.
  @retval EFI_OUT_OF_RESOURCES   Allocate memory failed.

**/
EFI_STATUS
EFIAPI
SaveVkBodyBackgroundBltBuffer (
  IN VK_CONTEXT *VkContext,
  IN UINTN      BltSize
  )
{
  EFI_STATUS                    Status;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *CurrentBltBuffer;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *CurrentBltBufferSave;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *OldBltBuffer;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *OldBltBufferSave;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Compound;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Background;
  UINTN                         Size;

  CurrentBltBufferSave = NULL;
  OldBltBufferSave     = NULL;

  //
  // Save original blt buffer first.
  //
  OldBltBuffer = AllocateZeroPool (VkContext->VkBodyBltSize);
  if (OldBltBuffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  if (VkContext->VkBodyBackgroundBltBuffer != NULL) {
    CopyMem (OldBltBuffer, VkContext->VkBodyBackgroundBltBuffer, VkContext->VkBodyBltSize);
  }

  if (VkContext->VkBodyBackgroundBltBuffer == NULL) {
    VkContext->VkBodyBltSize = BltSize;
    VkContext->VkBodyBackgroundBltBuffer = AllocateZeroPool (VkContext->VkBodyBltSize);
    ASSERT (VkContext->VkBodyBackgroundBltBuffer != NULL);
    if (VkContext->VkBodyBackgroundBltBuffer == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }
  } else if (BltSize > VkContext->VkBodyBltSize) {
    VkContext->VkBodyBltSize = BltSize;
    FreePool (VkContext->VkBodyBackgroundBltBuffer);
    VkContext->VkBodyBackgroundBltBuffer = NULL;
    VkContext->VkBodyBackgroundBltBuffer = AllocateZeroPool (VkContext->VkBodyBltSize);
    ASSERT (VkContext->VkBodyBackgroundBltBuffer != NULL);
    if (VkContext->VkBodyBackgroundBltBuffer == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }
  } else {
    ZeroMem (VkContext->VkBodyBackgroundBltBuffer, VkContext->VkBodyBltSize);
  }

  CurrentBltBuffer = NULL;
  if (VkContext->IsBackgroundChanged == TRUE) {
    //
    // Background changed, merge current visioning blt buffer with old background blt buffer.
    //
    Compound   = VkContext->VkBodyCompoundBltBuffer;
    Background = VkContext->VkBodyBackgroundBltBuffer;
    CurrentBltBuffer = AllocateZeroPool (VkContext->VkBodyBltSize);
    ASSERT (CurrentBltBuffer != NULL);
    if (CurrentBltBuffer == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }
    Status = VkContext->GraphicsOutput->Blt (
                                          VkContext->GraphicsOutput,
                                          CurrentBltBuffer,
                                          EfiBltVideoToBltBuffer,
                                          VkContext->VkBodyBltStartX,
                                          VkContext->VkBodyBltStartY,
                                          0,
                                          0,
                                          VkContext->VkBodyBltWidth,
                                          VkContext->VkBodyBltHeight,
                                          VkContext->VkBodyBltWidth * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
                                          );
    CurrentBltBufferSave = CurrentBltBuffer;
    OldBltBufferSave = OldBltBuffer;
    Size = VkContext->VkBodyBltHeight * VkContext->VkBodyBltWidth;
    while (Size-- != 0) {
      if ((Compound->Red   != CurrentBltBuffer->Red) ||
          (Compound->Green != CurrentBltBuffer->Green) ||
          (Compound->Blue  != CurrentBltBuffer->Blue)) {
        *Background = *CurrentBltBuffer;
      } else {
        *Background = *OldBltBuffer;
      }
      Compound++;
      Background++;
      CurrentBltBuffer++;
      OldBltBuffer++;
    }
  } else {
    //
    // Background NOT changed, save it to VkBodyBackgroundBltBuffer directly.
    //
    Status = VkContext->GraphicsOutput->Blt (
                                          VkContext->GraphicsOutput,
                                          VkContext->VkBodyBackgroundBltBuffer,
                                          EfiBltVideoToBltBuffer,
                                          VkContext->VkBodyBltStartX,
                                          VkContext->VkBodyBltStartY,
                                          0,
                                          0,
                                          VkContext->VkBodyBltWidth,
                                          VkContext->VkBodyBltHeight,
                                          VkContext->VkBodyBltWidth * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
                                          );
  }

  if (CurrentBltBufferSave != NULL) {
    FreePool (CurrentBltBufferSave);
  }
  if (OldBltBufferSave != NULL) {
    FreePool (OldBltBufferSave);
  }

  return Status;
}

/**
  Restore the background blt buffer.

  @param[in] VkContext           Address of an VK_CONTEXT structure.

  @retval EFI_SUCCESS            Success for the function.
  @retval EFI_UNSUPPORTED        Input blt buffer is NULL.
  @retval Others                 An unexpected error occurred.

**/
EFI_STATUS
EFIAPI
RestoreVkBodyBackgroundBltBuffer (
  IN VK_CONTEXT *VkContext
  )
{
  EFI_STATUS Status;

  if (VkContext->VkBodyBackgroundBltBuffer == NULL) {
    return EFI_UNSUPPORTED;
  }

  Status = VkContext->GraphicsOutput->Blt (
                                        VkContext->GraphicsOutput,
                                        VkContext->VkBodyBackgroundBltBuffer,
                                        EfiBltBufferToVideo,
                                        0,
                                        0,
                                        VkContext->VkBodyBltStartX,
                                        VkContext->VkBodyBltStartY,
                                        VkContext->VkBodyBltWidth,
                                        VkContext->VkBodyBltHeight,
                                        VkContext->VkBodyBltWidth * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
                                        );

  FreePool (VkContext->VkBodyBackgroundBltBuffer);
  VkContext->VkBodyBackgroundBltBuffer = NULL;

  return Status;
}

/**
  Set the position of character.

  @param[in] VkContext      Address of an VK_CONTEXT structure.
  @param[in] DestX          X position.
  @param[in] DestY          Y position.

  @retval EFI_SUCCESS       Success for the function.
  @retval Others            An unexpected error occurred.

**/
EFI_STATUS
SetCharacterPosition (
  IN VK_CONTEXT *VkContext,
  IN UINT32     DestX,
  IN UINT32     DestY
  )
{
  UINTN     Index;
  VK_STRUCT *KeyArryPtr;
  UINT32    KeyArrySize;

  switch (VkContext->TargetKeyboardDisplay) {
  case VkDisplayAttributeSimpleTop:
  case VkDisplayAttributeSimpleBottom:
    KeyArryPtr  = mSimpleKeyboardBody;
    KeyArrySize = DIM (mSimpleKeyboardBody);
    break;

  case VkDisplayAttributeFullBottom:
  case VkDisplayAttributeFullTop:
    KeyArryPtr = mFullKeyboardBody;
    KeyArrySize = DIM (mFullKeyboardBody);
    break;

  case VkDisplayAttributeNone:
    KeyArryPtr = mFullKeyboardBody;
    KeyArrySize = DIM (mFullKeyboardBody);
    break;

  default:
    return EFI_UNSUPPORTED;
  }

  for (Index = 0; Index < KeyArrySize; Index++) {
    VkContext->KeyboardBodyPtr[Index] = KeyArryPtr[Index];
  }
  VkContext->NumOfKeysInfo = KeyArrySize;

  for (Index = 0; Index < (VkContext->NumOfKeysInfo - 4); Index++) {
    VkContext->KeyboardBodyPtr[Index].DisStartX += (UINT16)DestX;
    VkContext->KeyboardBodyPtr[Index].DisStartY += (UINT16)DestY;
    VkContext->KeyboardBodyPtr[Index].DisEndX   += (UINT16)DestX;
    VkContext->KeyboardBodyPtr[Index].DisEndY   += (UINT16)DestY;
  }

  return EFI_SUCCESS;
}

/**
  Save the icon background blt buffer.

  @param[in] VkContext      Address of an VK_CONTEXT structure.
  @param[in] IconType       The icon type.

  @retval EFI_SUCCESS            Success for the function.
  @retval EFI_OUT_OF_RESOURCES   Allocate memory failed.
  @retval Others                 An unexpected error occurred.

**/
EFI_STATUS
EFIAPI
SaveVkIconBackgroundBltBuffer (
  IN VK_CONTEXT           *VkContext,
  IN VK_DISPLAY_ATTRIBUTE IconType
  )
{
  EFI_STATUS                    Status;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GraphicBlt;
  UINTN                         BltSize;
  UINTN                         Height;
  UINTN                         Width;
  INTN                          StartX;
  INTN                          StartY;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *TempIconBackBuffer;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *IconBackBuffer;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Compound;
  UINTN                         Size;
  BOOLEAN                       SaveCursor;

  Status             = EFI_SUCCESS;
  StartX             = 0;
  StartY             = 0;
  TempIconBackBuffer = NULL;
  IconBackBuffer     = NULL;
  Compound           = NULL;

  if ((IconType == VkDisplayAttributeFullTop) ||
      (IconType == VkDisplayAttributeSimpleTop)) {
    return EFI_SUCCESS;
  }

  SaveCursor = gST->ConOut->Mode->CursorVisible;
  gST->ConOut->EnableCursor (gST->ConOut, FALSE);

  if (IconType == VkDisplayAttributeFullBottom) {
    GraphicBlt = VkContext->FullIcon->Bitmap;
    BltSize    = VkContext->FullIcon->Height * VkContext->FullIcon->Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
    Height     = VkContext->FullIcon->Height;
    Width      = VkContext->FullIcon->Width;
    StartX     = VkContext->FullIconBackStartX;
    StartY     = VkContext->FullIconBackStartY;
  } else if (IconType == VkDisplayAttributeSimpleBottom) {
    GraphicBlt = VkContext->SmallIcon->Bitmap;
    BltSize    = VkContext->SmallIcon->Height * VkContext->SmallIcon->Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
    Height     = VkContext->SmallIcon->Height;
    Width      = VkContext->SmallIcon->Width;
    StartX     = VkContext->SimIconBackStartX;
    StartY     = VkContext->SimIconBackStartY;
  } else {
    gST->ConOut->EnableCursor (gST->ConOut, SaveCursor);
    return EFI_UNSUPPORTED;
  }

  IconBackBuffer = AllocateZeroPool (BltSize);
  if (IconBackBuffer == NULL) {
    gST->ConOut->EnableCursor (gST->ConOut, SaveCursor);
    return EFI_OUT_OF_RESOURCES;
  }
  TempIconBackBuffer = IconBackBuffer;
  Status = VkContext->GraphicsOutput->Blt (
                                        VkContext->GraphicsOutput,
                                        IconBackBuffer,
                                        EfiBltVideoToBltBuffer,
                                        StartX,
                                        StartY,
                                        0,
                                        0,
                                        Width,
                                        Height,
                                        Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
                                        );

  if (IconType == VkDisplayAttributeFullBottom) {
    //
    // Store full icon background framebuffer
    //
    VkContext->FullIconBackHeight = Height;
    VkContext->FullIconBackWidth  = Width;
    if (VkContext->FullIconUpdatedFlag == FALSE) {
      //
      // No icon draw, save the buffer directly.
      //
      if (VkContext->FullIconBackBuffer == NULL) {
        VkContext->FullIconBackBuffer = AllocateZeroPool (BltSize);
        VkContext->FullIconBackSize = BltSize;
      }
      CopyMem (VkContext->FullIconBackBuffer, IconBackBuffer, BltSize);
      VkContext->FullIconUpdatedFlag = TRUE;
    } else {
      if (CompareMem (VkContext->FullIconBackBuffer, IconBackBuffer, BltSize) != 0) {
        Compound = VkContext->FullIconBackBuffer;
        Size = Height * Width;
        while (Size-- != 0) {
          if ((GraphicBlt->Red   != IconBackBuffer->Red) ||
              (GraphicBlt->Green != IconBackBuffer->Green) ||
              (GraphicBlt->Blue  != IconBackBuffer->Blue)) {
            *Compound = *IconBackBuffer;
          }
          Compound++;
          GraphicBlt++;
          IconBackBuffer++;
        }
      }
    }
  } else if (IconType == VkDisplayAttributeSimpleBottom) {
    //
    // Store simple icon background framebuffer
    //
    VkContext->SimIconBackHeight = Height;
    VkContext->SimIconBackWidth  = Width;
    if (VkContext->SimIconUpdatedFlag == FALSE) {
      //
      // No icon draw, save the buffer directly.
      //
      if (VkContext->SimIconBackBuffer == NULL) {
        VkContext->SimIconBackBuffer = AllocateZeroPool (BltSize);
        VkContext->SimIconBackSize = BltSize;
      }
      CopyMem (VkContext->SimIconBackBuffer, IconBackBuffer, BltSize);
      VkContext->SimIconUpdatedFlag = TRUE;
    } else {
      if (CompareMem (VkContext->SimIconBackBuffer, IconBackBuffer, BltSize) != 0) {
        Compound = VkContext->SimIconBackBuffer;
        Size = Height * Width;
        while (Size-- != 0) {
          if ((GraphicBlt->Red   != IconBackBuffer->Red) ||
              (GraphicBlt->Green != IconBackBuffer->Green) ||
              (GraphicBlt->Blue  != IconBackBuffer->Blue)) {
            *Compound = *IconBackBuffer;
          }
          Compound++;
          GraphicBlt++;
          IconBackBuffer++;
        }
      }
    }
  }

  if (TempIconBackBuffer != NULL)  FreePool (TempIconBackBuffer);

  gST->ConOut->EnableCursor (gST->ConOut, SaveCursor);
  return Status;
}

/**
  Use to draw the keyboard icon.

  @param[in] VkContext  Pointer to virtual keyboard's context
  @param[in] VkImage    Image of keyboard to display on the screen.
  @param[in] Attribute  Attribute of keyboard to display on the screen.

  @retval EFI_SUCCESS           ConsoleControl has been flipped to graphics and keyboard icon displayed.
  @retval EFI_UNSUPPORTED       KeyboardFile not found
  @retval EFI_INVALID_PARAMETER Attribute is unknown.

**/
EFI_STATUS
EFIAPI
DrawVkIcon (
  IN VK_CONTEXT                    *VkContext,
  IN EFI_IMAGE_INPUT               *VkImage,
  IN VK_DISPLAY_ATTRIBUTE          Attribute
  )
{
  EFI_STATUS                    Status;
  UINT32                        SizeOfX;
  UINT32                        SizeOfY;
  INTN                          DestX;
  INTN                          DestY;
  UINTN                         CoordinateX;
  UINTN                         CoordinateY;
  EFI_GRAPHICS_OUTPUT_PROTOCOL  *GraphicsOutput;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt;
  UINTN                         BltSize;
  UINTN                         Height;
  UINTN                         Width;

  Status         = EFI_SUCCESS;
  GraphicsOutput = VkContext->GraphicsOutput;
  SizeOfX        = GraphicsOutput->Mode->Info->HorizontalResolution;
  SizeOfY        = GraphicsOutput->Mode->Info->VerticalResolution;
  CoordinateX    = 0;
  CoordinateY    = 0;
  Height         = VkImage->Height;
  Width          = VkImage->Width;
  Blt            = VkImage->Bitmap;
  BltSize        = sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * (UINT32)(Width * Height);

  //
  // Calculate the display position according to Attribute.
  //
  switch (Attribute) {
  case VkDisplayAttributeSimpleTop:
    DestX = CoordinateX;
    DestY = CoordinateY;
    break;

  case VkDisplayAttributeFullTop:
    DestX = (SizeOfX - Width - CoordinateX);
    DestY = CoordinateY;;
    break;

  case VkDisplayAttributeFullBottom:
    DestX                         = (SizeOfX - Width - CoordinateX);
    DestY                         = (SizeOfY - Height - CoordinateY);
    VkContext->FullIconBackStartX = DestX;
    VkContext->FullIconBackStartY = DestY;
    break;

  case VkDisplayAttributeSimpleBottom:
    DestX = CoordinateX;
    DestY = (SizeOfY - Height - CoordinateY);

    //
    // Save to check icon/screen cleared
    //
    if (VkContext->IconBltBuffer == NULL) {
      VkContext->IconBltSize   = BltSize;
      VkContext->IconBltWidth  = Width;
      VkContext->IconBltHeight = Height;
      VkContext->IconBltBuffer = AllocateZeroPool (BltSize);
    }
    CopyMem (VkContext->IconBltBuffer, Blt, VkContext->IconBltSize);

    VkContext->SimIconBackStartX = DestX;
    VkContext->SimIconBackStartY = DestY;
    break;

  case VkDisplayAttributeNone:
    return EFI_SUCCESS;

  default:
    return EFI_INVALID_PARAMETER;
  }

  if ((DestX >= 0) && (DestY >= 0)) {
    //
    // Store icon background framebuffer
    //
    SaveVkIconBackgroundBltBuffer (VkContext, Attribute);
    Status = GraphicsOutput->Blt (
                               GraphicsOutput,
                               Blt,
                               EfiBltBufferToVideo,
                               0,
                               0,
                               (UINTN) DestX,
                               (UINTN) DestY,
                               Width,
                               Height,
                               Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
                               );
  }

  return Status;
}

/**
  Use to draw the keyboard.

  @param[in] VkContext  Pointer to virtual keyboard's context
  @param[in] VkImage    Image of keyboard to display on the screen.
  @param[in] Attribute  Attribute of keyboard to display on the screen.

  @retval EFI_SUCCESS           ConsoleControl has been flipped to graphics and keyboard displayed.
  @retval EFI_UNSUPPORTED       KeyboardFile not found
  @retval EFI_INVALID_PARAMETER Attribute is unknown.

**/
EFI_STATUS
EFIAPI
DrawVkBody (
  IN VK_CONTEXT                    *VkContext,
  IN EFI_IMAGE_INPUT               *VkImage,
  IN VK_DISPLAY_ATTRIBUTE          Attribute
  )
{
  EFI_STATUS                    Status;
  UINT32                        SizeOfX;
  UINT32                        SizeOfY;
  INTN                          DestX;
  INTN                          DestY;
  UINTN                         BltSize;
  UINTN                         Height;
  UINTN                         Width;
  UINTN                         CoordinateY;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltIn;
  EFI_GRAPHICS_OUTPUT_PROTOCOL  *GraphicsOutput;

  Status         = EFI_SUCCESS;
  GraphicsOutput = VkContext->GraphicsOutput;
  SizeOfX        = GraphicsOutput->Mode->Info->HorizontalResolution;
  SizeOfY        = GraphicsOutput->Mode->Info->VerticalResolution;
  CoordinateY    = 0;
  Height         = VkImage->Height;
  Width          = VkImage->Width;
  BltSize        = sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * (UINT32)(Width * Height);
  BltIn          = AllocateCopyPool (BltSize, VkImage->Bitmap);

  if (BltIn == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Calculate the display position according to Attribute.
  //
  switch (Attribute) {
  case VkDisplayAttributeSimpleTop:
    DestX = ((SizeOfX / 2) - Width) / 4;
    DestY = CoordinateY;
    VkContext->CurrentKeyboardDisplay = VkDisplayAttributeSimpleTop;
    break;

  case VkDisplayAttributeSimpleBottom:
    DestX = ((SizeOfX / 2) - Width) / 4;
    DestY = (SizeOfY - Height - CoordinateY);
    VkContext->CurrentKeyboardDisplay = VkDisplayAttributeSimpleBottom;
    break;

  case VkDisplayAttributeFullTop:
    DestX = (SizeOfX - Width) / 2;
    DestY = CoordinateY;
    VkContext->CurrentKeyboardDisplay = VkDisplayAttributeFullTop;
    break;

  case VkDisplayAttributeFullBottom:
    DestX = (SizeOfX - Width) / 2;
    DestY = (SizeOfY - Height - CoordinateY);
    VkContext->CurrentKeyboardDisplay = VkDisplayAttributeFullBottom;
    break;

  case VkDisplayAttributeNone:
    VkContext->CurrentKeyboardDisplay = VkDisplayAttributeNone;
    goto DVKBODY_Exit;

  default:
    VkContext->CurrentKeyboardDisplay = VkDisplayAttributeNone;
    Status = EFI_INVALID_PARAMETER;
    goto DVKBODY_Exit;
  }
  if ((DestX >= 0) && (DestY >= 0)) {
    SetCharacterPosition (VkContext, (UINT32)DestX, (UINT32)DestY);

    //
    // Store current framebuffer
    //
    VkContext->VkBodyBltStartX = DestX;
    VkContext->VkBodyBltStartY = DestY;
    VkContext->VkBodyBltHeight = Height;
    VkContext->VkBodyBltWidth  = Width;
    SaveVkBodyBackgroundBltBuffer (VkContext, BltSize);

    //
    // Free compound buffer first.
    //
    if (VkContext->VkBodyCompoundBltBuffer != NULL) {
      FreePool (VkContext->VkBodyCompoundBltBuffer);
    }
    VkContext->VkBodyCompoundBltBuffer = NULL;
    ModifyShiftKeyColor (VkContext, &BltIn);
    MakeKeyboardTransparent (VkContext, TRUE, BltIn, &(VkContext->VkBodyCompoundBltBuffer));

    //
    // Draw keyboard body
    //
    Status = GraphicsOutput->Blt (
                               GraphicsOutput,
                               VkContext->VkBodyCompoundBltBuffer,
                               EfiBltBufferToVideo,
                               0,
                               0,
                               (UINTN) DestX,
                               (UINTN) DestY,
                               Width,
                               Height,
                               Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
                               );
  }


DVKBODY_Exit:
  if (BltIn != NULL) {
    FreePool (BltIn);
  }

  return Status;
}

/**
  Clear the keyboard body

  @param  VkContext             Code context.

  @retval EFI_SUCCESS           Clear rectangle is done.

**/
EFI_STATUS
HideVkBody (
  IN VK_CONTEXT *VkContext
  )
{
  RestoreVkBodyBackgroundBltBuffer (VkContext);
  VkContext->CurrentKeyboardDisplay = VkDisplayAttributeNone;

  return EFI_SUCCESS;
}


/**
  Clear the keyboard icon

  @param  VkContext             Code context.

  @retval EFI_SUCCESS           Clear rectangle is done.

**/
EFI_STATUS
HideVkIcon (
  IN VK_CONTEXT *VkContext
  )
{
  EFI_STATUS                    Status;

  if ((VkContext->FullIconBackBuffer == NULL) || (VkContext->SimIconBackBuffer == NULL)) {
    return EFI_UNSUPPORTED;
  }

  if ((VkContext->FullIconUpdatedFlag == FALSE) || (VkContext->SimIconUpdatedFlag == FALSE)) {
    return EFI_UNSUPPORTED;
  }
  Status = VkContext->GraphicsOutput->Blt (
                                        VkContext->GraphicsOutput,
                                        VkContext->FullIconBackBuffer,
                                        EfiBltBufferToVideo,
                                        0,
                                        0,
                                        VkContext->FullIconBackStartX,
                                        VkContext->FullIconBackStartY,
                                        VkContext->FullIconBackWidth,
                                        VkContext->FullIconBackHeight,
                                        VkContext->FullIconBackWidth * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
                                        );

  ZeroMem (VkContext->FullIconBackBuffer, VkContext->FullIconBackSize);
  VkContext->FullIconUpdatedFlag = FALSE;


  Status = VkContext->GraphicsOutput->Blt (
                                        VkContext->GraphicsOutput,
                                        VkContext->SimIconBackBuffer,
                                        EfiBltBufferToVideo,
                                        0,
                                        0,
                                        VkContext->SimIconBackStartX,
                                        VkContext->SimIconBackStartY,
                                        VkContext->SimIconBackWidth,
                                        VkContext->SimIconBackHeight,
                                        VkContext->SimIconBackWidth * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
                                        );
  if (!EFI_ERROR (Status)) {
    if (VkContext->ScreenCheckBuffer == NULL) {
      VkContext->ScreenCheckBufferSize = VkContext->SimIconBackHeight *
                                         VkContext->SimIconBackWidth *
                                         sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
      VkContext->ScreenCheckBuffer = AllocateZeroPool (VkContext->ScreenCheckBufferSize);
    }

    CopyMem (
      VkContext->ScreenCheckBuffer,
      VkContext->SimIconBackBuffer,
      (VkContext->SimIconBackHeight * VkContext->SimIconBackWidth * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL))
      );
  }
  ZeroMem (VkContext->SimIconBackBuffer, VkContext->SimIconBackSize);
  VkContext->SimIconUpdatedFlag = FALSE;

  return EFI_SUCCESS;
}

/**
  Draw key board on the display

  @param[in] VkContext          Graphic Protocol for draw the alphabet.

  @retval EFI_SUCCESS           Draw keyboard was done.
  @retval EFI_UNSUPPORTED       Did not get key mapping table.

**/
EFI_STATUS
DrawKeyboardLayout (
  IN VK_CONTEXT *VkContext
  )
{
  EFI_STATUS Status;
  if (!VkContext->IsIconShowed) {
    Status = DrawVkIcon (VkContext, VkContext->SmallIcon, VkDisplayAttributeSimpleTop);
    ASSERT_EFI_ERROR (Status);

    Status = DrawVkIcon (VkContext, VkContext->SmallIcon, VkDisplayAttributeSimpleBottom);
    ASSERT_EFI_ERROR (Status);

    Status = DrawVkIcon (VkContext, VkContext->FullIcon,  VkDisplayAttributeFullTop);
    ASSERT_EFI_ERROR (Status);

    Status = DrawVkIcon (VkContext, VkContext->FullIcon,  VkDisplayAttributeFullBottom);
    ASSERT_EFI_ERROR (Status);
    VkContext->IsIconShowed = TRUE;
  }

  if (VkContext->TargetKeyboardDisplay != VkContext->CurrentKeyboardDisplay) {
    switch (VkContext->TargetKeyboardDisplay) {
    case VkDisplayAttributeSimpleTop:
    case VkDisplayAttributeSimpleBottom:
      DrawVkBody (VkContext, VkContext->SimKeyBody, VkContext->TargetKeyboardDisplay);
      break;

    case VkDisplayAttributeFullTop:
    case VkDisplayAttributeFullBottom:
      if (VkContext->PageNumber <= VkPage1) {
        DrawVkBody (VkContext, VkContext->CapLeKeyBody, VkContext->TargetKeyboardDisplay);
      } else {
        DrawVkBody (VkContext, VkContext->DigKeyBody, VkContext->TargetKeyboardDisplay);
      }

    case VkDisplayAttributeNone:
      break;

    default:
      break;
    }
  }

  return EFI_SUCCESS;
}

/**
  Set the keyboard layout.

  @param[in] VkContext          Graphic Protocol for draw the alphabet.
  @param[in] Index              The layout selected.

  @retval EFI_SUCCESS           Draw keyboard was done.
  @retval Others                An unexpected error occurred.

**/
EFI_STATUS
KeyboardLayoutHandler (
  IN VK_CONTEXT *VkContext,
  IN UINT32     Index
  )
{
  EFI_STATUS Status;

  Status = EFI_SUCCESS;

  if (Index == (VkContext->NumOfKeysInfo - 4)) {
    //
    // Touch the LeftTop icon
    //
    if (VkContext->CurrentKeyboardDisplay == VkDisplayAttributeSimpleTop) {
      VkContext->TargetKeyboardDisplay = VkDisplayAttributeNone;
    } else {
      VkContext->TargetKeyboardDisplay = VkDisplayAttributeSimpleTop;
    }
  } else if (Index == (VkContext->NumOfKeysInfo - 3)) {
    //
    // Touch the LeftBottom icon
    //
    if (VkContext->CurrentKeyboardDisplay == VkDisplayAttributeSimpleBottom) {
      VkContext->TargetKeyboardDisplay = VkDisplayAttributeNone;
    } else {
      VkContext->TargetKeyboardDisplay = VkDisplayAttributeSimpleBottom;
    }
  } else if (Index == (VkContext->NumOfKeysInfo - 2)) {
    //
    // Touch the RightTop icon
    //
    if (VkContext->CurrentKeyboardDisplay == VkDisplayAttributeFullTop) {
      VkContext->TargetKeyboardDisplay = VkDisplayAttributeNone;
    } else {
      VkContext->TargetKeyboardDisplay = VkDisplayAttributeFullTop;
    }
  } else {
    //
    // Touch the RightBottom icon
    //
    if (VkContext->CurrentKeyboardDisplay == VkDisplayAttributeFullBottom) {
      VkContext->TargetKeyboardDisplay = VkDisplayAttributeNone;
    } else {
      VkContext->TargetKeyboardDisplay = VkDisplayAttributeFullBottom;
    }
  }

  if (VkContext->TargetKeyboardDisplay == VkDisplayAttributeNone) {
    //
    // Just hide the current keyboard
    //
    HideVkBody (VkContext);
    VkContext->KeyTouchedTimeOut = VK_REPEAT_TIMEOUT;

  } else {
    //
    // If current keyboard status is NOT none,
    // hide current keyboard first and then draw the target keyboard
    //
    if (VkContext->CurrentKeyboardDisplay != VkDisplayAttributeNone) {
      HideVkBody (VkContext);
    }
    Status = DrawKeyboardLayout (VkContext);
  }

  return Status;
}

/**
  This routine is used to check if icon has been cleared.

  @param[in] VkContext  Pointer to virtual keyboard's context

  @retval EFI_SUCCESS   Function completed.

**/
EFI_STATUS
CheckIconCleared (
  IN VK_CONTEXT *VkContext
  )
{
  EFI_STATUS                    Status;
  UINT32                        VerticalResolution;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer;

  Status = EFI_SUCCESS;
  BltBuffer = NULL;
  VkContext->IconReDrawCheck++;
  if (VkContext->IconReDrawCheck <= 10) {
    //
    // Check it every 10 * 100ms.
    //
    return Status;
  }

  //
  // Check if right-bottomed region is black, if yes, clean screen happened, need to re-draw keyboard.
  //
  VerticalResolution    = VkContext->GraphicsOutput->Mode->Info->VerticalResolution;
  BltBuffer             = AllocateZeroPool (VkContext->IconBltSize);
  if (BltBuffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  Status = VkContext->GraphicsOutput->Blt (
                                        VkContext->GraphicsOutput,
                                        BltBuffer,
                                        EfiBltVideoToBltBuffer,
                                        0,
                                        (VerticalResolution - VkContext->IconBltHeight),
                                        0,
                                        0,
                                        VkContext->IconBltWidth,
                                        VkContext->IconBltHeight,
                                        VkContext->IconBltWidth * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
                                        );
  if (EFI_ERROR (Status)) {
    FreePool (BltBuffer);
    return Status;
  }
  VkContext->IsIconShowed = TRUE;
  if (VkContext->IconBltBuffer == NULL) {
    //
    // No icon has been drawn.
    //
    VkContext->IsIconShowed = FALSE;
  } else {
    if (CompareMem (BltBuffer, VkContext->IconBltBuffer, VkContext->IconBltSize) != 0) {
      //
      // Icon has been overridden, need to re-draw.
      //
      VkContext->IsIconShowed = FALSE;
    }
  }

  if (VkContext->IsIconShowed == FALSE) {
    if (VkContext->ScreenCheckBuffer != NULL) {
      if (CompareMem (BltBuffer, VkContext->ScreenCheckBuffer, VkContext->IconBltSize) == 0) {
        //
        // Icon has been overridden, force to re-draw the keyboard.
        //
        Status = DrawKeyboardLayout (VkContext);
      } else {
        //
        // Save blt buffer of icon position and use it to check if icon is overridden.
        //
        CopyMem (VkContext->ScreenCheckBuffer, BltBuffer, VkContext->IconBltSize);
      }
    } else {
      //
      // Draw the keyboard.
      //
      Status = DrawKeyboardLayout (VkContext);
    }
  }

  if (BltBuffer != NULL) {
    FreePool (BltBuffer);
  }

  VkContext->IconReDrawCheck = 0;

  return Status;
}

/**
  ConvertCoordinate - Convert the touch panel's coordinate to display's coordinate.

  @param[in]  VkContext             Virtual Keyboard context.
  @param[in]  Point                 The coordinate reported from touch panel.
  @param[out] TouchX                The coordinate X converted to display panel.
  @param[out] TouchY                The coordinate Y converted to display panel..

  @retval EFI_SUCCESS               Convert success.

**/
EFI_STATUS
ConvertCoordinate (
  IN  VK_CONTEXT                 *VkContext,
  IN  EFI_ABSOLUTE_POINTER_STATE Point,
  OUT UINT32                     *TouchX,
  OUT UINT32                     *TouchY
  )
{
  UINT64 AbsoluteMaxX;
  UINT64 AbsoluteMaxY;
  UINT32 HorizontalResolution;
  UINT32 VerticalResolution;

  AbsoluteMaxX         = VkContext->AbsolutePointer->Mode->AbsoluteMaxX;
  AbsoluteMaxY         = VkContext->AbsolutePointer->Mode->AbsoluteMaxY;
  HorizontalResolution = VkContext->GraphicsOutput->Mode->Info->HorizontalResolution;
  VerticalResolution   = VkContext->GraphicsOutput->Mode->Info->VerticalResolution;
  *TouchX              = (UINT32) MultU64x32 (Point.CurrentX, HorizontalResolution) / (UINT32) AbsoluteMaxX;
  *TouchY              = (UINT32) MultU64x32 (Point.CurrentY, VerticalResolution) / (UINT32) AbsoluteMaxY;

  return EFI_SUCCESS;
}

/**
  This routine is used to check if screen has been cleared.

  @param[in] VkContext  Pointer to virtual keyboard's context

  @retval EFI_SUCCESS   Function completed.

**/
EFI_STATUS
CheckScreenCleared (
  IN VK_CONTEXT *VkContext
  )
{
  EFI_STATUS                    Status;
  UINT32                        HorizontalResolution;
  UINT32                        VerticalResolution;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBufferIndex;
  UINTN                         BltSize;
  BOOLEAN                       IsScreenCleared;

  //
  // Check left-bottom side.
  // If IconBltBuffer is null, checking is meaningless.
  //
  if (VkContext->IconBltBuffer == NULL) {
    return EFI_SUCCESS;
  }

  IsScreenCleared = FALSE;
  Status          = EFI_SUCCESS;
  if ((gST->ConOut->Mode->CursorColumn == 0) && (gST->ConOut->Mode->CursorRow == 0)) {
    //
    // System may call gST->ConOut->ClearScreen
    //
    HorizontalResolution  = VkContext->GraphicsOutput->Mode->Info->HorizontalResolution;
    VerticalResolution    = VkContext->GraphicsOutput->Mode->Info->VerticalResolution;
    BltBuffer             = AllocateZeroPool (VkContext->IconBltSize);
    if (BltBuffer == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }

    BltBufferIndex = BltBuffer;
    Status = VkContext->GraphicsOutput->Blt (
                                          VkContext->GraphicsOutput,
                                          BltBuffer,
                                          EfiBltVideoToBltBuffer,
                                          (HorizontalResolution - VkContext->IconBltWidth),
                                          (VerticalResolution - VkContext->IconBltHeight),
                                          0,
                                          0,
                                          VkContext->IconBltWidth,
                                          VkContext->IconBltHeight,
                                          VkContext->IconBltWidth * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
                                          );
    if (EFI_ERROR (Status)) {
      FreePool (BltBuffer);
      return Status;
    }
    BltSize = VkContext->IconBltHeight * VkContext->IconBltWidth;
    IsScreenCleared = TRUE;
    while (BltSize-- != 0) {
      if ((BltBufferIndex->Red != 0) || (BltBufferIndex->Green != 0) || (BltBufferIndex->Blue != 0)) {
        IsScreenCleared = FALSE;
        break;
      }
      BltBufferIndex++;
    }
    FreePool (BltBuffer);
  }

  if (IsScreenCleared) {
    VkContext->IsIconShowed   = FALSE;
    VkContext->CurrentKeyboardDisplay = VkDisplayAttributeNone;

    if (VkContext->VkBodyBackgroundBltBuffer != NULL) {
      FreePool (VkContext->VkBodyBackgroundBltBuffer);
      VkContext->VkBodyBackgroundBltBuffer = NULL;
    }

    if (VkContext->VkBodyCompoundBltBuffer != NULL) {
      FreePool (VkContext->VkBodyCompoundBltBuffer);
      VkContext->VkBodyCompoundBltBuffer = NULL;
    }

    if (VkContext->IconBltBuffer != NULL) {
      FreePool (VkContext->IconBltBuffer);
      VkContext->IconBltBuffer = NULL;
    }

  }

  return Status;
}

/**
  This routine is used to check if background beneath virtual keyboard has been cleared.

  @param[in] VkContext  Pointer to virtual keyboard's context

  @retval EFI_SUCCESS   Function completed.

**/
EFI_STATUS
CheckBackgroundChanged (
  IN VK_CONTEXT *VkContext
  )
{
  EFI_STATUS                    Status;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer;

  Status                         = EFI_SUCCESS;
  VkContext->IsBackgroundChanged = FALSE;

  if ((VkContext->CurrentKeyboardDisplay == VkDisplayAttributeNone) || (VkContext->VkBodyCompoundBltBuffer == NULL)) {
    return EFI_SUCCESS;
  }

  BltBuffer = AllocateZeroPool (VkContext->VkBodyBltSize);
  if (BltBuffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  Status = VkContext->GraphicsOutput->Blt (
                                        VkContext->GraphicsOutput,
                                        BltBuffer,
                                        EfiBltVideoToBltBuffer,
                                        VkContext->VkBodyBltStartX,
                                        VkContext->VkBodyBltStartY,
                                        0,
                                        0,
                                        VkContext->VkBodyBltWidth,
                                        VkContext->VkBodyBltHeight,
                                        VkContext->VkBodyBltWidth * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
                                        );
  if (EFI_ERROR (Status)) {
    FreePool (BltBuffer);
    return Status;
  }
  if (CompareMem (BltBuffer, VkContext->VkBodyCompoundBltBuffer, VkContext->VkBodyBltSize) != 0) {
    VkContext->IsBackgroundChanged = TRUE;
    VkContext->CurrentKeyboardDisplay = VkDisplayAttributeNone;
    DrawKeyboardLayout (VkContext);
  }

  if (BltBuffer != NULL) {
    FreePool (BltBuffer);
  }

  return Status;
}

/**
  Get unicode by VkContext->PageNumber and VkContext->KeyboardBodyPtr.

  @param[in]  VkContext            Address of an VK_CONTEXT structure.
  @param[in]  KeyItem              Key Item.
  @param[out] FontPtr              Follow VkContext->PageNumber to translate
                                   font unicode.

  @retval EFI_SUCCESS              Finish translating FontPtr.
  @retval EFI_INVALID_PARAMETER    VkContext or FontPtr is NULL.

**/
EFI_STATUS
VkGetMappingFont (
  IN  VK_CONTEXT *VkContext,
  IN  VK_STRUCT  KeyItem,
  OUT UINT32     *FontPtr
  )
{
  if ((VkContext == NULL) || (FontPtr == NULL) || (VkContext->PageNumber>=VkPageMaximum)) {
    return EFI_INVALID_PARAMETER;
  }
  *FontPtr = (UINT32) KeyItem.PageFont[VkContext->PageNumber];

  return EFI_SUCCESS;
}
