summaryrefslogtreecommitdiff
path: root/EdkModulePkg/Universal/UserInterface/SetupBrowser/Dxe/Ui.c
diff options
context:
space:
mode:
Diffstat (limited to 'EdkModulePkg/Universal/UserInterface/SetupBrowser/Dxe/Ui.c')
-rw-r--r--EdkModulePkg/Universal/UserInterface/SetupBrowser/Dxe/Ui.c3143
1 files changed, 3143 insertions, 0 deletions
diff --git a/EdkModulePkg/Universal/UserInterface/SetupBrowser/Dxe/Ui.c b/EdkModulePkg/Universal/UserInterface/SetupBrowser/Dxe/Ui.c
new file mode 100644
index 0000000..0d512fd
--- /dev/null
+++ b/EdkModulePkg/Universal/UserInterface/SetupBrowser/Dxe/Ui.c
@@ -0,0 +1,3143 @@
+/*++
+
+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:
+
+ Ui.c
+
+Abstract:
+
+ Implementation for UI.
+
+Revision History
+
+--*/
+
+#include "Setup.h"
+#include "Ui.h"
+#include "Colors.h"
+
+//
+// Implementation
+//
+VOID
+SetUnicodeMem (
+ IN VOID *Buffer,
+ IN UINTN Size,
+ IN CHAR16 Value
+ )
+/*++
+
+Routine Description:
+
+ Set Buffer to Value for Size bytes.
+
+Arguments:
+
+ Buffer - Memory to set.
+
+ Size - Number of bytes to set
+
+ Value - Value of the set operation.
+
+Returns:
+
+ None
+
+--*/
+{
+ CHAR16 *Ptr;
+
+ Ptr = Buffer;
+ while (Size--) {
+ *(Ptr++) = Value;
+ }
+}
+
+VOID
+UiInitMenu (
+ VOID
+ )
+/*++
+
+Routine Description:
+ Initialize Menu option list.
+
+Arguments:
+
+Returns:
+
+--*/
+{
+ InitializeListHead (&Menu);
+}
+
+VOID
+UiInitMenuList (
+ VOID
+ )
+/*++
+
+Routine Description:
+ Initialize Menu option list.
+
+Arguments:
+
+Returns:
+
+--*/
+{
+ InitializeListHead (&gMenuList);
+}
+
+VOID
+UiRemoveMenuListEntry (
+ IN UI_MENU_OPTION *Selection,
+ OUT UI_MENU_OPTION **PreviousSelection
+ )
+/*++
+
+Routine Description:
+ Remove Menu option list.
+
+Arguments:
+
+Returns:
+
+--*/
+{
+ UI_MENU_LIST *UiMenuList;
+
+ *PreviousSelection = AllocateZeroPool (sizeof (UI_MENU_OPTION));
+ ASSERT (*PreviousSelection != NULL);
+
+ if (!IsListEmpty (&gMenuList)) {
+ UiMenuList = CR (gMenuList.ForwardLink, UI_MENU_LIST, MenuLink, UI_MENU_LIST_SIGNATURE);
+ (*PreviousSelection)->IfrNumber = UiMenuList->Selection.IfrNumber;
+ (*PreviousSelection)->FormId = UiMenuList->Selection.FormId;
+ (*PreviousSelection)->Tags = UiMenuList->Selection.Tags;
+ (*PreviousSelection)->ThisTag = UiMenuList->Selection.ThisTag;
+ (*PreviousSelection)->Handle = UiMenuList->Selection.Handle;
+ gEntryNumber = UiMenuList->FormerEntryNumber;
+ RemoveEntryList (&UiMenuList->MenuLink);
+ gBS->FreePool (UiMenuList);
+ }
+}
+
+VOID
+UiFreeMenuList (
+ VOID
+ )
+/*++
+
+Routine Description:
+ Free Menu option linked list.
+
+Arguments:
+
+Returns:
+
+--*/
+{
+ UI_MENU_LIST *UiMenuList;
+
+ while (!IsListEmpty (&gMenuList)) {
+ UiMenuList = CR (gMenuList.ForwardLink, UI_MENU_LIST, MenuLink, UI_MENU_LIST_SIGNATURE);
+ RemoveEntryList (&UiMenuList->MenuLink);
+ gBS->FreePool (UiMenuList);
+ }
+}
+
+VOID
+UiAddMenuListEntry (
+ IN UI_MENU_OPTION *Selection
+ )
+/*++
+
+Routine Description:
+ Add one menu entry to the linked lst
+
+Arguments:
+
+Returns:
+
+--*/
+{
+ UI_MENU_LIST *UiMenuList;
+
+ UiMenuList = AllocateZeroPool (sizeof (UI_MENU_LIST));
+ ASSERT (UiMenuList != NULL);
+
+ UiMenuList->Signature = UI_MENU_LIST_SIGNATURE;
+ CopyMem (&UiMenuList->Selection, Selection, sizeof (UI_MENU_OPTION));
+
+ InsertHeadList (&gMenuList, &UiMenuList->MenuLink);
+}
+
+VOID
+UiFreeMenu (
+ VOID
+ )
+/*++
+
+Routine Description:
+ Free Menu option linked list.
+
+Arguments:
+
+Returns:
+
+--*/
+{
+ UI_MENU_OPTION *MenuOption;
+
+ while (!IsListEmpty (&Menu)) {
+ MenuOption = CR (Menu.ForwardLink, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ RemoveEntryList (&MenuOption->Link);
+
+ //
+ // We allocated space for this description when we did a GetToken, free it here
+ //
+ gBS->FreePool (MenuOption->Description);
+ gBS->FreePool (MenuOption);
+ }
+}
+
+VOID
+UpdateDateAndTime (
+ VOID
+ )
+/*++
+
+Routine Description:
+ Refresh screen with current date and/or time based on screen context
+
+Arguments:
+
+Returns:
+
+--*/
+{
+ CHAR16 *OptionString;
+ MENU_REFRESH_ENTRY *MenuRefreshEntry;
+ UINTN Index;
+ UINTN Loop;
+
+ OptionString = NULL;
+
+ if (gMenuRefreshHead != NULL) {
+
+ MenuRefreshEntry = gMenuRefreshHead;
+
+ do {
+ gST->ConOut->SetAttribute (gST->ConOut, MenuRefreshEntry->CurrentAttribute);
+ ProcessOptions (MenuRefreshEntry->MenuOption, FALSE, MenuRefreshEntry->FileFormTagsHead, NULL, &OptionString);
+
+ if (OptionString != NULL) {
+ //
+ // If leading spaces on OptionString - remove the spaces
+ //
+ for (Index = 0; OptionString[Index] == L' '; Index++)
+ ;
+
+ for (Loop = 0; OptionString[Index] != CHAR_NULL; Index++) {
+ OptionString[Loop] = OptionString[Index];
+ Loop++;
+ }
+
+ OptionString[Loop] = CHAR_NULL;
+
+ PrintStringAt (MenuRefreshEntry->CurrentColumn, MenuRefreshEntry->CurrentRow, OptionString);
+ }
+
+ MenuRefreshEntry = MenuRefreshEntry->Next;
+
+ } while (MenuRefreshEntry != NULL);
+ }
+
+ if (OptionString != NULL) {
+ gBS->FreePool (OptionString);
+ }
+}
+
+EFI_STATUS
+UiWaitForSingleEvent (
+ IN EFI_EVENT Event,
+ IN UINT64 Timeout OPTIONAL
+ )
+/*++
+
+Routine Description:
+ Wait for a given event to fire, or for an optional timeout to expire.
+
+Arguments:
+ Event - The event to wait for
+
+ Timeout - An optional timeout value in 100 ns units.
+
+Returns:
+
+ EFI_SUCCESS - Event fired before Timeout expired.
+ EFI_TIME_OUT - Timout expired before Event fired.
+
+--*/
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ EFI_EVENT TimerEvent;
+ EFI_EVENT WaitList[2];
+
+ if (Timeout) {
+ //
+ // Create a timer event
+ //
+ Status = gBS->CreateEvent (EFI_EVENT_TIMER, 0, NULL, NULL, &TimerEvent);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Set the timer event
+ //
+ gBS->SetTimer (
+ TimerEvent,
+ TimerRelative,
+ Timeout
+ );
+
+ //
+ // Wait for the original event or the timer
+ //
+ WaitList[0] = Event;
+ WaitList[1] = TimerEvent;
+ Status = gBS->WaitForEvent (2, WaitList, &Index);
+ gBS->CloseEvent (TimerEvent);
+
+ //
+ // If the timer expired, change the return to timed out
+ //
+ if (!EFI_ERROR (Status) && Index == 1) {
+ Status = EFI_TIMEOUT;
+ }
+ }
+ } else {
+ //
+ // Update screen every second
+ //
+ Timeout = ONE_SECOND;
+
+ do {
+ Status = gBS->CreateEvent (EFI_EVENT_TIMER, 0, NULL, NULL, &TimerEvent);
+
+ //
+ // Set the timer event
+ //
+ gBS->SetTimer (
+ TimerEvent,
+ TimerRelative,
+ Timeout
+ );
+
+ //
+ // Wait for the original event or the timer
+ //
+ WaitList[0] = Event;
+ WaitList[1] = TimerEvent;
+ Status = gBS->WaitForEvent (2, WaitList, &Index);
+
+ //
+ // If the timer expired, update anything that needs a refresh and keep waiting
+ //
+ if (!EFI_ERROR (Status) && Index == 1) {
+ Status = EFI_TIMEOUT;
+ UpdateDateAndTime ();
+ }
+
+ gBS->CloseEvent (TimerEvent);
+ } while (Status == EFI_TIMEOUT);
+ }
+
+ return Status;
+}
+
+VOID
+UiAddMenuOption (
+ IN CHAR16 *String,
+ IN EFI_HII_HANDLE Handle,
+ IN EFI_TAG *Tags,
+ IN VOID *FormBinary,
+ IN UINTN IfrNumber
+ )
+/*++
+
+Routine Description:
+ Add one menu option by specified description and context.
+
+Arguments:
+ String - String description for this option.
+ Context - Context data for entry.
+
+Returns:
+
+--*/
+{
+ UI_MENU_OPTION *MenuOption;
+
+ MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION));
+ ASSERT (MenuOption);
+
+ MenuOption->Signature = UI_MENU_OPTION_SIGNATURE;
+ MenuOption->Description = String;
+ MenuOption->Handle = Handle;
+ MenuOption->FormBinary = FormBinary;
+ MenuOption->IfrNumber = IfrNumber;
+ MenuOption->Skip = 1;
+ MenuOption->Tags = Tags;
+ MenuOption->TagIndex = 0;
+ MenuOption->ThisTag = &(MenuOption->Tags[MenuOption->TagIndex]);
+ MenuOption->EntryNumber = (UINT16) IfrNumber;
+
+ InsertTailList (&Menu, &MenuOption->Link);
+}
+
+VOID
+UiAddSubMenuOption (
+ IN CHAR16 *String,
+ IN EFI_HII_HANDLE Handle,
+ IN EFI_TAG *Tags,
+ IN UINTN TagIndex,
+ IN UINT16 FormId,
+ IN UINT16 MenuItemCount
+ )
+/*++
+
+Routine Description:
+ Add one menu option by specified description and context.
+
+Arguments:
+ String - String description for this option.
+ Context - Context data for entry.
+
+Returns:
+
+--*/
+{
+ UI_MENU_OPTION *MenuOption;
+
+ MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION));
+ ASSERT (MenuOption);
+
+ MenuOption->Signature = UI_MENU_OPTION_SIGNATURE;
+ MenuOption->Description = String;
+ MenuOption->Handle = Handle;
+ MenuOption->Skip = Tags[TagIndex].NumberOfLines;
+ MenuOption->IfrNumber = gActiveIfr;
+ MenuOption->Tags = Tags;
+ MenuOption->TagIndex = TagIndex;
+ MenuOption->ThisTag = &(MenuOption->Tags[MenuOption->TagIndex]);
+ MenuOption->Consistency = Tags[TagIndex].Consistency;
+ MenuOption->FormId = FormId;
+ MenuOption->GrayOut = Tags[TagIndex].GrayOut;
+ MenuOption->EntryNumber = MenuItemCount;
+
+ InsertTailList (&Menu, &MenuOption->Link);
+}
+
+EFI_STATUS
+CreateDialog (
+ IN UINTN NumberOfLines,
+ IN BOOLEAN HotKey,
+ IN UINTN MaximumStringSize,
+ OUT CHAR16 *StringBuffer,
+ OUT EFI_INPUT_KEY *KeyValue,
+ IN CHAR16 *String,
+ ...
+ )
+/*++
+
+Routine Description:
+ Routine used to abstract a generic dialog interface and return the selected key or string
+
+Arguments:
+ NumberOfLines - The number of lines for the dialog box
+ HotKey - Defines whether a single character is parsed (TRUE) and returned in KeyValue
+ or a string is returned in StringBuffer. Two special characters are considered when entering a string, a SCAN_ESC and
+ an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates string input and returns
+ MaximumStringSize - The maximum size in bytes of a typed in string (each character is a CHAR16) and the minimum string returned is two bytes
+ StringBuffer - The passed in pointer to the buffer which will hold the typed in string if HotKey is FALSE
+ KeyValue - The EFI_KEY value returned if HotKey is TRUE..
+ String - Pointer to the first string in the list
+ ... - A series of (quantity == NumberOfLines) text strings which will be used to construct the dialog box
+
+Returns:
+ EFI_SUCCESS - Displayed dialog and received user interaction
+ EFI_INVALID_PARAMETER - One of the parameters was invalid (e.g. (StringBuffer == NULL) && (HotKey == FALSE))
+ EFI_DEVICE_ERROR - User typed in an ESC character to exit the routine
+
+--*/
+{
+ VA_LIST Marker;
+ UINTN Count;
+ EFI_INPUT_KEY Key;
+ UINTN LargestString;
+ CHAR16 *TempString;
+ CHAR16 *BufferedString;
+ CHAR16 *StackString;
+ CHAR16 KeyPad[2];
+ UINTN Start;
+ UINTN Top;
+ UINTN Index;
+ EFI_STATUS Status;
+ BOOLEAN SelectionComplete;
+ UINTN InputOffset;
+ UINTN CurrentAttribute;
+ UINTN DimensionsWidth;
+ UINTN DimensionsHeight;
+
+ DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
+ DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
+
+ SelectionComplete = FALSE;
+ InputOffset = 0;
+ TempString = AllocateZeroPool (MaximumStringSize * 2);
+ BufferedString = AllocateZeroPool (MaximumStringSize * 2);
+ CurrentAttribute = gST->ConOut->Mode->Attribute;
+
+ ASSERT (TempString);
+ ASSERT (BufferedString);
+
+ VA_START (Marker, String);
+
+ //
+ // Zero the outgoing buffer
+ //
+ ZeroMem (StringBuffer, MaximumStringSize);
+
+ if (HotKey) {
+ if (KeyValue == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ } else {
+ if (StringBuffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ //
+ // Disable cursor
+ //
+ gST->ConOut->EnableCursor (gST->ConOut, FALSE);
+
+ LargestString = (GetStringWidth (String) / 2);
+
+ if (LargestString == L' ') {
+ InputOffset = 1;
+ }
+ //
+ // Determine the largest string in the dialog box
+ // Notice we are starting with 1 since String is the first string
+ //
+ for (Count = 1; Count < NumberOfLines; Count++) {
+ StackString = VA_ARG (Marker, CHAR16 *);
+
+ if (StackString[0] == L' ') {
+ InputOffset = Count + 1;
+ }
+
+ if ((GetStringWidth (StackString) / 2) > LargestString) {
+ //
+ // Size of the string visually and subtract the width by one for the null-terminator
+ //
+ LargestString = (GetStringWidth (StackString) / 2);
+ }
+ }
+
+ Start = (DimensionsWidth - LargestString - 2) / 2 + gScreenDimensions.LeftColumn + 1;
+ Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gScreenDimensions.TopRow - 1;
+
+ Count = 0;
+
+ //
+ // Display the Popup
+ //
+ CreateSharedPopUp (LargestString, NumberOfLines, &String);
+
+ //
+ // Take the first key typed and report it back?
+ //
+ if (HotKey) {
+ Status = WaitForKeyStroke (&Key);
+ CopyMem (KeyValue, &Key, sizeof (EFI_INPUT_KEY));
+
+ } else {
+ do {
+ Status = WaitForKeyStroke (&Key);
+
+ switch (Key.UnicodeChar) {
+ case CHAR_NULL:
+ switch (Key.ScanCode) {
+ case SCAN_ESC:
+ gBS->FreePool (TempString);
+ gBS->FreePool (BufferedString);
+ gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
+ gST->ConOut->EnableCursor (gST->ConOut, TRUE);
+ return EFI_DEVICE_ERROR;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case CHAR_CARRIAGE_RETURN:
+ SelectionComplete = TRUE;
+ gBS->FreePool (TempString);
+ gBS->FreePool (BufferedString);
+ gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
+ gST->ConOut->EnableCursor (gST->ConOut, TRUE);
+ return EFI_SUCCESS;
+ break;
+
+ case CHAR_BACKSPACE:
+ if (StringBuffer[0] != CHAR_NULL) {
+ for (Index = 0; StringBuffer[Index] != CHAR_NULL; Index++) {
+ TempString[Index] = StringBuffer[Index];
+ }
+ //
+ // Effectively truncate string by 1 character
+ //
+ TempString[Index - 1] = CHAR_NULL;
+ StrCpy (StringBuffer, TempString);
+ }
+
+ default:
+ //
+ // If it is the beginning of the string, don't worry about checking maximum limits
+ //
+ if ((StringBuffer[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
+ StrnCpy (StringBuffer, &Key.UnicodeChar, 1);
+ StrnCpy (TempString, &Key.UnicodeChar, 1);
+ } else if ((GetStringWidth (StringBuffer) < MaximumStringSize) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
+ KeyPad[0] = Key.UnicodeChar;
+ KeyPad[1] = CHAR_NULL;
+ StrCat (StringBuffer, 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, LargestString, L' ');
+
+ PrintStringAt (Start + 1, Top + InputOffset, BufferedString);
+
+ if ((GetStringWidth (StringBuffer) / 2) > (DimensionsWidth - 2)) {
+ Index = (GetStringWidth (StringBuffer) / 2) - DimensionsWidth + 2;
+ } else {
+ Index = 0;
+ }
+
+ for (Count = 0; Index + 1 < GetStringWidth (StringBuffer) / 2; Index++, Count++) {
+ BufferedString[Count] = StringBuffer[Index];
+ }
+
+ PrintStringAt (Start + 1, Top + InputOffset, BufferedString);
+ break;
+ }
+ } while (!SelectionComplete);
+ }
+
+ gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
+ gST->ConOut->EnableCursor (gST->ConOut, TRUE);
+ return EFI_SUCCESS;
+}
+
+VOID
+CreateSharedPopUp (
+ IN UINTN RequestedWidth,
+ IN UINTN NumberOfLines,
+ IN CHAR16 **ArrayOfStrings
+ )
+{
+ UINTN Index;
+ UINTN Count;
+ CHAR16 Character;
+ UINTN Start;
+ UINTN End;
+ UINTN Top;
+ UINTN Bottom;
+ CHAR16 *String;
+
+ UINTN DimensionsWidth;
+ UINTN DimensionsHeight;
+
+ DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
+ DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
+
+ Count = 0;
+
+ gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
+
+ if ((RequestedWidth + 2) > DimensionsWidth) {
+ RequestedWidth = DimensionsWidth - 2;
+ }
+ //
+ // Subtract the PopUp width from total Columns, allow for one space extra on
+ // each end plus a border.
+ //
+ Start = (DimensionsWidth - RequestedWidth - 2) / 2 + gScreenDimensions.LeftColumn + 1;
+ End = Start + RequestedWidth + 1;
+
+ Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gScreenDimensions.TopRow - 1;
+ Bottom = Top + NumberOfLines + 2;
+
+ Character = (CHAR16) BOXDRAW_DOWN_RIGHT;
+ PrintCharAt (Start, Top, Character);
+ Character = (CHAR16) BOXDRAW_HORIZONTAL;
+ for (Index = Start; Index + 2 < End; Index++) {
+ PrintChar (Character);
+ }
+
+ Character = (CHAR16) BOXDRAW_DOWN_LEFT;
+ PrintChar (Character);
+ Character = (CHAR16) BOXDRAW_VERTICAL;
+ for (Index = Top; Index + 2 < Bottom; Index++) {
+ String = ArrayOfStrings[Count];
+ Count++;
+
+ //
+ // This will clear the background of the line - we never know who might have been
+ // here before us. This differs from the next clear in that it used the non-reverse
+ // video for normal printing.
+ //
+ if (GetStringWidth (String) / 2 > 1) {
+ ClearLines (Start, End, Index + 1, Index + 1, POPUP_TEXT | POPUP_BACKGROUND);
+ }
+ //
+ // Passing in a space results in the assumption that this is where typing will occur
+ //
+ if (String[0] == L' ') {
+ ClearLines (Start + 1, End - 1, Index + 1, Index + 1, POPUP_INVERSE_TEXT | POPUP_INVERSE_BACKGROUND);
+ }
+ //
+ // Passing in a NULL results in a blank space
+ //
+ if (String[0] == CHAR_NULL) {
+ ClearLines (Start, End, Index + 1, Index + 1, POPUP_TEXT | POPUP_BACKGROUND);
+ }
+
+ PrintStringAt (
+ ((DimensionsWidth - GetStringWidth (String) / 2) / 2) + gScreenDimensions.LeftColumn + 1,
+ Index + 1,
+ String
+ );
+ gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
+ PrintCharAt (Start, Index + 1, Character);
+ PrintCharAt (End - 1, Index + 1, Character);
+ }
+
+ Character = (CHAR16) BOXDRAW_UP_RIGHT;
+ PrintCharAt (Start, Bottom - 1, Character);
+ Character = (CHAR16) BOXDRAW_HORIZONTAL;
+ for (Index = Start; Index + 2 < End; Index++) {
+ PrintChar (Character);
+ }
+
+ Character = (CHAR16) BOXDRAW_UP_LEFT;
+ PrintChar (Character);
+}
+
+VOID
+CreatePopUp (
+ IN UINTN RequestedWidth,
+ IN UINTN NumberOfLines,
+ IN CHAR16 *ArrayOfStrings,
+ ...
+ )
+{
+ CreateSharedPopUp (RequestedWidth, NumberOfLines, &ArrayOfStrings);
+}
+
+VOID
+UpdateStatusBar (
+ IN UINTN MessageType,
+ IN UINT8 Flags,
+ IN BOOLEAN State
+ )
+{
+ UINTN Index;
+ STATIC BOOLEAN InputError;
+ CHAR16 *NvUpdateMessage;
+ CHAR16 *InputErrorMessage;
+
+ NvUpdateMessage = GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE), gHiiHandle);
+ InputErrorMessage = GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE), gHiiHandle);
+
+ switch (MessageType) {
+ case INPUT_ERROR:
+ if (State) {
+ gST->ConOut->SetAttribute (gST->ConOut, ERROR_TEXT);
+ PrintStringAt (
+ gScreenDimensions.LeftColumn + gPromptBlockWidth,
+ gScreenDimensions.BottomRow - 1,
+ InputErrorMessage
+ );
+ InputError = TRUE;
+ } else {
+ gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT);
+ for (Index = 0; Index < (GetStringWidth (InputErrorMessage) - 2) / 2; Index++) {
+ PrintAt (gScreenDimensions.LeftColumn + gPromptBlockWidth + Index, gScreenDimensions.BottomRow - 1, (CHAR16 *) L" ");
+ }
+
+ InputError = FALSE;
+ }
+ break;
+
+ case NV_UPDATE_REQUIRED:
+ if (gClassOfVfr != EFI_FRONT_PAGE_SUBCLASS) {
+ if (State) {
+ gST->ConOut->SetAttribute (gST->ConOut, INFO_TEXT);
+ PrintStringAt (
+ gScreenDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth,
+ gScreenDimensions.BottomRow - 1,
+ NvUpdateMessage
+ );
+ gResetRequired = (BOOLEAN) (gResetRequired | (Flags & RESET_REQUIRED));
+
+ gNvUpdateRequired = TRUE;
+ } else {
+ gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT);
+ for (Index = 0; Index < (GetStringWidth (NvUpdateMessage) - 2) / 2; Index++) {
+ PrintAt (
+ (gScreenDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + Index),
+ gScreenDimensions.BottomRow - 1,
+ (CHAR16 *) L" "
+ );
+ }
+
+ gNvUpdateRequired = FALSE;
+ }
+ }
+ break;
+
+ case REFRESH_STATUS_BAR:
+ if (InputError) {
+ UpdateStatusBar (INPUT_ERROR, Flags, TRUE);
+ }
+
+ if (gNvUpdateRequired) {
+ UpdateStatusBar (NV_UPDATE_REQUIRED, Flags, TRUE);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ gBS->FreePool (InputErrorMessage);
+ gBS->FreePool (NvUpdateMessage);
+ return ;
+}
+
+VOID
+FreeData (
+ IN EFI_FILE_FORM_TAGS *FileFormTagsHead,
+ IN CHAR16 *FormattedString,
+ IN CHAR16 *OptionString
+ )
+/*++
+
+Routine Description:
+
+ Used to remove the allocated data instances
+
+Arguments:
+
+Returns:
+
+--*/
+{
+ EFI_FILE_FORM_TAGS *FileForm;
+ EFI_FILE_FORM_TAGS *PreviousFileForm;
+ EFI_FORM_TAGS *FormTags;
+ EFI_FORM_TAGS *PreviousFormTags;
+ EFI_IFR_BINARY *IfrBinary;
+ EFI_IFR_BINARY *PreviousIfrBinary;
+ EFI_INCONSISTENCY_DATA *Inconsistent;
+ EFI_VARIABLE_DEFINITION *VariableDefinition;
+ EFI_VARIABLE_DEFINITION *PreviousVariableDefinition;
+ VOID *Buffer;
+ UINTN Index;
+
+ FileForm = FileFormTagsHead;
+
+ if (FormattedString != NULL) {
+ gBS->FreePool (FormattedString);
+ }
+
+ if (OptionString != NULL) {
+ gBS->FreePool (OptionString);
+ }
+
+ for (; FileForm != NULL;) {
+ PreviousFileForm = NULL;
+
+ //
+ // Advance FileForm to the last entry
+ //
+ for (; FileForm->NextFile != NULL; FileForm = FileForm->NextFile) {
+ PreviousFileForm = FileForm;
+ }
+
+ FormTags = &FileForm->FormTags;
+
+ for (; FormTags != NULL;) {
+ FormTags = &FileForm->FormTags;
+ PreviousFormTags = NULL;
+
+ //
+ // Advance FormTags to the last entry
+ //
+ for (; FormTags->Next != NULL; FormTags = FormTags->Next) {
+ PreviousFormTags = FormTags;
+ }
+ //
+ // Walk through each of the tags and free the IntList allocation
+ //
+ for (Index = 0; FormTags->Tags[Index].Operand != EFI_IFR_END_FORM_OP; Index++) {
+ //
+ // It is more than likely that the very last page will contain an end formset
+ //
+ if (FormTags->Tags[Index].Operand == EFI_IFR_END_FORM_SET_OP) {
+ break;
+ }
+
+ if (FormTags->Tags[Index].IntList != NULL) {
+ gBS->FreePool (FormTags->Tags[Index].IntList);
+ }
+ }
+
+ if (PreviousFormTags != NULL) {
+ gBS->FreePool (FormTags->Tags);
+ FormTags = PreviousFormTags;
+ gBS->FreePool (FormTags->Next);
+ FormTags->Next = NULL;
+ } else {
+ gBS->FreePool (FormTags->Tags);
+ FormTags = NULL;
+ }
+ }
+ //
+ // Last FileForm entry's Inconsistent database
+ //
+ Inconsistent = FileForm->InconsistentTags;
+
+ //
+ // Advance Inconsistent to the last entry
+ //
+ for (; Inconsistent->Next != NULL; Inconsistent = Inconsistent->Next)
+ ;
+
+ for (; Inconsistent != NULL;) {
+ //
+ // Preserve the Previous pointer
+ //
+ Buffer = (VOID *) Inconsistent->Previous;
+
+ //
+ // Free the current entry
+ //
+ gBS->FreePool (Inconsistent);
+
+ //
+ // Restore the Previous pointer
+ //
+ Inconsistent = (EFI_INCONSISTENCY_DATA *) Buffer;
+ }
+
+ VariableDefinition = FileForm->VariableDefinitions;
+
+ for (; VariableDefinition != NULL;) {
+ VariableDefinition = FileForm->VariableDefinitions;
+ PreviousVariableDefinition = NULL;
+
+ //
+ // Advance VariableDefinitions to the last entry
+ //
+ for (; VariableDefinition->Next != NULL; VariableDefinition = VariableDefinition->Next) {
+ PreviousVariableDefinition = VariableDefinition;
+ }
+
+ gBS->FreePool (VariableDefinition->VariableName);
+ gBS->FreePool (VariableDefinition->NvRamMap);
+ gBS->FreePool (VariableDefinition->FakeNvRamMap);
+
+ if (PreviousVariableDefinition != NULL) {
+ VariableDefinition = PreviousVariableDefinition;
+ gBS->FreePool (VariableDefinition->Next);
+ VariableDefinition->Next = NULL;
+ } else {
+ gBS->FreePool (VariableDefinition);
+ VariableDefinition = NULL;
+ }
+ }
+
+ if (PreviousFileForm != NULL) {
+ FileForm = PreviousFileForm;
+ gBS->FreePool (FileForm->NextFile);
+ FileForm->NextFile = NULL;
+ } else {
+ gBS->FreePool (FileForm);
+ FileForm = NULL;
+ }
+ }
+
+ IfrBinary = gBinaryDataHead;
+
+ for (; IfrBinary != NULL;) {
+ IfrBinary = gBinaryDataHead;
+ PreviousIfrBinary = NULL;
+
+ //
+ // Advance IfrBinary to the last entry
+ //
+ for (; IfrBinary->Next != NULL; IfrBinary = IfrBinary->Next) {
+ PreviousIfrBinary = IfrBinary;
+ }
+
+ gBS->FreePool (IfrBinary->IfrPackage);
+
+ if (PreviousIfrBinary != NULL) {
+ IfrBinary = PreviousIfrBinary;
+ gBS->FreePool (IfrBinary->Next);
+ IfrBinary->Next = NULL;
+ } else {
+ gBS->FreePool (IfrBinary);
+ IfrBinary = NULL;
+ }
+ }
+
+ gBS->FreePool (gPreviousValue);
+ gPreviousValue = NULL;
+
+ //
+ // Free Browser Strings
+ //
+ gBS->FreePool (gPressEnter);
+ gBS->FreePool (gConfirmError);
+ gBS->FreePool (gConfirmPassword);
+ gBS->FreePool (gPromptForNewPassword);
+ gBS->FreePool (gPromptForPassword);
+ gBS->FreePool (gToggleCheckBox);
+ gBS->FreePool (gNumericInput);
+ gBS->FreePool (gMakeSelection);
+ gBS->FreePool (gMoveHighlight);
+ gBS->FreePool (gEscapeString);
+ gBS->FreePool (gEnterCommitString);
+ gBS->FreePool (gEnterString);
+ gBS->FreePool (gFunctionOneString);
+ gBS->FreePool (gFunctionTwoString);
+ gBS->FreePool (gFunctionNineString);
+ gBS->FreePool (gFunctionTenString);
+ return ;
+}
+
+BOOLEAN
+SelectionsAreValid (
+ IN UI_MENU_OPTION *MenuOption,
+ IN EFI_FILE_FORM_TAGS *FileFormTagsHead
+ )
+/*++
+
+Routine Description:
+ Initiate late consistency checks against the current page.
+
+Arguments:
+ None
+
+Returns:
+
+--*/
+{
+ LIST_ENTRY *Link;
+ EFI_TAG *Tag;
+ EFI_FILE_FORM_TAGS *FileFormTags;
+ CHAR16 *StringPtr;
+ CHAR16 NullCharacter;
+ EFI_STATUS Status;
+ UINTN Index;
+ UINT16 *NvRamMap;
+ STRING_REF PopUp;
+ EFI_INPUT_KEY Key;
+ EFI_VARIABLE_DEFINITION *VariableDefinition;
+
+ StringPtr = (CHAR16 *) L"\0";
+ NullCharacter = CHAR_NULL;
+
+ FileFormTags = FileFormTagsHead;
+
+ for (Index = 0; Index < MenuOption->IfrNumber; Index++) {
+ FileFormTags = FileFormTags->NextFile;
+ }
+
+ for (Link = Menu.ForwardLink; Link != &Menu; Link = Link->ForwardLink) {
+ MenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+
+ Tag = MenuOption->ThisTag;
+
+ ExtractRequestedNvMap (FileFormTags, Tag->VariableNumber, &VariableDefinition);
+ NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
+
+ //
+ // If the op-code has a late check, ensure consistency checks are now applied
+ //
+ if (Tag->Flags & EFI_IFR_FLAG_LATE_CHECK) {
+ if (ValueIsNotValid (TRUE, 0, Tag, FileFormTags, &PopUp)) {
+ if (PopUp != 0x0000) {
+ StringPtr = GetToken (PopUp, MenuOption->Handle);
+
+ CreatePopUp (GetStringWidth (StringPtr) / 2, 3, &NullCharacter, StringPtr, &NullCharacter);
+
+ do {
+ Status = WaitForKeyStroke (&Key);
+
+ switch (Key.UnicodeChar) {
+
+ case CHAR_CARRIAGE_RETURN:
+ //
+ // Since the value can be one byte long or two bytes long, do a CopyMem based on StorageWidth
+ //
+ CopyMem (NvRamMap, &Tag->OldValue, Tag->StorageWidth);
+ gBS->FreePool (StringPtr);
+ break;
+
+ default:
+ break;
+ }
+ } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
+ }
+
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+UINT16
+GetWidth (
+ IN EFI_TAG *Tag,
+ IN EFI_HII_HANDLE Handle
+ )
+/*++
+
+Routine Description:
+ Get the supported width for a particular op-code
+
+Arguments:
+ Tag - The Tag structure passed in.
+ Handle - The handle in the HII database being used
+
+Returns:
+ Returns the number of CHAR16 characters that is support.
+
+
+--*/
+{
+ CHAR16 *String;
+ UINTN Size;
+
+ Size = 0x00;
+
+ //
+ // See if the second text parameter is really NULL
+ //
+ if ((Tag->Operand == EFI_IFR_TEXT_OP) && (Tag->TextTwo != 0)) {
+ String = GetToken (Tag->TextTwo, Handle);
+ Size = StrLen (String);
+ gBS->FreePool (String);
+ }
+
+ if ((Tag->Operand == EFI_IFR_SUBTITLE_OP) ||
+ (Tag->Operand == EFI_IFR_REF_OP) ||
+ (Tag->Operand == EFI_IFR_PASSWORD_OP) ||
+ (Tag->Operand == EFI_IFR_STRING_OP) ||
+ (Tag->Operand == EFI_IFR_INVENTORY_OP) ||
+ //
+ // Allow a wide display if text op-code and no secondary text op-code
+ //
+ ((Tag->Operand == EFI_IFR_TEXT_OP) && (Size == 0x0000))
+ ) {
+ return (UINT16) (gPromptBlockWidth + gOptionBlockWidth);
+ } else {
+ return (UINT16) gPromptBlockWidth;
+ }
+}
+
+UINT16
+GetLineByWidth (
+ IN CHAR16 *InputString,
+ IN UINT16 LineWidth,
+ IN OUT UINTN *Index,
+ OUT CHAR16 **OutputString
+ )
+/*++
+
+Routine Description:
+ Will copy LineWidth amount of a string in the OutputString buffer and return the
+ number of CHAR16 characters that were copied into the OutputString buffer.
+
+Arguments:
+ InputString - String description for this option.
+ LineWidth - Width of the desired string to extract in CHAR16 characters
+ Index - Where in InputString to start the copy process
+ OutputString - Buffer to copy the string into
+
+Returns:
+ Returns the number of CHAR16 characters that were copied into the OutputString buffer.
+
+
+--*/
+{
+ static BOOLEAN Finished;
+ UINT16 Count;
+ UINT16 Count2;
+
+ if (Finished) {
+ Finished = FALSE;
+ return (UINT16) 0;
+ }
+
+ Count = LineWidth;
+ Count2 = 0;
+
+ *OutputString = AllocateZeroPool (((UINTN) (LineWidth + 1) * 2));
+
+ //
+ // Ensure we have got a valid buffer
+ //
+ if (*OutputString != NULL) {
+ //
+ // Fast-forward the string and see if there is a carriage-return in the string
+ //
+ for (; (InputString[*Index + Count2] != CHAR_CARRIAGE_RETURN) && (Count2 != LineWidth); Count2++)
+ ;
+
+ //
+ // Copy the desired LineWidth of data to the output buffer.
+ // Also make sure that we don't copy more than the string.
+ // Also make sure that if there are linefeeds, we account for them.
+ //
+ if ((StrSize (&InputString[*Index]) <= ((UINTN) (LineWidth + 1) * 2)) &&
+ (StrSize (&InputString[*Index]) <= ((UINTN) (Count2 + 1) * 2))
+ ) {
+ //
+ // Convert to CHAR16 value and show that we are done with this operation
+ //
+ LineWidth = (UINT16) ((StrSize (&InputString[*Index]) - 2) / 2);
+ if (LineWidth != 0) {
+ Finished = TRUE;
+ }
+ } else {
+ if (Count2 == LineWidth) {
+ //
+ // Rewind the string from the maximum size until we see a space to break the line
+ //
+ for (; (InputString[*Index + LineWidth] != CHAR_SPACE) && (LineWidth != 0); LineWidth--)
+ ;
+ if (LineWidth == 0) {
+ LineWidth = Count;
+ }
+ } else {
+ LineWidth = Count2;
+ }
+ }
+
+ CopyMem (*OutputString, &InputString[*Index], LineWidth * 2);
+
+ //
+ // If currently pointing to a space, increment the index to the first non-space character
+ //
+ for (;
+ (InputString[*Index + LineWidth] == CHAR_SPACE) || (InputString[*Index + LineWidth] == CHAR_CARRIAGE_RETURN);
+ (*Index)++
+ )
+ ;
+ *Index = (UINT16) (*Index + LineWidth);
+ return LineWidth;
+ } else {
+ return (UINT16) 0;
+ }
+}
+
+VOID
+UpdateOptionSkipLines (
+ IN EFI_IFR_DATA_ARRAY *PageData,
+ IN UI_MENU_OPTION *MenuOption,
+ IN EFI_FILE_FORM_TAGS *FileFormTagsHead,
+ IN CHAR16 **OptionalString,
+ IN UINTN SkipValue
+ )
+{
+ UINTN Index;
+ UINTN Loop;
+ UINT16 Width;
+ UINTN Row;
+ UINTN OriginalRow;
+ CHAR16 *OutputString;
+ CHAR16 *OptionString;
+
+ Row = 0;
+ OptionString = *OptionalString;
+ OutputString = NULL;
+
+ ProcessOptions (MenuOption, FALSE, FileFormTagsHead, PageData, &OptionString);
+
+ if (OptionString != NULL) {
+ //
+ // If leading spaces on OptionString - remove the spaces
+ //
+ for (Index = 0; OptionString[Index] == L' '; Index++)
+ ;
+
+ for (Loop = 0; OptionString[Index] != CHAR_NULL; Index++) {
+ OptionString[Loop] = OptionString[Index];
+ Loop++;
+ }
+
+ OptionString[Loop] = CHAR_NULL;
+
+ Width = (UINT16) gOptionBlockWidth;
+
+ OriginalRow = Row;
+
+ for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
+ //
+ // If there is more string to process print on the next row and increment the Skip value
+ //
+ if (StrLen (&OptionString[Index])) {
+ if (SkipValue == 0) {
+ Row++;
+ //
+ // Since the Number of lines for this menu entry may or may not be reflected accurately
+ // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
+ // some testing to ensure we are keeping this in-sync.
+ //
+ // If the difference in rows is greater than or equal to the skip value, increase the skip value
+ //
+ if ((Row - OriginalRow) >= MenuOption->Skip) {
+ MenuOption->Skip++;
+ }
+ }
+ }
+
+ gBS->FreePool (OutputString);
+ if (SkipValue != 0) {
+ SkipValue--;
+ }
+ }
+
+ Row = OriginalRow;
+ }
+
+ *OptionalString = OptionString;
+}
+//
+// Search table for UiDisplayMenu()
+//
+SCAN_CODE_TO_SCREEN_OPERATION gScanCodeToOperation[] = {
+ { SCAN_UP, UiUp },
+ { SCAN_DOWN, UiDown },
+ { SCAN_PAGE_UP, UiPageUp },
+ { SCAN_PAGE_DOWN, UiPageDown},
+ { SCAN_ESC, UiReset},
+ { SCAN_F2, UiPrevious},
+ { SCAN_LEFT, UiLeft },
+ { SCAN_RIGHT, UiRight },
+ { SCAN_F9, UiDefault},
+ { SCAN_F10, UiSave }
+};
+
+SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag[] = {
+ { UiNoOperation, CfUiNoOperation },
+ { UiDefault, CfUiDefault },
+ { UiSelect, CfUiSelect },
+ { UiUp, CfUiUp},
+ { UiDown, CfUiDown },
+ { UiLeft, CfUiLeft },
+ { UiRight, CfUiRight },
+ { UiReset, CfUiReset },
+ { UiSave, CfUiSave },
+ { UiPrevious, CfUiPrevious },
+ { UiPageUp, CfUiPageUp },
+ { UiPageDown, CfUiPageDown }
+};
+
+UI_MENU_OPTION *
+UiDisplayMenu (
+ IN BOOLEAN SubMenu,
+ IN EFI_FILE_FORM_TAGS *FileFormTagsHead,
+ OUT EFI_IFR_DATA_ARRAY *PageData
+ )
+/*++
+
+Routine Description:
+ Display menu and wait for user to select one menu option, then return it.
+ If AutoBoot is enabled, then if user doesn't select any option,
+ after period of time, it will automatically return the first menu option.
+
+Arguments:
+ SubMenu - Indicate is sub menu.
+ FileFormTagsHead - A pointer to the EFI_FILE_FORM_TAGS structure.
+ PageData - A pointer to the EFI_IFR_DATA_ARRAY.
+
+Returns:
+ Return the pointer of the menu which selected,
+ otherwise return NULL.
+
+--*/
+{
+ INTN SkipValue;
+ INTN Difference;
+ INTN OldSkipValue;
+ UINTN Row;
+ UINTN Col;
+ UINTN Temp;
+ UINTN Temp2;
+ UINTN TopRow;
+ UINTN BottomRow;
+ UINTN OriginalRow;
+ UINTN Index;
+ UINTN DataAndTimeLineNumberPad;
+ UINT32 Count;
+ INT16 OriginalTimeOut;
+ UINT8 *Location;
+ UINT16 Width;
+ CHAR16 *StringPtr;
+ CHAR16 *OptionString;
+ CHAR16 *OutputString;
+ CHAR16 *FormattedString;
+ CHAR16 YesResponse;
+ CHAR16 NoResponse;
+ BOOLEAN NewLine;
+ BOOLEAN Repaint;
+ BOOLEAN SavedValue;
+ EFI_STATUS Status;
+ UI_MENU_LIST *UiMenuList;
+ EFI_INPUT_KEY Key;
+ LIST_ENTRY *Link;
+ LIST_ENTRY *NewPos;
+ LIST_ENTRY *TopOfScreen;
+ LIST_ENTRY *SavedListEntry;
+ UI_MENU_OPTION *Selection;
+ UI_MENU_OPTION *MenuOption;
+ UI_MENU_OPTION *NextMenuOption;
+ UI_MENU_OPTION *SavedMenuOption;
+ UI_MENU_OPTION *PreviousMenuOption;
+ EFI_IFR_BINARY *IfrBinary;
+ UI_CONTROL_FLAG ControlFlag;
+ EFI_SCREEN_DESCRIPTOR LocalScreen;
+ EFI_FILE_FORM_TAGS *FileFormTags;
+ MENU_REFRESH_ENTRY *MenuRefreshEntry;
+ MENU_REFRESH_ENTRY *OldMenuRefreshEntry;
+ UI_SCREEN_OPERATION ScreenOperation;
+ EFI_VARIABLE_DEFINITION *VariableDefinition;
+ EFI_FORM_CALLBACK_PROTOCOL *FormCallback;
+ EFI_HII_VARIABLE_PACK_LIST *NvMapListHead;
+ EFI_HII_VARIABLE_PACK_LIST *NvMapListNode;
+ VOID *NvMap;
+ UINTN NvMapSize;
+
+ CopyMem (&LocalScreen, &gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR));
+
+ VariableDefinition = NULL;
+ Status = EFI_SUCCESS;
+ FormattedString = NULL;
+ OptionString = NULL;
+ ScreenOperation = UiNoOperation;
+ NewLine = TRUE;
+ FormCallback = NULL;
+ FileFormTags = NULL;
+ OutputString = NULL;
+ gUpArrow = FALSE;
+ gDownArrow = FALSE;
+ SkipValue = 0;
+ OldSkipValue = 0;
+ MenuRefreshEntry = gMenuRefreshHead;
+ OldMenuRefreshEntry = gMenuRefreshHead;
+ NextMenuOption = NULL;
+ PreviousMenuOption = NULL;
+ SavedMenuOption = NULL;
+ IfrBinary = NULL;
+ NvMap = NULL;
+ NvMapSize = 0;
+
+ ZeroMem (&Key, sizeof (EFI_INPUT_KEY));
+
+ if (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) {
+ TopRow = LocalScreen.TopRow + FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
+ Row = LocalScreen.TopRow + FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
+ } else {
+ TopRow = LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
+ Row = LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
+ }
+
+ if (SubMenu) {
+ Col = LocalScreen.LeftColumn;
+ } else {
+ Col = LocalScreen.LeftColumn + LEFT_SKIPPED_COLUMNS;
+ }
+
+ BottomRow = LocalScreen.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT - SCROLL_ARROW_HEIGHT - 1;
+
+ TopOfScreen = Menu.ForwardLink;
+ Repaint = TRUE;
+ MenuOption = NULL;
+
+ //
+ // Get user's selection
+ //
+ Selection = NULL;
+ NewPos = Menu.ForwardLink;
+ gST->ConOut->EnableCursor (gST->ConOut, FALSE);
+
+ UpdateStatusBar (REFRESH_STATUS_BAR, (UINT8) 0, TRUE);
+
+ ControlFlag = CfInitialization;
+
+ while (TRUE) {
+ switch (ControlFlag) {
+ case CfInitialization:
+ ControlFlag = CfCheckSelection;
+ if (gExitRequired) {
+ ScreenOperation = UiReset;
+ ControlFlag = CfScreenOperation;
+ } else if (gSaveRequired) {
+ ScreenOperation = UiSave;
+ ControlFlag = CfScreenOperation;
+ } else if (IsListEmpty (&Menu)) {
+ ControlFlag = CfReadKey;
+ }
+ break;
+
+ case CfCheckSelection:
+ if (Selection != NULL) {
+ ControlFlag = CfExit;
+ } else {
+ ControlFlag = CfRepaint;
+ }
+
+ FileFormTags = FileFormTagsHead;
+ break;
+
+ case CfRepaint:
+ ControlFlag = CfRefreshHighLight;
+
+ if (Repaint) {
+ //
+ // Display menu
+ //
+ SavedMenuOption = MenuOption;
+ gDownArrow = FALSE;
+ gUpArrow = FALSE;
+ Row = TopRow;
+
+ Temp = SkipValue;
+ Temp2 = SkipValue;
+
+ ClearLines (
+ LocalScreen.LeftColumn,
+ LocalScreen.RightColumn,
+ TopRow - SCROLL_ARROW_HEIGHT,
+ BottomRow + SCROLL_ARROW_HEIGHT,
+ FIELD_TEXT | FIELD_BACKGROUND
+ );
+
+ while (gMenuRefreshHead != NULL) {
+ OldMenuRefreshEntry = gMenuRefreshHead->Next;
+
+ gBS->FreePool (gMenuRefreshHead);
+
+ gMenuRefreshHead = OldMenuRefreshEntry;
+ }
+
+ for (Link = TopOfScreen; Link != &Menu; Link = Link->ForwardLink) {
+ MenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ MenuOption->Row = Row;
+ OriginalRow = Row;
+ MenuOption->Col = Col;
+ MenuOption->OptCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn;
+
+ if (SubMenu) {
+ if (MenuOption->ThisTag->GrayOut) {
+ gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
+ } else {
+ if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP) {
+ gST->ConOut->SetAttribute (gST->ConOut, SUBTITLE_TEXT | FIELD_BACKGROUND);
+ }
+ }
+
+ Width = GetWidth (MenuOption->ThisTag, MenuOption->Handle);
+
+ OriginalRow = Row;
+
+ for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
+ if ((Temp == 0) && (Row <= BottomRow)) {
+ PrintStringAt (Col, Row, OutputString);
+ }
+ //
+ // If there is more string to process print on the next row and increment the Skip value
+ //
+ if (StrLen (&MenuOption->Description[Index])) {
+ if (Temp == 0) {
+ Row++;
+ }
+ }
+
+ gBS->FreePool (OutputString);
+ if (Temp != 0) {
+ Temp--;
+ }
+ }
+
+ Temp = 0;
+
+ Row = OriginalRow;
+
+ gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
+ ProcessOptions (MenuOption, FALSE, FileFormTagsHead, PageData, &OptionString);
+
+ if (OptionString != NULL) {
+ //
+ // If leading spaces on OptionString - remove the spaces
+ //
+ for (Index = 0; OptionString[Index] == L' '; Index++) {
+ MenuOption->OptCol++;
+ }
+
+ for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
+ OptionString[Count] = OptionString[Index];
+ Count++;
+ }
+
+ OptionString[Count] = CHAR_NULL;
+
+ //
+ // If this is a date or time op-code and is used to reflect an RTC, register the op-code
+ //
+ if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP ||
+ MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP) &&
+ (MenuOption->ThisTag->StorageStart >= FileFormTags->FormTags.Tags[0].NvDataSize)) {
+
+ if (gMenuRefreshHead == NULL) {
+ MenuRefreshEntry = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
+ ASSERT (MenuRefreshEntry != NULL);
+ MenuRefreshEntry->MenuOption = MenuOption;
+ MenuRefreshEntry->FileFormTagsHead = FileFormTagsHead;
+ MenuRefreshEntry->CurrentColumn = MenuOption->OptCol;
+ MenuRefreshEntry->CurrentRow = MenuOption->Row;
+ MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND;
+ gMenuRefreshHead = MenuRefreshEntry;
+ } else {
+ //
+ // Advance to the last entry
+ //
+ for (MenuRefreshEntry = gMenuRefreshHead;
+ MenuRefreshEntry->Next != NULL;
+ MenuRefreshEntry = MenuRefreshEntry->Next
+ )
+ ;
+ MenuRefreshEntry->Next = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
+ ASSERT (MenuRefreshEntry->Next != NULL);
+ MenuRefreshEntry = MenuRefreshEntry->Next;
+ MenuRefreshEntry->MenuOption = MenuOption;
+ MenuRefreshEntry->FileFormTagsHead = FileFormTagsHead;
+ MenuRefreshEntry->CurrentColumn = MenuOption->OptCol;
+ MenuRefreshEntry->CurrentRow = MenuOption->Row;
+ MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND;
+ }
+ }
+
+ Width = (UINT16) gOptionBlockWidth;
+
+ OriginalRow = Row;
+
+ for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
+ if ((Temp2 == 0) && (Row <= BottomRow)) {
+ PrintStringAt (MenuOption->OptCol, Row, OutputString);
+ }
+ //
+ // If there is more string to process print on the next row and increment the Skip value
+ //
+ if (StrLen (&OptionString[Index])) {
+ if (Temp2 == 0) {
+ Row++;
+ //
+ // Since the Number of lines for this menu entry may or may not be reflected accurately
+ // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
+ // some testing to ensure we are keeping this in-sync.
+ //
+ // If the difference in rows is greater than or equal to the skip value, increase the skip value
+ //
+ if ((Row - OriginalRow) >= MenuOption->Skip) {
+ MenuOption->Skip++;
+ }
+ }
+ }
+
+ gBS->FreePool (OutputString);
+ if (Temp2 != 0) {
+ Temp2--;
+ }
+ }
+
+ Temp2 = 0;
+ Row = OriginalRow;
+ }
+ //
+ // If this is a text op with secondary text information
+ //
+ if ((MenuOption->ThisTag->Operand == EFI_IFR_TEXT_OP) && (MenuOption->ThisTag->TextTwo != 0)) {
+ StringPtr = GetToken (MenuOption->ThisTag->TextTwo, MenuOption->Handle);
+
+ Width = (UINT16) gOptionBlockWidth;
+
+ OriginalRow = Row;
+
+ for (Index = 0; GetLineByWidth (StringPtr, Width, &Index, &OutputString) != 0x0000;) {
+ if ((Temp == 0) && (Row <= BottomRow)) {
+ PrintStringAt (MenuOption->OptCol, Row, OutputString);
+ }
+ //
+ // If there is more string to process print on the next row and increment the Skip value
+ //
+ if (StrLen (&StringPtr[Index])) {
+ if (Temp2 == 0) {
+ Row++;
+ //
+ // Since the Number of lines for this menu entry may or may not be reflected accurately
+ // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
+ // some testing to ensure we are keeping this in-sync.
+ //
+ // If the difference in rows is greater than or equal to the skip value, increase the skip value
+ //
+ if ((Row - OriginalRow) >= MenuOption->Skip) {
+ MenuOption->Skip++;
+ }
+ }
+ }
+
+ gBS->FreePool (OutputString);
+ if (Temp2 != 0) {
+ Temp2--;
+ }
+ }
+
+ Row = OriginalRow;
+ gBS->FreePool (StringPtr);
+ }
+ } else {
+ //
+ // For now, assume left-justified 72 width max setup entries
+ //
+ PrintStringAt (Col, Row, MenuOption->Description);
+ }
+ //
+ // Tracker 6210 - need to handle the bottom of the display
+ //
+ if (MenuOption->Skip > 1) {
+ Row += MenuOption->Skip - SkipValue;
+ SkipValue = 0;
+ } else {
+ Row += MenuOption->Skip;
+ }
+
+ if (Row > BottomRow) {
+ if (!ValueIsScroll (FALSE, Link)) {
+ gDownArrow = TRUE;
+ }
+
+ Row = BottomRow + 1;
+ break;
+ }
+ }
+
+ if (!ValueIsScroll (TRUE, TopOfScreen)) {
+ gUpArrow = TRUE;
+ }
+
+ if (gUpArrow) {
+ gST->ConOut->SetAttribute (gST->ConOut, ARROW_TEXT | ARROW_BACKGROUND);
+ PrintAt (
+ LocalScreen.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
+ TopRow - SCROLL_ARROW_HEIGHT,
+ (CHAR16 *) L"%c",
+ ARROW_UP
+ );
+ gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
+ }
+
+ if (gDownArrow) {
+ gST->ConOut->SetAttribute (gST->ConOut, ARROW_TEXT | ARROW_BACKGROUND);
+ PrintAt (
+ LocalScreen.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
+ BottomRow + SCROLL_ARROW_HEIGHT,
+ (CHAR16 *) L"%c",
+ ARROW_DOWN
+ );
+ gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
+ }
+
+ if (SavedMenuOption != NULL) {
+ MenuOption = SavedMenuOption;
+ }
+ }
+ break;
+
+ case CfRefreshHighLight:
+ ControlFlag = CfUpdateHelpString;
+ //
+ // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
+ // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
+ //
+ SavedValue = Repaint;
+ Repaint = FALSE;
+
+ if (NewPos != NULL) {
+ gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
+ if (SubMenu) {
+ if (gLastOpr && (gEntryNumber != -1)) {
+ MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ if (gEntryNumber != MenuOption->EntryNumber) {
+ ScreenOperation = UiDown;
+ ControlFlag = CfScreenOperation;
+ break;
+ } else {
+ gLastOpr = FALSE;
+ }
+ }
+
+ ProcessOptions (MenuOption, FALSE, FileFormTagsHead, PageData, &OptionString);
+ gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
+ if (OptionString != NULL) {
+ //
+ // If leading spaces on OptionString - remove the spaces
+ //
+ for (Index = 0; OptionString[Index] == L' '; Index++)
+ ;
+
+ for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
+ OptionString[Count] = OptionString[Index];
+ Count++;
+ }
+
+ OptionString[Count] = CHAR_NULL;
+
+ Width = (UINT16) gOptionBlockWidth;
+
+ OriginalRow = MenuOption->Row;
+
+ for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
+ if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
+ PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString);
+ }
+ //
+ // If there is more string to process print on the next row and increment the Skip value
+ //
+ if (StrLen (&OptionString[Index])) {
+ MenuOption->Row++;
+ }
+
+ gBS->FreePool (OutputString);
+ }
+
+ MenuOption->Row = OriginalRow;
+ } else {
+ if (NewLine) {
+ if (MenuOption->ThisTag->GrayOut) {
+ gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
+ } else {
+ if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP) {
+ gST->ConOut->SetAttribute (gST->ConOut, SUBTITLE_TEXT | FIELD_BACKGROUND);
+ }
+ }
+
+ OriginalRow = MenuOption->Row;
+ Width = GetWidth (MenuOption->ThisTag, MenuOption->Handle);
+
+ for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
+ if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
+ PrintStringAt (Col, MenuOption->Row, OutputString);
+ }
+ //
+ // If there is more string to process print on the next row and increment the Skip value
+ //
+ if (StrLen (&MenuOption->Description[Index])) {
+ MenuOption->Row++;
+ }
+
+ gBS->FreePool (OutputString);
+ }
+
+ MenuOption->Row = OriginalRow;
+ gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
+ }
+ }
+ } else {
+ gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
+ gST->ConOut->OutputString (gST->ConOut, MenuOption->Description);
+ }
+
+ MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+
+ if ((gPriorMenuEntry != 0) && (MenuOption->EntryNumber != gPriorMenuEntry) && (NewPos->ForwardLink != &Menu)) {
+ ScreenOperation = UiDown;
+ ControlFlag = CfScreenOperation;
+ break;
+ } else {
+ gPriorMenuEntry = 0;
+ }
+ //
+ // This is only possible if we entered this page and the first menu option is
+ // a "non-menu" item. In that case, force it UiDown
+ //
+ if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || MenuOption->ThisTag->GrayOut) {
+ //
+ // If we previously hit an UP command and we are still sitting on a text operation
+ // we must continue going up
+ //
+ if (ScreenOperation == UiUp) {
+ ControlFlag = CfScreenOperation;
+ break;
+ } else {
+ ScreenOperation = UiDown;
+ ControlFlag = CfScreenOperation;
+ break;
+ }
+ }
+ //
+ // Set reverse attribute
+ //
+ gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT);
+ gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
+
+ //
+ // Assuming that we have a refresh linked-list created, lets annotate the
+ // appropriate entry that we are highlighting with its new attribute. Just prior to this
+ // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
+ //
+ if (gMenuRefreshHead != NULL) {
+ for (MenuRefreshEntry = gMenuRefreshHead; MenuRefreshEntry != NULL; MenuRefreshEntry = MenuRefreshEntry->Next) {
+ MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND;
+ if (MenuRefreshEntry->MenuOption == MenuOption) {
+ MenuRefreshEntry->CurrentAttribute = FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT;
+ }
+ }
+ }
+
+ if (SubMenu) {
+ ProcessOptions (MenuOption, FALSE, FileFormTagsHead, PageData, &OptionString);
+ if (OptionString != NULL) {
+ //
+ // If leading spaces on OptionString - remove the spaces
+ //
+ for (Index = 0; OptionString[Index] == L' '; Index++)
+ ;
+
+ for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
+ OptionString[Count] = OptionString[Index];
+ Count++;
+ }
+
+ OptionString[Count] = CHAR_NULL;
+
+ Width = (UINT16) gOptionBlockWidth;
+
+ OriginalRow = MenuOption->Row;
+
+ for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
+ if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
+ PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString);
+ }
+ //
+ // If there is more string to process print on the next row and increment the Skip value
+ //
+ if (StrLen (&OptionString[Index])) {
+ MenuOption->Row++;
+ }
+
+ gBS->FreePool (OutputString);
+ }
+
+ MenuOption->Row = OriginalRow;
+ } else {
+ if (NewLine) {
+ OriginalRow = MenuOption->Row;
+
+ Width = GetWidth (MenuOption->ThisTag, MenuOption->Handle);
+
+ for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
+ if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
+ PrintStringAt (Col, MenuOption->Row, OutputString);
+ }
+ //
+ // If there is more string to process print on the next row and increment the Skip value
+ //
+ if (StrLen (&MenuOption->Description[Index])) {
+ MenuOption->Row++;
+ }
+
+ gBS->FreePool (OutputString);
+ }
+
+ MenuOption->Row = OriginalRow;
+
+ }
+ }
+
+ if (((NewPos->ForwardLink != &Menu) && (ScreenOperation == UiDown)) ||
+ ((NewPos->BackLink != &Menu) && (ScreenOperation == UiUp)) ||
+ (ScreenOperation == UiNoOperation)
+ ) {
+ UpdateKeyHelp (MenuOption, FALSE);
+ }
+ } else {
+ gST->ConOut->OutputString (gST->ConOut, MenuOption->Description);
+ }
+ //
+ // Clear reverse attribute
+ //
+ gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
+ }
+ //
+ // Repaint flag will be used when process CfUpdateHelpString, so restore its value
+ // if we didn't break halfway when process CfRefreshHighLight.
+ //
+ Repaint = SavedValue;
+ break;
+
+ case CfUpdateHelpString:
+ ControlFlag = CfPrepareToReadKey;
+
+ if (SubMenu &&
+ (Repaint || NewLine ||
+ (MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) ||
+ (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) &&
+ !(gClassOfVfr == EFI_GENERAL_APPLICATION_SUBCLASS)) {
+ //
+ // Don't print anything if it is a NULL help token
+ //
+ if (MenuOption->ThisTag->Help == 0x00000000) {
+ StringPtr = (CHAR16 *) L"\0";
+ } else {
+ StringPtr = GetToken (MenuOption->ThisTag->Help, MenuOption->Handle);
+ }
+
+ ProcessHelpString (StringPtr, &FormattedString, BottomRow - TopRow);
+
+ gST->ConOut->SetAttribute (gST->ConOut, HELP_TEXT | FIELD_BACKGROUND);
+
+ for (Index = 0; Index < BottomRow - TopRow; Index++) {
+ //
+ // Pad String with spaces to simulate a clearing of the previous line
+ //
+ for (; GetStringWidth (&FormattedString[Index * gHelpBlockWidth]) / 2 < gHelpBlockWidth;) {
+ StrCat (&FormattedString[Index * gHelpBlockWidth], (CHAR16 *) L" ");
+ }
+
+ PrintStringAt (
+ LocalScreen.RightColumn - gHelpBlockWidth,
+ Index + TopRow,
+ &FormattedString[Index * gHelpBlockWidth]
+ );
+ }
+ }
+ //
+ // Reset this flag every time we finish using it.
+ //
+ Repaint = FALSE;
+ NewLine = FALSE;
+ break;
+
+ case CfPrepareToReadKey:
+ ControlFlag = CfReadKey;
+
+ for (Index = 0; Index < MenuOption->IfrNumber; Index++) {
+ FileFormTags = FileFormTags->NextFile;
+ }
+
+ ScreenOperation = UiNoOperation;
+
+ Status = gBS->HandleProtocol (
+ (VOID *) (UINTN) FileFormTags->FormTags.Tags[0].CallbackHandle,
+ &gEfiFormCallbackProtocolGuid,
+ (VOID **) &FormCallback
+ );
+
+ break;
+
+ case CfReadKey:
+ ControlFlag = CfScreenOperation;
+
+ OriginalTimeOut = FrontPageTimeOutValue;
+ do {
+ if (FrontPageTimeOutValue >= 0 && (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) && FrontPageTimeOutValue != (INT16) -1) {
+ //
+ // Remember that if set to 0, must immediately boot an option
+ //
+ if (FrontPageTimeOutValue == 0) {
+ FrontPageTimeOutValue = 0xFFFF;
+ Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_TIMEOUT;
+ }
+ break;
+ }
+
+ Status = UiWaitForSingleEvent (gST->ConIn->WaitForKey, ONE_SECOND);
+ if (Status == EFI_TIMEOUT) {
+ EFI_IFR_DATA_ENTRY *DataEntry;
+
+ DataEntry = (EFI_IFR_DATA_ENTRY *) (PageData + 1);
+
+ PageData->EntryCount = 1;
+ Count = (UINT32) ((OriginalTimeOut - FrontPageTimeOutValue) * 100 / OriginalTimeOut);
+ CopyMem (&DataEntry->Data, &Count, sizeof (UINT32));
+
+ if ((FormCallback != NULL) && (FormCallback->Callback != NULL)) {
+ FormCallback->Callback (
+ FormCallback,
+ 0xFFFF,
+ (EFI_IFR_DATA_ARRAY *) PageData,
+ NULL
+ );
+ }
+ //
+ // Count down 1 second
+ //
+ FrontPageTimeOutValue--;
+
+ } else {
+ ASSERT (!EFI_ERROR (Status));
+ PageData->EntryCount = 0;
+ if ((FormCallback != NULL) && (FormCallback->Callback != NULL)) {
+ FormCallback->Callback (
+ FormCallback,
+ 0xFFFE,
+ (EFI_IFR_DATA_ARRAY *) PageData,
+ NULL
+ );
+ }
+
+ FrontPageTimeOutValue = 0xFFFF;
+ }
+ } else {
+ //
+ // Wait for user's selection, no auto boot
+ //
+ Status = UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0);
+ }
+ } while (Status == EFI_TIMEOUT);
+
+ if (gFirstIn) {
+ gFirstIn = FALSE;
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
+ DisableQuietBoot ();
+ }
+
+ if (Status == EFI_TIMEOUT) {
+ Key.UnicodeChar = CHAR_CARRIAGE_RETURN;
+ } else {
+ Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
+ //
+ // if we encounter error, continue to read another key in.
+ //
+ if (EFI_ERROR (Status)) {
+ ControlFlag = CfReadKey;
+ continue;
+ }
+ }
+
+ switch (Key.UnicodeChar) {
+ case CHAR_CARRIAGE_RETURN:
+ Selection = MenuOption;
+ ScreenOperation = UiSelect;
+ gDirection = 0;
+ break;
+
+ //
+ // We will push the adjustment of these numeric values directly to the input handler
+ //
+ case '+':
+ case '-':
+ if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
+
+ if (Key.UnicodeChar == '+') {
+ gDirection = SCAN_RIGHT;
+ } else {
+ gDirection = SCAN_LEFT;
+ }
+
+ Status = ProcessOptions (MenuOption, TRUE, FileFormTagsHead, NULL, &OptionString);
+ }
+ break;
+
+ case '^':
+ ScreenOperation = UiUp;
+ break;
+
+ case 'V':
+ case 'v':
+ ScreenOperation = UiDown;
+ break;
+
+ case ' ':
+ if (gClassOfVfr != EFI_FRONT_PAGE_SUBCLASS) {
+ if (SubMenu) {
+ if (MenuOption->ThisTag->Operand == EFI_IFR_CHECKBOX_OP && !(MenuOption->ThisTag->GrayOut)) {
+ gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
+ gST->ConOut->OutputString (gST->ConOut, MenuOption->Description);
+ Selection = MenuOption;
+ ScreenOperation = UiSelect;
+ }
+ }
+ }
+ break;
+
+ case CHAR_NULL:
+ if (((Key.ScanCode == SCAN_F1) && ((gFunctionKeySetting & FUNCTION_ONE) != FUNCTION_ONE)) ||
+ ((Key.ScanCode == SCAN_F2) && ((gFunctionKeySetting & FUNCTION_TWO) != FUNCTION_TWO)) ||
+ ((Key.ScanCode == SCAN_F9) && ((gFunctionKeySetting & FUNCTION_NINE) != FUNCTION_NINE)) ||
+ ((Key.ScanCode == SCAN_F10) && ((gFunctionKeySetting & FUNCTION_TEN) != FUNCTION_TEN))
+ ) {
+ //
+ // If the function key has been disabled, just ignore the key.
+ //
+ } else {
+ for (Index = 0; Index < sizeof (gScanCodeToOperation) / sizeof (gScanCodeToOperation[0]); Index++) {
+ if (Key.ScanCode == gScanCodeToOperation[Index].ScanCode) {
+ if ((Key.ScanCode == SCAN_F9) || (Key.ScanCode == SCAN_F10)) {
+ if (SubMenu) {
+ ScreenOperation = gScanCodeToOperation[Index].ScreenOperation;
+ }
+ } else {
+ ScreenOperation = gScanCodeToOperation[Index].ScreenOperation;
+ }
+ }
+ }
+ }
+ break;
+ }
+ break;
+
+ case CfScreenOperation:
+ IfrBinary = gBinaryDataHead;
+
+ //
+ // Advance to the Ifr we are using
+ //
+ for (Index = 0; Index < gActiveIfr; Index++) {
+ IfrBinary = IfrBinary->Next;
+ }
+
+ if (ScreenOperation != UiPrevious && ScreenOperation != UiReset) {
+ //
+ // If the screen has no menu items, and the user didn't select UiPrevious, or UiReset
+ // ignore the selection and go back to reading keys.
+ //
+ if (IsListEmpty (&Menu)) {
+ ControlFlag = CfReadKey;
+ break;
+ }
+ //
+ // if there is nothing logical to place a cursor on, just move on to wait for a key.
+ //
+ for (Link = Menu.ForwardLink; Link != &Menu; Link = Link->ForwardLink) {
+ NextMenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ if (!(NextMenuOption->ThisTag->GrayOut) && (NextMenuOption->ThisTag->Operand != EFI_IFR_SUBTITLE_OP)) {
+ break;
+ }
+ }
+
+ if (Link == &Menu) {
+ ControlFlag = CfPrepareToReadKey;
+ break;
+ }
+ }
+
+ for (Index = 0;
+ Index < sizeof (gScreenOperationToControlFlag) / sizeof (gScreenOperationToControlFlag[0]);
+ Index++
+ ) {
+ if (ScreenOperation == gScreenOperationToControlFlag[Index].ScreenOperation) {
+ ControlFlag = gScreenOperationToControlFlag[Index].ControlFlag;
+ }
+ }
+
+ break;
+
+ case CfUiPrevious:
+ ControlFlag = CfCheckSelection;
+ //
+ // Check for tags that might have LATE_CHECK enabled. If they do, we can't switch pages or save NV data.
+ //
+ if (MenuOption != NULL) {
+ if (!SelectionsAreValid (MenuOption, FileFormTagsHead)) {
+ Selection = NULL;
+ Repaint = TRUE;
+ break;
+ }
+ }
+
+ if (IsListEmpty (&gMenuList)) {
+ Selection = NULL;
+ if (IsListEmpty (&Menu)) {
+ ControlFlag = CfReadKey;
+ }
+ break;
+ }
+
+ gLastOpr = TRUE;
+
+ while (gMenuRefreshHead != NULL) {
+ OldMenuRefreshEntry = gMenuRefreshHead->Next;
+
+ gBS->FreePool (gMenuRefreshHead);
+
+ gMenuRefreshHead = OldMenuRefreshEntry;
+ }
+ //
+ // Remove the Cached page entry, free and init the menus, flag Selection as jumping to previous page and a valid Tag
+ //
+ if (SubMenu) {
+ UiRemoveMenuListEntry (MenuOption, &Selection);
+ Selection->Previous = TRUE;
+ UiFreeMenu ();
+ UiInitMenu ();
+ }
+
+ gActiveIfr = Selection->IfrNumber;
+ return Selection;
+
+ case CfUiSelect:
+ ControlFlag = CfCheckSelection;
+
+ ExtractRequestedNvMap (FileFormTags, MenuOption->ThisTag->VariableNumber, &VariableDefinition);
+
+ if (SubMenu) {
+ if ((MenuOption->ThisTag->Operand == EFI_IFR_TEXT_OP &&
+ !(MenuOption->ThisTag->Flags & EFI_IFR_FLAG_INTERACTIVE)) ||
+ (MenuOption->ThisTag->GrayOut) ||
+ (MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) ||
+ (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
+ Selection = NULL;
+ break;
+ }
+
+ NewLine = TRUE;
+ UpdateKeyHelp (MenuOption, TRUE);
+ Status = ProcessOptions (MenuOption, TRUE, FileFormTagsHead, PageData, &OptionString);
+
+ if (EFI_ERROR (Status)) {
+ Selection = NULL;
+ Repaint = TRUE;
+ break;
+ }
+
+ if (OptionString != NULL) {
+ PrintStringAt (LocalScreen.LeftColumn + gPromptBlockWidth + 1, MenuOption->Row, OptionString);
+ }
+
+ if (MenuOption->ThisTag->Flags & EFI_IFR_FLAG_INTERACTIVE) {
+ Selection = MenuOption;
+ }
+
+ if (Selection == NULL) {
+ break;
+ }
+
+ Location = (UINT8 *) &PageData->EntryCount;
+
+ //
+ // If not a goto, dump single piece of data, otherwise dump everything
+ //
+ if (Selection->ThisTag->Operand == EFI_IFR_REF_OP) {
+ //
+ // Check for tags that might have LATE_CHECK enabled. If they do, we can't switch pages or save NV data.
+ //
+ if (!SelectionsAreValid (MenuOption, FileFormTagsHead)) {
+ Selection = NULL;
+ Repaint = TRUE;
+ break;
+ }
+
+ UiAddMenuListEntry (Selection);
+ gPriorMenuEntry = 0;
+
+ //
+ // Now that we added a menu entry specific to a goto, we can always go back when someone hits the UiPrevious
+ //
+ UiMenuList = CR (gMenuList.ForwardLink, UI_MENU_LIST, MenuLink, UI_MENU_LIST_SIGNATURE);
+ UiMenuList->FormerEntryNumber = MenuOption->EntryNumber;
+
+ gLastOpr = FALSE;
+
+ //
+ // Rewind to the beginning of the menu
+ //
+ for (; NewPos->BackLink != &Menu; NewPos = NewPos->BackLink)
+ ;
+
+ //
+ // Get Total Count of Menu entries
+ //
+ for (Count = 1; NewPos->ForwardLink != &Menu; NewPos = NewPos->ForwardLink) {
+ Count++;
+ }
+ //
+ // Rewind to the beginning of the menu
+ //
+ for (; NewPos->BackLink != &Menu; NewPos = NewPos->BackLink)
+ ;
+
+ //
+ // Copy the number of entries being described to the PageData location
+ //
+ CopyMem (&Location[0], &Count, sizeof (UINT32));
+
+ for (Index = 4; NewPos->ForwardLink != &Menu; Index = Index + MenuOption->ThisTag->StorageWidth + 2) {
+
+ MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ Location[Index] = MenuOption->ThisTag->Operand;
+ Location[Index + 1] = (UINT8) (MenuOption->ThisTag->StorageWidth + 4);
+ CopyMem (
+ &Location[Index + 4],
+ &VariableDefinition->NvRamMap[MenuOption->ThisTag->StorageStart],
+ MenuOption->ThisTag->StorageWidth
+ );
+ NewPos = NewPos->ForwardLink;
+ }
+ } else {
+
+ gPriorMenuEntry = MenuOption->EntryNumber;
+
+ Count = 1;
+
+ //
+ // Copy the number of entries being described to the PageData location
+ //
+ CopyMem (&Location[0], &Count, sizeof (UINT32));
+
+ //
+ // Start at PageData[4] since the EntryCount is a UINT32
+ //
+ Index = 4;
+
+ //
+ // Copy data to destination
+ //
+ Location[Index] = MenuOption->ThisTag->Operand;
+ Location[Index + 1] = (UINT8) (MenuOption->ThisTag->StorageWidth + 4);
+ CopyMem (
+ &Location[Index + 4],
+ &VariableDefinition->NvRamMap[MenuOption->ThisTag->StorageStart],
+ MenuOption->ThisTag->StorageWidth
+ );
+ }
+ }
+ break;
+
+ case CfUiReset:
+ ControlFlag = CfCheckSelection;
+ gLastOpr = FALSE;
+ if (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) {
+ break;
+ }
+ //
+ // If NV flag is up, prompt user
+ //
+ if (gNvUpdateRequired) {
+ Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
+
+ YesResponse = gYesResponse[0];
+ NoResponse = gNoResponse[0];
+
+ do {
+ CreateDialog (3, TRUE, 0, NULL, &Key, gEmptyString, gAreYouSure, gEmptyString);
+ } while
+ (
+ (Key.ScanCode != SCAN_ESC) &&
+ ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (NoResponse | UPPER_LOWER_CASE_OFFSET)) &&
+ ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (YesResponse | UPPER_LOWER_CASE_OFFSET))
+ );
+
+ //
+ // If the user hits the YesResponse key
+ //
+ if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (YesResponse | UPPER_LOWER_CASE_OFFSET)) {
+ } else {
+ Repaint = TRUE;
+ NewLine = TRUE;
+ break;
+ }
+ }
+ //
+ // Check for tags that might have LATE_CHECK enabled. If they do, we can't switch pages or save NV data.
+ //
+ if (MenuOption != NULL) {
+ if (!SelectionsAreValid (MenuOption, FileFormTagsHead)) {
+ Selection = NULL;
+ Repaint = TRUE;
+ NewLine = TRUE;
+ break;
+ }
+ }
+
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
+ gST->ConOut->EnableCursor (gST->ConOut, TRUE);
+
+ if (SubMenu) {
+ UiFreeMenuList ();
+ gST->ConOut->ClearScreen (gST->ConOut);
+ return NULL;
+ }
+
+ UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->Flags, FALSE);
+ UpdateStatusBar (NV_UPDATE_REQUIRED, MenuOption->ThisTag->Flags, FALSE);
+
+ if (IfrBinary->UnRegisterOnExit) {
+ Hii->RemovePack (Hii, MenuOption->Handle);
+ }
+
+ UiFreeMenu ();
+
+ //
+ // Clean up the allocated data buffers
+ //
+ FreeData (FileFormTagsHead, FormattedString, OptionString);
+
+ gST->ConOut->ClearScreen (gST->ConOut);
+ return NULL;
+
+ case CfUiLeft:
+ ControlFlag = CfCheckSelection;
+ if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
+ if (MenuOption->Skip == 1) {
+ //
+ // In the tail of the Date/Time op-code set, go left.
+ //
+ NewPos = NewPos->BackLink;
+ } else {
+ //
+ // In the middle of the Data/Time op-code set, go left.
+ //
+ NextMenuOption = CR (NewPos->ForwardLink, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ if (NextMenuOption->Skip == 1) {
+ NewPos = NewPos->BackLink;
+ }
+ }
+ }
+ break;
+
+ case CfUiRight:
+ ControlFlag = CfCheckSelection;
+ if ((MenuOption->Skip == 0) &&
+ ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP))
+ ) {
+ //
+ // We are in the head or middle of the Date/Time op-code set, advance right.
+ //
+ NewPos = NewPos->ForwardLink;
+ }
+ break;
+
+ case CfUiUp:
+ ControlFlag = CfCheckSelection;
+
+ if (NewPos->BackLink != &Menu) {
+ NewLine = TRUE;
+ //
+ // Adjust Date/Time position before we advance forward.
+ //
+ AdjustDateAndTimePosition (TRUE, &NewPos);
+
+ //
+ // Caution that we have already rewind to the top, don't go backward in this situation.
+ //
+ if (NewPos->BackLink != &Menu) {
+ NewPos = NewPos->BackLink;
+ }
+
+ PreviousMenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+
+ //
+ // Since the behavior of hitting the up arrow on a Date/Time op-code is intended
+ // to be one that back to the previous set of op-codes, we need to advance to the sencond
+ // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
+ // checking can be done.
+ //
+ DataAndTimeLineNumberPad = AdjustDateAndTimePosition (TRUE, &NewPos);
+
+ if (SubMenu) {
+ //
+ // If the previous MenuOption contains a display-only op-code, skip to the next one
+ //
+ if (PreviousMenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || PreviousMenuOption->ThisTag->GrayOut) {
+ //
+ // This is ok as long as not at the end of the list
+ //
+ if (NewPos->BackLink == &Menu) {
+ //
+ // If we are at the start of the list, then this list must start with a display only
+ // piece of data, so do not allow the backward motion
+ //
+ ScreenOperation = UiDown;
+
+ if (PreviousMenuOption->Row <= TopRow) {
+ if (TopOfScreen->BackLink != &Menu) {
+ TopOfScreen = TopOfScreen->BackLink;
+ Repaint = TRUE;
+ }
+ }
+
+ UpdateStatusBar (INPUT_ERROR, PreviousMenuOption->ThisTag->Flags, FALSE);
+ break;
+ }
+ }
+ }
+ //
+ // Check the previous menu entry to see if it was a zero-length advance. If it was,
+ // don't worry about a redraw.
+ //
+ if ((MenuOption->Row - PreviousMenuOption->Skip - DataAndTimeLineNumberPad < TopRow) ||
+ (PreviousMenuOption->Skip > MenuOption->Row)
+ ) {
+ do {
+ if (TopOfScreen->BackLink == &Menu) {
+ break;
+ }
+
+ Repaint = TRUE;
+
+ //
+ // Is the current top of screen a zero-advance op-code?
+ // If so, keep moving forward till we hit a >0 advance op-code
+ //
+ SavedMenuOption = CR (TopOfScreen->BackLink, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ TopOfScreen = TopOfScreen->BackLink;
+ } while (SavedMenuOption->Skip == 0);
+ //
+ // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
+ //
+ AdjustDateAndTimePosition (TRUE, &TopOfScreen);
+ }
+
+ UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->Flags, FALSE);
+ } else {
+ if (SubMenu) {
+ SavedMenuOption = MenuOption;
+ MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || MenuOption->ThisTag->GrayOut) {
+ //
+ // If we are at the end of the list and sitting on a text op, we need to more forward
+ //
+ ScreenOperation = UiDown;
+ ControlFlag = CfScreenOperation;
+ break;
+ }
+
+ MenuOption = SavedMenuOption;
+ }
+ }
+ break;
+
+ case CfUiPageUp:
+ ControlFlag = CfCheckSelection;
+
+ SavedListEntry = NewPos;
+ Link = TopOfScreen;
+ for (Index = BottomRow; Index >= TopRow + 1; Index -= MenuOption->Skip) {
+ if (Link->BackLink == &Menu) {
+ TopOfScreen = Link;
+ Link = SavedListEntry;
+ MenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ break;
+ }
+
+ NewLine = TRUE;
+ Repaint = TRUE;
+ Link = Link->BackLink;
+ MenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ TopOfScreen = Link;
+ SavedListEntry = Link;
+ }
+
+ NewPos = Link;
+
+ //
+ // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
+ // Don't do this when we are already in the first page.
+ //
+ if (Repaint) {
+ AdjustDateAndTimePosition (TRUE, &TopOfScreen);
+ AdjustDateAndTimePosition (TRUE, &NewPos);
+ MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ }
+ break;
+
+ case CfUiPageDown:
+ ControlFlag = CfCheckSelection;
+
+ SavedListEntry = NewPos;
+ Link = TopOfScreen;
+ NewPos = TopOfScreen;
+ for (Index = TopRow; Index <= BottomRow - 1; Index += MenuOption->Skip) {
+ if (NewPos->ForwardLink == &Menu) {
+ NewPos = SavedListEntry;
+ MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ Link = TopOfScreen;
+ NewLine = FALSE;
+ Repaint = FALSE;
+ break;
+ }
+
+ NewLine = TRUE;
+ Repaint = TRUE;
+ MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ NewPos = NewPos->ForwardLink;
+ Link = NewPos;
+ }
+
+ TopOfScreen = Link;
+
+ //
+ // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
+ // Don't do this when we are already in the last page.
+ //
+ if (Repaint) {
+ AdjustDateAndTimePosition (TRUE, &TopOfScreen);
+ AdjustDateAndTimePosition (TRUE, &NewPos);
+ MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ }
+ break;
+
+ case CfUiDown:
+ ControlFlag = CfCheckSelection;
+ //
+ // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
+ // to be one that progresses to the next set of op-codes, we need to advance to the last
+ // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
+ // checking can be done. The only other logic we need to introduce is that if a Date/Time
+ // op-code is the last entry in the menu, we need to rewind back to the first op-code of
+ // the Date/Time op-code.
+ //
+ DataAndTimeLineNumberPad = AdjustDateAndTimePosition (FALSE, &NewPos);
+
+ if (NewPos->ForwardLink != &Menu) {
+ NewLine = TRUE;
+ NewPos = NewPos->ForwardLink;
+ NextMenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+
+ if (SubMenu) {
+ //
+ // If the next MenuOption contains a display-only op-code, skip to the next one
+ // Also if the next MenuOption is date or time,
+ //
+ if (NextMenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || NextMenuOption->ThisTag->GrayOut) {
+ //
+ // This is ok as long as not at the end of the list
+ //
+ if (NewPos == &Menu) {
+ //
+ // If we are at the end of the list, then this list must end with a display only
+ // piece of data, so do not allow the forward motion
+ //
+ UpdateStatusBar (INPUT_ERROR, NextMenuOption->ThisTag->Flags, FALSE);
+ NewPos = NewPos->BackLink;
+ ScreenOperation = UiUp;
+ break;
+ }
+ }
+ }
+ //
+ // An option might be multi-line, so we need to reflect that data in the overall skip value
+ //
+ UpdateOptionSkipLines (PageData, NextMenuOption, FileFormTagsHead, &OptionString, SkipValue);
+
+ if (NextMenuOption->Skip > 1) {
+ Temp = MenuOption->Row + MenuOption->Skip + NextMenuOption->Skip - 1;
+ } else {
+ Temp = MenuOption->Row + MenuOption->Skip + DataAndTimeLineNumberPad;
+ }
+ //
+ // If we are going to scroll
+ //
+ if (Temp > BottomRow) {
+ do {
+ //
+ // Is the current top of screen a zero-advance op-code?
+ // If so, keep moving forward till we hit a >0 advance op-code
+ //
+ SavedMenuOption = CR (TopOfScreen, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+
+ //
+ // If bottom op-code is more than one line or top op-code is more than one line
+ //
+ if ((NextMenuOption->Skip > 1) || (MenuOption->Skip > 1)) {
+ //
+ // Is the bottom op-code greater than or equal in size to the top op-code?
+ //
+ if ((Temp - BottomRow) >= (SavedMenuOption->Skip - OldSkipValue)) {
+ //
+ // Skip the top op-code
+ //
+ TopOfScreen = TopOfScreen->ForwardLink;
+ Difference = (Temp - BottomRow) - (SavedMenuOption->Skip - OldSkipValue);
+
+ OldSkipValue = Difference;
+
+ SavedMenuOption = CR (TopOfScreen, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+
+ //
+ // If we have a remainder, skip that many more op-codes until we drain the remainder
+ //
+ for (;
+ Difference >= (INTN) SavedMenuOption->Skip;
+ Difference = Difference - (INTN) SavedMenuOption->Skip
+ ) {
+ //
+ // Since the Difference is greater than or equal to this op-code's skip value, skip it
+ //
+ TopOfScreen = TopOfScreen->ForwardLink;
+ SavedMenuOption = CR (TopOfScreen, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ if (Difference < (INTN) SavedMenuOption->Skip) {
+ Difference = SavedMenuOption->Skip - Difference - 1;
+ break;
+ } else {
+ if (Difference == (INTN) SavedMenuOption->Skip) {
+ TopOfScreen = TopOfScreen->ForwardLink;
+ SavedMenuOption = CR (TopOfScreen, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ Difference = SavedMenuOption->Skip - Difference;
+ break;
+ }
+ }
+ }
+ //
+ // Since we will act on this op-code in the next routine, and increment the
+ // SkipValue, set the skips to one less than what is required.
+ //
+ SkipValue = Difference - 1;
+
+ } else {
+ //
+ // Since we will act on this op-code in the next routine, and increment the
+ // SkipValue, set the skips to one less than what is required.
+ //
+ SkipValue = OldSkipValue + (Temp - BottomRow) - 1;
+ }
+ } else {
+ if ((OldSkipValue + 1) == (INTN) SavedMenuOption->Skip) {
+ TopOfScreen = TopOfScreen->ForwardLink;
+ break;
+ } else {
+ SkipValue = OldSkipValue;
+ }
+ }
+ //
+ // If the op-code at the top of the screen is more than one line, let's not skip it yet
+ // Let's set a skip flag to smoothly scroll the top of the screen.
+ //
+ if (SavedMenuOption->Skip > 1) {
+ if (SavedMenuOption == NextMenuOption) {
+ SkipValue = 0;
+ } else {
+ SkipValue++;
+ }
+ } else {
+ SkipValue = 0;
+ TopOfScreen = TopOfScreen->ForwardLink;
+ }
+ } while (SavedMenuOption->Skip == 0);
+
+ Repaint = TRUE;
+ OldSkipValue = SkipValue;
+ }
+
+ UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->Flags, FALSE);
+
+ } else {
+ if (SubMenu) {
+ SavedMenuOption = MenuOption;
+ MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || MenuOption->ThisTag->GrayOut) {
+ //
+ // If we are at the end of the list and sitting on a text op, we need to more forward
+ //
+ ScreenOperation = UiUp;
+ ControlFlag = CfScreenOperation;
+ break;
+ }
+
+ MenuOption = SavedMenuOption;
+ //
+ // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
+ //
+ AdjustDateAndTimePosition (TRUE, &NewPos);
+ }
+ }
+ break;
+
+ case CfUiSave:
+ ControlFlag = CfCheckSelection;
+ //
+ // Check for tags that might have LATE_CHECK enabled. If they do, we can't switch pages or save NV data.
+ //
+ if (MenuOption != NULL) {
+ if (!SelectionsAreValid (MenuOption, FileFormTagsHead)) {
+ Selection = NULL;
+ Repaint = TRUE;
+ break;
+ }
+ }
+ //
+ // If callbacks are active, and the callback has a Write method, try to use it
+ //
+ if (FileFormTags->VariableDefinitions->VariableName == NULL) {
+ if ((FormCallback != NULL) && (FormCallback->NvWrite != NULL)) {
+ Status = FormCallback->NvWrite (
+ FormCallback,
+ (CHAR16 *) L"Setup",
+ &FileFormTags->FormTags.Tags[0].GuidValue,
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+ VariableDefinition->VariableSize,
+ (VOID *) VariableDefinition->NvRamMap,
+ &gResetRequired
+ );
+
+ } else {
+ Status = gRT->SetVariable (
+ (CHAR16 *) L"Setup",
+ &FileFormTags->FormTags.Tags[0].GuidValue,
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+ VariableDefinition->VariableSize,
+ (VOID *) VariableDefinition->NvRamMap
+ );
+ }
+ } else {
+ VariableDefinition = FileFormTags->VariableDefinitions;
+
+ for (; VariableDefinition != NULL; VariableDefinition = VariableDefinition->Next) {
+ if ((FormCallback != NULL) && (FormCallback->NvWrite != NULL)) {
+ Status = FormCallback->NvWrite (
+ FormCallback,
+ VariableDefinition->VariableName,
+ &VariableDefinition->Guid,
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+ VariableDefinition->VariableSize,
+ (VOID *) VariableDefinition->NvRamMap,
+ &gResetRequired
+ );
+
+ } else {
+ Status = gRT->SetVariable (
+ VariableDefinition->VariableName,
+ &VariableDefinition->Guid,
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+ VariableDefinition->VariableSize,
+ (VOID *) VariableDefinition->NvRamMap
+ );
+ }
+ }
+ }
+
+ UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->Flags, FALSE);
+ UpdateStatusBar (NV_UPDATE_REQUIRED, MenuOption->ThisTag->Flags, FALSE);
+ break;
+
+ case CfUiDefault:
+ ControlFlag = CfCheckSelection;
+
+ NvMapListHead = NULL;
+
+ Status = Hii->GetDefaultImage (Hii, MenuOption->Handle, EFI_IFR_FLAG_DEFAULT, &NvMapListHead);
+
+ if (!EFI_ERROR (Status)) {
+ ASSERT_EFI_ERROR (NULL != NvMapListHead);
+
+ NvMapListNode = NvMapListHead;
+
+ while (NULL != NvMapListNode) {
+ if (FileFormTags->VariableDefinitions->VariableId == NvMapListNode->VariablePack->VariableId) {
+ NvMap = (VOID *) ((CHAR8 *) NvMapListNode->VariablePack + sizeof (EFI_HII_VARIABLE_PACK) + NvMapListNode->VariablePack->VariableNameLength);
+ NvMapSize = NvMapListNode->VariablePack->Header.Length - sizeof (EFI_HII_VARIABLE_PACK) - NvMapListNode->VariablePack->VariableNameLength;
+ break;
+ }
+ NvMapListNode = NvMapListNode->NextVariablePack;
+ }
+
+ //
+ // Free the buffer that was allocated.
+ //
+ gBS->FreePool (FileFormTags->VariableDefinitions->NvRamMap);
+ gBS->FreePool (FileFormTags->VariableDefinitions->FakeNvRamMap);
+
+ //
+ // Allocate, copy the NvRamMap.
+ //
+ FileFormTags->VariableDefinitions->VariableFakeSize = (UINT16) (FileFormTags->VariableDefinitions->VariableFakeSize - FileFormTags->VariableDefinitions->VariableSize);
+ FileFormTags->VariableDefinitions->VariableSize = (UINT16) NvMapSize;
+ FileFormTags->VariableDefinitions->VariableFakeSize = (UINT16) (FileFormTags->VariableDefinitions->VariableFakeSize + FileFormTags->VariableDefinitions->VariableSize);
+
+ FileFormTags->VariableDefinitions->NvRamMap = AllocateZeroPool (FileFormTags->VariableDefinitions->VariableSize);
+ FileFormTags->VariableDefinitions->FakeNvRamMap = AllocateZeroPool (NvMapSize + FileFormTags->VariableDefinitions->VariableFakeSize);
+
+ CopyMem (FileFormTags->VariableDefinitions->NvRamMap, NvMap, NvMapSize);
+ gBS->FreePool (NvMapListHead);
+ }
+
+ UpdateStatusBar (NV_UPDATE_REQUIRED, MenuOption->ThisTag->Flags, TRUE);
+ Repaint = TRUE;
+ //
+ // After the repaint operation, we should refresh the highlight.
+ //
+ NewLine = TRUE;
+ break;
+
+ case CfUiNoOperation:
+ ControlFlag = CfCheckSelection;
+ break;
+
+ case CfExit:
+ while (gMenuRefreshHead != NULL) {
+ OldMenuRefreshEntry = gMenuRefreshHead->Next;
+
+ gBS->FreePool (gMenuRefreshHead);
+
+ gMenuRefreshHead = OldMenuRefreshEntry;
+ }
+
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0, Row + 4);
+ gST->ConOut->EnableCursor (gST->ConOut, TRUE);
+ gST->ConOut->OutputString (gST->ConOut, (CHAR16 *) L"\n");
+
+ gActiveIfr = MenuOption->IfrNumber;
+ return Selection;
+
+ default:
+ break;
+ }
+ }
+}
+
+BOOLEAN
+ValueIsScroll (
+ IN BOOLEAN Direction,
+ IN LIST_ENTRY *CurrentPos
+ )
+/*++
+
+Routine Description:
+ Determine if the menu is the last menu that can be selected.
+
+Arguments:
+ Direction - the scroll direction. False is down. True is up.
+
+Returns:
+ FALSE -- the menu isn't the last menu that can be selected.
+ TRUE -- the menu is the last menu that can be selected.
+--*/
+{
+ LIST_ENTRY *Temp;
+ UI_MENU_OPTION *MenuOption;
+ MenuOption = NULL;
+
+ Temp = Direction ? CurrentPos->BackLink : CurrentPos->ForwardLink;
+
+ if (Temp == &Menu) {
+ return TRUE;
+ }
+
+ for (; Temp != &Menu; Temp = Direction ? Temp->BackLink : Temp->ForwardLink) {
+ MenuOption = CR (Temp, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ if (!(MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || MenuOption->ThisTag->GrayOut)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+UINTN
+AdjustDateAndTimePosition (
+ IN BOOLEAN DirectionUp,
+ IN LIST_ENTRY **CurrentPosition
+ )
+/*++
+Routine Description:
+ Adjust Data and Time tag position accordingly.
+ Data format : [01/02/2004] [11:22:33]
+ Line number : 0 0 1 0 0 1
+
+Arguments:
+ Direction - the up or down direction. False is down. True is up.
+ CurrentPos - Current position.
+
+Returns:
+ Return line number to pad. It is possible that we stand on a zero-advance
+ data or time opcode, so pad one line when we judge if we are going to scroll outside.
+--*/
+{
+ UINTN Count;
+ LIST_ENTRY *NewPosition;
+ UI_MENU_OPTION *MenuOption;
+ UINTN PadLineNumber;
+
+ PadLineNumber = 0;
+ NewPosition = *CurrentPosition;
+ MenuOption = CR (NewPosition, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+
+ if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
+ //
+ // Calculate the distance from current position to the last Date/Time op-code.
+ //
+ Count = 0;
+ while (MenuOption->ThisTag->NumberOfLines == 0) {
+ Count++;
+ NewPosition = NewPosition->ForwardLink;
+ MenuOption = CR (NewPosition, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
+ PadLineNumber = 1;
+ }
+
+ NewPosition = *CurrentPosition;
+ if (DirectionUp) {
+ //
+ // Since the behavior of hitting the up arrow on a Date/Time op-code is intended
+ // to be one that back to the previous set of op-codes, we need to advance to the first
+ // Date/Time op-code and leave the remaining logic in CfUiUp intact so the appropriate
+ // checking can be done.
+ //
+ while (Count++ < 2) {
+ NewPosition = NewPosition->BackLink;
+ }
+ } else {
+ //
+ // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
+ // to be one that progresses to the next set of op-codes, we need to advance to the last
+ // Date/Time op-code and leave the remaining logic in CfUiDown intact so the appropriate
+ // checking can be done.
+ //
+ while (Count-- > 0) {
+ NewPosition = NewPosition->ForwardLink;
+ }
+ }
+
+ *CurrentPosition = NewPosition;
+ }
+
+ return PadLineNumber;
+}