summaryrefslogtreecommitdiff
path: root/EdkModulePkg/Universal/UserInterface/SetupBrowser/Dxe/InputHandler.c
diff options
context:
space:
mode:
Diffstat (limited to 'EdkModulePkg/Universal/UserInterface/SetupBrowser/Dxe/InputHandler.c')
-rw-r--r--EdkModulePkg/Universal/UserInterface/SetupBrowser/Dxe/InputHandler.c1580
1 files changed, 1580 insertions, 0 deletions
diff --git a/EdkModulePkg/Universal/UserInterface/SetupBrowser/Dxe/InputHandler.c b/EdkModulePkg/Universal/UserInterface/SetupBrowser/Dxe/InputHandler.c
new file mode 100644
index 0000000..d84fcf0
--- /dev/null
+++ b/EdkModulePkg/Universal/UserInterface/SetupBrowser/Dxe/InputHandler.c
@@ -0,0 +1,1580 @@
+/*++
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ InputHandler.C
+
+Abstract:
+
+ Implementation for handling user input from the User Interface
+
+Revision History
+
+--*/
+
+#include "Setup.h"
+#include "Ui.h"
+#include "Colors.h"
+
+#ifndef EFI_MAX
+#define EFI_MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b))
+#endif
+
+EFI_STATUS
+ReadString (
+ IN UI_MENU_OPTION *MenuOption,
+ OUT CHAR16 *StringPtr
+ )
+{
+ EFI_STATUS Status;
+ EFI_INPUT_KEY Key;
+ CHAR16 NullCharacter;
+ UINTN ScreenSize;
+ EFI_TAG *Tag;
+ CHAR16 Space[2];
+ CHAR16 KeyPad[2];
+ BOOLEAN SelectionComplete;
+ CHAR16 *TempString;
+ CHAR16 *BufferedString;
+ UINTN Index;
+ UINTN Count;
+ UINTN Start;
+ UINTN Top;
+ CHAR16 *PromptForDataString;
+ UINTN DimensionsWidth;
+ UINTN DimensionsHeight;
+ BOOLEAN CursorVisible;
+
+ DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
+ DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
+
+ PromptForDataString = GetToken (STRING_TOKEN (PROMPT_FOR_DATA), gHiiHandle);
+
+ NullCharacter = CHAR_NULL;
+ ScreenSize = GetStringWidth (PromptForDataString) / 2;
+ Tag = MenuOption->ThisTag;
+ Space[0] = L' ';
+ Space[1] = CHAR_NULL;
+ SelectionComplete = FALSE;
+
+ TempString = AllocateZeroPool (MenuOption->ThisTag->Maximum * 2);
+ ASSERT (TempString);
+
+ if (ScreenSize < (Tag->Maximum / (UINTN) 2)) {
+ ScreenSize = Tag->Maximum / 2;
+ }
+
+ if ((ScreenSize + 2) > DimensionsWidth) {
+ ScreenSize = DimensionsWidth - 2;
+ }
+
+ BufferedString = AllocateZeroPool (ScreenSize * 2);
+ ASSERT (BufferedString);
+
+ Start = (DimensionsWidth - ScreenSize - 2) / 2 + gScreenDimensions.LeftColumn + 1;
+ Top = ((DimensionsHeight - 6) / 2) + gScreenDimensions.TopRow - 1;
+
+ //
+ // Display prompt for string
+ //
+ CreatePopUp (ScreenSize, 4, &NullCharacter, PromptForDataString, Space, &NullCharacter);
+
+ gBS->FreePool (PromptForDataString);
+
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
+
+ CursorVisible = gST->ConOut->Mode->CursorVisible;
+ gST->ConOut->EnableCursor (gST->ConOut, TRUE);
+
+ do {
+ Status = WaitForKeyStroke (&Key);
+
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
+ switch (Key.UnicodeChar) {
+ case CHAR_NULL:
+ switch (Key.ScanCode) {
+ case SCAN_LEFT:
+ break;
+
+ case SCAN_RIGHT:
+ break;
+
+ case SCAN_ESC:
+ gBS->FreePool (TempString);
+ gBS->FreePool (BufferedString);
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
+ gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
+ return EFI_DEVICE_ERROR;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case CHAR_CARRIAGE_RETURN:
+ if (GetStringWidth (StringPtr) >= MenuOption->ThisTag->Minimum) {
+ SelectionComplete = TRUE;
+ gBS->FreePool (TempString);
+ gBS->FreePool (BufferedString);
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
+ gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
+ return EFI_SUCCESS;
+ } else {
+ ScreenSize = GetStringWidth (gMiniString) / 2;
+ CreatePopUp (ScreenSize, 4, &NullCharacter, gMiniString, gPressEnter, &NullCharacter);
+ //
+ // Simply create a popup to tell the user that they had typed in too few characters.
+ // To save code space, we can then treat this as an error and return back to the menu.
+ //
+ do {
+ Status = WaitForKeyStroke (&Key);
+ } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
+ gBS->FreePool (TempString);
+ gBS->FreePool (BufferedString);
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
+ gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
+ return EFI_DEVICE_ERROR;
+ }
+
+ break;
+
+ case CHAR_BACKSPACE:
+ if (StringPtr[0] != CHAR_NULL) {
+ for (Index = 0; StringPtr[Index] != CHAR_NULL; Index++) {
+ TempString[Index] = StringPtr[Index];
+ }
+ //
+ // Effectively truncate string by 1 character
+ //
+ TempString[Index - 1] = CHAR_NULL;
+ StrCpy (StringPtr, TempString);
+ }
+
+ default:
+ //
+ // If it is the beginning of the string, don't worry about checking maximum limits
+ //
+ if ((StringPtr[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
+ StrnCpy (StringPtr, &Key.UnicodeChar, 1);
+ StrnCpy (TempString, &Key.UnicodeChar, 1);
+ } else if ((GetStringWidth (StringPtr) < MenuOption->ThisTag->Maximum) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
+ KeyPad[0] = Key.UnicodeChar;
+ KeyPad[1] = CHAR_NULL;
+ StrCat (StringPtr, KeyPad);
+ StrCat (TempString, KeyPad);
+ }
+ //
+ // If the width of the input string is now larger than the screen, we nee to
+ // adjust the index to start printing portions of the string
+ //
+ SetUnicodeMem (BufferedString, ScreenSize - 1, L' ');
+
+ PrintStringAt (Start + 1, Top + 3, BufferedString);
+
+ if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {
+ Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;
+ } else {
+ Index = 0;
+ }
+
+ for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {
+ BufferedString[Count] = StringPtr[Index];
+ }
+
+ PrintStringAt (Start + 1, Top + 3, BufferedString);
+ break;
+ }
+
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
+ gST->ConOut->SetCursorPosition (gST->ConOut, Start + GetStringWidth (StringPtr) / 2, Top + 3);
+ } while (!SelectionComplete);
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
+ gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
+ return Status;
+}
+
+EFI_STATUS
+ReadPassword (
+ IN UI_MENU_OPTION *MenuOption,
+ IN BOOLEAN PromptForPassword,
+ IN EFI_TAG *Tag,
+ IN EFI_IFR_DATA_ARRAY *PageData,
+ IN BOOLEAN SecondEntry,
+ IN EFI_FILE_FORM_TAGS *FileFormTags,
+ OUT CHAR16 *StringPtr
+ )
+{
+ EFI_STATUS Status;
+ UINTN PasswordSize;
+ UINTN ScreenSize;
+ CHAR16 NullCharacter;
+ CHAR16 Space[2];
+ EFI_INPUT_KEY Key;
+ CHAR16 KeyPad[2];
+ UINTN Index;
+ UINTN Start;
+ UINTN Top;
+ CHAR16 *TempString;
+ CHAR16 *TempString2;
+ BOOLEAN Confirmation;
+ BOOLEAN ConfirmationComplete;
+ EFI_HII_CALLBACK_PACKET *Packet;
+ EFI_FORM_CALLBACK_PROTOCOL *FormCallback;
+ EFI_VARIABLE_DEFINITION *VariableDefinition;
+ UINTN DimensionsWidth;
+ UINTN DimensionsHeight;
+ EFI_IFR_DATA_ENTRY *DataEntry;
+
+ DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
+ DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
+
+ VariableDefinition = NULL;
+ PasswordSize = 0;
+ NullCharacter = CHAR_NULL;
+ Space[0] = L' ';
+ Space[1] = CHAR_NULL;
+ Confirmation = FALSE;
+ ConfirmationComplete = FALSE;
+ Status = EFI_SUCCESS;
+ FormCallback = NULL;
+ Packet = NULL;
+
+ //
+ // Remember that dynamic pages in an environment where all pages are not
+ // dynamic require us to call back to the user to give them an opportunity
+ // to register fresh information in the HII database so that we can extract it.
+ //
+ Status = gBS->HandleProtocol (
+ (VOID *) (UINTN) MenuOption->Tags[0].CallbackHandle,
+ &gEfiFormCallbackProtocolGuid,
+ (VOID **) &FormCallback
+ );
+
+ TempString = AllocateZeroPool (MenuOption->ThisTag->Maximum * 2);
+ TempString2 = AllocateZeroPool (MenuOption->ThisTag->Maximum * 2);
+
+ ASSERT (TempString);
+ ASSERT (TempString2);
+
+ if (Tag->Flags & EFI_IFR_FLAG_INTERACTIVE) {
+ //
+ // Password requires a callback to determine if a password exists
+ //
+ DataEntry = (EFI_IFR_DATA_ENTRY *) (PageData + 1);
+ DataEntry->OpCode = EFI_IFR_PASSWORD_OP;
+ DataEntry->Length = 3;
+
+ ExtractRequestedNvMap (FileFormTags, Tag->VariableNumber, &VariableDefinition);
+
+ //
+ // The user is about to be prompted with a password field, Data = 0 (Return Status determines the type of prompt)
+ //
+ DataEntry->Data = (VOID *) (UINTN) (UINT8) (0 + SecondEntry * 2);
+ PageData->NvRamMap = VariableDefinition->NvRamMap;
+
+ if ((FormCallback != NULL) && (FormCallback->Callback != NULL)) {
+ Status = FormCallback->Callback (
+ FormCallback,
+ Tag->Key,
+ PageData,
+ &Packet
+ );
+ }
+ //
+ // If error on return, continue with the reading of a typed in password to verify user knows password
+ // If no error, there is no password set, so prompt for new password
+ // if the previous callback was to verify the user knew password, and user typed it correctly - should return no error
+ //
+ if (!EFI_ERROR (Status)) {
+ PromptForPassword = FALSE;
+
+ //
+ // Simulate this as the second entry into this routine for an interactive behavior
+ //
+ SecondEntry = TRUE;
+ } else if (Status == EFI_NOT_READY) {
+Error:
+ if (Packet != NULL) {
+ //
+ // Upon error, we will likely receive a string to print out
+ // Display error popup
+ //
+ ScreenSize = EFI_MAX(GetStringWidth (Packet->String), GetStringWidth (gPressEnter)) / 2;
+ CreatePopUp (ScreenSize, 4, &NullCharacter, Packet->String, gPressEnter, &NullCharacter);
+ gBS->FreePool (Packet);
+
+ do {
+ Status = WaitForKeyStroke (&Key);
+ } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
+ }
+
+ gBS->FreePool (TempString);
+ gBS->FreePool (TempString2);
+ return EFI_NOT_READY;
+ }
+ }
+
+ do {
+ //
+ // Display PopUp Screen
+ //
+ ScreenSize = GetStringWidth (gPromptForNewPassword) / 2;
+ if (GetStringWidth (gConfirmPassword) / 2 > ScreenSize) {
+ ScreenSize = GetStringWidth (gConfirmPassword) / 2;
+ }
+
+ Start = (DimensionsWidth - ScreenSize - 4) / 2 + gScreenDimensions.LeftColumn + 2;
+ Top = ((DimensionsHeight - 6) / 2) + gScreenDimensions.TopRow - 1;
+
+ if (!Confirmation) {
+ if (PromptForPassword) {
+ CreatePopUp (ScreenSize, 4, &NullCharacter, gPromptForPassword, Space, &NullCharacter);
+ } else {
+ CreatePopUp (ScreenSize, 4, &NullCharacter, gPromptForNewPassword, Space, &NullCharacter);
+ }
+ } else {
+ CreatePopUp (ScreenSize, 4, &NullCharacter, gConfirmPassword, Space, &NullCharacter);
+ StringPtr[0] = CHAR_NULL;
+ }
+
+ do {
+ Status = WaitForKeyStroke (&Key);
+
+ switch (Key.UnicodeChar) {
+ case CHAR_NULL:
+ if (Key.ScanCode == SCAN_ESC) {
+ return EFI_NOT_READY;
+ }
+
+ ConfirmationComplete = FALSE;
+ break;
+
+ case CHAR_CARRIAGE_RETURN:
+ if (Tag->Flags & EFI_IFR_FLAG_INTERACTIVE) {
+ //
+ // User just typed a string in
+ //
+ DataEntry = (EFI_IFR_DATA_ENTRY *) (PageData + 1);
+ DataEntry->OpCode = EFI_IFR_PASSWORD_OP;
+
+ //
+ // If the user just typed in a password, Data = 1
+ // If the user just typed in a password to confirm the previous password, Data = 2
+ //
+ if (!Confirmation) {
+ DataEntry->Length = 3;
+ DataEntry->Data = (VOID *) (UINTN) (UINT8) (1 + SecondEntry * 2);
+
+ if ((FormCallback != NULL) && (FormCallback->Callback != NULL)) {
+ Status = FormCallback->Callback (
+ FormCallback,
+ Tag->Key,
+ PageData,
+ &Packet
+ );
+ }
+
+ DataEntry->Length = sizeof (EFI_IFR_DATA_ENTRY);
+ DataEntry->Data = (VOID *) TempString;
+ } else {
+ DataEntry->Length = 3;
+ DataEntry->Data = (VOID *) (UINTN) (UINT8) (2 + SecondEntry * 2);
+
+ if ((FormCallback != NULL) && (FormCallback->Callback != NULL)) {
+ Status = FormCallback->Callback (
+ FormCallback,
+ Tag->Key,
+ PageData,
+ &Packet
+ );
+ }
+
+ DataEntry->Length = sizeof (EFI_IFR_DATA_ENTRY);
+ DataEntry->Data = (VOID *) TempString2;
+ }
+
+ if ((FormCallback != NULL) && (FormCallback->Callback != NULL)) {
+ Status = FormCallback->Callback (
+ FormCallback,
+ Tag->Key,
+ PageData,
+ &Packet
+ );
+ }
+ //
+ // If this was the confirmation round of callbacks
+ // and an error comes back, display an error
+ //
+ if (Confirmation) {
+ if (EFI_ERROR (Status)) {
+ if (Packet->String == NULL) {
+ ScreenSize = EFI_MAX (GetStringWidth (gConfirmError), GetStringWidth (gPressEnter)) / 2;
+ CreatePopUp (ScreenSize, 4, &NullCharacter, gConfirmError, gPressEnter, &NullCharacter);
+ } else {
+ ScreenSize = EFI_MAX (GetStringWidth (Packet->String), GetStringWidth (gPressEnter)) / 2;
+ CreatePopUp (ScreenSize, 4, &NullCharacter, Packet->String, gPressEnter, &NullCharacter);
+ gBS->FreePool (Packet);
+ }
+
+ StringPtr[0] = CHAR_NULL;
+ do {
+ Status = WaitForKeyStroke (&Key);
+
+ if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
+ gBS->FreePool (TempString);
+ gBS->FreePool (TempString2);
+ return EFI_NOT_READY;
+ }
+ } while (1);
+ } else {
+ gBS->FreePool (TempString);
+ gBS->FreePool (TempString2);
+ return EFI_NOT_READY;
+ }
+ } else {
+ //
+ // User typed a string in and it wasn't valid somehow from the callback
+ // For instance, callback may have said that some invalid characters were contained in the string
+ //
+ if (Status == EFI_NOT_READY) {
+ goto Error;
+ }
+
+ if (PromptForPassword && EFI_ERROR (Status)) {
+ gBS->FreePool (TempString);
+ gBS->FreePool (TempString2);
+ return EFI_DEVICE_ERROR;
+ }
+ }
+ }
+
+ if (Confirmation) {
+ //
+ // Compare tempstring and tempstring2, if the same, return with StringPtr success
+ // Otherwise, kick and error box, and return an error
+ //
+ if (StrCmp (TempString, TempString2) == 0) {
+ gBS->FreePool (TempString);
+ gBS->FreePool (TempString2);
+ return EFI_SUCCESS;
+ } else {
+ ScreenSize = EFI_MAX (GetStringWidth (gConfirmError), GetStringWidth (gPressEnter)) / 2;
+ CreatePopUp (ScreenSize, 4, &NullCharacter, gConfirmError, gPressEnter, &NullCharacter);
+ StringPtr[0] = CHAR_NULL;
+ do {
+ Status = WaitForKeyStroke (&Key);
+ if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
+ gBS->FreePool (TempString);
+ gBS->FreePool (TempString2);
+ return EFI_DEVICE_ERROR;
+ }
+ } while (1);
+ }
+ }
+
+ if (PromptForPassword) {
+ //
+ // I was asked for a password, return it back in StringPtr
+ //
+ gBS->FreePool (TempString);
+ gBS->FreePool (TempString2);
+ return EFI_SUCCESS;
+ } else {
+ //
+ // If the two passwords were not the same kick an error popup
+ //
+ Confirmation = TRUE;
+ ConfirmationComplete = TRUE;
+ break;
+ }
+
+ case CHAR_BACKSPACE:
+ if (StringPtr[0] != CHAR_NULL) {
+ if (!Confirmation) {
+ for (Index = 0; StringPtr[Index] != CHAR_NULL; Index++) {
+ TempString[Index] = StringPtr[Index];
+ }
+ //
+ // Effectively truncate string by 1 character
+ //
+ TempString[Index - 1] = CHAR_NULL;
+ StrCpy (StringPtr, TempString);
+ } else {
+ for (Index = 0; StringPtr[Index] != CHAR_NULL; Index++) {
+ TempString2[Index] = StringPtr[Index];
+ }
+ //
+ // Effectively truncate string by 1 character
+ //
+ TempString2[Index - 1] = CHAR_NULL;
+ StrCpy (StringPtr, TempString2);
+ }
+
+ ConfirmationComplete = FALSE;
+ } else {
+ ConfirmationComplete = FALSE;
+ }
+
+ //
+ // Must be a character we are interested in!
+ //
+ default:
+ if ((StringPtr[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
+ if (!Confirmation) {
+ StrnCpy (StringPtr, &Key.UnicodeChar, 1);
+ StrnCpy (TempString, &Key.UnicodeChar, 1);
+ } else {
+ StrnCpy (StringPtr, &Key.UnicodeChar, 1);
+ StrnCpy (TempString2, &Key.UnicodeChar, 1);
+ ConfirmationComplete = FALSE;
+ }
+ } else if ((GetStringWidth (StringPtr) / 2 <= (UINTN) (MenuOption->ThisTag->Maximum - 1) / 2) &&
+ (Key.UnicodeChar != CHAR_BACKSPACE)
+ ) {
+ KeyPad[0] = Key.UnicodeChar;
+ KeyPad[1] = CHAR_NULL;
+ if (!Confirmation) {
+ StrCat (StringPtr, KeyPad);
+ StrCat (TempString, KeyPad);
+ } else {
+ StrCat (StringPtr, KeyPad);
+ StrCat (TempString2, KeyPad);
+ }
+ }
+
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
+ for (Index = 1; Index < ScreenSize; Index++) {
+ PrintCharAt (Start + Index, Top + 3, L' ');
+ }
+
+ gST->ConOut->SetCursorPosition (
+ gST->ConOut,
+ (DimensionsWidth - GetStringWidth (StringPtr) / 2) / 2 + gScreenDimensions.LeftColumn,
+ Top + 3
+ );
+ for (Index = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++) {
+ PrintChar (L'*');
+ }
+
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
+ break;
+ }
+ //
+ // end switch
+ //
+ } while (!ConfirmationComplete);
+
+ } while (1);
+ gBS->FreePool (TempString);
+ gBS->FreePool (TempString2);
+ return Status;
+}
+
+VOID
+EncodePassword (
+ IN CHAR16 *Password,
+ IN UINT8 MaxSize
+ )
+{
+ UINTN Index;
+ UINTN Loop;
+ CHAR16 *Buffer;
+ CHAR16 *Key;
+
+ Key = (CHAR16 *) L"MAR10648567";
+ Buffer = AllocateZeroPool (MaxSize);
+
+ ASSERT (Buffer);
+
+ for (Index = 0; Key[Index] != 0; Index++) {
+ for (Loop = 0; Loop < (UINT8) (MaxSize / 2); Loop++) {
+ Buffer[Loop] = (CHAR16) (Password[Loop] ^ Key[Index]);
+ }
+ }
+
+ CopyMem (Password, Buffer, MaxSize);
+
+ gBS->FreePool (Buffer);
+ return ;
+}
+
+EFI_STATUS
+GetNumericInput (
+ IN UI_MENU_OPTION *MenuOption,
+ IN EFI_FILE_FORM_TAGS *FileFormTagsHead,
+ IN BOOLEAN ManualInput,
+ IN EFI_TAG *Tag,
+ IN UINTN NumericType,
+ OUT UINT16 *Value
+ )
+/*++
+
+Routine Description:
+
+ This routine reads a numeric value from the user input.
+
+Arguments:
+
+ MenuOption - Pointer to the current input menu.
+
+ FileFormTagsHead - Pointer to the root of formset.
+
+ ManualInput - If the input is manual or not.
+
+ Tag - Pointer to all the attributes and values associated with a tag.
+
+ Value - Pointer to the numeric value that is going to be read.
+
+Returns:
+
+ EFI_SUCCESS - If numerical input is read successfully
+ EFI_DEVICE_ERROR - If operation fails
+
+--*/
+{
+ EFI_INPUT_KEY Key;
+ BOOLEAN SelectionComplete;
+ UINTN Column;
+ UINTN Row;
+ CHAR16 FormattedNumber[6];
+ UINTN PreviousNumber[6];
+ INTN Number;
+ UINTN Count;
+ UINT16 BackupValue;
+ STRING_REF PopUp;
+ CHAR16 NullCharacter;
+ CHAR16 *StringPtr;
+ EFI_FILE_FORM_TAGS *FileFormTags;
+ EFI_STATUS Status;
+ EFI_VARIABLE_DEFINITION *VariableDefinition;
+ UINTN Loop;
+
+ NullCharacter = CHAR_NULL;
+ StringPtr = NULL;
+ Column = MenuOption->OptCol;
+ Row = MenuOption->Row;
+ Number = 0;
+ PreviousNumber[0] = 0;
+ Count = 0;
+ SelectionComplete = FALSE;
+ BackupValue = Tag->Value;
+ FileFormTags = FileFormTagsHead;
+
+ if (ManualInput) {
+ PrintAt (Column, Row, (CHAR16 *) L"[ ]");
+ Column++;
+ if (Tag->Operand != EFI_IFR_TIME_OP) {
+ *Value = BackupValue;
+ }
+ }
+ //
+ // First time we enter this handler, we need to check to see if
+ // we were passed an increment or decrement directive
+ //
+ do {
+ Key.UnicodeChar = CHAR_NULL;
+ if (gDirection != 0) {
+ Key.ScanCode = gDirection;
+ gDirection = 0;
+ goto TheKey2;
+ }
+
+ Status = WaitForKeyStroke (&Key);
+
+TheKey2:
+ switch (Key.UnicodeChar) {
+ case '+':
+ case '-':
+ if ((Tag->Operand == EFI_IFR_DATE_OP) || (Tag->Operand == EFI_IFR_TIME_OP)) {
+ Key.UnicodeChar = CHAR_NULL;
+ if (Key.UnicodeChar == '+') {
+ Key.ScanCode = SCAN_RIGHT;
+ } else {
+ Key.ScanCode = SCAN_LEFT;
+ }
+
+ goto TheKey2;
+ }
+ break;
+
+ case CHAR_NULL:
+ switch (Key.ScanCode) {
+ case SCAN_LEFT:
+ case SCAN_RIGHT:
+ if ((Tag->Operand == EFI_IFR_DATE_OP) || (Tag->Operand == EFI_IFR_TIME_OP)) {
+ //
+ // By setting this value, we will return back to the caller.
+ // We need to do this since an auto-refresh will destroy the adjustment
+ // based on what the real-time-clock is showing. So we always commit
+ // upon changing the value.
+ //
+ gDirection = SCAN_DOWN;
+ }
+
+ if (!ManualInput) {
+ Tag->Value = *Value;
+ if (Key.ScanCode == SCAN_LEFT) {
+ Number = *Value - Tag->Step;
+ if (Number < Tag->Minimum) {
+ Number = Tag->Minimum;
+ }
+ } else if (Key.ScanCode == SCAN_RIGHT) {
+ Number = *Value + Tag->Step;
+ if (Number > Tag->Maximum) {
+ Number = Tag->Maximum;
+ }
+ }
+
+ Tag->Value = (UINT16) Number;
+ *Value = (UINT16) Number;
+ UnicodeValueToString (
+ FormattedNumber,
+ FALSE,
+ (UINTN) Number,
+ (sizeof (FormattedNumber) / sizeof (FormattedNumber[0]))
+ );
+ Number = (UINT16) GetStringWidth (FormattedNumber);
+
+ gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
+ if ((Tag->Operand == EFI_IFR_DATE_OP) || (Tag->Operand == EFI_IFR_TIME_OP)) {
+ for (Loop = 0; Loop < (UINTN) ((Number >= 8) ? 4 : 2); Loop++) {
+ PrintAt (MenuOption->OptCol + Loop, MenuOption->Row, (CHAR16 *) L" ");
+ }
+ } else {
+ for (Loop = 0; Loop < gOptionBlockWidth; Loop++) {
+ PrintAt (MenuOption->OptCol + Loop, MenuOption->Row, (CHAR16 *) L" ");
+ }
+ }
+
+ gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT);
+
+ if ((MenuOption->Col + gPromptBlockWidth + 1) == MenuOption->OptCol) {
+ PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);
+ Column = MenuOption->OptCol + 1;
+ }
+ //
+ // If Number looks like "3", convert it to "03/"
+ //
+ if (Number == 4 && (NumericType == DATE_NUMERIC)) {
+ FormattedNumber[3] = FormattedNumber[1];
+ FormattedNumber[2] = DATE_SEPARATOR;
+ FormattedNumber[1] = FormattedNumber[0];
+ FormattedNumber[0] = L'0';
+ Number = 8;
+ }
+ //
+ // If Number looks like "13", convert it to "13/"
+ //
+ if (Number == 6 && (NumericType == DATE_NUMERIC)) {
+ FormattedNumber[3] = FormattedNumber[2];
+ FormattedNumber[2] = DATE_SEPARATOR;
+ Number = 8;
+ }
+
+ if (Number == 4 &&
+ (NumericType == TIME_NUMERIC) &&
+ (MenuOption->Col + gPromptBlockWidth + 8) != MenuOption->OptCol
+ ) {
+ FormattedNumber[3] = FormattedNumber[1];
+ FormattedNumber[2] = TIME_SEPARATOR;
+ FormattedNumber[1] = FormattedNumber[0];
+ FormattedNumber[0] = L'0';
+ Number = 8;
+ }
+
+ if (Number == 4 &&
+ (NumericType == TIME_NUMERIC) &&
+ (MenuOption->Col + gPromptBlockWidth + 8) == MenuOption->OptCol
+ ) {
+ FormattedNumber[3] = FormattedNumber[1];
+ FormattedNumber[2] = RIGHT_NUMERIC_DELIMITER;
+ FormattedNumber[1] = FormattedNumber[0];
+ FormattedNumber[0] = L'0';
+ Number = 8;
+ }
+
+ PrintStringAt (Column, Row, FormattedNumber);
+ if (Number == 10 && (NumericType == DATE_NUMERIC)) {
+ PrintChar (RIGHT_NUMERIC_DELIMITER);
+ }
+
+ if (NumericType == REGULAR_NUMERIC) {
+ PrintChar (RIGHT_NUMERIC_DELIMITER);
+ }
+ }
+ break;
+
+ case SCAN_UP:
+ case SCAN_DOWN:
+ goto EnterCarriageReturn;
+
+ case SCAN_ESC:
+ return EFI_DEVICE_ERROR;
+
+ default:
+ break;
+ }
+
+ break;
+
+EnterCarriageReturn:
+
+ case CHAR_CARRIAGE_RETURN:
+ //
+ // Check to see if the Value is something reasonable against consistency limitations.
+ // If not, let's kick the error specified.
+ //
+ //
+ // This gives us visibility to the FileFormTags->NvRamMap to check things
+ // ActiveIfr is a global maintained by the menuing code to ensure that we
+ // are pointing to the correct formset's file data.
+ //
+ for (Count = 0; Count < gActiveIfr; Count++) {
+ FileFormTags = FileFormTags->NextFile;
+ }
+
+ ExtractRequestedNvMap (FileFormTags, Tag->VariableNumber, &VariableDefinition);
+
+ CopyMem (&VariableDefinition->NvRamMap[Tag->StorageStart], &Tag->Value, Tag->StorageWidth);
+
+ //
+ // Data associated with a NULL device (in the fake NV storage)
+ //
+ if (Tag->StorageWidth == (UINT16) 0) {
+ CopyMem (&VariableDefinition->FakeNvRamMap[Tag->StorageStart], &Tag->Value, 2);
+ }
+ //
+ // If a late check is required save off the information. This is used when consistency checks
+ // are required, but certain values might be bound by an impossible consistency check such as
+ // if two questions are bound by consistency checks and each only has two possible choices, there
+ // would be no way for a user to switch the values. Thus we require late checking.
+ //
+ if (Tag->Flags & EFI_IFR_FLAG_LATE_CHECK) {
+ CopyMem (&Tag->OldValue, &BackupValue, Tag->StorageWidth);
+ } else {
+ //
+ // In theory, passing the value and the Id are sufficient to determine what needs
+ // to be done. The Id is the key to look for the entry needed in the Inconsistency
+ // database. That will yields operand and ID data - and since the ID's correspond
+ // to the NV storage, we can determine the values for other IDs there.
+ //
+ if (ValueIsNotValid (TRUE, 0, Tag, FileFormTags, &PopUp)) {
+ if (PopUp == 0x0000) {
+ SelectionComplete = TRUE;
+ break;
+ }
+
+ StringPtr = GetToken (PopUp, MenuOption->Handle);
+
+ CreatePopUp (GetStringWidth (StringPtr) / 2, 3, &NullCharacter, StringPtr, &NullCharacter);
+
+ do {
+ Status = WaitForKeyStroke (&Key);
+
+ switch (Key.UnicodeChar) {
+
+ case CHAR_CARRIAGE_RETURN:
+ SelectionComplete = TRUE;
+ gBS->FreePool (StringPtr);
+ break;
+
+ default:
+ break;
+ }
+ } while (!SelectionComplete);
+
+ Tag->Value = BackupValue;
+ *Value = BackupValue;
+
+ CopyMem (&VariableDefinition->NvRamMap[Tag->StorageStart], &Tag->Value, Tag->StorageWidth);
+
+ //
+ // Data associated with a NULL device (in the fake NV storage)
+ //
+ if (Tag->StorageWidth == (UINT16) 0) {
+ CopyMem (&VariableDefinition->FakeNvRamMap[Tag->StorageStart], &Tag->Value, 2);
+ }
+
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ return EFI_SUCCESS;
+ break;
+
+ case CHAR_BACKSPACE:
+ if (ManualInput) {
+ if (Count == 0) {
+ break;
+ }
+ //
+ // Remove a character
+ //
+ Number = PreviousNumber[Count - 1];
+ *Value = (UINT16) Number;
+ UpdateStatusBar (INPUT_ERROR, Tag->Flags, FALSE);
+ Count--;
+ Column--;
+ PrintAt (Column, Row, (CHAR16 *) L" ");
+ }
+ break;
+
+ default:
+ if (ManualInput) {
+ if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {
+ UpdateStatusBar (INPUT_ERROR, Tag->Flags, TRUE);
+ break;
+ }
+ //
+ // If Count 0-4 is complete, there is no way more is valid
+ //
+ if (Count > 4) {
+ break;
+ }
+ //
+ // Someone typed something valid!
+ //
+ if (Count != 0) {
+ Number = Number * 10 + (Key.UnicodeChar - L'0');
+ } else {
+ Number = Key.UnicodeChar - L'0';
+ }
+
+ if (Number > Tag->Maximum) {
+ UpdateStatusBar (INPUT_ERROR, Tag->Flags, TRUE);
+ Number = PreviousNumber[Count];
+ break;
+ } else {
+ UpdateStatusBar (INPUT_ERROR, Tag->Flags, FALSE);
+ }
+
+ Count++;
+
+ PreviousNumber[Count] = Number;
+ *Value = (UINT16) Number;
+ Tag->Value = (UINT16) Number;
+
+ PrintCharAt (Column, Row, Key.UnicodeChar);
+ Column++;
+ }
+ break;
+ }
+ } while (!SelectionComplete);
+ return EFI_SUCCESS;
+}
+//
+// Notice that this is at least needed for the ordered list manipulation.
+// Left/Right doesn't make sense for this op-code
+//
+EFI_STATUS
+GetSelectionInputPopUp (
+ IN UI_MENU_OPTION *MenuOption,
+ IN EFI_TAG *Tag,
+ IN UINTN ValueCount,
+ OUT UINT16 *Value,
+ OUT UINT16 *KeyValue
+ )
+{
+ EFI_STATUS Status;
+ EFI_INPUT_KEY Key;
+ UINTN Index;
+ UINTN TempIndex;
+ CHAR16 *StringPtr;
+ CHAR16 *TempStringPtr;
+ UINT16 Token;
+ UINTN Index2;
+ UINTN TopOptionIndex;
+ UINTN HighlightPosition;
+ UINTN Start;
+ UINTN End;
+ UINTN Top;
+ UINTN Bottom;
+ UINT16 TempValue;
+ UINTN Count;
+ UINTN PopUpMenuLines;
+ UINTN MenuLinesInView;
+ UINTN PopUpWidth;
+ CHAR16 Character;
+ UINTN FirstOption;
+ BOOLEAN FirstOptionFoundFlag;
+ INT32 SavedAttribute;
+ EFI_TAG TagBackup;
+ UINT8 *ValueArray;
+ UINT8 *ValueArrayBackup;
+ UINT8 ValueBackup;
+ BOOLEAN Initialized;
+ BOOLEAN KeyInitialized;
+ BOOLEAN ShowDownArrow;
+ BOOLEAN ShowUpArrow;
+ UINTN DimensionsWidth;
+ UINTN DimensionsHeight;
+
+ DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
+ DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
+
+ TempValue = 0;
+ TempIndex = 0;
+ ValueArray = (UINT8 *) Value;
+ ValueArrayBackup = NULL;
+ Initialized = FALSE;
+ KeyInitialized = FALSE;
+ ShowDownArrow = FALSE;
+ ShowUpArrow = FALSE;
+
+ if (Tag->Operand == EFI_IFR_ORDERED_LIST_OP) {
+ ValueArrayBackup = AllocateZeroPool (Tag->StorageWidth);
+ ASSERT (ValueArrayBackup != NULL);
+ CopyMem (ValueArrayBackup, ValueArray, ValueCount);
+ TempValue = *(UINT8 *) (ValueArray);
+ if (ValueArray[0] != 0x00) {
+ Initialized = TRUE;
+ }
+
+ for (Index = 0; ValueArray[Index] != 0x00; Index++)
+ ;
+ ValueCount = Index;
+ } else {
+ TempValue = *Value;
+ }
+
+ Count = 0;
+ PopUpWidth = 0;
+
+ FirstOption = MenuOption->TagIndex;
+ FirstOptionFoundFlag = FALSE;
+
+ StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);
+ ASSERT (StringPtr);
+
+ //
+ // Initialization for "One of" pop-up menu
+ //
+ //
+ // Get the number of one of options present and its size
+ //
+ for (Index = MenuOption->TagIndex; MenuOption->Tags[Index].Operand != EFI_IFR_END_ONE_OF_OP; Index++) {
+ if (MenuOption->Tags[Index].Operand == EFI_IFR_ONE_OF_OPTION_OP &&
+ !MenuOption->Tags[Index].Suppress) {
+ if (!FirstOptionFoundFlag) {
+ FirstOption = Index;
+ FirstOptionFoundFlag = TRUE;
+ }
+
+ Count++;
+ Token = MenuOption->Tags[Index].Text;
+
+ //
+ // If this is an ordered list that is initialized
+ //
+ if (Initialized) {
+ for (ValueBackup = (UINT8) MenuOption->TagIndex;
+ MenuOption->Tags[ValueBackup].Operand != EFI_IFR_END_OP;
+ ValueBackup++
+ ) {
+ if (MenuOption->Tags[ValueBackup].Value == ((UINT8 *) ValueArrayBackup)[Index - MenuOption->TagIndex - 1]) {
+ StringPtr = GetToken (MenuOption->Tags[ValueBackup].Text, MenuOption->Handle);
+ break;
+ }
+ }
+ } else {
+ StringPtr = GetToken (Token, MenuOption->Handle);
+ }
+
+ if (StrLen (StringPtr) > PopUpWidth) {
+ PopUpWidth = StrLen (StringPtr);
+ }
+
+ gBS->FreePool (StringPtr);
+ }
+ }
+ //
+ // Perform popup menu initialization.
+ //
+ PopUpMenuLines = Count;
+ PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;
+
+ SavedAttribute = gST->ConOut->Mode->Attribute;
+ gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
+
+ if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {
+ PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;
+ }
+
+ Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gScreenDimensions.LeftColumn;
+ End = Start + PopUpWidth + POPUP_FRAME_WIDTH;
+ Top = gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT;
+ Bottom = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT;
+
+ MenuLinesInView = Bottom - Top - 1;
+ if (MenuLinesInView >= PopUpMenuLines) {
+ Top = Top + (MenuLinesInView - PopUpMenuLines) / 2;
+ Bottom = Top + PopUpMenuLines + 1;
+ } else {
+ TempValue = MenuOption->Tags[MenuOption->TagIndex + 1].Value;
+ ShowDownArrow = TRUE;
+ }
+
+ TopOptionIndex = 1;
+ HighlightPosition = 0;
+ do {
+ if (Initialized) {
+ for (Index = MenuOption->TagIndex, Index2 = 0; Index2 < ValueCount; Index++, Index2++) {
+ //
+ // Set the value for the item we are looking for
+ //
+ Count = ValueArrayBackup[Index2];
+
+ //
+ // If we hit the end of the Array, we are complete
+ //
+ if (Count == 0) {
+ break;
+ }
+
+ if (MenuOption->Tags[Index].Operand == EFI_IFR_ONE_OF_OPTION_OP) {
+ for (ValueBackup = (UINT8) MenuOption->TagIndex;
+ MenuOption->Tags[ValueBackup].Operand != EFI_IFR_END_ONE_OF_OP;
+ ValueBackup++
+ ) {
+ //
+ // We just found what we are looking for
+ //
+ if (MenuOption->Tags[ValueBackup].Value == Count) {
+ //
+ // As long as the two indexes aren't the same, we have
+ // two different op-codes we need to swap internally
+ //
+ if (Index != ValueBackup) {
+ //
+ // Backup destination tag, then copy source to destination, then copy backup to source location
+ //
+ CopyMem (&TagBackup, &MenuOption->Tags[Index], sizeof (EFI_TAG));
+ CopyMem (&MenuOption->Tags[Index], &MenuOption->Tags[ValueBackup], sizeof (EFI_TAG));
+ CopyMem (&MenuOption->Tags[ValueBackup], &TagBackup, sizeof (EFI_TAG));
+ } else {
+ //
+ // If the indexes are the same, then the op-code is where he belongs
+ //
+ }
+ }
+ }
+ } else {
+ //
+ // Since this wasn't an option op-code (likely the ordered list op-code) decerement Index2
+ //
+ Index2--;
+ }
+ }
+ }
+ //
+ // Clear that portion of the screen
+ //
+ ClearLines (Start, End, Top, Bottom, POPUP_TEXT | POPUP_BACKGROUND);
+
+ //
+ // Draw "One of" pop-up menu
+ //
+ Character = (CHAR16) BOXDRAW_DOWN_RIGHT;
+ PrintCharAt (Start, Top, Character);
+ for (Index = Start; Index + 2 < End; Index++) {
+ if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {
+ Character = (CHAR16) GEOMETRICSHAPE_UP_TRIANGLE;
+ } else {
+ Character = (CHAR16) BOXDRAW_HORIZONTAL;
+ }
+
+ PrintChar (Character);
+ }
+
+ Character = (CHAR16) BOXDRAW_DOWN_LEFT;
+ PrintChar (Character);
+ Character = (CHAR16) BOXDRAW_VERTICAL;
+ for (Index = Top + 1; Index < Bottom; Index++) {
+ PrintCharAt (Start, Index, Character);
+ PrintCharAt (End - 1, Index, Character);
+ }
+ //
+ // Display the One of options
+ //
+ Index2 = Top + 1;
+ for (Index = MenuOption->TagIndex + TopOptionIndex;
+ (MenuOption->Tags[Index].Operand != EFI_IFR_END_ONE_OF_OP) && (Index2 < Bottom);
+ Index++
+ ) {
+ if (MenuOption->Tags[Index].Operand == EFI_IFR_ONE_OF_OPTION_OP) {
+ Token = MenuOption->Tags[Index].Text;
+ if (Initialized) {
+ for (ValueBackup = (UINT8) MenuOption->TagIndex;
+ MenuOption->Tags[ValueBackup].Operand != EFI_IFR_END_ONE_OF_OP;
+ ValueBackup++
+ ) {
+ if (MenuOption->Tags[ValueBackup].Value == ((UINT8 *) ValueArrayBackup)[Index - MenuOption->TagIndex - 1]) {
+ StringPtr = GetToken (MenuOption->Tags[ValueBackup].Text, MenuOption->Handle);
+ break;
+ }
+ }
+ } else {
+ ValueBackup = (UINT8) Index;
+ StringPtr = GetToken (Token, MenuOption->Handle);
+ }
+ //
+ // If the string occupies multiple lines, truncate it to fit in one line,
+ // and append a "..." for indication.
+ //
+ if (StrLen (StringPtr) > (PopUpWidth - 1)) {
+ TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));
+ CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));
+ gBS->FreePool (StringPtr);
+ StringPtr = TempStringPtr;
+ StrCat (StringPtr, (CHAR16 *) L"...");
+ }
+ //
+ // Code to display the text should go here. Follwed by the [*]
+ //
+ if (MenuOption->Tags[ValueBackup].Suppress == TRUE) {
+ //
+ // Don't show the one, so decrease the Index2 for balance
+ //
+ Index2--;
+ } else if (MenuOption->Tags[ValueBackup].GrayOut == TRUE) {
+ //
+ // Gray Out the one
+ //
+ gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | POPUP_BACKGROUND);
+ PrintStringAt (Start + 2, Index2, StringPtr);
+ gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
+ } else if (MenuOption->Tags[ValueBackup].Value == TempValue) {
+ //
+ // Highlight the selected one
+ //
+ gST->ConOut->SetAttribute (gST->ConOut, PICKLIST_HIGHLIGHT_TEXT | PICKLIST_HIGHLIGHT_BACKGROUND);
+ PrintStringAt (Start + 2, Index2, StringPtr);
+ gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
+ HighlightPosition = Index2;
+ } else {
+ gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
+ PrintStringAt (Start + 2, Index2, StringPtr);
+ }
+
+ gBS->FreePool (StringPtr);
+ Index2 = Index2 + 1;
+ }
+ }
+
+ Character = (CHAR16) BOXDRAW_UP_RIGHT;
+ PrintCharAt (Start, Bottom, Character);
+ for (Index = Start; Index + 2 < End; Index++) {
+ if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {
+ Character = (CHAR16) GEOMETRICSHAPE_DOWN_TRIANGLE;
+ } else {
+ Character = (CHAR16) BOXDRAW_HORIZONTAL;
+ }
+
+ PrintChar (Character);
+ }
+
+ Character = (CHAR16) BOXDRAW_UP_LEFT;
+ PrintChar (Character);
+ //
+ // Get User selection and change TempValue if necessary
+ //
+ //
+ // Stop: One of pop-up menu
+ //
+ Key.UnicodeChar = CHAR_NULL;
+ if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {
+ Key.ScanCode = gDirection;
+ gDirection = 0;
+ goto TheKey;
+ }
+
+ if (!KeyInitialized) {
+ if (MenuOption->ThisTag->Operand == EFI_IFR_ONE_OF_OP) {
+ *KeyValue = MenuOption->Tags[MenuOption->TagIndex + 1].Key;
+ } else {
+ *KeyValue = MenuOption->ThisTag->Key;
+ }
+
+ KeyInitialized = TRUE;
+ }
+
+ Status = WaitForKeyStroke (&Key);
+
+TheKey:
+ switch (Key.UnicodeChar) {
+ case '+':
+ case '-':
+ //
+ // If an ordered list op-code, we will allow for a popup of +/- keys
+ // to create an ordered list of items
+ //
+ if (Tag->Operand == EFI_IFR_ORDERED_LIST_OP) {
+ if (Key.UnicodeChar == '+') {
+ if ((TopOptionIndex > 1) && (HighlightPosition == (Top + 1))) {
+ //
+ // Highlight reaches the top of the popup window, scroll one menu item.
+ //
+ TopOptionIndex--;
+ ShowDownArrow = TRUE;
+ }
+
+ if (TopOptionIndex == 1) {
+ ShowUpArrow = FALSE;
+ }
+ } else {
+ if (((TopOptionIndex + MenuLinesInView) <= PopUpMenuLines) && (HighlightPosition == (Bottom - 1))) {
+ //
+ // Highlight reaches the bottom of the popup window, scroll one menu item.
+ //
+ TopOptionIndex++;
+ ShowUpArrow = TRUE;
+ }
+
+ if ((TopOptionIndex + MenuLinesInView) == (PopUpMenuLines + 1)) {
+ ShowDownArrow = FALSE;
+ }
+ }
+
+ for (Index = MenuOption->TagIndex + TopOptionIndex;
+ MenuOption->Tags[Index].Operand != EFI_IFR_END_ONE_OF_OP;
+ Index++
+ ) {
+ if (MenuOption->Tags[Index].Operand == EFI_IFR_ORDERED_LIST_OP) {
+ continue;
+ }
+
+ if (Key.UnicodeChar == '+') {
+ TempIndex = Index - 1;
+ } else {
+ TempIndex = Index + 1;
+ }
+ //
+ // Is this the current tag we are on?
+ //
+ if (MenuOption->Tags[Index].Value == TempValue) {
+ //
+ // Is this prior tag a valid choice? If not, bail out
+ //
+ if (MenuOption->Tags[TempIndex].Operand == EFI_IFR_ONE_OF_OPTION_OP) {
+ //
+ // Copy the destination tag to the local variable
+ //
+ CopyMem (&TagBackup, &MenuOption->Tags[TempIndex], sizeof (EFI_TAG));
+ //
+ // Copy the current tag to the tag location before us
+ //
+ CopyMem (&MenuOption->Tags[TempIndex], &MenuOption->Tags[Index], sizeof (EFI_TAG));
+ //
+ // Copy the backed up tag to the current location
+ //
+ CopyMem (&MenuOption->Tags[Index], &TagBackup, sizeof (EFI_TAG));
+
+ //
+ // Adjust the array of values
+ //
+ for (Index = 0; Index < ValueCount; Index++) {
+ if (ValueArrayBackup[Index] == (UINT8) TempValue) {
+ if (Key.UnicodeChar == '+') {
+ if (Index == 0) {
+ //
+ // It is the top of the array already
+ //
+ break;
+ }
+
+ TempIndex = Index - 1;
+ } else {
+ if ((Index + 1) == ValueCount) {
+ //
+ // It is the bottom of the array already
+ //
+ break;
+ }
+
+ TempIndex = Index + 1;
+ }
+
+ ValueBackup = ValueArrayBackup[TempIndex];
+ ValueArrayBackup[TempIndex] = ValueArrayBackup[Index];
+ ValueArrayBackup[Index] = ValueBackup;
+ Initialized = TRUE;
+ break;
+ }
+ }
+ break;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ case CHAR_NULL:
+ switch (Key.ScanCode) {
+ case SCAN_UP:
+ case SCAN_DOWN:
+ if (Key.ScanCode == SCAN_UP) {
+ if ((TopOptionIndex > 1) && (HighlightPosition == (Top + 1))) {
+ //
+ // Highlight reaches the top of the popup window, scroll one menu item.
+ //
+ TopOptionIndex--;
+ ShowDownArrow = TRUE;
+ }
+
+ if (TopOptionIndex == 1) {
+ ShowUpArrow = FALSE;
+ }
+ } else {
+ if (((TopOptionIndex + MenuLinesInView) <= PopUpMenuLines) && (HighlightPosition == (Bottom - 1))) {
+ //
+ // Highlight reaches the bottom of the popup window, scroll one menu item.
+ //
+ TopOptionIndex++;
+ ShowUpArrow = TRUE;
+ }
+
+ if ((TopOptionIndex + MenuLinesInView) == (PopUpMenuLines + 1)) {
+ ShowDownArrow = FALSE;
+ }
+ }
+
+ for (Index = MenuOption->TagIndex + TopOptionIndex;
+ MenuOption->Tags[Index].Operand != EFI_IFR_END_ONE_OF_OP;
+ Index++
+ ) {
+ if (MenuOption->Tags[Index].Operand == EFI_IFR_ONE_OF_OPTION_OP) {
+ if (Initialized) {
+ for (Index = 0; (ValueArrayBackup[Index] != TempValue) && (Index < ValueCount); Index++)
+ ;
+
+ //
+ // Did we hit the end of the array? Either get the first TempValue or the next one
+ //
+ if (Key.ScanCode == SCAN_UP) {
+ if (Index == 0) {
+ TempValue = ValueArrayBackup[0];
+ } else {
+ TempValue = ValueArrayBackup[Index - 1];
+ }
+ } else {
+ if ((Index + 1) == ValueCount) {
+ TempValue = ValueArrayBackup[Index];
+ } else {
+ TempValue = ValueArrayBackup[Index + 1];
+ }
+ }
+ break;
+ } else {
+ if (Key.ScanCode == SCAN_UP) {
+ TempIndex = Index - 1;
+
+ //
+ // Keep going until meets meaningful tag.
+ //
+ while ((MenuOption->Tags[TempIndex].Operand != EFI_IFR_ONE_OF_OPTION_OP &&
+ MenuOption->Tags[TempIndex].Operand != EFI_IFR_ONE_OF_OP &&
+ MenuOption->Tags[TempIndex].Operand != EFI_IFR_END_ONE_OF_OP)
+ ||
+ (MenuOption->Tags[TempIndex].Operand == EFI_IFR_ONE_OF_OPTION_OP &&
+ (MenuOption->Tags[TempIndex].Suppress || MenuOption->Tags[TempIndex].GrayOut))) {
+ TempIndex--;
+ }
+ } else {
+ TempIndex = Index + 1;
+
+ //
+ // Keep going until meets meaningful tag.
+ //
+ while ((MenuOption->Tags[TempIndex].Operand != EFI_IFR_ONE_OF_OPTION_OP &&
+ MenuOption->Tags[TempIndex].Operand != EFI_IFR_ONE_OF_OP &&
+ MenuOption->Tags[TempIndex].Operand != EFI_IFR_END_ONE_OF_OP)
+ ||
+ (MenuOption->Tags[TempIndex].Operand == EFI_IFR_ONE_OF_OPTION_OP &&
+ (MenuOption->Tags[TempIndex].Suppress || MenuOption->Tags[TempIndex].GrayOut))) {
+ TempIndex++;
+ }
+ }
+ //
+ // The option value is the same as what is stored in NV store. This is where we take action
+ //
+ if (MenuOption->Tags[Index].Value == TempValue) {
+ //
+ // Only if the previous op-code is an option can we select it, otherwise we are at the left-most option
+ //
+ if (MenuOption->Tags[TempIndex].Operand == EFI_IFR_ONE_OF_OPTION_OP) {
+ TempValue = MenuOption->Tags[TempIndex].Value;
+ *KeyValue = MenuOption->Tags[TempIndex].Key;
+ } else {
+ TempValue = MenuOption->Tags[Index].Value;
+ *KeyValue = MenuOption->Tags[Index].Key;
+ }
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ case SCAN_ESC:
+ gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
+ if (ValueArrayBackup != NULL) {
+ gBS->FreePool (ValueArrayBackup);
+ }
+
+ return EFI_DEVICE_ERROR;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case CHAR_CARRIAGE_RETURN:
+ //
+ // return the current selection
+ //
+ if (Tag->Operand == EFI_IFR_ORDERED_LIST_OP) {
+ CopyMem (ValueArray, ValueArrayBackup, ValueCount);
+ gBS->FreePool (ValueArrayBackup);
+ } else {
+ *Value = TempValue;
+ }
+
+ gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
+ return EFI_SUCCESS;
+
+ default:
+ break;
+ }
+ } while (1);
+
+ gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+WaitForKeyStroke (
+ OUT EFI_INPUT_KEY *Key
+ )
+{
+ EFI_STATUS Status;
+
+ do {
+ UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0);
+ Status = gST->ConIn->ReadKeyStroke (gST->ConIn, Key);
+ } while (EFI_ERROR(Status));
+
+ return Status;
+}