]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/DisplayEngineDxe/Popup.c
MdeModulePkg/DisplayEngine: Add implementation of HiiPopup protocol
[mirror_edk2.git] / MdeModulePkg / Universal / DisplayEngineDxe / Popup.c
diff --git a/MdeModulePkg/Universal/DisplayEngineDxe/Popup.c b/MdeModulePkg/Universal/DisplayEngineDxe/Popup.c
new file mode 100644 (file)
index 0000000..7c76f69
--- /dev/null
@@ -0,0 +1,730 @@
+/** @file\r
+Implementation for Hii Popup Protocol.\r
+\r
+Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "FormDisplay.h"\r
+\r
+EFI_SCREEN_DESCRIPTOR  gPopupDimensions;\r
+LIST_ENTRY             gUserSelectableOptions;\r
+EFI_STRING             gMessageString;\r
+UINTN                  gMesStrLineNum;\r
+UINTN                  gMaxRowWidth;\r
+\r
+/**\r
+  Free the user selectable option structure data.\r
+\r
+  @param  OptionList  Point to the selectable option list which need to be freed.\r
+\r
+**/\r
+VOID\r
+FreeSelectableOptions(\r
+  LIST_ENTRY           *OptionList\r
+  )\r
+{\r
+  LIST_ENTRY              *Link;\r
+  USER_SELECTABLE_OPTION  *SelectableOption;\r
+\r
+  while (!IsListEmpty (OptionList)) {\r
+    Link = GetFirstNode (OptionList);\r
+    SelectableOption = SELECTABLE_OPTION_FROM_LINK (Link);\r
+    RemoveEntryList (&SelectableOption->Link);\r
+    FreePool (SelectableOption);\r
+  }\r
+}\r
+\r
+/**\r
+  Display one selectable option.\r
+\r
+  @param  SelectableOption  The selectable option need to be drew.\r
+  @param  Highlight         Whether the option need to be highlighted.\r
+\r
+**/\r
+VOID\r
+DisplayOneSelectableOption(\r
+  IN USER_SELECTABLE_OPTION    *SelectableOption,\r
+  IN BOOLEAN                   Highlight\r
+  )\r
+{\r
+  if (Highlight) {\r
+    gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());\r
+  }\r
+  PrintStringAt (SelectableOption->OptionCol, SelectableOption->OptionRow, SelectableOption->OptionString);\r
+  gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());\r
+}\r
+\r
+/**\r
+  Add one selectable option to option list. This is the work function for AddUserSelectableOptions.\r
+\r
+  @param  PopupType     The option need to be drew.\r
+  @param  OptionType    The type of this selection option.\r
+  @param  OptionString  Point to the option string that to be shown.\r
+  @param  OptionCol     The column that the option need to be drew at.\r
+  @param  OptionRow     The row that the option need to be drew at.\r
+\r
+  @retval  EFI_SUCCESS           This function implement successfully.\r
+  @retval  EFI_OUT_OF_RESOURCES  There are not enough resources available.\r
+\r
+**/\r
+EFI_STATUS\r
+AddOneSelectableOption (\r
+  IN EFI_HII_POPUP_TYPE           PopupType,\r
+  IN EFI_HII_POPUP_SELECTION      OptionType,\r
+  IN CHAR16                       *OptionString,\r
+  IN UINTN                        OptionCol,\r
+  IN UINTN                        OptionRow\r
+  )\r
+{\r
+  USER_SELECTABLE_OPTION  *UserSelectableOption;\r
+\r
+  UserSelectableOption = AllocateZeroPool (sizeof (USER_SELECTABLE_OPTION));\r
+  if (UserSelectableOption == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // Initialize the user selectable option based on the PopupType and OptionType.\r
+  // And then add the option to the option list gUserSelectableOptions.\r
+  //\r
+  UserSelectableOption->Signature = USER_SELECTABLE_OPTION_SIGNATURE;\r
+  UserSelectableOption->OptionString = OptionString;\r
+  UserSelectableOption->OptionType = OptionType;\r
+  UserSelectableOption->OptionCol = OptionCol;\r
+  UserSelectableOption->OptionRow = OptionRow;\r
+  UserSelectableOption->MinSequence = 0;\r
+\r
+  switch (PopupType) {\r
+  case EfiHiiPopupTypeOk:\r
+    UserSelectableOption->MaxSequence = 0;\r
+    UserSelectableOption->Sequence= 0;\r
+    break;\r
+  case EfiHiiPopupTypeOkCancel:\r
+    UserSelectableOption->MaxSequence = 1;\r
+    if (OptionType == EfiHiiPopupSelectionOk) {\r
+      UserSelectableOption->Sequence= 0;\r
+    } else {\r
+      UserSelectableOption->Sequence= 1;\r
+    }\r
+    break;\r
+  case EfiHiiPopupTypeYesNo:\r
+    UserSelectableOption->MaxSequence = 1;\r
+    if (OptionType == EfiHiiPopupSelectionYes) {\r
+      UserSelectableOption->Sequence = 0;\r
+    } else {\r
+      UserSelectableOption->Sequence = 1;\r
+    }\r
+    break;\r
+  case EfiHiiPopupTypeYesNoCancel:\r
+    UserSelectableOption->MaxSequence = 2;\r
+    if (OptionType == EfiHiiPopupSelectionYes) {\r
+      UserSelectableOption->Sequence = 0;\r
+    } else if (OptionType == EfiHiiPopupSelectionNo){\r
+      UserSelectableOption->Sequence = 1;\r
+    } else {\r
+      UserSelectableOption->Sequence = 2;\r
+    }\r
+    break;\r
+  default:\r
+    break;\r
+  }\r
+  InsertTailList (&gUserSelectableOptions, &UserSelectableOption->Link);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Add user selectable options to option list for different types of Popup.\r
+\r
+  @param  PopupType    Type of the popup to display.\r
+\r
+  @retval  EFI_SUCCESS           This function implement successfully.\r
+  @retval  EFI_OUT_OF_RESOURCES  There are not enough resources available.\r
+\r
+**/\r
+EFI_STATUS\r
+AddUserSelectableOptions (\r
+  IN  EFI_HII_POPUP_TYPE  PopupType\r
+  )\r
+{\r
+  EFI_STATUS   Status;\r
+  UINTN        EndCol;\r
+  UINTN        StartCol;\r
+  UINTN        OptionCol;\r
+  UINTN        OptionRow;\r
+  UINTN        ColDimension;\r
+\r
+  Status = EFI_SUCCESS;\r
+  EndCol = gPopupDimensions.RightColumn;\r
+  StartCol = gPopupDimensions.LeftColumn;\r
+  OptionRow = gPopupDimensions.BottomRow - POPUP_BORDER;\r
+  ColDimension = EndCol - StartCol + 1;\r
+\r
+  InitializeListHead (&gUserSelectableOptions);\r
+\r
+  switch (PopupType) {\r
+  case EfiHiiPopupTypeOk:\r
+    //\r
+    // Add [Ok] option to the option list.\r
+    //\r
+    OptionCol = StartCol + (ColDimension - USER_SELECTABLE_OPTION_OK_WIDTH) / 2;\r
+    Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionOk, gOkOption, OptionCol, OptionRow);\r
+    break;\r
+  case EfiHiiPopupTypeOkCancel:\r
+    //\r
+    // Add [Ok] and [Cancel] options to the option list.\r
+    //\r
+    OptionCol = StartCol + (ColDimension - USER_SELECTABLE_OPTION_OK_CAL_WIDTH) / 3;\r
+    Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionOk, gOkOption, OptionCol, OptionRow);\r
+    OptionCol = EndCol - (ColDimension - USER_SELECTABLE_OPTION_OK_CAL_WIDTH) / 3 - (GetStringWidth (gCancelOption) -2) / 2 + 1;\r
+    Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionCancel, gCancelOption, OptionCol, OptionRow);\r
+    break;\r
+  case EfiHiiPopupTypeYesNo:\r
+    //\r
+    // Add [Yes] and [No] options to the option list.\r
+    //\r
+    OptionCol = StartCol + (ColDimension - USER_SELECTABLE_OPTION_YES_NO_WIDTH) / 3;\r
+    Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionYes, gYesOption, OptionCol, OptionRow);\r
+    OptionCol = EndCol - (ColDimension - USER_SELECTABLE_OPTION_YES_NO_WIDTH) / 3 - (GetStringWidth (gNoOption)- 2) / 2 + 1;\r
+    Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionNo, gNoOption, OptionCol, OptionRow);\r
+    break;\r
+  case EfiHiiPopupTypeYesNoCancel:\r
+    //\r
+    // Add [Yes], [No] and [Cancel] options to the option list.\r
+    //\r
+    OptionCol = StartCol + (ColDimension - USER_SELECTABLE_OPTION_YES_NO_CAL_WIDTH) / 4;\r
+    Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionYes, gYesOption, OptionCol, OptionRow);\r
+    OptionCol = StartCol + (ColDimension - (GetStringWidth (gNoOption) -2) / 2) / 2;\r
+    Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionNo, gNoOption, OptionCol, OptionRow);\r
+    OptionCol = EndCol - (ColDimension - USER_SELECTABLE_OPTION_YES_NO_CAL_WIDTH) / 4 - (GetStringWidth (gCancelOption) - 2) / 2 + 1;\r
+    Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionCancel, gCancelOption, OptionCol, OptionRow);\r
+    break;\r
+  default:\r
+    break;\r
+  }\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Show selectable options to user and get the one that user select.\r
+\r
+  @param  PopupType      Type of the popup to display.\r
+  @param  UserSelection  User selection.\r
+\r
+**/\r
+VOID\r
+GetUserSelection (\r
+  IN  EFI_HII_POPUP_TYPE       PopupType,\r
+  OUT EFI_HII_POPUP_SELECTION  *UserSelection\r
+  )\r
+{\r
+  LIST_ENTRY                       *HighlightPos;\r
+  LIST_ENTRY                       *Link;\r
+  USER_SELECTABLE_OPTION           *SelectableOption;\r
+  USER_SELECTABLE_OPTION           *HighlightOption;\r
+  EFI_INPUT_KEY                    KeyValue;\r
+  EFI_STATUS                       Status;\r
+\r
+  //\r
+  // Display user selectable options in gUserSelectableOptions and get the option which user selects.\r
+  //\r
+  HighlightPos = gUserSelectableOptions.ForwardLink;\r
+  do {\r
+    for (Link = gUserSelectableOptions.ForwardLink; Link != &gUserSelectableOptions; Link = Link->ForwardLink) {\r
+      SelectableOption          = SELECTABLE_OPTION_FROM_LINK (Link);\r
+      DisplayOneSelectableOption (SelectableOption, (BOOLEAN)(Link == HighlightPos));\r
+    }\r
+    //\r
+    //If UserSelection is NULL, there is no need to handle the key user input, just return.\r
+    //\r
+    if (UserSelection == NULL) {\r
+      return;\r
+    }\r
+\r
+    Status = WaitForKeyStroke (&KeyValue);\r
+    ASSERT_EFI_ERROR (Status);\r
+\r
+    HighlightOption = SELECTABLE_OPTION_FROM_LINK (HighlightPos);\r
+    switch (KeyValue.UnicodeChar) {\r
+    case CHAR_NULL:\r
+      switch (KeyValue.ScanCode) {\r
+      case SCAN_RIGHT:\r
+        if (HighlightOption->Sequence < HighlightOption->MaxSequence) {\r
+          HighlightPos = HighlightPos->ForwardLink;\r
+        } else {\r
+          HighlightPos = gUserSelectableOptions.ForwardLink;\r
+        }\r
+        break;\r
+      case SCAN_LEFT:\r
+        if (HighlightOption->Sequence > HighlightOption->MinSequence) {\r
+          HighlightPos = HighlightPos->BackLink;\r
+        } else {\r
+          HighlightPos = gUserSelectableOptions.BackLink;\r
+        }\r
+        break;\r
+      default:\r
+        break;\r
+      }\r
+      break;\r
+\r
+    case CHAR_CARRIAGE_RETURN:\r
+      *UserSelection = HighlightOption->OptionType;\r
+      return;\r
+    default:\r
+      if (((KeyValue.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (*gConfirmOptYes | UPPER_LOWER_CASE_OFFSET)) &&\r
+        (PopupType == EfiHiiPopupTypeYesNo || PopupType == EfiHiiPopupTypeYesNoCancel)) {\r
+        *UserSelection = EfiHiiPopupSelectionYes;\r
+        return;\r
+      } else if ((KeyValue.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (*gConfirmOptNo| UPPER_LOWER_CASE_OFFSET) &&\r
+        (PopupType == EfiHiiPopupTypeYesNo || PopupType == EfiHiiPopupTypeYesNoCancel)){\r
+        *UserSelection = EfiHiiPopupSelectionNo;\r
+        return;\r
+      } else if ((KeyValue.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (*gConfirmOptOk | UPPER_LOWER_CASE_OFFSET) &&\r
+        (PopupType == EfiHiiPopupTypeOk || PopupType == EfiHiiPopupTypeOkCancel)){\r
+        *UserSelection = EfiHiiPopupSelectionOk;\r
+        return;\r
+      } else if ((KeyValue.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (*gConfirmOptCancel| UPPER_LOWER_CASE_OFFSET) &&\r
+        (PopupType == EfiHiiPopupTypeOkCancel || PopupType == EfiHiiPopupTypeYesNoCancel)){\r
+        *UserSelection = EfiHiiPopupSelectionCancel;\r
+        return;\r
+      }\r
+      break;\r
+    }\r
+  } while (TRUE);\r
+}\r
+\r
+/**\r
+  Get the offset in the input string when the width reaches to a fixed one.\r
+\r
+  The input string may contain NARROW_CHAR and WIDE_CHAR.\r
+  Notice: the input string doesn't contain line break characters.\r
+\r
+  @param  String      The input string to be counted.\r
+  @param  MaxWidth    The max length this function supported.\r
+  @param  Offset      The max index of the string can be show out. If string's width less than MaxWidth, offset will point to the "\0" of the string.\r
+\r
+**/\r
+VOID\r
+GetStringOffsetWithWidth (\r
+  IN  CHAR16               *String,\r
+  IN  UINTN                MaxWidth,\r
+  OUT UINTN                *Offset\r
+  )\r
+{\r
+  UINTN   StringWidth;\r
+  UINTN   CharWidth;\r
+  UINTN   StrOffset;\r
+\r
+  StringWidth = 0;\r
+  CharWidth   = 1;\r
+\r
+  for (StrOffset = 0; String[StrOffset] != CHAR_NULL; StrOffset++) {\r
+    switch (String[StrOffset]) {\r
+    case NARROW_CHAR:\r
+      CharWidth = 1;\r
+      break;\r
+    case WIDE_CHAR:\r
+      CharWidth = 2;\r
+      break;\r
+    default:\r
+      StringWidth += CharWidth;\r
+      if (StringWidth >= MaxWidth) {\r
+        *Offset = StrOffset;\r
+        return;\r
+      }\r
+    }\r
+  }\r
+  *Offset = StrOffset;\r
+}\r
+\r
+/**\r
+  Parse the message to check if it contains line break characters.\r
+  For once call, caller can get the string for one line and the width of the string.\r
+  This function call be called recursively to parse the whole InputString.\r
+\r
+  (Notice: current implementation, it only checks \r, \n characters, it deals \r,\n,\n\r same as \r\n.)\r
+\r
+  @param  InputString       String description for this option.\r
+  @param  OutputString      Buffer to copy the string into, caller is responsible for freeing the buffer.\r
+  @param  OutputStrWidth    The width of OutputString.\r
+  @param  Index             Where in InputString to start the copy process\r
+\r
+  @return Returns the number of CHAR16 characters that were copied into the OutputString buffer, include the '\0' info.\r
+\r
+**/\r
+UINTN\r
+ParseMessageString (\r
+  IN     CHAR16    *InputString,\r
+  OUT    CHAR16    **OutputString,\r
+  OUT    UINTN     *OutputStrWidth,\r
+  IN OUT UINTN     *Index\r
+  )\r
+{\r
+  UINTN          StrOffset;\r
+\r
+  if (InputString == NULL || Index == NULL || OutputString == NULL) {\r
+    return 0;\r
+  }\r
+\r
+  *OutputStrWidth = 0;\r
+\r
+  //\r
+  //Check the string to see if there are line break characters in the string\r
+  //\r
+  for (StrOffset = 0;\r
+    InputString[*Index + StrOffset] != CHAR_CARRIAGE_RETURN && InputString[*Index + StrOffset] != CHAR_LINEFEED && InputString[*Index + StrOffset] != CHAR_NULL;\r
+    StrOffset++\r
+  );\r
+\r
+  //\r
+  // The CHAR_NULL has process last time, this time just return 0 to stand for finishing parsing the InputString.\r
+  //\r
+  if (StrOffset == 0 && (InputString[*Index + StrOffset] == CHAR_NULL)) {\r
+    return 0;\r
+  }\r
+\r
+  //\r
+  // Copy the string to OutputString buffer and calculate the width of OutputString.\r
+  //\r
+  *OutputString = AllocateZeroPool ((StrOffset + 1) * sizeof(CHAR16));\r
+  if (*OutputString == NULL) {\r
+    return 0;\r
+  }\r
+  CopyMem ((*OutputString), &InputString[*Index], StrOffset * sizeof(CHAR16));\r
+  *OutputStrWidth = (GetStringWidth (*OutputString) -2) / 2;\r
+\r
+  //\r
+  // Update the value of Index, can be used for marking where to check the input string for next call.\r
+  //\r
+  if (InputString[*Index + StrOffset] == CHAR_LINEFEED) {\r
+    //\r
+    // Skip the /n or /n/r info.\r
+    //\r
+    if (InputString[*Index + StrOffset + 1] == CHAR_CARRIAGE_RETURN) {\r
+      *Index = (*Index + StrOffset + 2);\r
+    } else {\r
+      *Index = (*Index + StrOffset + 1);\r
+    }\r
+  } else if (InputString[*Index + StrOffset] == CHAR_CARRIAGE_RETURN) {\r
+    //\r
+    // Skip the /r or /r/n info.\r
+    //\r
+    if (InputString[*Index + StrOffset + 1] == CHAR_LINEFEED) {\r
+      *Index = (*Index + StrOffset + 2);\r
+    } else {\r
+      *Index = (*Index + StrOffset + 1);\r
+    }\r
+  } else {\r
+    *Index = (*Index + StrOffset);\r
+  }\r
+\r
+  return StrOffset + 1;\r
+}\r
+\r
+/**\r
+  Calculate the position of the popup.\r
+\r
+  @param  PopupType       Type of the popup to display.\r
+  @param  ScreenForPopup  The screen dimensions for the popup.\r
+\r
+**/\r
+VOID\r
+CalculatePopupPosition (\r
+  IN  EFI_HII_POPUP_TYPE     PopupType,\r
+  OUT EFI_SCREEN_DESCRIPTOR  *ScreenForPopup\r
+  )\r
+{\r
+  CHAR16              *OutputString;\r
+  UINTN               StringIndex;\r
+  UINTN               OutputStrWidth;\r
+  UINTN               OptionRowWidth;\r
+  UINTN               Columns;\r
+  UINTN               Rows;\r
+\r
+  OptionRowWidth = 0;\r
+\r
+  //\r
+  // Calculate the row number which is needed to show the message string and the max width of the string in one row.\r
+  //\r
+  for (StringIndex = 0; ParseMessageString (gMessageString, &OutputString, &OutputStrWidth, &StringIndex) != 0;) {\r
+    gMesStrLineNum ++;\r
+    if (gMaxRowWidth < OutputStrWidth) {\r
+      gMaxRowWidth = OutputStrWidth;\r
+    }\r
+    FreePool (OutputString);\r
+  }\r
+\r
+  //\r
+  // Calculate the row width for the selectable options.(OptionRowWidth = Number * SkipWidth + OptionWidth)\r
+  //\r
+  if (PopupType == EfiHiiPopupTypeOk) {\r
+    OptionRowWidth = USER_SELECTABLE_OPTION_SKIP_WIDTH *2 + USER_SELECTABLE_OPTION_OK_WIDTH;\r
+  } else if (PopupType == EfiHiiPopupTypeOkCancel) {\r
+    OptionRowWidth = USER_SELECTABLE_OPTION_SKIP_WIDTH *3 + USER_SELECTABLE_OPTION_OK_CAL_WIDTH;\r
+  } else if (PopupType == EfiHiiPopupTypeYesNo) {\r
+    OptionRowWidth = USER_SELECTABLE_OPTION_SKIP_WIDTH *3 + USER_SELECTABLE_OPTION_YES_NO_WIDTH;\r
+  } else if (PopupType == EfiHiiPopupTypeYesNoCancel) {\r
+    OptionRowWidth = USER_SELECTABLE_OPTION_SKIP_WIDTH *4 + USER_SELECTABLE_OPTION_YES_NO_CAL_WIDTH;\r
+  }\r
+  if (OptionRowWidth > gMaxRowWidth) {\r
+    gMaxRowWidth = OptionRowWidth;\r
+  }\r
+\r
+  //\r
+  // Avialble row width for message string = screen width - left popup border width - right popup border width.\r
+  // Avialble line number for message string = screen height - 1 - popup header height -  popup footer height.\r
+  // (Notice: screen height - 1 because in current UI page, the bottom row of srceen is usded to show Status Bar,not for form itself.\r
+  // So we don't use the bottom row for popup either. If macro STATUS_BAR_HEIGHT changed, we also need to update the height here.)\r
+  //\r
+  // Select the smaller one between actual dimension of message string and the avialble dimension for message string.\r
+  //\r
+  gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Columns, &Rows);\r
+  gMaxRowWidth = MIN (gMaxRowWidth, Columns - 2 * POPUP_BORDER);\r
+  gMesStrLineNum = MIN (gMesStrLineNum, Rows -1 - POPUP_FOOTER_HEIGHT - POPUP_HEADER_HEIGHT);\r
+\r
+  //\r
+  // Calculate the start column, end column, top row and bottom row for the popup.\r
+  //\r
+  ScreenForPopup->LeftColumn = (Columns -2 * POPUP_BORDER - gMaxRowWidth) / 2;\r
+  ScreenForPopup->RightColumn = ScreenForPopup->LeftColumn + gMaxRowWidth + 2 * POPUP_BORDER - 1;\r
+  ScreenForPopup->TopRow = (Rows - 1 - POPUP_FOOTER_HEIGHT - POPUP_HEADER_HEIGHT - gMesStrLineNum) / 2;\r
+  ScreenForPopup->BottomRow = ScreenForPopup->TopRow + gMesStrLineNum + POPUP_FOOTER_HEIGHT + POPUP_HEADER_HEIGHT - 1;\r
+}\r
+\r
+/**\r
+  Draw the Message box.\r
+  +-------------------------------------------+\r
+  |            ERROR/WARNING/INFO             |\r
+  |-------------------------------------------|\r
+  |              popup messages               |\r
+  |                                           |\r
+  |          user selectable options          |\r
+  +-------------------------------------------+\r
+\r
+  @param  PopupStyle   Popup style to use.\r
+\r
+**/\r
+EFI_STATUS\r
+DrawMessageBox (\r
+  IN  EFI_HII_POPUP_STYLE    PopupStyle\r
+  )\r
+{\r
+  UINTN             Index;\r
+  UINTN             Length;\r
+  UINTN             EndCol;\r
+  UINTN             TopRow;\r
+  UINTN             StartCol;\r
+  UINTN             BottomRow;\r
+  CHAR16            Character;\r
+  UINTN             DisplayRow;\r
+  UINTN             StringIndex;\r
+  CHAR16            *TempString;\r
+  CHAR16            *OutputString;\r
+  UINTN             ColDimension;\r
+  UINTN             OutputStrWidth;\r
+  UINTN             DrawMesStrRowNum;\r
+\r
+  EndCol = gPopupDimensions.RightColumn;\r
+  TopRow = gPopupDimensions.TopRow;\r
+  StartCol = gPopupDimensions.LeftColumn;\r
+  BottomRow = gPopupDimensions.BottomRow;\r
+  ColDimension = EndCol - StartCol + 1;\r
+  DrawMesStrRowNum = 0;\r
+\r
+  //\r
+  // 1. Draw the top of the message box.\r
+  //\r
+  Character = BOXDRAW_DOWN_RIGHT;\r
+  PrintCharAt (StartCol, TopRow, Character);\r
+  Character = BOXDRAW_HORIZONTAL;\r
+  for (Index = StartCol; Index + 1 < EndCol; Index++) {\r
+    PrintCharAt ((UINTN)-1, (UINTN)-1, Character);\r
+  }\r
+  Character = BOXDRAW_DOWN_LEFT;\r
+  PrintCharAt ((UINTN)-1, (UINTN)-1, Character);\r
+\r
+  //\r
+  // 2. Draw the prompt string for different popup styles.\r
+  //\r
+  Character = BOXDRAW_VERTICAL;\r
+  DisplayRow = TopRow + POPUP_BORDER;\r
+  ClearLines (StartCol,  EndCol, DisplayRow, DisplayRow, GetPopupColor ());\r
+  PrintCharAt (StartCol, DisplayRow, Character);\r
+  PrintCharAt (EndCol, DisplayRow, Character);\r
+  if (PopupStyle == EfiHiiPopupStyleError) {\r
+    PrintStringAt ((ColDimension - (GetStringWidth (gErrorPopup) - 2) / 2) / 2 + StartCol, DisplayRow, gErrorPopup);\r
+  } else if (PopupStyle == EfiHiiPopupStyleWarning) {\r
+    PrintStringAt ((ColDimension - (GetStringWidth (gWarningPopup) - 2) / 2) / 2 + StartCol, DisplayRow, gWarningPopup);\r
+  } else {\r
+    PrintStringAt ((ColDimension - (GetStringWidth (gInfoPopup) - 2) / 2) / 2 + StartCol, DisplayRow, gInfoPopup);\r
+  }\r
+\r
+  //\r
+  // 3. Draw the horizontal line below the prompt string for different popup styles.\r
+  //\r
+  DisplayRow = TopRow + POPUP_BORDER + POPUP_STYLE_STRING_HEIGHT;\r
+  ClearLines (StartCol,  EndCol, DisplayRow, DisplayRow, GetPopupColor ());\r
+  Character = BOXDRAW_HORIZONTAL;\r
+  for (Index = StartCol + 1; Index < EndCol; Index++) {\r
+    PrintCharAt (Index, DisplayRow, Character);\r
+  }\r
+  Character = BOXDRAW_VERTICAL;\r
+  PrintCharAt (StartCol, DisplayRow, Character);\r
+  PrintCharAt (EndCol, DisplayRow, Character);\r
+\r
+  //\r
+  // 4. Draw the mesage string.\r
+  //\r
+  DisplayRow = TopRow + POPUP_HEADER_HEIGHT;\r
+  for (Index = DisplayRow ,StringIndex = 0; ParseMessageString (gMessageString, &OutputString, &OutputStrWidth,  &StringIndex) != 0 && DrawMesStrRowNum < gMesStrLineNum;) {\r
+    ClearLines (StartCol,  EndCol, Index, Index, GetPopupColor ());\r
+    PrintCharAt (StartCol, Index, Character);\r
+    PrintCharAt (EndCol, Index, Character);\r
+    if (OutputStrWidth > gMaxRowWidth) {\r
+      //\r
+      //OutputStrWidth > MaxMesStrWidth, cut off the string and print print ... instead.\r
+      //\r
+      GetStringOffsetWithWidth (OutputString, gMaxRowWidth, &Length);\r
+      TempString = AllocateZeroPool ((Length + 1) * sizeof (CHAR16));\r
+      if (TempString == NULL) {\r
+        FreePool (OutputString);\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+      StrnCpyS (TempString, Length + 1, OutputString, Length - 3);\r
+      StrCatS (TempString, Length + 1, L"...");\r
+      PrintStringAt ((ColDimension - gMaxRowWidth) / 2 + StartCol, Index, TempString);\r
+      FreePool (TempString);\r
+    } else {\r
+      PrintStringAt ((ColDimension - OutputStrWidth) / 2 + StartCol, Index, OutputString);\r
+    }\r
+    Index ++;\r
+    DrawMesStrRowNum ++;\r
+    FreePool (OutputString);\r
+  }\r
+\r
+  //\r
+  // 5. Draw an empty line after message string.\r
+  //\r
+  ClearLines (StartCol,  EndCol, Index, Index, GetPopupColor ());\r
+  PrintCharAt (StartCol, Index, Character);\r
+  PrintCharAt (EndCol, Index, Character);\r
+  //\r
+  // Check whether the actual string row number beyond the MesStrRowNum, if yes, print the ...... in the row.\r
+  //\r
+  if (OutputStrWidth > 0 && DrawMesStrRowNum >= gMesStrLineNum) {\r
+    PrintStringAt ((ColDimension - StrLen (L"......")) / 2 + StartCol, Index, L"......");\r
+  }\r
+\r
+  //\r
+  // 6. Draw an empty line which is used to show user selectable options, will draw concrete option strings in function GetUserSelection().\r
+  //\r
+  Character = BOXDRAW_VERTICAL;\r
+  DisplayRow = BottomRow - POPUP_BORDER;\r
+  ClearLines (StartCol,  EndCol, DisplayRow, DisplayRow, GetPopupColor ());\r
+  PrintCharAt (StartCol, DisplayRow, Character);\r
+  PrintCharAt (EndCol, DisplayRow, Character);\r
+\r
+  //\r
+  // 7. Draw the bottom of the message box.\r
+  //\r
+  Character = BOXDRAW_UP_RIGHT;\r
+  PrintCharAt (StartCol, BottomRow, Character);\r
+  Character = BOXDRAW_HORIZONTAL;\r
+  for (Index = StartCol; Index + 1 < EndCol; Index++) {\r
+    PrintCharAt ((UINTN)-1, (UINTN) -1, Character);\r
+  }\r
+  Character = BOXDRAW_UP_LEFT;\r
+  PrintCharAt ((UINTN)-1, (UINTN) -1, Character);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Displays a popup window.\r
+\r
+  @param  This           A pointer to the EFI_HII_POPUP_PROTOCOL instance.\r
+  @param  PopupStyle     Popup style to use.\r
+  @param  PopupType      Type of the popup to display.\r
+  @param  HiiHandle      HII handle of the string pack containing Message\r
+  @param  Message        A message to display in the popup box.\r
+  @param  UserSelection  User selection.\r
+\r
+  @retval EFI_SUCCESS            The popup box was successfully displayed.\r
+  @retval EFI_INVALID_PARAMETER  HiiHandle and Message do not define a valid HII string.\r
+  @retval EFI_INVALID_PARAMETER  PopupType is not one of the values defined by this specification.\r
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to display the popup box.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CreatePopup (\r
+  IN  EFI_HII_POPUP_PROTOCOL  *This,\r
+  IN  EFI_HII_POPUP_STYLE     PopupStyle,\r
+  IN  EFI_HII_POPUP_TYPE      PopupType,\r
+  IN  EFI_HII_HANDLE          HiiHandle,\r
+  IN  EFI_STRING_ID           Message,\r
+  OUT EFI_HII_POPUP_SELECTION *UserSelection OPTIONAL\r
+  )\r
+{\r
+  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *ConOut;\r
+  EFI_SIMPLE_TEXT_OUTPUT_MODE      SavedConsoleMode;\r
+  EFI_STATUS                       Status;\r
+\r
+  if ((PopupType < EfiHiiPopupTypeOk) || (PopupType > EfiHiiPopupTypeYesNoCancel)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if((HiiHandle == NULL) || (Message == 0)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  gMessageString = HiiGetString (HiiHandle, Message, NULL);\r
+  if((gMessageString == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ConOut = gST->ConOut;\r
+  gMaxRowWidth = 0;\r
+  gMesStrLineNum = 0;\r
+\r
+  CopyMem (&SavedConsoleMode, ConOut->Mode, sizeof (SavedConsoleMode));\r
+  ConOut->EnableCursor (ConOut, FALSE);\r
+  ConOut->SetAttribute (ConOut, GetPopupColor ());\r
+\r
+  CalculatePopupPosition (PopupType, &gPopupDimensions);\r
+\r
+  Status = DrawMessageBox (PopupStyle);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Done;\r
+  }\r
+\r
+  //\r
+  // Add user selectable options to option list: gUserSelectableOptions\r
+  //\r
+  Status = AddUserSelectableOptions (PopupType);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Done;\r
+  }\r
+\r
+  GetUserSelection (PopupType, UserSelection);\r
+\r
+Done:\r
+  //\r
+  // Restore Conout attributes and free the resources allocate before.\r
+  //\r
+  ConOut->EnableCursor (ConOut, SavedConsoleMode.CursorVisible);\r
+  ConOut->SetCursorPosition (ConOut, SavedConsoleMode.CursorColumn, SavedConsoleMode.CursorRow);\r
+  ConOut->SetAttribute (ConOut, SavedConsoleMode.Attribute);\r
+  FreeSelectableOptions (&gUserSelectableOptions);\r
+  FreePool (gMessageString);\r
+\r
+  return Status;\r
+}\r
+\r