/**@file Implementation for UI. Copyright (c) 2006 - 2007, 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. **/ #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); 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); 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 // FreePool (MenuOption->Description); FreePool (MenuOption); } } STATIC 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) { 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 (EVT_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 (EVT_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; 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) { WaitForKeyStroke (&Key); CopyMem (KeyValue, &Key, sizeof (EFI_INPUT_KEY)); } else { do { WaitForKeyStroke (&Key); switch (Key.UnicodeChar) { case CHAR_NULL: switch (Key.ScanCode) { case SCAN_ESC: FreePool (TempString); 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; FreePool (TempString); 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 & EFI_IFR_FLAG_RESET_REQUIRED) == EFI_IFR_FLAG_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; } FreePool (InputErrorMessage); 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) { FreePool (FormattedString); } if (OptionString != NULL) { 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) { FreePool (FormTags->Tags[Index].IntList); } } if (PreviousFormTags != NULL) { FreePool (FormTags->Tags); FormTags = PreviousFormTags; FreePool (FormTags->Next); FormTags->Next = NULL; } else { 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 // 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; } FreePool (VariableDefinition->VariableName); if (VariableDefinition->NvRamMap != NULL) { FreePool (VariableDefinition->NvRamMap); } if (VariableDefinition->FakeNvRamMap != NULL) { FreePool (VariableDefinition->FakeNvRamMap); } if (PreviousVariableDefinition != NULL) { VariableDefinition = PreviousVariableDefinition; FreePool (VariableDefinition->Next); VariableDefinition->Next = NULL; } else { FreePool (VariableDefinition); VariableDefinition = NULL; } } if (PreviousFileForm != NULL) { FileForm = PreviousFileForm; FreePool (FileForm->NextFile); FileForm->NextFile = NULL; } else { 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; } FreePool (IfrBinary->IfrPackage); if (PreviousIfrBinary != NULL) { IfrBinary = PreviousIfrBinary; FreePool (IfrBinary->Next); IfrBinary->Next = NULL; } else { FreePool (IfrBinary); IfrBinary = NULL; } } FreePool (gPreviousValue); gPreviousValue = NULL; // // Free Browser Strings // FreePool (gPressEnter); FreePool (gConfirmError); FreePool (gConfirmPassword); FreePool (gPromptForNewPassword); FreePool (gPromptForPassword); FreePool (gToggleCheckBox); FreePool (gNumericInput); FreePool (gMakeSelection); FreePool (gMoveHighlight); FreePool (gEscapeString); FreePool (gEnterCommitString); FreePool (gEnterString); FreePool (gFunctionOneString); FreePool (gFunctionTwoString); FreePool (gFunctionNineString); FreePool (gFunctionTenString); return ; } STATIC 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; 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 { 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); 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); 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) { // //NARROW_CHAR can not be printed in screen, so if a line only contain the two CHARs: 'NARROW_CHAR + CHAR_CARRIAGE_RETURN' , it is a empty line in Screen. //To avoid displaying this empty line in screen, just skip the two CHARs here. // if ((InputString[*Index] == NARROW_CHAR) && (InputString[*Index + 1] == CHAR_CARRIAGE_RETURN)) { *Index = *Index + 2; } // // 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; } } STATIC 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; 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) { 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++; } } } 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; 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++; } } 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 (MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP || MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP ) { // // 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++; } } } 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++; } } } FreePool (OutputString); if (Temp2 != 0) { Temp2--; } } Row = OriginalRow; 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 (MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP || MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP ) { // // 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++; } 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++; } 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 (MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP || MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP ) { // // 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++; } 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++; } 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]) / 2 < gHelpBlockWidth;) { StrCat (&FormattedString[Index * gHelpBlockWidth * 2], (CHAR16 *) L" "); } PrintStringAt ( LocalScreen.RightColumn - gHelpBlockWidth, Index + TopRow, &FormattedString[Index * gHelpBlockWidth * 2] ); } } // // 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; 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. // FreePool (FileFormTags->VariableDefinitions->NvRamMap); 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); ASSERT (FileFormTags->VariableDefinitions->NvRamMap != NULL); FileFormTags->VariableDefinitions->FakeNvRamMap = AllocateZeroPool (NvMapSize + FileFormTags->VariableDefinitions->VariableFakeSize); ASSERT (FileFormTags->VariableDefinitions->FakeNvRamMap != NULL); CopyMem (FileFormTags->VariableDefinitions->NvRamMap, NvMap, NvMapSize); 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; 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; }