mirror of https://github.com/acidanthera/audk.git
1573 lines
52 KiB
C
1573 lines
52 KiB
C
/*++
|
|
|
|
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 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;
|
|
UINTN WidthOfString;
|
|
|
|
DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
|
|
DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
|
|
|
|
VariableDefinition = NULL;
|
|
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
|
|
//
|
|
WidthOfString = GetStringWidth (Packet->String);
|
|
ScreenSize = EFI_MAX(WidthOfString, GetStringWidth (gPressEnter)) / 2;
|
|
CreatePopUp (ScreenSize, 4, &NullCharacter, Packet->String, gPressEnter, &NullCharacter);
|
|
gBS->FreePool (Packet);
|
|
|
|
do {
|
|
Status = WaitForKeyStroke (&Key);
|
|
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
|
|
}
|
|
|
|
Status = EFI_NOT_READY;
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
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) {
|
|
WidthOfString = GetStringWidth (gConfirmError);
|
|
ScreenSize = EFI_MAX (WidthOfString, GetStringWidth (gPressEnter)) / 2;
|
|
CreatePopUp (ScreenSize, 4, &NullCharacter, gConfirmError, gPressEnter, &NullCharacter);
|
|
} else {
|
|
WidthOfString = GetStringWidth (Packet->String);
|
|
ScreenSize = EFI_MAX (WidthOfString, 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) {
|
|
Status = EFI_NOT_READY;
|
|
goto Done;
|
|
}
|
|
} while (1);
|
|
} else {
|
|
Status = EFI_NOT_READY;
|
|
goto Done;
|
|
}
|
|
} 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)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto Done;
|
|
}
|
|
}
|
|
}
|
|
|
|
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) {
|
|
Status = EFI_SUCCESS;
|
|
goto Done;
|
|
} else {
|
|
WidthOfString = GetStringWidth (gConfirmError);
|
|
ScreenSize = EFI_MAX (WidthOfString, GetStringWidth (gPressEnter)) / 2;
|
|
CreatePopUp (ScreenSize, 4, &NullCharacter, gConfirmError, gPressEnter, &NullCharacter);
|
|
StringPtr[0] = CHAR_NULL;
|
|
do {
|
|
Status = WaitForKeyStroke (&Key);
|
|
if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto Done;
|
|
}
|
|
} while (1);
|
|
}
|
|
}
|
|
|
|
if (PromptForPassword) {
|
|
//
|
|
// I was asked for a password, return it back in StringPtr
|
|
//
|
|
Status = EFI_SUCCESS;
|
|
goto Done;
|
|
} 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);
|
|
|
|
Done:
|
|
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_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;
|
|
}
|
|
|
|
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 {
|
|
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_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;
|
|
BOOLEAN FirstOptionFoundFlag;
|
|
INT32 SavedAttribute;
|
|
EFI_TAG TagBackup;
|
|
UINT8 *ValueArray;
|
|
UINT8 *ValueArrayBackup;
|
|
UINT8 ValueBackup;
|
|
BOOLEAN Initialized;
|
|
BOOLEAN KeyInitialized;
|
|
BOOLEAN ShowDownArrow;
|
|
BOOLEAN ShowUpArrow;
|
|
UINTN DimensionsWidth;
|
|
|
|
DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
|
|
|
|
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;
|
|
|
|
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) {
|
|
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));
|
|
ASSERT (TempStringPtr != NULL);
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
goto Done;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
} while (1);
|
|
|
|
Done:
|
|
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;
|
|
}
|