| /**@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; | |
| } |