From 7c6c064ca8ebb5baf5104e8a4764cd3c19ef8bf1 Mon Sep 17 00:00:00 2001 From: Eric Dong Date: Mon, 12 Aug 2013 02:19:56 +0000 Subject: [PATCH 1/1] Update Browser to provide the customization possibilities. Signed-off-by: Eric Dong Reviewed-by: Liming Gao Tested-by: Laszlo Ersek git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@14540 6f19259b-4bc3-4df7-8a09-765794883524 --- .../Include/Library/CustomizedDisplayLib.h | 356 ++ .../Include/Protocol/DisplayProtocol.h | 350 ++ .../Include/Protocol/FormBrowserEx2.h | 90 + .../Library/CustomizedDisplayLib/Colors.h | 44 + .../CustomizedDisplayLib.c | 936 +++++ .../CustomizedDisplayLib.inf | 74 + .../CustomizedDisplayLib.uni | Bin 0 -> 7256 bytes .../CustomizedDisplayLibInternal.c | 937 +++++ .../CustomizedDisplayLibInternal.h | 307 ++ MdeModulePkg/MdeModulePkg.dec | 10 + MdeModulePkg/MdeModulePkg.dsc | 2 + .../DisplayEngineDxe/DisplayEngineDxe.inf | 70 + .../Universal/DisplayEngineDxe/FormDisplay.c | 3200 +++++++++++++++++ .../Universal/DisplayEngineDxe/FormDisplay.h | 580 +++ .../DisplayEngineDxe/FormDisplayStr.uni | Bin 0 -> 8660 bytes .../Universal/DisplayEngineDxe/InputHandler.c | 1531 ++++++++ .../Universal/DisplayEngineDxe/Print.c | 54 + .../DisplayEngineDxe/ProcessOptions.c | 1286 +++++++ .../Universal/SetupBrowserDxe/Expression.c | 6 +- .../Universal/SetupBrowserDxe/Expression.h | 265 ++ .../Universal/SetupBrowserDxe/IfrParse.c | 65 +- .../Universal/SetupBrowserDxe/Presentation.c | 2581 ++++++++----- .../Universal/SetupBrowserDxe/Setup.c | 2359 +++++++----- .../Universal/SetupBrowserDxe/Setup.h | 814 +++-- .../SetupBrowserDxe/SetupBrowserDxe.inf | 28 +- 25 files changed, 13652 insertions(+), 2293 deletions(-) create mode 100644 MdeModulePkg/Include/Library/CustomizedDisplayLib.h create mode 100644 MdeModulePkg/Include/Protocol/DisplayProtocol.h create mode 100644 MdeModulePkg/Include/Protocol/FormBrowserEx2.h create mode 100644 MdeModulePkg/Library/CustomizedDisplayLib/Colors.h create mode 100644 MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.c create mode 100644 MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf create mode 100644 MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.uni create mode 100644 MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLibInternal.c create mode 100644 MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLibInternal.h create mode 100644 MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf create mode 100644 MdeModulePkg/Universal/DisplayEngineDxe/FormDisplay.c create mode 100644 MdeModulePkg/Universal/DisplayEngineDxe/FormDisplay.h create mode 100644 MdeModulePkg/Universal/DisplayEngineDxe/FormDisplayStr.uni create mode 100644 MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c create mode 100644 MdeModulePkg/Universal/DisplayEngineDxe/Print.c create mode 100644 MdeModulePkg/Universal/DisplayEngineDxe/ProcessOptions.c create mode 100644 MdeModulePkg/Universal/SetupBrowserDxe/Expression.h diff --git a/MdeModulePkg/Include/Library/CustomizedDisplayLib.h b/MdeModulePkg/Include/Library/CustomizedDisplayLib.h new file mode 100644 index 0000000000..9644d9331c --- /dev/null +++ b/MdeModulePkg/Include/Library/CustomizedDisplayLib.h @@ -0,0 +1,356 @@ +/** @file + This library class defines a set of interfaces to customize Display module + +Copyright (c) 2013, 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 that 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. + +**/ + +#ifndef __CUSTOMIZED_DISPLAY_LIB_H__ +#define __CUSTOMIZED_DISPLAY_LIB_H__ + +#include + +/** ++------------------------------------------------------------------------------+ +? Setup Page ? ++------------------------------------------------------------------------------+ + +Statement +Statement +Statement + + + + + ++------------------------------------------------------------------------------+ +?F1=Scroll Help F9=Reset to Defaults F10=Save and Exit ? +| ^"=Move Highlight Toggles Checkbox Esc=Discard Changes | ++------------------------------------------------------------------------------+ + StatusBar +**/ + +/** + This funtion defines Page Frame and Backgroud. + + Based on the above layout, it will be responsible for HeaderHeight, FooterHeight, + StatusBarHeight and Backgroud. And, it will reserve Screen for Statement. + + @param[in] FormData Form Data to be shown in Page. + @param[out] ScreenForStatement Screen to be used for Statement. (Prompt, Value and Help) + + @return Status +**/ +EFI_STATUS +EFIAPI +DisplayPageFrame ( + IN FORM_DISPLAY_ENGINE_FORM *FormData, + OUT EFI_SCREEN_DESCRIPTOR *ScreenForStatement + ); + +/** + Clear Screen to the initial state. +**/ +VOID +EFIAPI +ClearDisplayPage ( + VOID + ); + +/** + This function updates customized key panel's help information. + The library will prepare those Strings for the basic key, ESC, Enter, Up/Down/Left/Right, +/-. + and arrange them in Footer panel. + + @param[in] FormData Form Data to be shown in Page. FormData has the highlighted statement. + @param[in] Statement The statement current selected. + @param[in] Selected Whether or not a tag be selected. TRUE means Enter has hit this question. +**/ +VOID +EFIAPI +RefreshKeyHelp ( + IN FORM_DISPLAY_ENGINE_FORM *FormData, + IN FORM_DISPLAY_ENGINE_STATEMENT *Statement, + IN BOOLEAN Selected + ); + +/** + Update status bar. + + This function updates the status bar on the bottom of menu screen. It just shows StatusBar. + Original logic in this function should be splitted out. + + @param[in] MessageType The type of message to be shown. InputError or Configuration Changed. + @param[in] State Show or Clear Message. +**/ +VOID +EFIAPI +UpdateStatusBar ( + IN UINTN MessageType, + IN BOOLEAN State + ); + +/** + Create popup window. + + This function draws OEM/Vendor specific pop up windows. + + @param[out] Key User Input Key + @param ... String to be shown in Popup. The variable argument list is terminated by a NULL. + +**/ +VOID +EFIAPI +CreateDialog ( + OUT EFI_INPUT_KEY *Key, OPTIONAL + ... + ); + +/** + Confirm how to handle the changed data. + + @return Action BROWSER_ACTION_SUBMIT, BROWSER_ACTION_DISCARD or other values. +**/ +UINTN +EFIAPI +ConfirmDataChange ( + VOID + ); + +/** + OEM specifies whether Setup exits Page by ESC key. + + This function customized the behavior that whether Setup exits Page so that + system able to boot when configuration is not changed. + + @retval TRUE Exits FrontPage + @retval FALSE Don't exit FrontPage. +**/ +BOOLEAN +EFIAPI +FormExitPolicy ( + VOID + ); + +/** + Set Timeout value for a ceratain Form to get user response. + + This function allows to set timeout value on a ceratain form if necessary. + If timeout is not zero, the form will exit if user has no response in timeout. + + @param[in] FormData Form Data to be shown in Page + + @return 0 No timeout for this form. + @return > 0 Timeout value in 100 ns units. +**/ +UINT64 +EFIAPI +FormExitTimeout ( + IN FORM_DISPLAY_ENGINE_FORM *FormData + ); + +// +// Print Functions +// +/** + Prints a unicode string to the default console, at + the supplied cursor position, using L"%s" format. + + @param Column The cursor position to print the string at. When it is -1, use current Position. + @param Row The cursor position to print the string at. When it is -1, use current Position. + @param String String pointer. + + @return Length of string printed to the console + +**/ +UINTN +EFIAPI +PrintStringAt ( + IN UINTN Column, + IN UINTN Row, + IN CHAR16 *String + ); + + +/** + Prints a unicode string with the specified width to the default console, at + the supplied cursor position, using L"%s" format. + + @param Column The cursor position to print the string at. When it is -1, use current Position. + @param Row The cursor position to print the string at. When it is -1, use current Position. + @param String String pointer. + @param Width Width for String to be printed. If the print length of String < Width, + Space char (L' ') will be used to append String. + + @return Length of string printed to the console + +**/ +UINTN +EFIAPI +PrintStringAtWithWidth ( + IN UINTN Column, + IN UINTN Row, + IN CHAR16 *String, + IN UINTN Width + ); + +/** + Prints a chracter to the default console, at + the supplied cursor position, using L"%c" format. + + @param Column The cursor position to print the string at. When it is -1, use current Position. + @param Row The cursor position to print the string at. When it is -1, use current Position. + @param Character Character to print. + + @return Length of string printed to the console. + +**/ +UINTN +EFIAPI +PrintCharAt ( + IN UINTN Column, + IN UINTN Row, + CHAR16 Character + ); + +/** + Clear retangle with specified text attribute. + + @param LeftColumn Left column of retangle. + @param RightColumn Right column of retangle. + @param TopRow Start row of retangle. + @param BottomRow End row of retangle. + @param TextAttribute The character foreground and background. + +**/ +VOID +EFIAPI +ClearLines ( + IN UINTN LeftColumn, + IN UINTN RightColumn, + IN UINTN TopRow, + IN UINTN BottomRow, + IN UINTN TextAttribute + ); + +// +// Color Setting Functions +// +/** + Get OEM/Vendor specific popup attribute colors. + + @retval Byte code color setting for popup color. +**/ +UINT8 +EFIAPI +GetPopupColor ( + VOID + ); + +/** + Get OEM/Vendor specific popup attribute colors. + + @retval Byte code color setting for popup inverse color. +**/ +UINT8 +EFIAPI +GetPopupInverseColor ( + VOID + ); + +/** + Get OEM/Vendor specific PickList color attribute. + + @retval Byte code color setting for pick list color. +**/ +UINT8 +EFIAPI +GetPickListColor ( + VOID + ); + +/** + Get OEM/Vendor specific arrow color attribute. + + @retval Byte code color setting for arrow color. +**/ +UINT8 +EFIAPI +GetArrowColor ( + VOID + ); + +/** + Get OEM/Vendor specific info text color attribute. + + @retval Byte code color setting for info text color. +**/ +UINT8 +EFIAPI +GetInfoTextColor ( + VOID + ); + +/** + Get OEM/Vendor specific help text color attribute. + + @retval Byte code color setting for help text color. +**/ +UINT8 +EFIAPI +GetHelpTextColor ( + VOID + ); + +/** + Get OEM/Vendor specific grayed out text color attribute. + + @retval Byte code color setting for grayed out text color. +**/ +UINT8 +EFIAPI +GetGrayedTextColor ( + VOID + ); + +/** + Get OEM/Vendor specific highlighted text color attribute. + + @retval Byte code color setting for highlight text color. +**/ +UINT8 +EFIAPI +GetHighlightTextColor ( + VOID + ); + +/** + Get OEM/Vendor specific field text color attribute. + + @retval Byte code color setting for field text color. +**/ +UINT8 +EFIAPI +GetFieldTextColor ( + VOID + ); + +/** + Get OEM/Vendor specific subtitle text color attribute. + + @retval Byte code color setting for subtitle text color. +**/ +UINT8 +EFIAPI +GetSubTitleTextColor ( + VOID + ); + +#endif diff --git a/MdeModulePkg/Include/Protocol/DisplayProtocol.h b/MdeModulePkg/Include/Protocol/DisplayProtocol.h new file mode 100644 index 0000000000..d4f0debf9c --- /dev/null +++ b/MdeModulePkg/Include/Protocol/DisplayProtocol.h @@ -0,0 +1,350 @@ +/** @file + FormDiplay protocol to show Form + +Copyright (c) 2013, 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 that 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. + +**/ + +#ifndef __DISPLAY_PROTOCOL_H__ +#define __DISPLAY_PROTOCOL_H__ + +#include + +#define EDKII_FORM_DISPLAY_ENGINE_PROTOCOL_GUID \ + { 0x9bbe29e9, 0xfda1, 0x41ec, { 0xad, 0x52, 0x45, 0x22, 0x13, 0x74, 0x2d, 0x2e } } + +// +// Do nothing. +// +#define BROWSER_ACTION_NONE BIT16 +// +// ESC Exit +// +#define BROWSER_ACTION_FORM_EXIT BIT17 + +#define BROWSER_SUCCESS 0x0 +#define BROWSER_ERROR BIT31 +#define BROWSER_SUBMIT_FAIL BROWSER_ERROR | 0x01 +#define BROWSER_NO_SUBMIT_IF BROWSER_ERROR | 0x02 +#define BROWSER_FORM_NOT_FOUND BROWSER_ERROR | 0x03 +#define BROWSER_FORM_SUPPRESS BROWSER_ERROR | 0x04 +#define BROWSER_PROTOCOL_NOT_FOUND BROWSER_ERROR | 0x05 + +#define FORM_DISPLAY_ENGINE_STATEMENT_VERSION_1 0x10000 +#define FORM_DISPLAY_ENGINE_VERSION_1 0x10000 + +typedef struct { + // + // HII Data Type + // + UINT8 Type; + // + // Buffer Data and Length if Type is EFI_IFR_TYPE_BUFFER or EFI_IFR_TYPE_STRING + // + UINT8 *Buffer; + UINT16 BufferLen; + EFI_IFR_TYPE_VALUE Value; +} EFI_HII_VALUE; + +#define DISPLAY_QUESTION_OPTION_SIGNATURE SIGNATURE_32 ('Q', 'O', 'P', 'T') + +typedef struct { + UINTN Signature; + LIST_ENTRY Link; + // + // OneOfOption Data + // + EFI_IFR_ONE_OF_OPTION *OptionOpCode; + // + // Option ImageId and AnimationId + // + EFI_IMAGE_ID ImageId; + EFI_ANIMATION_ID AnimationId; +} DISPLAY_QUESTION_OPTION; + +#define DISPLAY_QUESTION_OPTION_FROM_LINK(a) CR (a, DISPLAY_QUESTION_OPTION, Link, DISPLAY_QUESTION_OPTION_SIGNATURE) + +typedef struct _FORM_DISPLAY_ENGINE_STATEMENT FORM_DISPLAY_ENGINE_STATEMENT; +typedef struct _FORM_DISPLAY_ENGINE_FORM FORM_DISPLAY_ENGINE_FORM; + +#define STATEMENT_VALID 0x0 +#define STATEMENT_INVALID BIT31 + +#define INCOSISTENT_IF_TRUE STATEMENT_INVALID | 0x01 +#define WARNING_IF_TRUE STATEMENT_INVALID | 0x02 +#define STRING_TOO_LONG STATEMENT_INVALID | 0x03 +// ... to be extended. + +typedef struct { + // + // StringId for INCONSITENT_IF or WARNING_IF + // + EFI_STRING_ID StringId; + // + // TimeOut for WARNING_IF + // + UINT8 TimeOut; +} STATEMENT_ERROR_INFO; + +/** + Perform value check for a question. + + @param Form Form where Statement is in. + @param Statement Value will check for it. + @param Value New value will be checked. + + @retval Status Value Status + +**/ +typedef +UINT32 +(EFIAPI *VALIDATE_QUESTION) ( + IN FORM_DISPLAY_ENGINE_FORM *Form, + IN FORM_DISPLAY_ENGINE_STATEMENT *Statement, + IN EFI_HII_VALUE *Value, + OUT STATEMENT_ERROR_INFO *ErrorInfo + ); + +/** + Perform Password check. + Passwork may be encrypted by driver that requires the specific check. + + @param Form Form where Password Statement is in. + @param Statement Password statement + @param PasswordString Password string to be checked. It may be NULL. + NULL means to restore password. + "" string can be used to checked whether old password does exist. + + @return Status Status of Password check. +**/ +typedef +EFI_STATUS +(EFIAPI *PASSWORD_CHECK) ( + IN FORM_DISPLAY_ENGINE_FORM *Form, + IN FORM_DISPLAY_ENGINE_STATEMENT *Statement, + IN EFI_STRING PasswordString OPTIONAL + ); + +#define FORM_DISPLAY_ENGINE_STATEMENT_SIGNATURE SIGNATURE_32 ('F', 'S', 'T', 'A') + +// +// Attribute for Statement and Form +// +#define HII_DISPLAY_GRAYOUT BIT0 +#define HII_DISPLAY_LOCK BIT1 +#define HII_DISPLAY_READONLY BIT2 +#define HII_DISPLAY_MODAL BIT3 + +struct _FORM_DISPLAY_ENGINE_STATEMENT{ + UINTN Signature; + // + // Version for future structure extension + // + UINTN Version; + // + // link to all the statement which will show in the display form. + // + LIST_ENTRY DisplayLink; + // + // Pointer to statement opcode. + // for Guided Opcode. All buffers will be here if GUIDED opcode scope is set. + // + EFI_IFR_OP_HEADER *OpCode; + // + // Question CurrentValue + // + EFI_HII_VALUE CurrentValue; + // + // Flag to describe whether setting is changed or not. + // Displayer may depend on it to show it with the different color. + // + BOOLEAN SettingChangedFlag; + // + // nested Statement list inside of EFI_IFR_SUBTITLE + // + LIST_ENTRY NestStatementList; + // + // nested EFI_IFR_ONE_OF_OPTION list (QUESTION_OPTION) + // + LIST_ENTRY OptionListHead; + // + // Statement attributes: GRAYOUT, LOCK and READONLY + // + UINT32 Attribute; + + // + // ValidateQuestion to do InconsistIf check + // It may be NULL if any value is valid. + // + VALIDATE_QUESTION ValidateQuestion; + + // + // Password additional check. It may be NULL when the additional check is not required. + // + PASSWORD_CHECK PasswordCheck; + + // + // Statement ImageId and AnimationId + // + EFI_IMAGE_ID ImageId; + EFI_ANIMATION_ID AnimationId; +}; + +#define FORM_DISPLAY_ENGINE_STATEMENT_FROM_LINK(a) CR (a, FORM_DISPLAY_ENGINE_STATEMENT, DisplayLink, FORM_DISPLAY_ENGINE_STATEMENT_SIGNATURE) + +#define BROWSER_HOT_KEY_SIGNATURE SIGNATURE_32 ('B', 'H', 'K', 'S') + +typedef struct { + UINTN Signature; + LIST_ENTRY Link; + + EFI_INPUT_KEY *KeyData; + // + // Action is Discard, Default, Submit, Reset and Exit. + // + UINT32 Action; + UINT16 DefaultId; + // + // HotKey Help String + // + EFI_STRING HelpString; +} BROWSER_HOT_KEY; + +#define BROWSER_HOT_KEY_FROM_LINK(a) CR (a, BROWSER_HOT_KEY, Link, BROWSER_HOT_KEY_SIGNATURE) + +#define FORM_DISPLAY_ENGINE_FORM_SIGNATURE SIGNATURE_32 ('F', 'F', 'R', 'M') + +struct _FORM_DISPLAY_ENGINE_FORM { + UINTN Signature; + // + // Version for future structure extension + // + UINTN Version; + // + // Statement List inside of Form + // + LIST_ENTRY StatementListHead; + // + // Statement List outside of Form + // + LIST_ENTRY StatementListOSF; + // + // The input screen dimenstions info. + // + EFI_SCREEN_DESCRIPTOR *ScreenDimensions; + // + // FormSet information + // + EFI_GUID FormSetGuid; + // + // HiiHandle can be used to get String, Image or Animation + // + EFI_HII_HANDLE HiiHandle; + + // + // Form ID and Title. + // + UINT16 FormId; + EFI_STRING_ID FormTitle; + // + // Form Attributes: Lock, Modal. + // + UINT32 Attribute; + // + // Flag to describe whether setting is changed or not. + // Displayer depends on it to show ChangedFlag. + // + BOOLEAN SettingChangedFlag; + + // + // Statement to be HighLighted + // + FORM_DISPLAY_ENGINE_STATEMENT *HighLightedStatement; + // + // Event to notify Displayer that FormData is updated to be refreshed. + // + EFI_EVENT FormRefreshEvent; + // + // Additional Hotkey registered by BrowserEx protocol. + // + LIST_ENTRY HotKeyListHead; + + // + // Form ImageId and AnimationId + // + EFI_IMAGE_ID ImageId; + EFI_ANIMATION_ID AnimationId; + + // + // If Status is error, display needs to handle it. + // + UINT32 BrowserStatus; + // + // String for error status. It may be NULL. + // + EFI_STRING ErrorString; +}; + +#define FORM_DISPLAY_ENGINE_FORM_FROM_LINK(a) CR (a, FORM_DISPLAY_ENGINE_FORM, Link, FORM_DISPLAY_ENGINE_FORM_SIGNATURE) + +typedef struct { + FORM_DISPLAY_ENGINE_STATEMENT *SelectedStatement; // Selected Statement and InputValue + + EFI_HII_VALUE InputValue; + + UINT32 Action; // If SelectedStatement is NULL, Action will be used. + // Trig Action (Discard, Default, Submit, Reset and Exit) + UINT16 DefaultId; +} USER_INPUT; + +/** + Display one form, and return user input. + + @param FormData Form Data to be shown. + @param UserInputData User input data. + + @retval EFI_SUCCESS Form Data is shown, and user input is got. +**/ +typedef +EFI_STATUS +(EFIAPI *FORM_DISPLAY) ( + IN FORM_DISPLAY_ENGINE_FORM *FormData, + OUT USER_INPUT *UserInputData +); + +/** + Exit Display and Clear Screen to the original state. + +**/ +typedef +VOID +(EFIAPI *EXIT_DISPLAY) ( + VOID +); + +/** + Confirm how to handle the changed data. + + @return Action of Submit, Discard and None +**/ +typedef +UINTN +(EFIAPI *CONFIRM_DATA_CHANGE) ( + VOID +); + +typedef struct { + FORM_DISPLAY FormDisplay; + EXIT_DISPLAY ExitDisplay; + CONFIRM_DATA_CHANGE ConfirmDataChange; +} EDKII_FORM_DISPLAY_ENGINE_PROTOCOL; + +extern EFI_GUID gEdkiiFormDisplayEngineProtocolGuid; +#endif diff --git a/MdeModulePkg/Include/Protocol/FormBrowserEx2.h b/MdeModulePkg/Include/Protocol/FormBrowserEx2.h new file mode 100644 index 0000000000..817f80d4c4 --- /dev/null +++ b/MdeModulePkg/Include/Protocol/FormBrowserEx2.h @@ -0,0 +1,90 @@ +/** @file + Extension Form Browser Protocol provides the services that can be used to + register the different hot keys for the standard Browser actions described in UEFI specification. + +Copyright (c) 2011, 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 that 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. + +**/ + +#ifndef __FORM_BROWSER_EXTENSION2_H__ +#define __FORM_BROWSER_EXTENSION2_H__ + +#include + +#define EDKII_FORM_BROWSER_EXTENSION2_PROTOCOL_GUID \ + { 0xa770c357, 0xb693, 0x4e6d, { 0xa6, 0xcf, 0xd2, 0x1c, 0x72, 0x8e, 0x55, 0xb }} + +typedef struct _EDKII_FORM_BROWSER_EXTENSION2_PROTOCOL EDKII_FORM_BROWSER_EXTENSION2_PROTOCOL; + +#define BROWSER_EXTENSION2_VERSION_1 0x10000 + +/** + Check whether the browser data has been modified. + + @retval TRUE Browser data is modified. + @retval FALSE No browser data is modified. + +**/ +typedef +BOOLEAN +(EFIAPI *IS_BROWSER_DATA_MODIFIED) ( + VOID + ); + +/** + Execute the action requested by the Action parameter. + + @param[in] Action Execute the request action. + @param[in] DefaultId The default Id info when need to load default value. + + @retval EFI_SUCCESS Execute the request action succss. + +**/ +typedef +EFI_STATUS +(EFIAPI *EXECUTE_ACTION) ( + IN UINT32 Action, + IN UINT16 DefaultId + ); + +#define FORM_ENTRY_INFO_SIGNATURE SIGNATURE_32 ('f', 'e', 'i', 's') + +typedef struct { + UINTN Signature; + LIST_ENTRY Link; + + EFI_HII_HANDLE HiiHandle; + EFI_GUID FormSetGuid; + UINT16 FormId; + UINT16 QuestionId; +} FORM_ENTRY_INFO; + +#define FORM_ENTRY_INFO_FROM_LINK(a) CR (a, FORM_ENTRY_INFO, Link, FORM_ENTRY_INFO_SIGNATURE) + +struct _EDKII_FORM_BROWSER_EXTENSION2_PROTOCOL { + /// + /// Version for protocol future extension. + /// + UINT32 Version; + SET_SCOPE SetScope; + REGISTER_HOT_KEY RegisterHotKey; + REGISTER_EXIT_HANDLER RegiserExitHandler; + IS_BROWSER_DATA_MODIFIED IsBrowserDataModified; + EXECUTE_ACTION ExecuteAction; + /// + /// A list of type FORMID_INFO is Browser View Form History List. + /// + LIST_ENTRY FormViewHistoryHead; +}; + +extern EFI_GUID gEdkiiFormBrowserEx2ProtocolGuid; + +#endif + diff --git a/MdeModulePkg/Library/CustomizedDisplayLib/Colors.h b/MdeModulePkg/Library/CustomizedDisplayLib/Colors.h new file mode 100644 index 0000000000..2db8b99614 --- /dev/null +++ b/MdeModulePkg/Library/CustomizedDisplayLib/Colors.h @@ -0,0 +1,44 @@ +/** @file +MACRO definitions for color used in Setup Browser. + +Copyright (c) 2004 - 2011, 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. + +**/ +// +// Unicode collation protocol in + +#ifndef _COLORS_H_ +#define _COLORS_H_ + +// +// Screen Color Settings +// +#define PICKLIST_HIGHLIGHT_TEXT EFI_WHITE +#define PICKLIST_HIGHLIGHT_BACKGROUND EFI_BACKGROUND_CYAN +#define TITLE_TEXT EFI_WHITE +#define TITLE_BACKGROUND EFI_BACKGROUND_BLUE +#define KEYHELP_TEXT EFI_LIGHTGRAY +#define KEYHELP_BACKGROUND EFI_BACKGROUND_BLACK +#define SUBTITLE_BACKGROUND EFI_BACKGROUND_LIGHTGRAY +#define BANNER_TEXT EFI_BLUE +#define BANNER_BACKGROUND EFI_BACKGROUND_LIGHTGRAY +#define FIELD_TEXT_GRAYED EFI_DARKGRAY +#define FIELD_BACKGROUND EFI_BACKGROUND_LIGHTGRAY +#define POPUP_TEXT EFI_LIGHTGRAY +#define POPUP_BACKGROUND EFI_BACKGROUND_BLUE +#define POPUP_INVERSE_TEXT EFI_LIGHTGRAY +#define POPUP_INVERSE_BACKGROUND EFI_BACKGROUND_BLACK +#define HELP_TEXT EFI_BLUE +#define ERROR_TEXT EFI_RED | EFI_BRIGHT +#define INFO_TEXT EFI_YELLOW | EFI_BRIGHT +#define ARROW_TEXT EFI_RED | EFI_BRIGHT +#define ARROW_BACKGROUND EFI_BACKGROUND_LIGHTGRAY + +#endif diff --git a/MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.c b/MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.c new file mode 100644 index 0000000000..84df7cc249 --- /dev/null +++ b/MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.c @@ -0,0 +1,936 @@ +/** @file + + This library class defines a set of interfaces to customize Display module + +Copyright (c) 2013, 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 that 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 "CustomizedDisplayLibInternal.h" + +EFI_GUID gCustomizedDisplayLibGuid = { 0x99fdc8fd, 0x849b, 0x4eba, { 0xad, 0x13, 0xfb, 0x96, 0x99, 0xc9, 0xa, 0x4d } }; + +EFI_HII_HANDLE mCDLStringPackHandle; +UINT16 gClassOfVfr; // Formset class information +UINT16 gLastClassOfVfr = 0; +BANNER_DATA *gBannerData; + +UINTN gFooterHeight; + +/** ++------------------------------------------------------------------------------+ +| Setup Page | ++------------------------------------------------------------------------------+ + +Statement +Statement +Statement + + + + + ++------------------------------------------------------------------------------+ +| F1=Scroll Help F9=Reset to Defaults F10=Save and Exit | +| ^"=Move Highlight Toggles Checkbox Esc=Discard Changes | ++------------------------------------------------------------------------------+ + StatusBar +**/ + +/** + This funtion defines Page Frame and Backgroud. + + Based on the above layout, it will be responsible for HeaderHeight, FooterHeight, + StatusBarHeight and Backgroud. And, it will reserve Screen for Statement. + + @param[in] FormData Form Data to be shown in Page. + @param[out] ScreenForStatement Screen to be used for Statement. (Prompt, Value and Help) + + @return Status +**/ +EFI_STATUS +EFIAPI +DisplayPageFrame ( + IN FORM_DISPLAY_ENGINE_FORM *FormData, + OUT EFI_SCREEN_DESCRIPTOR *ScreenForStatement + ) +{ + EFI_STATUS Status; + + ASSERT (FormData != NULL && ScreenForStatement != NULL); + if (FormData == NULL || ScreenForStatement == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = ScreenDiemensionInfoValidate (FormData); + if (EFI_ERROR (Status)) { + return Status; + } + + gClassOfVfr = FORMSET_CLASS_PLATFORM_SETUP; + if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) { + gClassOfVfr = FORMSET_CLASS_MODEL_PAGE; + } + + ProcessExternedOpcode(FormData); + + // + // Calculate the ScreenForStatement. + // + ScreenForStatement->BottomRow = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - gFooterHeight; + if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) == FORMSET_CLASS_FRONT_PAGE) { + ScreenForStatement->TopRow = gScreenDimensions.TopRow + FRONT_PAGE_HEADER_HEIGHT; + } else { + ScreenForStatement->TopRow = gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT; + } + ScreenForStatement->LeftColumn = gScreenDimensions.LeftColumn; + ScreenForStatement->RightColumn = gScreenDimensions.RightColumn; + + // + // If Last Vfr Class is same to current Vfr Class, + // they will have the same page frame. So, Page Frame is not required to be repainted. + // + if (gLastClassOfVfr == gClassOfVfr) { + UpdateStatusBar(NV_UPDATE_REQUIRED, FormData->SettingChangedFlag); + PrintFormTitle(FormData); + return EFI_SUCCESS; + } + + // + // Record last ClassOfVfr and Streen Information. + // + gLastClassOfVfr = gClassOfVfr; + + // + // Ensure we are in Text mode + // + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + + ClearLines (0, gScreenDimensions.RightColumn, 0, gScreenDimensions.BottomRow, KEYHELP_BACKGROUND); + + if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) { + return EFI_SUCCESS; + } + + if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) == FORMSET_CLASS_FRONT_PAGE) { + PrintBannerInfo (FormData); + } + + PrintFramework (); + + UpdateStatusBar(NV_UPDATE_REQUIRED, FormData->SettingChangedFlag); + + PrintFormTitle(FormData); + + return EFI_SUCCESS; +} + +/** + This function updates customized key panel's help information. + The library will prepare those Strings for the basic key, ESC, Enter, Up/Down/Left/Right, +/-. + and arrange them in Footer panel. + + @param[in] FormData Form Data to be shown in Page. FormData has the highlighted statement. + @param[in] Statement The statement current selected. + @param[in] Selected Whether or not a tag be selected. TRUE means Enter has hit this question. +**/ +VOID +EFIAPI +RefreshKeyHelp ( + IN FORM_DISPLAY_ENGINE_FORM *FormData, + IN FORM_DISPLAY_ENGINE_STATEMENT *Statement, + IN BOOLEAN Selected + ) +{ + UINTN SecCol; + UINTN ThdCol; + UINTN LeftColumnOfHelp; + UINTN RightColumnOfHelp; + UINTN TopRowOfHelp; + UINTN BottomRowOfHelp; + UINTN StartColumnOfHelp; + EFI_IFR_NUMERIC *NumericOp; + EFI_IFR_DATE *DateOp; + EFI_IFR_TIME *TimeOp; + BOOLEAN HexDisplay; + + ASSERT (FormData != NULL); + if (FormData == NULL) { + return; + } + + gST->ConOut->SetAttribute (gST->ConOut, KEYHELP_TEXT | KEYHELP_BACKGROUND); + + if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) { + return; + } + + SecCol = gScreenDimensions.LeftColumn + (gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn) / 3; + ThdCol = gScreenDimensions.LeftColumn + (gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn) / 3 * 2; + + StartColumnOfHelp = gScreenDimensions.LeftColumn + 2; + LeftColumnOfHelp = gScreenDimensions.LeftColumn + 1; + RightColumnOfHelp = gScreenDimensions.RightColumn - 2; + TopRowOfHelp = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - gFooterHeight + 1; + BottomRowOfHelp = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - 2; + + ClearLines (LeftColumnOfHelp, RightColumnOfHelp, TopRowOfHelp, BottomRowOfHelp, KEYHELP_TEXT | KEYHELP_BACKGROUND); + if (Statement == NULL) { + // + // Print Key for Form without showable statement. + // + PrintHotKeyHelpString (FormData); + PrintStringAt (ThdCol, BottomRowOfHelp, gEscapeString); + return; + } + + HexDisplay = FALSE; + NumericOp = NULL; + DateOp = NULL; + TimeOp = NULL; + if (Statement->OpCode->OpCode == EFI_IFR_NUMERIC_OP) { + NumericOp = (EFI_IFR_NUMERIC *) Statement->OpCode; + HexDisplay = (NumericOp->Flags & EFI_IFR_DISPLAY_UINT_HEX) == EFI_IFR_DISPLAY_UINT_HEX; + } else if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP) { + DateOp = (EFI_IFR_DATE *) Statement->OpCode; + HexDisplay = (DateOp->Flags & EFI_IFR_DISPLAY_UINT_HEX) == EFI_IFR_DISPLAY_UINT_HEX; + } else if (Statement->OpCode->OpCode == EFI_IFR_TIME_OP) { + TimeOp = (EFI_IFR_TIME *) Statement->OpCode; + HexDisplay = (TimeOp->Flags & EFI_IFR_DISPLAY_UINT_HEX) == EFI_IFR_DISPLAY_UINT_HEX; + } + switch (Statement->OpCode->OpCode) { + case EFI_IFR_ORDERED_LIST_OP: + case EFI_IFR_ONE_OF_OP: + case EFI_IFR_NUMERIC_OP: + case EFI_IFR_TIME_OP: + case EFI_IFR_DATE_OP: + if (!Selected) { + PrintHotKeyHelpString (FormData); + + if ((gClassOfVfr & FORMSET_CLASS_PLATFORM_SETUP) == FORMSET_CLASS_PLATFORM_SETUP) { + PrintStringAt (ThdCol, BottomRowOfHelp, gEscapeString); + } + + if ((Statement->OpCode->OpCode == EFI_IFR_DATE_OP) || + (Statement->OpCode->OpCode == EFI_IFR_TIME_OP)) { + PrintAt ( + 0, + StartColumnOfHelp, + BottomRowOfHelp, + L"%c%c%c%c%s", + ARROW_UP, + ARROW_DOWN, + ARROW_RIGHT, + ARROW_LEFT, + gMoveHighlight + ); + PrintStringAt (SecCol, BottomRowOfHelp, gEnterString); + PrintStringAt (StartColumnOfHelp, TopRowOfHelp, gAdjustNumber); + } else { + PrintAt (0, StartColumnOfHelp, BottomRowOfHelp, L"%c%c%s", ARROW_UP, ARROW_DOWN, gMoveHighlight); + if (Statement->OpCode->OpCode == EFI_IFR_NUMERIC_OP && NumericOp != NULL && LibGetFieldFromNum(Statement->OpCode) != 0) { + PrintStringAt (StartColumnOfHelp, TopRowOfHelp, gAdjustNumber); + } + PrintStringAt (SecCol, BottomRowOfHelp, gEnterString); + } + } else { + PrintStringAt (SecCol, BottomRowOfHelp, gEnterCommitString); + + // + // If it is a selected numeric with manual input, display different message + // + if ((Statement->OpCode->OpCode == EFI_IFR_NUMERIC_OP) || + (Statement->OpCode->OpCode == EFI_IFR_DATE_OP) || + (Statement->OpCode->OpCode == EFI_IFR_TIME_OP)) { + PrintStringAt ( + SecCol, + TopRowOfHelp, + HexDisplay ? gHexNumericInput : gDecNumericInput + ); + } else if (Statement->OpCode->OpCode != EFI_IFR_ORDERED_LIST_OP) { + PrintAt (0, StartColumnOfHelp, BottomRowOfHelp, L"%c%c%s", ARROW_UP, ARROW_DOWN, gMoveHighlight); + } + + if (Statement->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP) { + PrintStringAt (StartColumnOfHelp, TopRowOfHelp, gPlusString); + PrintStringAt (ThdCol, TopRowOfHelp, gMinusString); + } + + PrintStringAt (ThdCol, BottomRowOfHelp, gEnterEscapeString); + } + break; + + case EFI_IFR_CHECKBOX_OP: + PrintHotKeyHelpString (FormData); + + if ((gClassOfVfr & FORMSET_CLASS_PLATFORM_SETUP) == FORMSET_CLASS_PLATFORM_SETUP) { + PrintStringAt (ThdCol, BottomRowOfHelp, gEscapeString); + } + + PrintAt (0, StartColumnOfHelp, BottomRowOfHelp, L"%c%c%s", ARROW_UP, ARROW_DOWN, gMoveHighlight); + PrintStringAt (SecCol, BottomRowOfHelp, gToggleCheckBox); + break; + + case EFI_IFR_REF_OP: + case EFI_IFR_PASSWORD_OP: + case EFI_IFR_STRING_OP: + case EFI_IFR_TEXT_OP: + case EFI_IFR_ACTION_OP: + case EFI_IFR_RESET_BUTTON_OP: + case EFI_IFR_SUBTITLE_OP: + if (!Selected) { + PrintHotKeyHelpString (FormData); + + if ((gClassOfVfr & FORMSET_CLASS_PLATFORM_SETUP) == FORMSET_CLASS_PLATFORM_SETUP) { + PrintStringAt (ThdCol, BottomRowOfHelp, gEscapeString); + } + + PrintAt (0, StartColumnOfHelp, BottomRowOfHelp, L"%c%c%s", ARROW_UP, ARROW_DOWN, gMoveHighlight); + if (Statement->OpCode->OpCode != EFI_IFR_TEXT_OP && Statement->OpCode->OpCode != EFI_IFR_SUBTITLE_OP) { + PrintStringAt (SecCol, BottomRowOfHelp, gEnterString); + } + } else { + if (Statement->OpCode->OpCode != EFI_IFR_REF_OP) { + PrintStringAt ( + (gScreenDimensions.RightColumn - LibGetStringWidth (gEnterCommitString) / 2) / 2, + BottomRowOfHelp, + gEnterCommitString + ); + PrintStringAt (ThdCol, BottomRowOfHelp, gEnterEscapeString); + } + } + break; + + default: + break; + } +} + +/** + Update status bar. + + This function updates the status bar on the bottom of menu screen. It just shows StatusBar. + Original logic in this function should be splitted out. + + @param[in] MessageType The type of message to be shown. InputError or Configuration Changed. + @param[in] State Show or Clear Message. +**/ +VOID +EFIAPI +UpdateStatusBar ( + IN UINTN MessageType, + IN BOOLEAN State + ) +{ + UINTN Index; + CHAR16 OptionWidth; + + OptionWidth = (CHAR16) ((gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn) / 3); + + switch (MessageType) { + case INPUT_ERROR: + if (State) { + gST->ConOut->SetAttribute (gST->ConOut, ERROR_TEXT); + PrintStringAt ( + gScreenDimensions.LeftColumn + OptionWidth, + gScreenDimensions.BottomRow - 1, + gInputErrorMessage + ); + } else { + gST->ConOut->SetAttribute (gST->ConOut, KEYHELP_BACKGROUND); + for (Index = 0; Index < (LibGetStringWidth (gInputErrorMessage) - 2) / 2; Index++) { + PrintStringAt (gScreenDimensions.LeftColumn + OptionWidth + Index, gScreenDimensions.BottomRow - 1, L" "); + } + } + break; + + case NV_UPDATE_REQUIRED: + // + // Global setting support. Show configuration change on every form. + // + if (State) { + gST->ConOut->SetAttribute (gST->ConOut, INFO_TEXT); + PrintStringAt ( + gScreenDimensions.LeftColumn + OptionWidth * 2, + gScreenDimensions.BottomRow - 1, + gNvUpdateMessage + ); + } else { + gST->ConOut->SetAttribute (gST->ConOut, KEYHELP_BACKGROUND); + for (Index = 0; Index < (LibGetStringWidth (gNvUpdateMessage) - 2) / 2; Index++) { + PrintStringAt ( + (gScreenDimensions.LeftColumn + OptionWidth * 2 + Index), + gScreenDimensions.BottomRow - 1, + L" " + ); + } + } + break; + + default: + break; + } +} + +/** + Create popup window. It will replace CreateDialog(). + + This function draws OEM/Vendor specific pop up windows. + + @param[out] Key User Input Key + @param ... String to be shown in Popup. The variable argument list is terminated by a NULL. + +**/ +VOID +EFIAPI +CreateDialog ( + OUT EFI_INPUT_KEY *Key, OPTIONAL + ... + ) +{ + VA_LIST Marker; + EFI_INPUT_KEY KeyValue; + EFI_STATUS Status; + UINTN LargestString; + UINTN LineNum; + UINTN Index; + UINTN Count; + CHAR16 Character; + UINTN Start; + UINTN End; + UINTN Top; + UINTN Bottom; + CHAR16 *String; + UINTN DimensionsWidth; + UINTN DimensionsHeight; + UINTN CurrentAttribute; + + // + // If screen dimension info is not ready, get it from console. + // + if (gScreenDimensions.RightColumn == 0 || gScreenDimensions.BottomRow == 0) { + ZeroMem (&gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR)); + gST->ConOut->QueryMode ( + gST->ConOut, + gST->ConOut->Mode->Mode, + &gScreenDimensions.RightColumn, + &gScreenDimensions.BottomRow + ); + } + + DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn; + DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow; + + LargestString = 0; + LineNum = 0; + VA_START (Marker, Key); + while ((String = VA_ARG (Marker, CHAR16 *)) != NULL) { + LineNum ++; + + if ((LibGetStringWidth (String) / 2) > LargestString) { + LargestString = (LibGetStringWidth (String) / 2); + } + } + VA_END (Marker); + + if ((LargestString + 2) > DimensionsWidth) { + LargestString = DimensionsWidth - 2; + } + + CurrentAttribute = gST->ConOut->Mode->Attribute; + gST->ConOut->EnableCursor (gST->ConOut, FALSE); + gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ()); + + // + // Subtract the PopUp width from total Columns, allow for one space extra on + // each end plus a border. + // + Start = (DimensionsWidth - LargestString - 2) / 2 + gScreenDimensions.LeftColumn + 1; + End = Start + LargestString + 1; + + Top = ((DimensionsHeight - LineNum - 2) / 2) + gScreenDimensions.TopRow - 1; + Bottom = Top + LineNum + 2; + + Character = BOXDRAW_DOWN_RIGHT; + PrintCharAt (Start, Top, Character); + Character = BOXDRAW_HORIZONTAL; + for (Index = Start; Index + 2 < End; Index++) { + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); + } + + Character = BOXDRAW_DOWN_LEFT; + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); + Character = BOXDRAW_VERTICAL; + + Count = 0; + VA_START (Marker, Key); + for (Index = Top; Index + 2 < Bottom; Index++, Count++) { + String = VA_ARG (Marker, CHAR16*); + + if (String[0] == CHAR_NULL) { + // + // Passing in a NULL results in a blank space + // + ClearLines (Start, End, Index + 1, Index + 1, GetPopupColor ()); + } else if (String[0] == L' ') { + // + // Passing in a space results in the assumption that this is where typing will occur + // + ClearLines (Start + 1, End - 1, Index + 1, Index + 1, POPUP_INVERSE_TEXT | POPUP_INVERSE_BACKGROUND); + PrintStringAt ( + ((DimensionsWidth - LibGetStringWidth (String) / 2) / 2) + gScreenDimensions.LeftColumn + 1, + Index + 1, + String + 1 + ); + } else { + // + // 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. + // + ClearLines (Start, End, Index + 1, Index + 1, GetPopupColor ()); + PrintStringAt ( + ((DimensionsWidth - LibGetStringWidth (String) / 2) / 2) + gScreenDimensions.LeftColumn + 1, + Index + 1, + String + ); + } + + gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ()); + PrintCharAt (Start, Index + 1, Character); + PrintCharAt (End - 1, Index + 1, Character); + } + VA_END (Marker); + + Character = BOXDRAW_UP_RIGHT; + PrintCharAt (Start, Bottom - 1, Character); + Character = BOXDRAW_HORIZONTAL; + for (Index = Start; Index + 2 < End; Index++) { + PrintCharAt ((UINTN)-1, (UINTN) -1, Character); + } + + Character = BOXDRAW_UP_LEFT; + PrintCharAt ((UINTN)-1, (UINTN) -1, Character); + + if (Key != NULL) { + Status = WaitForKeyStroke (&KeyValue); + ASSERT_EFI_ERROR (Status); + CopyMem (Key, &KeyValue, sizeof (EFI_INPUT_KEY)); + } + + gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute); + gST->ConOut->EnableCursor (gST->ConOut, TRUE); +} + +/** + Confirm how to handle the changed data. + + @return Action BROWSER_ACTION_SUBMIT, BROWSER_ACTION_DISCARD or other values. +**/ +UINTN +EFIAPI +ConfirmDataChange ( + VOID + ) +{ + CHAR16 YesResponse; + CHAR16 NoResponse; + EFI_INPUT_KEY Key; + + gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + + YesResponse = gYesResponse[0]; + NoResponse = gNoResponse[0]; + + // + // If NV flag is up, prompt user + // + do { + CreateDialog (&Key, gLibEmptyString, gSaveChanges, gAreYouSure, gLibEmptyString, NULL); + } 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 (Key.ScanCode == SCAN_ESC) { + return BROWSER_ACTION_NONE; + } else if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (YesResponse | UPPER_LOWER_CASE_OFFSET)) { + return BROWSER_ACTION_SUBMIT; + } else { + return BROWSER_ACTION_DISCARD; + } +} + +/** + OEM specifies whether Setup exits Page by ESC key. + + This function customized the behavior that whether Setup exits Page so that + system able to boot when configuration is not changed. + + @retval TRUE Exits FrontPage + @retval FALSE Don't exit FrontPage. +**/ +BOOLEAN +EFIAPI +FormExitPolicy ( + VOID + ) +{ + return (gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) == FORMSET_CLASS_FRONT_PAGE ? FALSE : TRUE; +} + +/** + Set Timeout value for a ceratain Form to get user response. + + This function allows to set timeout value on a ceratain form if necessary. + If timeout is not zero, the form will exit if user has no response in timeout. + + @param[in] FormData Form Data to be shown in Page + + @return 0 No timeout for this form. + @return > 0 Timeout value in 100 ns units. +**/ +UINT64 +EFIAPI +FormExitTimeout ( + IN FORM_DISPLAY_ENGINE_FORM *FormData + ) +{ + return 0; +} +// +// Print Functions +// +/** + Prints a unicode string to the default console, at + the supplied cursor position, using L"%s" format. + + @param Column The cursor position to print the string at. When it is -1, use current Position. + @param Row The cursor position to print the string at. When it is -1, use current Position. + @param String String pointer. + + @return Length of string printed to the console + +**/ +UINTN +EFIAPI +PrintStringAt ( + IN UINTN Column, + IN UINTN Row, + IN CHAR16 *String + ) +{ + return PrintAt (0, Column, Row, L"%s", String); +} + +/** + Prints a unicode string to the default console, at + the supplied cursor position, using L"%s" format. + + @param Column The cursor position to print the string at. When it is -1, use current Position. + @param Row The cursor position to print the string at. When it is -1, use current Position. + @param String String pointer. + @param Width Width for String. + + @return Length of string printed to the console + +**/ +UINTN +EFIAPI +PrintStringAtWithWidth ( + IN UINTN Column, + IN UINTN Row, + IN CHAR16 *String, + IN UINTN Width + ) +{ + return PrintAt (Width, Column, Row, L"%s", String); +} + +/** + Prints a chracter to the default console, at + the supplied cursor position, using L"%c" format. + + @param Column The cursor position to print the string at. When it is -1, use current Position. + @param Row The cursor position to print the string at. When it is -1, use current Position. + @param Character Character to print. + + @return Length of string printed to the console. + +**/ +UINTN +EFIAPI +PrintCharAt ( + IN UINTN Column, + IN UINTN Row, + CHAR16 Character + ) +{ + return PrintAt (0, Column, Row, L"%c", Character); +} + +/** + Clear retangle with specified text attribute. + + @param LeftColumn Left column of retangle. + @param RightColumn Right column of retangle. + @param TopRow Start row of retangle. + @param BottomRow End row of retangle. + @param TextAttribute The character foreground and background. + +**/ +VOID +EFIAPI +ClearLines ( + IN UINTN LeftColumn, + IN UINTN RightColumn, + IN UINTN TopRow, + IN UINTN BottomRow, + IN UINTN TextAttribute + ) +{ + CHAR16 *Buffer; + UINTN Row; + + // + // For now, allocate an arbitrarily long buffer + // + Buffer = AllocateZeroPool (0x10000); + ASSERT (Buffer != NULL); + + // + // Set foreground and background as defined + // + gST->ConOut->SetAttribute (gST->ConOut, TextAttribute); + + // + // Much faster to buffer the long string instead of print it a character at a time + // + LibSetUnicodeMem (Buffer, RightColumn - LeftColumn, L' '); + + // + // Clear the desired area with the appropriate foreground/background + // + for (Row = TopRow; Row <= BottomRow; Row++) { + PrintStringAt (LeftColumn, Row, Buffer); + } + + gST->ConOut->SetCursorPosition (gST->ConOut, LeftColumn, TopRow); + + FreePool (Buffer); +} + +// +// Color Setting Functions +// + +/** + Get OEM/Vendor specific popup attribute colors. + + @retval Byte code color setting for popup color. +**/ +UINT8 +EFIAPI +GetPopupColor ( + VOID + ) +{ + return POPUP_TEXT | POPUP_BACKGROUND; +} + +/** + Get OEM/Vendor specific popup attribute colors. + + @retval Byte code color setting for popup inverse color. +**/ +UINT8 +EFIAPI +GetPopupInverseColor ( + VOID + ) +{ + return POPUP_INVERSE_TEXT | POPUP_INVERSE_BACKGROUND; +} + +/** + Get OEM/Vendor specific PickList color attribute. + + @retval Byte code color setting for pick list color. +**/ +UINT8 +EFIAPI +GetPickListColor ( + VOID + ) +{ + return PICKLIST_HIGHLIGHT_TEXT | PICKLIST_HIGHLIGHT_BACKGROUND; +} + +/** + Get OEM/Vendor specific arrow color attribute. + + @retval Byte code color setting for arrow color. +**/ +UINT8 +EFIAPI +GetArrowColor ( + VOID + ) +{ + return ARROW_TEXT | ARROW_BACKGROUND; +} + +/** + Get OEM/Vendor specific info text color attribute. + + @retval Byte code color setting for info text color. +**/ +UINT8 +EFIAPI +GetInfoTextColor ( + VOID + ) +{ + return INFO_TEXT | FIELD_BACKGROUND; +} + +/** + Get OEM/Vendor specific help text color attribute. + + @retval Byte code color setting for help text color. +**/ +UINT8 +EFIAPI +GetHelpTextColor ( + VOID + ) +{ + return HELP_TEXT | FIELD_BACKGROUND; +} + +/** + Get OEM/Vendor specific grayed out text color attribute. + + @retval Byte code color setting for grayed out text color. +**/ +UINT8 +EFIAPI +GetGrayedTextColor ( + VOID + ) +{ + return FIELD_TEXT_GRAYED | FIELD_BACKGROUND; +} + +/** + Get OEM/Vendor specific highlighted text color attribute. + + @retval Byte code color setting for highlight text color. +**/ +UINT8 +EFIAPI +GetHighlightTextColor ( + VOID + ) +{ + return PcdGet8 (PcdBrowserFieldTextHighlightColor) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor); +} + +/** + Get OEM/Vendor specific field text color attribute. + + @retval Byte code color setting for field text color. +**/ +UINT8 +EFIAPI +GetFieldTextColor ( + VOID + ) +{ + return PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND; +} + +/** + Get OEM/Vendor specific subtitle text color attribute. + + @retval Byte code color setting for subtitle text color. +**/ +UINT8 +EFIAPI +GetSubTitleTextColor ( + VOID + ) +{ + return PcdGet8 (PcdBrowserSubtitleTextColor) | FIELD_BACKGROUND; +} + +/** + Clear Screen to the initial state. +**/ +VOID +EFIAPI +ClearDisplayPage ( + VOID + ) +{ + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + gST->ConOut->ClearScreen (gST->ConOut); + gLastClassOfVfr = 0; +} + +/** + Constructor of Customized Display Library Instance. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. + +**/ +EFI_STATUS +EFIAPI +CustomizedDisplayLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + mCDLStringPackHandle = HiiAddPackages (&gCustomizedDisplayLibGuid, ImageHandle, CustomizedDisplayLibStrings, NULL); + ASSERT (mCDLStringPackHandle != NULL); + + InitializeLibStrings(); + + return EFI_SUCCESS; +} + +/** + Destructor of Customized Display Library Instance. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The destructor completed successfully. + @retval Other value The destructor did not complete successfully. + +**/ +EFI_STATUS +EFIAPI +CustomizedDisplayLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + HiiRemovePackages(mCDLStringPackHandle); + + FreeLibStrings (); + + return EFI_SUCCESS; +} + diff --git a/MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf b/MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf new file mode 100644 index 0000000000..ade45b6a51 --- /dev/null +++ b/MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf @@ -0,0 +1,74 @@ +## +# This file contains an 'Intel Peripheral Driver' and is +# licensed for Intel CPUs and chipsets under the terms of your +# license agreement with Intel or your vendor. This file may +# be modified by the user, subject to additional terms of the +# license agreement +## +## @file +# +# General BDS defines and produce general interfaces for platform BDS driver including: +# 1) BDS boot policy interface; +# 2) BDS boot device connect interface; +# 3) BDS Misc interfaces for mainting boot variable, ouput string, etc. +# +# Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+# This software and associated documentation (if any) is furnished +# under a license and may only be used or copied in accordance +# with the terms of the license. Except as permitted by such +# license, no part of this software or documentation may be +# reproduced, stored in a retrieval system, or transmitted in any +# form or by any means without the express written consent of +# Intel Corporation. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CustomizedDisplayLib + FILE_GUID = 80B92017-EC64-4923-938D-94FAEE85832E + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = CustomizedDisplayLib|DXE_DRIVER UEFI_APPLICATION + CONSTRUCTOR = CustomizedDisplayLibConstructor + DESTRUCTOR = CustomizedDisplayLibDestructor +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + CustomizedDisplayLib.c + Colors.h + CustomizedDisplayLibInternal.h + CustomizedDisplayLibInternal.c + CustomizedDisplayLib.uni + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + MemoryAllocationLib + BaseLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + BaseMemoryLib + DebugLib + PrintLib + HiiLib + DevicePathLib + PcdLib + +[Guids] + gEfiIfrTianoGuid + +[Protocols] + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdBrowserSubtitleTextColor ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdBrowserFieldTextColor ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdBrowserFieldTextHighlightColor ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdBrowserFieldBackgroundHighlightColor ## CONSUMES \ No newline at end of file diff --git a/MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.uni b/MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.uni new file mode 100644 index 0000000000000000000000000000000000000000..18a5c3b56347a846506d113f47bbebf92645a1f0 GIT binary patch literal 7256 zcmd6s+fo}x5Qh6YRe6UsTsScX>^MgkI3l;wa#BwF>V}(=+<#?^&C*#Gcp`*O&GMrP<*#v8r|K%r32KEo<64>(llt zJFu^Leq+zLzU6ItP?!Xn)~4l=o$#&^x{RUw9$#C$wQYu)3iWD{j@=8TBkR$( zYah5D(sIrgY~5aEP;G*wMXQ8yyNuTiW1mvqW9&noeOera@&RZ&c1latUDB^j`-Cq- zqj_4qoiJLIUZZqF?-S|{cxwkl8aIxw-iL!3KJeaRggx8mu366m3XPp2@wrX|>Ntv! z32$r2i)8B9ai-sbZP+5^x7V2i%43y0k6OXhgB$d}*#8Wl6mc&a6HX6R*O5 zOpWVCs6WywkLo%z&gLbp_6Pkt0}h0#pJ7%O)JGRDgPqB?oL#l=2Ciq2zl(-uL6Bv? z$CrJuA7X9SYi1bb9lyv)d`g~WtAVP4{jAv%_lm7jUV+mDR`uNE>k1aO#ak^fy=8Tt zHNK}Kq*_?PqHDC+2qUlYCQH_Rf~UsSs1rOFLRqD!Lu`rtM!j&Yg&9?@Fq4qg`M$!a z8e=WYzD`Nl^-Xhb(n@VKtG9YhTlL)G`Gy)Hmj~7cv$zyB+a8XSj=lmH4eUyDF5+jA zxA;t`@4&ZgK~_;mm#4hFENGcfzd>D})@MjAue?B8@_`M$b{St@(JJU$33^o<*>8Wu z_-?oEzw*9g#?zS5ihH0q4)!DVvlxn`o5qr!45FQ6cDd(mGQ6oYiRALtX7F`+rL1)t z&56YccvGFMv4;gtgns|+a}1JcdmS`7!BcX!?Jw@?=e>Nk=$S5|Yg`)KWj?plI#w*M zZ@*F;=giau`E~TdKNlqM$n?tQg9fE*X+bumDBdMzI>y`Qyi1EBd|9T7@Ks^C6Ig43 zD}njPLzGwGjvBa`$11+F$|WBx$7&JItQ8dF^c6>3irt6FWyE+DFy4<(MW!kdsDU0n z9iKJ);sATBvG&Nj9|C_7(?*Cdia2*+?5-?jJ*kWZMs$&}ad~XSk!7jaD=Vxyzl2CC zPptE{O3e24Dl2{Qdl^oqi0#Tu=dsU2d(Zl#xRBVd#KRLxFu zY*Fh?Lgp)kM_}yU5Yx17YZWTzb9q^cFy>?0RA5dSfbwu(-Q(KeIeY?wv$3rWj&B`o zLM4pukHV^KSnI^c8`x9)m&M(WzcTE(-Hm2%5sVkCvE%a9=o|Z3n=%p4*0idY<8Td6 zQq*6D%_Z*2B=y`x1GimeIGhjpr(54L2(|xIuI2mCaj~XV$+=Q?q#c&_u)Y^OMWg4z z7L_NfzBu-2cTpWiJ;gljDt3caHn7Xl43^>UnLTHJ{({~4clN#gU_aVVpp5%kk1s7y zCrHr7i?tJsx#(@WyIXw`+I&1LM%#l;ap>$Sw?7PD+NNAjMF91{7N)OoYv`$PGS_Rx|{`V zVhK$yk5$?iXxH*P^_^kVA2sjY>xz)(?YIc(0*IB(#$B|p@YV2Dan>V;&d215eZFh2 zR&2|4BJM8np+WAXlK@5ERqh*H6?9Ph-(2got{wK(&5(VhCw9K7`fga~a^s4>cSSe} z``L2?t^6Kici~GXFFI=}%VUuhD+m_YgkGHo7q9u&vTkEE{iDk6`iWd zKX%aXR*-pX_{GoIM#X?KdfpkEmgCHM9p5Z-9TrB(tGY5W*T*JvoH;MEBA@4>T6=UB zsyL|gxiVK7nYFsaxTmZ`CC^IRL`&r-y}=j#TcMp&tk3tWJwTr939HVu68nSoK{EL1VU#4VotrGOjBd(VWFyF-j6C5q zaEG7%@91!kT1o%4iM;EC{4ti2w)Ou-BxyHjD6c8+;ip|`FST!nyyOJUk1PJA3>?{? x85WdN=mf!;@O?>hAfao!NRwG^JU7w4P +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that 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 "CustomizedDisplayLibInternal.h" + +EFI_SCREEN_DESCRIPTOR gScreenDimensions; +CHAR16 *mLibUnknownString; +extern EFI_HII_HANDLE mCDLStringPackHandle; +CHAR16 *mSpaceBuffer; +#define SPACE_BUFFER_SIZE 1000 + +// +// Browser Global Strings +// +CHAR16 *gEnterString; +CHAR16 *gEnterCommitString; +CHAR16 *gEnterEscapeString; +CHAR16 *gEscapeString; +CHAR16 *gMoveHighlight; +CHAR16 *gDecNumericInput; +CHAR16 *gHexNumericInput; +CHAR16 *gToggleCheckBox; +CHAR16 *gLibEmptyString; +CHAR16 *gAreYouSure; +CHAR16 *gYesResponse; +CHAR16 *gNoResponse; +CHAR16 *gPlusString; +CHAR16 *gMinusString; +CHAR16 *gAdjustNumber; +CHAR16 *gSaveChanges; +CHAR16 *gNvUpdateMessage; +CHAR16 *gInputErrorMessage; + +/** + + Print banner info for front page. + + @param[in] FormData Form Data to be shown in Page + +**/ +VOID +PrintBannerInfo ( + IN FORM_DISPLAY_ENGINE_FORM *FormData + ) +{ + UINT8 Line; + UINT8 Alignment; + CHAR16 *StrFrontPageBanner; + UINT8 RowIdx; + UINT8 ColumnIdx; + + // + // ClearLines(0, LocalScreen.RightColumn, 0, BANNER_HEIGHT-1, BANNER_TEXT | BANNER_BACKGROUND); + // + ClearLines ( + gScreenDimensions.LeftColumn, + gScreenDimensions.RightColumn, + gScreenDimensions.TopRow, + FRONT_PAGE_HEADER_HEIGHT - 1 + gScreenDimensions.TopRow, + BANNER_TEXT | BANNER_BACKGROUND + ); + + // + // for (Line = 0; Line < BANNER_HEIGHT; Line++) { + // + for (Line = (UINT8) gScreenDimensions.TopRow; Line < BANNER_HEIGHT + (UINT8) gScreenDimensions.TopRow; Line++) { + // + // for (Alignment = 0; Alignment < BANNER_COLUMNS; Alignment++) { + // + for (Alignment = (UINT8) gScreenDimensions.LeftColumn; + Alignment < BANNER_COLUMNS + (UINT8) gScreenDimensions.LeftColumn; + Alignment++ + ) { + RowIdx = (UINT8) (Line - (UINT8) gScreenDimensions.TopRow); + ColumnIdx = (UINT8) (Alignment - (UINT8) gScreenDimensions.LeftColumn); + + ASSERT (RowIdx < BANNER_HEIGHT && ColumnIdx < BANNER_COLUMNS); + + if (gBannerData!= NULL && gBannerData->Banner[RowIdx][ColumnIdx] != 0x0000) { + StrFrontPageBanner = LibGetToken (gBannerData->Banner[RowIdx][ColumnIdx], FormData->HiiHandle); + } else { + continue; + } + + switch (Alignment - gScreenDimensions.LeftColumn) { + case 0: + // + // Handle left column + // + PrintStringAt (gScreenDimensions.LeftColumn + BANNER_LEFT_COLUMN_INDENT, Line, StrFrontPageBanner); + break; + + case 1: + // + // Handle center column + // + PrintStringAt ( + gScreenDimensions.LeftColumn + (gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn) / 3, + Line, + StrFrontPageBanner + ); + break; + + case 2: + // + // Handle right column + // + PrintStringAt ( + gScreenDimensions.LeftColumn + (gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn) * 2 / 3, + Line, + StrFrontPageBanner + ); + break; + } + + FreePool (StrFrontPageBanner); + } + } +} + +/** + + Print framework for a page. + +**/ +VOID +PrintFramework ( + VOID + ) +{ + UINTN Index; + CHAR16 Character; + CHAR16 *Buffer; + UINTN Row; + + Buffer = AllocateZeroPool (0x10000); + ASSERT (Buffer != NULL); + Character = BOXDRAW_HORIZONTAL; + for (Index = 0; Index + 2 < (gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn); Index++) { + Buffer[Index] = Character; + } + + ClearLines ( + gScreenDimensions.LeftColumn, + gScreenDimensions.RightColumn, + gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - gFooterHeight, + gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - 1, + KEYHELP_TEXT | KEYHELP_BACKGROUND + ); + + if ((gClassOfVfr & FORMSET_CLASS_PLATFORM_SETUP) == FORMSET_CLASS_PLATFORM_SETUP) { + ClearLines ( + gScreenDimensions.LeftColumn, + gScreenDimensions.RightColumn, + gScreenDimensions.TopRow, + gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT - 1, + TITLE_TEXT | TITLE_BACKGROUND + ); + // + // Print Top border line + // +------------------------------------------------------------------------------+ + // ? ? + // +------------------------------------------------------------------------------+ + // + Character = BOXDRAW_DOWN_RIGHT; + + PrintCharAt ((UINTN) -1, (UINTN) -1, Character); + PrintStringAt ((UINTN) -1, (UINTN) -1, Buffer); + + Character = BOXDRAW_DOWN_LEFT; + PrintCharAt ((UINTN) -1, (UINTN) -1, Character); + + Character = BOXDRAW_VERTICAL; + for (Row = gScreenDimensions.TopRow + 1; Row <= gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT - 2; Row++) { + PrintCharAt (gScreenDimensions.LeftColumn, Row, Character); + PrintCharAt (gScreenDimensions.RightColumn - 1, Row, Character); + } + + Character = BOXDRAW_UP_RIGHT; + PrintCharAt (gScreenDimensions.LeftColumn, gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT - 1, Character); + PrintStringAt ((UINTN) -1, (UINTN) -1, Buffer); + + Character = BOXDRAW_UP_LEFT; + PrintCharAt ((UINTN) -1, (UINTN) -1, Character); + + // + // Print Bottom border line + // +------------------------------------------------------------------------------+ + // ? ? + // +------------------------------------------------------------------------------+ + // + Character = BOXDRAW_DOWN_RIGHT; + PrintCharAt (gScreenDimensions.LeftColumn, gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - gFooterHeight, Character); + + PrintStringAt ((UINTN) -1, (UINTN) -1, Buffer); + + Character = BOXDRAW_DOWN_LEFT; + PrintCharAt ((UINTN) -1, (UINTN) -1, Character); + Character = BOXDRAW_VERTICAL; + for (Row = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - gFooterHeight + 1; + Row <= gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - 2; + Row++ + ) { + PrintCharAt (gScreenDimensions.LeftColumn, Row, Character); + PrintCharAt (gScreenDimensions.RightColumn - 1, Row, Character); + } + + Character = BOXDRAW_UP_RIGHT; + PrintCharAt (gScreenDimensions.LeftColumn, gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - 1, Character); + + PrintStringAt ((UINTN) -1, (UINTN) -1, Buffer); + + Character = BOXDRAW_UP_LEFT; + PrintCharAt ((UINTN) -1, (UINTN) -1, Character); + } + + FreePool (Buffer); +} + +/** + Print the form title. + + @param[in] FormData Form Data to be shown in Page + +**/ +VOID +PrintFormTitle ( + IN FORM_DISPLAY_ENGINE_FORM *FormData + ) +{ + CHAR16 *TitleStr; + + if ((gClassOfVfr & FORMSET_CLASS_PLATFORM_SETUP) != FORMSET_CLASS_PLATFORM_SETUP) { + // + // Only Setup Page need Title. + // + return; + } + + TitleStr = LibGetToken (FormData->FormTitle, FormData->HiiHandle); + ASSERT (TitleStr != NULL); + + gST->ConOut->SetAttribute (gST->ConOut, TITLE_TEXT | TITLE_BACKGROUND); + + ClearLines ( + gScreenDimensions.LeftColumn + 1, + gScreenDimensions.RightColumn - 1, + gScreenDimensions.TopRow + 1, + gScreenDimensions.TopRow + 1, + TITLE_TEXT | TITLE_BACKGROUND + ); + + PrintStringAt ( + (gScreenDimensions.RightColumn + gScreenDimensions.LeftColumn - LibGetStringWidth (TitleStr) / 2) / 2, + gScreenDimensions.TopRow + 1, + TitleStr + ); + + FreePool (TitleStr); +} + +/** + Process some op code which is not recognized by browser core. + + @param OpCodeData The pointer to the op code buffer. + + @return EFI_SUCCESS Pass the statement success. + +**/ +VOID +ProcessUserOpcode( + IN EFI_IFR_OP_HEADER *OpCodeData + ) +{ + switch (OpCodeData->OpCode) { + case EFI_IFR_GUID_OP: + if (CompareGuid (&gEfiIfrTianoGuid, (EFI_GUID *)((CHAR8*) OpCodeData + sizeof (EFI_IFR_OP_HEADER)))) { + // + // Tiano specific GUIDed opcodes + // + switch (((EFI_IFR_GUID_LABEL *) OpCodeData)->ExtendOpCode) { + case EFI_IFR_EXTEND_OP_LABEL: + // + // just ignore label + // + break; + + case EFI_IFR_EXTEND_OP_BANNER: + // + // Only in front page form set, we care about the banner data. + // + if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) == FORMSET_CLASS_FRONT_PAGE) { + // + // Initialize Driver private data + // + if (gBannerData == NULL) { + gBannerData = AllocateZeroPool (sizeof (BANNER_DATA)); + ASSERT (gBannerData != NULL); + } + + CopyMem ( + &gBannerData->Banner[((EFI_IFR_GUID_BANNER *) OpCodeData)->LineNumber][ + ((EFI_IFR_GUID_BANNER *) OpCodeData)->Alignment], + &((EFI_IFR_GUID_BANNER *) OpCodeData)->Title, + sizeof (EFI_STRING_ID) + ); + } + break; + + case EFI_IFR_EXTEND_OP_SUBCLASS: + if (((EFI_IFR_GUID_SUBCLASS *) OpCodeData)->SubClass == EFI_FRONT_PAGE_SUBCLASS) { + gClassOfVfr = FORMSET_CLASS_FRONT_PAGE; + } + break; + + default: + break; + } + } + break; + + default: + break; + } +} + +/** + Process some op codes which is out side of current form. + + @param FormData Pointer to the form data. + + @return EFI_SUCCESS Pass the statement success. + +**/ +VOID +ProcessExternedOpcode ( + IN FORM_DISPLAY_ENGINE_FORM *FormData + ) +{ + LIST_ENTRY *Link; + FORM_DISPLAY_ENGINE_STATEMENT *Statement; + + Link = GetFirstNode (&FormData->StatementListOSF); + while (!IsNull (&FormData->StatementListOSF, Link)) { + Statement = FORM_DISPLAY_ENGINE_STATEMENT_FROM_LINK (Link); + Link = GetNextNode (&FormData->StatementListOSF, Link); + + ProcessUserOpcode(Statement->OpCode); + } + + Link = GetFirstNode (&FormData->StatementListHead); + while (!IsNull (&FormData->StatementListHead, Link)) { + Statement = FORM_DISPLAY_ENGINE_STATEMENT_FROM_LINK (Link); + Link = GetNextNode (&FormData->StatementListHead, Link); + + ProcessUserOpcode(Statement->OpCode); + } +} + +/** + Validate the input screen diemenstion info. + + @param FormData The input form data info. + + @return EFI_SUCCESS The input screen info is acceptable. + @return EFI_INVALID_PARAMETER The input screen info is not acceptable. + +**/ +EFI_STATUS +ScreenDiemensionInfoValidate ( + IN FORM_DISPLAY_ENGINE_FORM *FormData + ) +{ + LIST_ENTRY *Link; + UINTN Index; + + // + // Calculate total number of Register HotKeys. + // + Index = 0; + if (!IsListEmpty (&FormData->HotKeyListHead)){ + Link = GetFirstNode (&FormData->HotKeyListHead); + while (!IsNull (&FormData->HotKeyListHead, Link)) { + Link = GetNextNode (&FormData->HotKeyListHead, Link); + Index ++; + } + } + + // + // Show three HotKeys help information on one row. + // + gFooterHeight = FOOTER_HEIGHT + (Index / 3); + + + ZeroMem (&gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR)); + gST->ConOut->QueryMode ( + gST->ConOut, + gST->ConOut->Mode->Mode, + &gScreenDimensions.RightColumn, + &gScreenDimensions.BottomRow + ); + + // + // Check local dimension vs. global dimension. + // + if (FormData->ScreenDimensions != NULL) { + if ((gScreenDimensions.RightColumn < FormData->ScreenDimensions->RightColumn) || + (gScreenDimensions.BottomRow < FormData->ScreenDimensions->BottomRow) + ) { + return EFI_INVALID_PARAMETER; + } else { + // + // Local dimension validation. + // + if ((FormData->ScreenDimensions->RightColumn > FormData->ScreenDimensions->LeftColumn) && + (FormData->ScreenDimensions->BottomRow > FormData->ScreenDimensions->TopRow) && + ((FormData->ScreenDimensions->RightColumn - FormData->ScreenDimensions->LeftColumn) > 2) && + ((FormData->ScreenDimensions->BottomRow - FormData->ScreenDimensions->TopRow) > STATUS_BAR_HEIGHT + + FRONT_PAGE_HEADER_HEIGHT + gFooterHeight + 3)) { + CopyMem (&gScreenDimensions, (VOID *) FormData->ScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR)); + } else { + return EFI_INVALID_PARAMETER; + } + } + } + + return EFI_SUCCESS; +} + +/** + Get the string based on the StringId and HII Package List Handle. + + @param Token The String's ID. + @param HiiHandle The package list in the HII database to search for + the specified string. + + @return The output string. + +**/ +CHAR16 * +LibGetToken ( + IN EFI_STRING_ID Token, + IN EFI_HII_HANDLE HiiHandle + ) +{ + EFI_STRING String; + + String = HiiGetString (HiiHandle, Token, NULL); + if (String == NULL) { + String = AllocateCopyPool (StrSize (mLibUnknownString), mLibUnknownString); + ASSERT (String != NULL); + } + + return (CHAR16 *) String; +} + + +/** + Count the storage space of a Unicode string. + + This function handles the Unicode string with NARROW_CHAR + and WIDE_CHAR control characters. NARROW_HCAR and WIDE_CHAR + does not count in the resultant output. If a WIDE_CHAR is + hit, then 2 Unicode character will consume an output storage + space with size of CHAR16 till a NARROW_CHAR is hit. + + If String is NULL, then ASSERT (). + + @param String The input string to be counted. + + @return Storage space for the input string. + +**/ +UINTN +LibGetStringWidth ( + IN CHAR16 *String + ) +{ + UINTN Index; + UINTN Count; + UINTN IncrementValue; + + ASSERT (String != NULL); + if (String == NULL) { + return 0; + } + + Index = 0; + Count = 0; + IncrementValue = 1; + + do { + // + // Advance to the null-terminator or to the first width directive + // + for (; + (String[Index] != NARROW_CHAR) && (String[Index] != WIDE_CHAR) && (String[Index] != 0); + Index++, Count = Count + IncrementValue + ) + ; + + // + // We hit the null-terminator, we now have a count + // + if (String[Index] == 0) { + break; + } + // + // We encountered a narrow directive - strip it from the size calculation since it doesn't get printed + // and also set the flag that determines what we increment by.(if narrow, increment by 1, if wide increment by 2) + // + if (String[Index] == NARROW_CHAR) { + // + // Skip to the next character + // + Index++; + IncrementValue = 1; + } else { + // + // Skip to the next character + // + Index++; + IncrementValue = 2; + } + } while (String[Index] != 0); + + // + // Increment by one to include the null-terminator in the size + // + Count++; + + return Count * sizeof (CHAR16); +} + +/** + Show all registered HotKey help strings on bottom Rows. + + @param FormData The curent input form data info. + +**/ +VOID +PrintHotKeyHelpString ( + IN FORM_DISPLAY_ENGINE_FORM *FormData + ) +{ + UINTN CurrentCol; + UINTN CurrentRow; + UINTN BottomRowOfHotKeyHelp; + UINTN ColumnWidth; + UINTN Index; + EFI_SCREEN_DESCRIPTOR LocalScreen; + LIST_ENTRY *Link; + BROWSER_HOT_KEY *HotKey; + + if (IsListEmpty (&FormData->HotKeyListHead)) { + return; + } + + CopyMem (&LocalScreen, &gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR)); + ColumnWidth = (LocalScreen.RightColumn - LocalScreen.LeftColumn) / 3; + BottomRowOfHotKeyHelp = LocalScreen.BottomRow - STATUS_BAR_HEIGHT - 3; + + // + // Calculate total number of Register HotKeys. + // + Index = 0; + Link = GetFirstNode (&FormData->HotKeyListHead); + while (!IsNull (&FormData->HotKeyListHead, Link)) { + HotKey = BROWSER_HOT_KEY_FROM_LINK (Link); + // + // Help string can't exceed ColumnWidth. One Row will show three Help information. + // + if (StrLen (HotKey->HelpString) > ColumnWidth) { + HotKey->HelpString[ColumnWidth] = L'\0'; + } + // + // Calculate help information Column and Row. + // + if ((Index % 3) != 2) { + CurrentCol = LocalScreen.LeftColumn + (2 - Index % 3) * ColumnWidth; + } else { + CurrentCol = LocalScreen.LeftColumn + 2; + } + CurrentRow = BottomRowOfHotKeyHelp - Index / 3; + // + // Print HotKey help string on bottom Row. + // + PrintStringAt (CurrentCol, CurrentRow, HotKey->HelpString); + + // + // Get Next Hot Key. + // + Link = GetNextNode (&FormData->HotKeyListHead, Link); + Index ++; + } + + return; +} + +/** + Get step info from numeric opcode. + + @param[in] OpCode The input numeric op code. + + @return step info for this opcode. +**/ +UINT64 +LibGetFieldFromNum ( + IN EFI_IFR_OP_HEADER *OpCode + ) +{ + EFI_IFR_NUMERIC *NumericOp; + UINT64 Step; + + NumericOp = (EFI_IFR_NUMERIC *) OpCode; + + switch (NumericOp->Flags & EFI_IFR_NUMERIC_SIZE) { + case EFI_IFR_NUMERIC_SIZE_1: + Step = NumericOp->data.u8.Step; + break; + + case EFI_IFR_NUMERIC_SIZE_2: + Step = NumericOp->data.u16.Step; + break; + + case EFI_IFR_NUMERIC_SIZE_4: + Step = NumericOp->data.u32.Step; + break; + + case EFI_IFR_NUMERIC_SIZE_8: + Step = NumericOp->data.u64.Step; + break; + + default: + Step = 0; + break; + } + + return Step; +} + +/** + Initialize the HII String Token to the correct values. + +**/ +VOID +InitializeLibStrings ( + VOID + ) +{ + mLibUnknownString = L"!"; + + gEnterString = LibGetToken (STRING_TOKEN (ENTER_STRING), mCDLStringPackHandle); + gEnterCommitString = LibGetToken (STRING_TOKEN (ENTER_COMMIT_STRING), mCDLStringPackHandle); + gEnterEscapeString = LibGetToken (STRING_TOKEN (ENTER_ESCAPE_STRING), mCDLStringPackHandle); + gEscapeString = LibGetToken (STRING_TOKEN (ESCAPE_STRING), mCDLStringPackHandle); + gMoveHighlight = LibGetToken (STRING_TOKEN (MOVE_HIGHLIGHT), mCDLStringPackHandle); + gDecNumericInput = LibGetToken (STRING_TOKEN (DEC_NUMERIC_INPUT), mCDLStringPackHandle); + gHexNumericInput = LibGetToken (STRING_TOKEN (HEX_NUMERIC_INPUT), mCDLStringPackHandle); + gToggleCheckBox = LibGetToken (STRING_TOKEN (TOGGLE_CHECK_BOX), mCDLStringPackHandle); + + gAreYouSure = LibGetToken (STRING_TOKEN (ARE_YOU_SURE), mCDLStringPackHandle); + gYesResponse = LibGetToken (STRING_TOKEN (ARE_YOU_SURE_YES), mCDLStringPackHandle); + gNoResponse = LibGetToken (STRING_TOKEN (ARE_YOU_SURE_NO), mCDLStringPackHandle); + gPlusString = LibGetToken (STRING_TOKEN (PLUS_STRING), mCDLStringPackHandle); + gMinusString = LibGetToken (STRING_TOKEN (MINUS_STRING), mCDLStringPackHandle); + gAdjustNumber = LibGetToken (STRING_TOKEN (ADJUST_NUMBER), mCDLStringPackHandle); + gSaveChanges = LibGetToken (STRING_TOKEN (SAVE_CHANGES), mCDLStringPackHandle); + + gLibEmptyString = LibGetToken (STRING_TOKEN (EMPTY_STRING), mCDLStringPackHandle); + + gNvUpdateMessage = LibGetToken (STRING_TOKEN (NV_UPDATE_MESSAGE), mCDLStringPackHandle); + gInputErrorMessage = LibGetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE), mCDLStringPackHandle); + + // + // SpaceBuffer; + // + mSpaceBuffer = AllocatePool ((SPACE_BUFFER_SIZE + 1) * sizeof (CHAR16)); + ASSERT (mSpaceBuffer != NULL); + LibSetUnicodeMem (mSpaceBuffer, SPACE_BUFFER_SIZE, L' '); + mSpaceBuffer[SPACE_BUFFER_SIZE] = L'\0'; +} + + +/** + Free the HII String. + +**/ +VOID +FreeLibStrings ( + VOID + ) +{ + FreePool (gEnterString); + FreePool (gEnterCommitString); + FreePool (gEnterEscapeString); + FreePool (gEscapeString); + FreePool (gMoveHighlight); + FreePool (gDecNumericInput); + FreePool (gHexNumericInput); + FreePool (gToggleCheckBox); + + FreePool (gAreYouSure); + FreePool (gYesResponse); + FreePool (gNoResponse); + FreePool (gPlusString); + FreePool (gMinusString); + FreePool (gAdjustNumber); + FreePool (gSaveChanges); + + FreePool (gLibEmptyString); + + FreePool (gNvUpdateMessage); + FreePool (gInputErrorMessage); + + FreePool (mSpaceBuffer); +} + +/** + Wait for a key to be pressed by user. + + @param Key The key which is pressed by user. + + @retval EFI_SUCCESS The function always completed successfully. + +**/ +EFI_STATUS +WaitForKeyStroke ( + OUT EFI_INPUT_KEY *Key + ) +{ + EFI_STATUS Status; + UINTN Index; + + while (TRUE) { + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, Key); + if (!EFI_ERROR (Status)) { + break; + } + + if (Status != EFI_NOT_READY) { + continue; + } + + gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Index); + } + return Status; +} + + +/** + Set Buffer to Value for Size bytes. + + @param Buffer Memory to set. + @param Size Number of bytes to set + @param Value Value of the set operation. + +**/ +VOID +LibSetUnicodeMem ( + IN VOID *Buffer, + IN UINTN Size, + IN CHAR16 Value + ) +{ + CHAR16 *Ptr; + + Ptr = Buffer; + while ((Size--) != 0) { + *(Ptr++) = Value; + } +} + +/** + The internal function prints to the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL + protocol instance. + + @param Width Width of string to be print. + @param Column The position of the output string. + @param Row The position of the output string. + @param Out The EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL instance. + @param Fmt The format string. + @param Args The additional argument for the variables in the format string. + + @return Number of Unicode character printed. + +**/ +UINTN +PrintInternal ( + IN UINTN Width, + IN UINTN Column, + IN UINTN Row, + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Out, + IN CHAR16 *Fmt, + IN VA_LIST Args + ) +{ + CHAR16 *Buffer; + CHAR16 *BackupBuffer; + UINTN Index; + UINTN PreviousIndex; + UINTN Count; + UINTN PrintWidth; + UINTN CharWidth; + + // + // For now, allocate an arbitrarily long buffer + // + Buffer = AllocateZeroPool (0x10000); + BackupBuffer = AllocateZeroPool (0x10000); + ASSERT (Buffer); + ASSERT (BackupBuffer); + + if (Column != (UINTN) -1) { + Out->SetCursorPosition (Out, Column, Row); + } + + UnicodeVSPrint (Buffer, 0x10000, Fmt, Args); + + Out->Mode->Attribute = Out->Mode->Attribute & 0x7f; + + Out->SetAttribute (Out, Out->Mode->Attribute); + + Index = 0; + PreviousIndex = 0; + Count = 0; + PrintWidth = 0; + CharWidth = 1; + + do { + for (; (Buffer[Index] != NARROW_CHAR) && (Buffer[Index] != WIDE_CHAR) && (Buffer[Index] != 0); Index++) { + BackupBuffer[Index] = Buffer[Index]; + } + + if (Buffer[Index] == 0) { + break; + } + // + // Null-terminate the temporary string + // + BackupBuffer[Index] = 0; + + // + // Print this out, we are about to switch widths + // + Out->OutputString (Out, &BackupBuffer[PreviousIndex]); + Count += StrLen (&BackupBuffer[PreviousIndex]); + PrintWidth += Count * CharWidth; + + // + // Preserve the current index + 1, since this is where we will start printing from next + // + PreviousIndex = Index + 1; + + // + // We are at a narrow or wide character directive. Set attributes and strip it and print it + // + if (Buffer[Index] == NARROW_CHAR) { + // + // Preserve bits 0 - 6 and zero out the rest + // + Out->Mode->Attribute = Out->Mode->Attribute & 0x7f; + Out->SetAttribute (Out, Out->Mode->Attribute); + CharWidth = 1; + } else { + // + // Must be wide, set bit 7 ON + // + Out->Mode->Attribute = Out->Mode->Attribute | EFI_WIDE_ATTRIBUTE; + Out->SetAttribute (Out, Out->Mode->Attribute); + CharWidth = 2; + } + + Index++; + + } while (Buffer[Index] != 0); + + // + // We hit the end of the string - print it + // + Out->OutputString (Out, &BackupBuffer[PreviousIndex]); + Count += StrLen (&BackupBuffer[PreviousIndex]); + PrintWidth += Count * CharWidth; + if (PrintWidth < Width) { + Out->Mode->Attribute = Out->Mode->Attribute & 0x7f; + Out->SetAttribute (Out, Out->Mode->Attribute); + Out->OutputString (Out, &mSpaceBuffer[SPACE_BUFFER_SIZE - Width + PrintWidth]); + } + + FreePool (Buffer); + FreePool (BackupBuffer); + return Count; +} + +/** + Prints a formatted unicode string to the default console, at + the supplied cursor position. + + @param Width Width of String to be printed. + @param Column The cursor position to print the string at. + @param Row The cursor position to print the string at. + @param Fmt Format string. + @param ... Variable argument list for format string. + + @return Length of string printed to the console + +**/ +UINTN +EFIAPI +PrintAt ( + IN UINTN Width, + IN UINTN Column, + IN UINTN Row, + IN CHAR16 *Fmt, + ... + ) +{ + VA_LIST Args; + UINTN LengthOfPrinted; + + VA_START (Args, Fmt); + LengthOfPrinted = PrintInternal (Width, Column, Row, gST->ConOut, Fmt, Args); + VA_END (Args); + return LengthOfPrinted; +} + diff --git a/MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLibInternal.h b/MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLibInternal.h new file mode 100644 index 0000000000..ab197cfe72 --- /dev/null +++ b/MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLibInternal.h @@ -0,0 +1,307 @@ +/** @file + + This library class defines a set of interfaces to customize Display module + +Copyright (c) 2013, 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 that 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. + +**/ + +#ifndef __CUSTOMIZED_DISPLAY_LIB_INTERNAL_H__ +#define __CUSTOMIZED_DISPLAY_LIB_INTERNAL_H__ + + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Colors.h" + + + +#define FORMSET_CLASS_PLATFORM_SETUP 0x0001 +#define FORMSET_CLASS_FRONT_PAGE 0x0002 +#define FORMSET_CLASS_MODEL_PAGE 0x0004 + + +#define FRONT_PAGE_HEADER_HEIGHT 6 +#define NONE_FRONT_PAGE_HEADER_HEIGHT 3 +#define FOOTER_HEIGHT 4 +#define STATUS_BAR_HEIGHT 1 + +// +// Screen definitions +// +#define BANNER_HEIGHT 6 +#define BANNER_COLUMNS 3 +#define BANNER_LEFT_COLUMN_INDENT 1 + +// +// Character definitions +// +#define UPPER_LOWER_CASE_OFFSET 0x20 + +// +// This is the Input Error Message +// +#define INPUT_ERROR 1 + +// +// This is the NV RAM update required Message +// +#define NV_UPDATE_REQUIRED 2 + +typedef struct { + EFI_STRING_ID Banner[BANNER_HEIGHT][BANNER_COLUMNS]; +} BANNER_DATA; + +extern UINT16 gClassOfVfr; // Formset class information +extern BANNER_DATA *gBannerData; +extern EFI_SCREEN_DESCRIPTOR gScreenDimensions; +extern UINTN gFooterHeight; + +// +// Browser Global Strings +// +extern CHAR16 *gEnterString; +extern CHAR16 *gEnterCommitString; +extern CHAR16 *gEnterEscapeString; +extern CHAR16 *gEscapeString; +extern CHAR16 *gMoveHighlight; +extern CHAR16 *gDecNumericInput; +extern CHAR16 *gHexNumericInput; +extern CHAR16 *gToggleCheckBox; +extern CHAR16 *gLibEmptyString; +extern CHAR16 *gAreYouSure; +extern CHAR16 *gYesResponse; +extern CHAR16 *gNoResponse; +extern CHAR16 *gPlusString; +extern CHAR16 *gMinusString; +extern CHAR16 *gAdjustNumber; +extern CHAR16 *gSaveChanges; +extern CHAR16 *gNvUpdateMessage; +extern CHAR16 *gInputErrorMessage; +/** + + Print banner info for front page. + + @param[in] FormData Form Data to be shown in Page + +**/ +VOID +PrintBannerInfo ( + IN FORM_DISPLAY_ENGINE_FORM *FormData + ); + +/** + + Print framework for a page. + +**/ +VOID +PrintFramework ( + VOID + ); + +/** + Print the form title. + + @param[in] FormData Form Data to be shown in Page + +**/ +VOID +PrintFormTitle ( + IN FORM_DISPLAY_ENGINE_FORM *FormData + ); + +/** + Validate the input screen diemenstion info. + + @param FormData The input form data info. + + @return EFI_SUCCESS The input screen info is acceptable. + @return EFI_INVALID_PARAMETER The input screen info is not acceptable. + +**/ +EFI_STATUS +ScreenDiemensionInfoValidate ( + IN FORM_DISPLAY_ENGINE_FORM *FormData + ); + +/** + Get the string based on the StringId and HII Package List Handle. + + @param Token The String's ID. + @param HiiHandle The package list in the HII database to search for + the specified string. + + @return The output string. + +**/ +CHAR16 * +LibGetToken ( + IN EFI_STRING_ID Token, + IN EFI_HII_HANDLE HiiHandle + ); + +/** + Count the storage space of a Unicode string. + + This function handles the Unicode string with NARROW_CHAR + and WIDE_CHAR control characters. NARROW_HCAR and WIDE_CHAR + does not count in the resultant output. If a WIDE_CHAR is + hit, then 2 Unicode character will consume an output storage + space with size of CHAR16 till a NARROW_CHAR is hit. + + If String is NULL, then ASSERT (). + + @param String The input string to be counted. + + @return Storage space for the input string. + +**/ +UINTN +LibGetStringWidth ( + IN CHAR16 *String + ); + +/** + Show all registered HotKey help strings on bottom Rows. + + @param FormData The curent input form data info. + +**/ +VOID +PrintHotKeyHelpString ( + IN FORM_DISPLAY_ENGINE_FORM *FormData + ); + +/** + Get step info from numeric opcode. + + @param[in] OpCode The input numeric op code. + + @return step info for this opcode. +**/ +UINT64 +LibGetFieldFromNum ( + IN EFI_IFR_OP_HEADER *OpCode + ); + +/** + Initialize the HII String Token to the correct values. + +**/ +VOID +InitializeLibStrings ( + VOID + ); + +/** + Free the HII String. + +**/ +VOID +FreeLibStrings ( + VOID + ); + +/** + Wait for a key to be pressed by user. + + @param Key The key which is pressed by user. + + @retval EFI_SUCCESS The function always completed successfully. + +**/ +EFI_STATUS +WaitForKeyStroke ( + OUT EFI_INPUT_KEY *Key + ); + +/** + Set Buffer to Value for Size bytes. + + @param Buffer Memory to set. + @param Size Number of bytes to set + @param Value Value of the set operation. + +**/ +VOID +LibSetUnicodeMem ( + IN VOID *Buffer, + IN UINTN Size, + IN CHAR16 Value + ); + +/** + Prints a formatted unicode string to the default console, at + the supplied cursor position. + + @param Width Width of String to be printed. + @param Column The cursor position to print the string at. + @param Row The cursor position to print the string at. + @param Fmt Format string. + @param ... Variable argument list for format string. + + @return Length of string printed to the console + +**/ +UINTN +EFIAPI +PrintAt ( + IN UINTN Width, + IN UINTN Column, + IN UINTN Row, + IN CHAR16 *Fmt, + ... + ); + +/** + Process some op codes which is out side of current form. + + @param FormData Pointer to the form data. + +**/ +VOID +ProcessExternedOpcode ( + IN FORM_DISPLAY_ENGINE_FORM *FormData + ); + +#endif diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index 3105519297..4ae24eb80e 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -98,6 +98,10 @@ # CpuExceptionHandlerLib|Include/Library/CpuExceptionHandlerLib.h + ## @libraryclass Provides platform specific display interface. + # + CustomizedDisplayLib|Include/Library/CustomizedDisplayLib.h + [Guids] ## MdeModule package token space guid # Include/Guid/MdeModulePkgTokenSpace.h @@ -361,6 +365,12 @@ ## Include/Protocol/BootLogo.h gEfiBootLogoProtocolGuid = { 0xcdea2bd3, 0xfc25, 0x4c1c, { 0xb9, 0x7c, 0xb3, 0x11, 0x86, 0x6, 0x49, 0x90 } } + ## Include/Protocol/DisplayProtocol.h + gEdkiiFormDisplayEngineProtocolGuid = { 0x9bbe29e9, 0xfda1, 0x41ec, { 0xad, 0x52, 0x45, 0x22, 0x13, 0x74, 0x2d, 0x2e } } + + ## Include/Protocol/FormBrowserEx2.h + gEdkiiFormBrowserEx2ProtocolGuid = { 0xa770c357, 0xb693, 0x4e6d, { 0xa6, 0xcf, 0xd2, 0x1c, 0x72, 0x8e, 0x55, 0xb } } + [PcdsFeatureFlag] ## Indicate whether platform can support update capsule across a system reset gEfiMdeModulePkgTokenSpaceGuid.PcdSupportUpdateCapsuleReset|FALSE|BOOLEAN|0x0001001d diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index 781fbbf3b4..7895834c24 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -76,6 +76,7 @@ CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf PalLib|MdePkg/Library/BasePalLibNull/BasePalLibNull.inf + CustomizedDisplayLib|MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf # # Misc # @@ -293,6 +294,7 @@ MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf + MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf MdeModulePkg/Application/VariableInfo/VariableInfo.inf MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.inf MdeModulePkg/Universal/Variable/Pei/VariablePei.inf diff --git a/MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf b/MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf new file mode 100644 index 0000000000..2170369d9e --- /dev/null +++ b/MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf @@ -0,0 +1,70 @@ +## @file +# The DXE driver produces FORM BROWSER protocols defined in UEFI HII 2.1 specificatin. +# +# Copyright (c) 2007 - 2013, 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. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DisplayEngine + FILE_GUID = E660EA85-058E-4b55-A54B-F02F83A24707 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeDisplayEngine + UNLOAD_IMAGE = UnloadDisplayEngine +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + FormDisplayStr.uni + FormDisplay.c + FormDisplay.h + Print.c + ProcessOptions.c + InputHandler.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + UefiBootServicesTableLib + DebugLib + BaseMemoryLib + BaseLib + PrintLib + HiiLib + MemoryAllocationLib + CustomizedDisplayLib + +[Protocols] + gEdkiiFormDisplayEngineProtocolGuid + gEdkiiFormBrowserEx2ProtocolGuid + +[Guids] + gEfiIfrTianoGuid ## CONSUMES ## GUID + +[Depex] + gEfiHiiDatabaseProtocolGuid AND gEfiHiiConfigRoutingProtocolGuid AND gEdkiiFormBrowserEx2ProtocolGuid + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdBrowserGrayOutTextStatement ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdBrowerGrayOutReadOnlyMenu ## CONSUMES + +[BuildOptions] + MSFT:*_*_*_CC_FLAGS = /Od + diff --git a/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplay.c b/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplay.c new file mode 100644 index 0000000000..f6322293fc --- /dev/null +++ b/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplay.c @@ -0,0 +1,3200 @@ +/** @file +Entry and initialization module for the browser. + +Copyright (c) 2007 - 2013, 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 "FormDisplay.h" + +// +// 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_LEFT, + UiLeft, + }, + { + SCAN_RIGHT, + UiRight, + } +}; + +UINTN mScanCodeNumber = sizeof (gScanCodeToOperation) / sizeof (gScanCodeToOperation[0]); + +SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag[] = { + { + UiNoOperation, + CfUiNoOperation, + }, + { + UiSelect, + CfUiSelect, + }, + { + UiUp, + CfUiUp, + }, + { + UiDown, + CfUiDown, + }, + { + UiLeft, + CfUiLeft, + }, + { + UiRight, + CfUiRight, + }, + { + UiReset, + CfUiReset, + }, + { + UiPageUp, + CfUiPageUp, + }, + { + UiPageDown, + CfUiPageDown + }, + { + UiHotKey, + CfUiHotKey + } +}; + +EFI_GUID gDisplayEngineGuid = { + 0xE38C1029, 0xE38F, 0x45b9, {0x8F, 0x0D, 0xE2, 0xE6, 0x0B, 0xC9, 0xB2, 0x62} +}; + +UINTN gSequence; +EFI_SCREEN_DESCRIPTOR gStatementDimensions; +EFI_SCREEN_DESCRIPTOR gOldStatementDimensions = {0}; +BOOLEAN mStatementLayoutIsChanged = TRUE; +USER_INPUT *gUserInput; +FORM_DISPLAY_ENGINE_FORM *gFormData; +EFI_HII_HANDLE gHiiHandle; +UINT16 gDirection; +LIST_ENTRY gMenuOption; +DISPLAY_HIGHLIGHT_MENU_INFO gHighligthMenuInfo = {0}; + +// +// Browser Global Strings +// +CHAR16 *gFormNotFound; +CHAR16 *gNoSubmitIf; +CHAR16 *gBrwoserError; +CHAR16 *gSaveFailed; +CHAR16 *gPromptForData; +CHAR16 *gPromptForPassword; +CHAR16 *gPromptForNewPassword; +CHAR16 *gConfirmPassword; +CHAR16 *gConfirmError; +CHAR16 *gPassowordInvalid; +CHAR16 *gPressEnter; +CHAR16 *gEmptyString; +CHAR16 *gMiniString; +CHAR16 *gOptionMismatch; +CHAR16 *gFormSuppress; +CHAR16 *gProtocolNotFound; + +CHAR16 gPromptBlockWidth; +CHAR16 gOptionBlockWidth; +CHAR16 gHelpBlockWidth; +CHAR16 *mUnknownString; + +FORM_DISPLAY_DRIVER_PRIVATE_DATA mPrivateData = { + FORM_DISPLAY_DRIVER_SIGNATURE, + NULL, + { + FormDisplay, + ClearDisplayPage, + ConfirmDataChange + } +}; + + +/** + Get the string based on the StringId and HII Package List Handle. + + @param Token The String's ID. + @param HiiHandle The package list in the HII database to search for + the specified string. + + @return The output string. + +**/ +CHAR16 * +GetToken ( + IN EFI_STRING_ID Token, + IN EFI_HII_HANDLE HiiHandle + ) +{ + EFI_STRING String; + + String = HiiGetString (HiiHandle, Token, NULL); + if (String == NULL) { + String = AllocateCopyPool (StrSize (mUnknownString), mUnknownString); + ASSERT (String != NULL); + } + + return (CHAR16 *) String; +} + + +/** + Initialize the HII String Token to the correct values. + +**/ +VOID +InitializeDisplayStrings ( + VOID + ) +{ + mUnknownString = GetToken (STRING_TOKEN (UNKNOWN_STRING), gHiiHandle); + gSaveFailed = GetToken (STRING_TOKEN (SAVE_FAILED), gHiiHandle); + gPromptForData = GetToken (STRING_TOKEN (PROMPT_FOR_DATA), gHiiHandle); + gPromptForPassword = GetToken (STRING_TOKEN (PROMPT_FOR_PASSWORD), gHiiHandle); + gPromptForNewPassword = GetToken (STRING_TOKEN (PROMPT_FOR_NEW_PASSWORD), gHiiHandle); + gConfirmPassword = GetToken (STRING_TOKEN (CONFIRM_PASSWORD), gHiiHandle); + gConfirmError = GetToken (STRING_TOKEN (CONFIRM_ERROR), gHiiHandle); + gPassowordInvalid = GetToken (STRING_TOKEN (PASSWORD_INVALID), gHiiHandle); + gPressEnter = GetToken (STRING_TOKEN (PRESS_ENTER), gHiiHandle); + gEmptyString = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle); + gMiniString = GetToken (STRING_TOKEN (MINI_STRING), gHiiHandle); + gOptionMismatch = GetToken (STRING_TOKEN (OPTION_MISMATCH), gHiiHandle); + gFormSuppress = GetToken (STRING_TOKEN (FORM_SUPPRESSED), gHiiHandle); + gProtocolNotFound = GetToken (STRING_TOKEN (PROTOCOL_NOT_FOUND), gHiiHandle); + gFormNotFound = GetToken (STRING_TOKEN (STATUS_BROWSER_FORM_NOT_FOUND), gHiiHandle); + gNoSubmitIf = GetToken (STRING_TOKEN (STATUS_BROWSER_NO_SUBMIT_IF), gHiiHandle); + gBrwoserError = GetToken (STRING_TOKEN (STATUS_BROWSER_ERROR), gHiiHandle); +} + +/** + Free up the resource allocated for all strings required + by Setup Browser. + +**/ +VOID +FreeDisplayStrings ( + VOID + ) +{ + FreePool (mUnknownString); + FreePool (gEmptyString); + FreePool (gSaveFailed); + FreePool (gPromptForData); + FreePool (gPromptForPassword); + FreePool (gPromptForNewPassword); + FreePool (gConfirmPassword); + FreePool (gConfirmError); + FreePool (gPassowordInvalid); + FreePool (gPressEnter); + FreePool (gMiniString); + FreePool (gOptionMismatch); + FreePool (gFormSuppress); + FreePool (gProtocolNotFound); + FreePool (gBrwoserError); + FreePool (gNoSubmitIf); + FreePool (gFormNotFound); +} + +/** + Get prompt string id from the opcode data buffer. + + @param OpCode The input opcode buffer. + + @return The prompt string id. + +**/ +EFI_STRING_ID +GetPrompt ( + IN EFI_IFR_OP_HEADER *OpCode + ) +{ + EFI_IFR_STATEMENT_HEADER *Header; + + if (OpCode->Length <= sizeof (EFI_IFR_OP_HEADER)) { + return 0; + } + + Header = (EFI_IFR_STATEMENT_HEADER *) (OpCode + 1); + + return Header->Prompt; +} + +/** + Get the supported width for a particular op-code + + @param Statement The curent statement. + + @return Returns the number of CHAR16 characters that is support. + +**/ +UINT16 +GetWidth ( + IN FORM_DISPLAY_ENGINE_STATEMENT *Statement + ) +{ + CHAR16 *String; + UINTN Size; + UINT16 Width; + EFI_IFR_TEXT *TestOp; + + Size = 0; + + // + // See if the second text parameter is really NULL + // + if (Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) { + TestOp = (EFI_IFR_TEXT *) Statement->OpCode; + if (TestOp->TextTwo != 0) { + String = GetToken (TestOp->TextTwo, gFormData->HiiHandle); + Size = StrLen (String); + FreePool (String); + } + } + + if ((Statement->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) || + (Statement->OpCode->OpCode == EFI_IFR_REF_OP) || + (Statement->OpCode->OpCode == EFI_IFR_PASSWORD_OP) || + (Statement->OpCode->OpCode == EFI_IFR_ACTION_OP) || + (Statement->OpCode->OpCode == EFI_IFR_RESET_BUTTON_OP) || + // + // Allow a wide display if text op-code and no secondary text op-code + // + ((Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) && (Size == 0)) + ) { + Width = (UINT16) (gPromptBlockWidth + gOptionBlockWidth); + } else { + Width = (UINT16) gPromptBlockWidth; + } + + return (UINT16) (Width - LEFT_SKIPPED_COLUMNS); +} + +/** + 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. + The output string format is: + Glyph Info + String info + '\0'. + + In the code, it deals \r,\n,\r\n same as \n\r, also it not process the \r or \g. + + @param InputString String description for this option. + @param LineWidth Width of the desired string to extract in CHAR16 + characters + @param GlyphWidth The glyph width of the begin of the char in the string. + @param Index Where in InputString to start the copy process + @param OutputString Buffer to copy the string into + + @return Returns the number of CHAR16 characters that were copied into the OutputString + buffer, include extra glyph info and '\0' info. + +**/ +UINT16 +GetLineByWidth ( + IN CHAR16 *InputString, + IN UINT16 LineWidth, + IN OUT UINT16 *GlyphWidth, + IN OUT UINTN *Index, + OUT CHAR16 **OutputString + ) +{ + UINT16 StrOffset; + UINT16 GlyphOffset; + UINT16 OriginalGlyphWidth; + BOOLEAN ReturnFlag; + UINT16 LastSpaceOffset; + UINT16 LastGlyphWidth; + + if (InputString == NULL || Index == NULL || OutputString == NULL) { + return 0; + } + + if (LineWidth == 0 || *GlyphWidth == 0) { + return 0; + } + + // + // Save original glyph width. + // + OriginalGlyphWidth = *GlyphWidth; + LastGlyphWidth = OriginalGlyphWidth; + ReturnFlag = FALSE; + LastSpaceOffset = 0; + + // + // 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 (StrOffset = 0, GlyphOffset = 0; GlyphOffset <= LineWidth; StrOffset++) { + switch (InputString[*Index + StrOffset]) { + case NARROW_CHAR: + *GlyphWidth = 1; + break; + + case WIDE_CHAR: + *GlyphWidth = 2; + break; + + case CHAR_CARRIAGE_RETURN: + case CHAR_LINEFEED: + case CHAR_NULL: + ReturnFlag = TRUE; + break; + + default: + GlyphOffset = GlyphOffset + *GlyphWidth; + + // + // Record the last space info in this line. Will be used in rewind. + // + if ((InputString[*Index + StrOffset] == CHAR_SPACE) && (GlyphOffset <= LineWidth)) { + LastSpaceOffset = StrOffset; + LastGlyphWidth = *GlyphWidth; + } + break; + } + + if (ReturnFlag) { + break; + } + } + + // + // Rewind the string from the maximum size until we see a space to break the line + // + if (GlyphOffset > LineWidth) { + // + // Rewind the string to last space char in this line. + // + if (LastSpaceOffset != 0) { + StrOffset = LastSpaceOffset; + *GlyphWidth = LastGlyphWidth; + } else { + // + // Roll back to last char in the line width. + // + StrOffset--; + } + } + + // + // The CHAR_NULL has process last time, this time just return 0 to stand for the end. + // + if (StrOffset == 0 && (InputString[*Index + StrOffset] == CHAR_NULL)) { + return 0; + } + + // + // Need extra glyph info and '\0' info, so +2. + // + *OutputString = AllocateZeroPool (((UINTN) (StrOffset + 2) * sizeof(CHAR16))); + if (*OutputString == NULL) { + return 0; + } + + // + // Save the glyph info at the begin of the string, will used by Print function. + // + if (OriginalGlyphWidth == 1) { + *(*OutputString) = NARROW_CHAR; + } else { + *(*OutputString) = WIDE_CHAR; + } + + CopyMem ((*OutputString) + 1, &InputString[*Index], StrOffset * sizeof(CHAR16)); + + if (InputString[*Index + StrOffset] == CHAR_SPACE) { + // + // Skip the space info at the begin of next line. + // + *Index = (UINT16) (*Index + StrOffset + 1); + } else if (InputString[*Index + StrOffset] == CHAR_LINEFEED) { + // + // Skip the /n or /n/r info. + // + if (InputString[*Index + StrOffset + 1] == CHAR_CARRIAGE_RETURN) { + *Index = (UINT16) (*Index + StrOffset + 2); + } else { + *Index = (UINT16) (*Index + StrOffset + 1); + } + } else if (InputString[*Index + StrOffset] == CHAR_CARRIAGE_RETURN) { + // + // Skip the /r or /r/n info. + // + if (InputString[*Index + StrOffset + 1] == CHAR_LINEFEED) { + *Index = (UINT16) (*Index + StrOffset + 2); + } else { + *Index = (UINT16) (*Index + StrOffset + 1); + } + } else { + *Index = (UINT16) (*Index + StrOffset); + } + + // + // Include extra glyph info and '\0' info, so +2. + // + return StrOffset + 2; +} + +/** + Add one menu option by specified description and context. + + @param Statement Statement of this Menu Option. + @param MenuItemCount The index for this Option in the Menu. + @param NestIn Whether this statement is nest in another statement. + +**/ +VOID +UiAddMenuOption ( + IN FORM_DISPLAY_ENGINE_STATEMENT *Statement, + IN UINT16 *MenuItemCount, + IN BOOLEAN NestIn + ) +{ + UI_MENU_OPTION *MenuOption; + UINTN Index; + UINTN Count; + CHAR16 *String; + UINT16 NumberOfLines; + UINT16 GlyphWidth; + UINT16 Width; + UINTN ArrayEntry; + CHAR16 *OutputString; + EFI_STRING_ID PromptId; + + NumberOfLines = 1; + ArrayEntry = 0; + GlyphWidth = 1; + Count = 1; + MenuOption = NULL; + + PromptId = GetPrompt (Statement->OpCode); + ASSERT (PromptId != 0); + + String = GetToken (PromptId, gFormData->HiiHandle); + ASSERT (String != NULL); + + Width = GetWidth (Statement); + for (; GetLineByWidth (String, Width, &GlyphWidth,&ArrayEntry, &OutputString) != 0x0000;) { + // + // If there is more string to process print on the next row and increment the Skip value + // + if (StrLen (&String[ArrayEntry]) != 0) { + NumberOfLines++; + } + FreePool (OutputString); + } + + if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP || Statement->OpCode->OpCode == EFI_IFR_TIME_OP) { + // + // Add three MenuOptions for Date/Time + // Data format : [01/02/2004] [11:22:33] + // Line number : 0 0 1 0 0 1 + // + NumberOfLines = 0; + Count = 3; + } + + for (Index = 0; Index < Count; Index++) { + MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION)); + ASSERT (MenuOption); + + MenuOption->Signature = UI_MENU_OPTION_SIGNATURE; + MenuOption->Description = String; + MenuOption->Handle = gFormData->HiiHandle; + MenuOption->ThisTag = Statement; + MenuOption->NestInStatement = NestIn; + MenuOption->EntryNumber = *MenuItemCount; + + if (Index == 2) { + // + // Override LineNumber for the MenuOption in Date/Time sequence + // + MenuOption->Skip = 1; + } else { + MenuOption->Skip = NumberOfLines; + } + MenuOption->Sequence = Index; + + if ((Statement->Attribute & HII_DISPLAY_GRAYOUT) != 0) { + MenuOption->GrayOut = TRUE; + } else { + MenuOption->GrayOut = FALSE; + } + + if ((Statement->Attribute & HII_DISPLAY_LOCK) != 0 || (gFormData->Attribute & HII_DISPLAY_LOCK) != 0) { + MenuOption->GrayOut = TRUE; + } + + // + // If the form or the question has the lock attribute, deal same as grayout. + // + if ((gFormData->Attribute & HII_DISPLAY_LOCK) != 0 || (Statement->Attribute & HII_DISPLAY_LOCK) != 0) { + MenuOption->GrayOut = TRUE; + } + + switch (Statement->OpCode->OpCode) { + case EFI_IFR_ORDERED_LIST_OP: + case EFI_IFR_ONE_OF_OP: + case EFI_IFR_NUMERIC_OP: + case EFI_IFR_TIME_OP: + case EFI_IFR_DATE_OP: + case EFI_IFR_CHECKBOX_OP: + case EFI_IFR_PASSWORD_OP: + case EFI_IFR_STRING_OP: + // + // User could change the value of these items + // + MenuOption->IsQuestion = TRUE; + break; + case EFI_IFR_TEXT_OP: + if (FeaturePcdGet (PcdBrowserGrayOutTextStatement)) { + // + // Initializing GrayOut option as TRUE for Text setup options + // so that those options will be Gray in colour and un selectable. + // + MenuOption->GrayOut = TRUE; + } + break; + default: + MenuOption->IsQuestion = FALSE; + break; + } + + if ((Statement->Attribute & HII_DISPLAY_READONLY) != 0) { + MenuOption->ReadOnly = TRUE; + if (FeaturePcdGet (PcdBrowerGrayOutReadOnlyMenu)) { + MenuOption->GrayOut = TRUE; + } + } + + InsertTailList (&gMenuOption, &MenuOption->Link); + } + + (*MenuItemCount)++; +} + +/** + Create the menu list base on the form data info. + +**/ +VOID +ConvertStatementToMenu ( + VOID + ) +{ + UINT16 MenuItemCount; + LIST_ENTRY *Link; + LIST_ENTRY *NestLink; + FORM_DISPLAY_ENGINE_STATEMENT *Statement; + FORM_DISPLAY_ENGINE_STATEMENT *NestStatement; + + MenuItemCount = 0; + InitializeListHead (&gMenuOption); + + Link = GetFirstNode (&gFormData->StatementListHead); + while (!IsNull (&gFormData->StatementListHead, Link)) { + Statement = FORM_DISPLAY_ENGINE_STATEMENT_FROM_LINK (Link); + Link = GetNextNode (&gFormData->StatementListHead, Link); + + // + // Skip the opcode not recognized by Display core. + // + if (Statement->OpCode->OpCode == EFI_IFR_GUID_OP) { + continue; + } + + UiAddMenuOption (Statement, &MenuItemCount, FALSE); + + // + // Check the statement nest in this host statement. + // + NestLink = GetFirstNode (&Statement->NestStatementList); + while (!IsNull (&Statement->NestStatementList, NestLink)) { + NestStatement = FORM_DISPLAY_ENGINE_STATEMENT_FROM_LINK (NestLink); + NestLink = GetNextNode (&Statement->NestStatementList, NestLink); + + UiAddMenuOption (NestStatement, &MenuItemCount, TRUE); + } + } +} + +/** + Count the storage space of a Unicode string. + + This function handles the Unicode string with NARROW_CHAR + and WIDE_CHAR control characters. NARROW_HCAR and WIDE_CHAR + does not count in the resultant output. If a WIDE_CHAR is + hit, then 2 Unicode character will consume an output storage + space with size of CHAR16 till a NARROW_CHAR is hit. + + If String is NULL, then ASSERT (). + + @param String The input string to be counted. + + @return Storage space for the input string. + +**/ +UINTN +GetStringWidth ( + IN CHAR16 *String + ) +{ + UINTN Index; + UINTN Count; + UINTN IncrementValue; + + ASSERT (String != NULL); + if (String == NULL) { + return 0; + } + + Index = 0; + Count = 0; + IncrementValue = 1; + + do { + // + // Advance to the null-terminator or to the first width directive + // + for (; + (String[Index] != NARROW_CHAR) && (String[Index] != WIDE_CHAR) && (String[Index] != 0); + Index++, Count = Count + IncrementValue + ) + ; + + // + // We hit the null-terminator, we now have a count + // + if (String[Index] == 0) { + break; + } + // + // We encountered a narrow directive - strip it from the size calculation since it doesn't get printed + // and also set the flag that determines what we increment by.(if narrow, increment by 1, if wide increment by 2) + // + if (String[Index] == NARROW_CHAR) { + // + // Skip to the next character + // + Index++; + IncrementValue = 1; + } else { + // + // Skip to the next character + // + Index++; + IncrementValue = 2; + } + } while (String[Index] != 0); + + // + // Increment by one to include the null-terminator in the size + // + Count++; + + return Count * sizeof (CHAR16); +} + +/** + Base on the input option string to update the skip value for a menu option. + + @param MenuOption The MenuOption to be checked. + @param OptionString The input option string. + +**/ +VOID +UpdateSkipInfoForMenu ( + IN UI_MENU_OPTION *MenuOption, + IN CHAR16 *OptionString + ) +{ + UINTN Index; + UINT16 Width; + UINTN Row; + CHAR16 *OutputString; + UINT16 GlyphWidth; + + Width = (UINT16) gOptionBlockWidth; + GlyphWidth = 1; + Row = 1; + + for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) { + if (StrLen (&OptionString[Index]) != 0) { + Row++; + } + + FreePool (OutputString); + } + + if ((Row > MenuOption->Skip) && + (MenuOption->ThisTag->OpCode->OpCode != EFI_IFR_DATE_OP) && + (MenuOption->ThisTag->OpCode->OpCode != EFI_IFR_TIME_OP)) { + MenuOption->Skip = Row; + } +} + +/** + Update display lines for a Menu Option. + + @param MenuOption The MenuOption to be checked. + +**/ +VOID +UpdateOptionSkipLines ( + IN UI_MENU_OPTION *MenuOption + ) +{ + CHAR16 *OptionString; + + OptionString = NULL; + + ProcessOptions (MenuOption, FALSE, &OptionString, TRUE); + if (OptionString != NULL) { + UpdateSkipInfoForMenu (MenuOption, OptionString); + + FreePool (OptionString); + } + + if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TEXT_OP) && (((EFI_IFR_TEXT*)MenuOption->ThisTag->OpCode)->TextTwo != 0)) { + OptionString = GetToken (((EFI_IFR_TEXT*)MenuOption->ThisTag->OpCode)->TextTwo, gFormData->HiiHandle); + + if (OptionString != NULL) { + UpdateSkipInfoForMenu (MenuOption, OptionString); + + FreePool (OptionString); + } + } +} + +/** + Check whether this Menu Option could be highlighted. + + This is an internal function. + + @param MenuOption The MenuOption to be checked. + + @retval TRUE This Menu Option is selectable. + @retval FALSE This Menu Option could not be selected. + +**/ +BOOLEAN +IsSelectable ( + UI_MENU_OPTION *MenuOption + ) +{ + if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) || + MenuOption->GrayOut || MenuOption->ReadOnly) { + return FALSE; + } else { + return TRUE; + } +} + +/** + Move to next selectable statement. + + This is an internal function. + + @param GoUp The navigation direction. TRUE: up, FALSE: down. + @param CurrentPosition Current position. + @param GapToTop Gap position to top or bottom. + + @return The row distance from current MenuOption to next selectable MenuOption. + + @retval -1 Reach the begin of the menu, still can't find the selectable menu. + @retval Value Find the selectable menu, maybe the truly selectable, maybe the l + last menu showing at current form. + +**/ +INTN +MoveToNextStatement ( + IN BOOLEAN GoUp, + IN OUT LIST_ENTRY **CurrentPosition, + IN UINTN GapToTop + ) +{ + INTN Distance; + LIST_ENTRY *Pos; + UI_MENU_OPTION *NextMenuOption; + UI_MENU_OPTION *PreMenuOption; + + Distance = 0; + Pos = *CurrentPosition; + PreMenuOption = MENU_OPTION_FROM_LINK (Pos); + + while (TRUE) { + NextMenuOption = MENU_OPTION_FROM_LINK (Pos); + // + // NextMenuOption->Row == 0 means this menu has not calculate + // the NextMenuOption->Skip value yet, just calculate here. + // + if (NextMenuOption->Row == 0) { + UpdateOptionSkipLines (NextMenuOption); + } + + if (GoUp && (PreMenuOption != NextMenuOption)) { + // + // In this case, still can't find the selectable menu, + // return the last one in the showing form. + // + if ((UINTN) Distance + NextMenuOption->Skip > GapToTop) { + NextMenuOption = PreMenuOption; + break; + } + + // + // Current Position doesn't need to be caculated when go up. + // Caculate distanct at first when go up + // + Distance += NextMenuOption->Skip; + } + + if (IsSelectable (NextMenuOption)) { + break; + } + + // + // Arrive at begin of the menu list. + // + if ((GoUp ? Pos->BackLink : Pos->ForwardLink) == &gMenuOption) { + Distance = -1; + break; + } + + if (!GoUp) { + // + // In this case, still can't find the selectable menu, + // return the last one in the showing form. + // + if ((UINTN) Distance + NextMenuOption->Skip > GapToTop) { + NextMenuOption = PreMenuOption; + break; + } + + Distance += NextMenuOption->Skip; + } + + PreMenuOption = NextMenuOption; + Pos = (GoUp ? Pos->BackLink : Pos->ForwardLink); + } + + *CurrentPosition = &NextMenuOption->Link; + return Distance; +} + + +/** + Process option string for date/time opcode. + + @param MenuOption Menu option point to date/time. + @param OptionString Option string input for process. + @param AddOptCol Whether need to update MenuOption->OptCol. + +**/ +VOID +ProcessStringForDateTime ( + UI_MENU_OPTION *MenuOption, + CHAR16 *OptionString, + BOOLEAN AddOptCol + ) +{ + UINTN Index; + UINTN Count; + FORM_DISPLAY_ENGINE_STATEMENT *Statement; + EFI_IFR_DATE *Date; + EFI_IFR_TIME *Time; + + ASSERT (MenuOption != NULL && OptionString != NULL); + + Statement = MenuOption->ThisTag; + Date = NULL; + Time = NULL; + if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP) { + Date = (EFI_IFR_DATE *) Statement->OpCode; + } else if (Statement->OpCode->OpCode == EFI_IFR_TIME_OP) { + Time = (EFI_IFR_TIME *) Statement->OpCode; + } + + // + // If leading spaces on OptionString - remove the spaces + // + for (Index = 0; OptionString[Index] == L' '; Index++) { + // + // Base on the blockspace to get the option column info. + // + if (AddOptCol) { + MenuOption->OptCol++; + } + } + + for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) { + OptionString[Count] = OptionString[Index]; + Count++; + } + OptionString[Count] = CHAR_NULL; + + // + // Enable to suppress field in the opcode base on the flag. + // + if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP) { + // + // OptionString format is: <**: **: ****> + // |month|day|year| + // 4 3 5 + // + if ((Date->Flags & EFI_QF_DATE_MONTH_SUPPRESS) && (MenuOption->Sequence == 0)) { + // + // At this point, only "<**:" in the optionstring. + // Clean the day's ** field, after clean, the format is "< :" + // + SetUnicodeMem (&OptionString[1], 2, L' '); + } else if ((Date->Flags & EFI_QF_DATE_DAY_SUPPRESS) && (MenuOption->Sequence == 1)) { + // + // At this point, only "**:" in the optionstring. + // Clean the month's "**" field, after clean, the format is " :" + // + SetUnicodeMem (&OptionString[0], 2, L' '); + } else if ((Date->Flags & EFI_QF_DATE_YEAR_SUPPRESS) && (MenuOption->Sequence == 2)) { + // + // At this point, only "****>" in the optionstring. + // Clean the year's "****" field, after clean, the format is " >" + // + SetUnicodeMem (&OptionString[0], 4, L' '); + } + } else if (Statement->OpCode->OpCode == EFI_IFR_TIME_OP) { + // + // OptionString format is: <**: **: **> + // |hour|minute|second| + // 4 3 3 + // + if ((Time->Flags & QF_TIME_HOUR_SUPPRESS) && (MenuOption->Sequence == 0)) { + // + // At this point, only "<**:" in the optionstring. + // Clean the hour's ** field, after clean, the format is "< :" + // + SetUnicodeMem (&OptionString[1], 2, L' '); + } else if ((Time->Flags & QF_TIME_MINUTE_SUPPRESS) && (MenuOption->Sequence == 1)) { + // + // At this point, only "**:" in the optionstring. + // Clean the minute's "**" field, after clean, the format is " :" + // + SetUnicodeMem (&OptionString[0], 2, L' '); + } else if ((Time->Flags & QF_TIME_SECOND_SUPPRESS) && (MenuOption->Sequence == 2)) { + // + // At this point, only "**>" in the optionstring. + // Clean the second's "**" field, after clean, the format is " >" + // + SetUnicodeMem (&OptionString[0], 2, L' '); + } + } +} + + +/** + Adjust Data and Time position accordingly. + Data format : [01/02/2004] [11:22:33] + Line number : 0 0 1 0 0 1 + + This is an internal function. + + @param DirectionUp the up or down direction. False is down. True is + up. + @param CurrentPosition Current position. On return: Point to the last + Option (Year or Second) if up; Point to the first + Option (Month or Hour) if down. + + @return Return line number to pad. It is possible that we stand on a zero-advance + @return data or time opcode, so pad one line when we judge if we are going to scroll outside. + +**/ +UINTN +AdjustDateAndTimePosition ( + IN BOOLEAN DirectionUp, + IN OUT LIST_ENTRY **CurrentPosition + ) +{ + UINTN Count; + LIST_ENTRY *NewPosition; + UI_MENU_OPTION *MenuOption; + UINTN PadLineNumber; + + PadLineNumber = 0; + NewPosition = *CurrentPosition; + MenuOption = MENU_OPTION_FROM_LINK (NewPosition); + + if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_DATE_OP) || + (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TIME_OP)) { + // + // Calculate the distance from current position to the last Date/Time MenuOption + // + Count = 0; + while (MenuOption->Skip == 0) { + Count++; + NewPosition = NewPosition->ForwardLink; + MenuOption = MENU_OPTION_FROM_LINK (NewPosition); + PadLineNumber = 1; + } + + NewPosition = *CurrentPosition; + if (DirectionUp) { + // + // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended + // to be one that back to the previous set of MenuOptions, we need to advance to the first + // Date/Time MenuOption 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 MenuOption is intended + // to be one that progresses to the next set of MenuOptions, we need to advance to the last + // Date/Time MenuOption 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; +} + +/** + Get step info from numeric opcode. + + @param[in] OpCode The input numeric op code. + + @return step info for this opcode. +**/ +UINT64 +GetFieldFromNum ( + IN EFI_IFR_OP_HEADER *OpCode + ) +{ + EFI_IFR_NUMERIC *NumericOp; + UINT64 Step; + + NumericOp = (EFI_IFR_NUMERIC *) OpCode; + + switch (NumericOp->Flags & EFI_IFR_NUMERIC_SIZE) { + case EFI_IFR_NUMERIC_SIZE_1: + Step = NumericOp->data.u8.Step; + break; + + case EFI_IFR_NUMERIC_SIZE_2: + Step = NumericOp->data.u16.Step; + break; + + case EFI_IFR_NUMERIC_SIZE_4: + Step = NumericOp->data.u32.Step; + break; + + case EFI_IFR_NUMERIC_SIZE_8: + Step = NumericOp->data.u64.Step; + break; + + default: + Step = 0; + break; + } + + return Step; +} + +/** + Find the registered HotKey based on KeyData. + + @param[in] KeyData A pointer to a buffer that describes the keystroke + information for the hot key. + + @return The registered HotKey context. If no found, NULL will return. +**/ +BROWSER_HOT_KEY * +GetHotKeyFromRegisterList ( + IN EFI_INPUT_KEY *KeyData + ) +{ + LIST_ENTRY *Link; + BROWSER_HOT_KEY *HotKey; + + Link = GetFirstNode (&gFormData->HotKeyListHead); + while (!IsNull (&gFormData->HotKeyListHead, Link)) { + HotKey = BROWSER_HOT_KEY_FROM_LINK (Link); + + if (HotKey->KeyData->ScanCode == KeyData->ScanCode) { + return HotKey; + } + + Link = GetNextNode (&gFormData->HotKeyListHead, Link); + } + + return NULL; +} + + +/** + Determine if the menu is the last menu that can be selected. + + This is an internal function. + + @param Direction The scroll direction. False is down. True is up. + @param CurrentPos The current focus. + + @return FALSE -- the menu isn't the last menu that can be selected. + @return TRUE -- the menu is the last menu that can be selected. + +**/ +BOOLEAN +ValueIsScroll ( + IN BOOLEAN Direction, + IN LIST_ENTRY *CurrentPos + ) +{ + LIST_ENTRY *Temp; + + Temp = Direction ? CurrentPos->BackLink : CurrentPos->ForwardLink; + + if (Temp == &gMenuOption) { + return TRUE; + } + + return FALSE; +} + +/** + Wait for a given event to fire, or for an optional timeout to expire. + + @param Event The event to wait for + + @retval UI_EVENT_TYPE The type of the event which is trigged. + +**/ +UI_EVENT_TYPE +UiWaitForEvent ( + IN EFI_EVENT Event + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN EventNum; + UINT64 Timeout; + EFI_EVENT TimerEvent; + EFI_EVENT WaitList[3]; + UI_EVENT_TYPE EventType; + + TimerEvent = NULL; + Timeout = FormExitTimeout(gFormData); + + if (Timeout != 0) { + Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent); + + // + // Set the timer event + // + gBS->SetTimer ( + TimerEvent, + TimerRelative, + Timeout + ); + } + + WaitList[0] = Event; + EventNum = 1; + if (gFormData->FormRefreshEvent != NULL) { + WaitList[EventNum] = gFormData->FormRefreshEvent; + EventNum ++; + } + + if (Timeout != 0) { + WaitList[EventNum] = TimerEvent; + EventNum ++; + } + + Status = gBS->WaitForEvent (EventNum, WaitList, &Index); + ASSERT_EFI_ERROR (Status); + + switch (Index) { + case 0: + EventType = UIEventKey; + break; + + case 1: + if (gFormData->FormRefreshEvent != NULL) { + EventType = UIEventDriver; + } else { + ASSERT (Timeout != 0 && EventNum == 2); + EventType = UIEventTimeOut; + } + break; + + default: + ASSERT (Index == 2 && EventNum == 3); + EventType = UIEventTimeOut; + break; + } + + if (Timeout != 0) { + gBS->CloseEvent (TimerEvent); + } + + return EventType; +} + +/** + Get question id info from the input opcode header. + + @param OpCode The input opcode header pointer. + + @retval The question id for this opcode. + +**/ +EFI_QUESTION_ID +GetQuestionIdInfo ( + IN EFI_IFR_OP_HEADER *OpCode + ) +{ + EFI_IFR_QUESTION_HEADER *QuestionHeader; + + if (OpCode->Length < sizeof (EFI_IFR_OP_HEADER) + sizeof (EFI_IFR_QUESTION_HEADER)) { + return 0; + } + + QuestionHeader = (EFI_IFR_QUESTION_HEADER *)((UINT8 *) OpCode + sizeof(EFI_IFR_OP_HEADER)); + + return QuestionHeader->QuestionId; +} + +/** + Find the first menu which will be show at the top. + + @param FormData The data info for this form. + @param TopOfScreen The link_entry pointer to top menu. + @param HighlightMenu The menu which will be highlight. + @param SkipValue The skip value for the top menu. + +**/ +VOID +FindTopMenu ( + IN FORM_DISPLAY_ENGINE_FORM *FormData, + OUT LIST_ENTRY **TopOfScreen, + OUT LIST_ENTRY **HighlightMenu, + OUT INTN *SkipValue + ) +{ + LIST_ENTRY *Link; + LIST_ENTRY *NewPos; + UINTN TopRow; + UINTN BottomRow; + UINTN Index; + UI_MENU_OPTION *SavedMenuOption; + UINTN EndRow; + + TopRow = gStatementDimensions.TopRow + SCROLL_ARROW_HEIGHT; + BottomRow = gStatementDimensions.BottomRow - SCROLL_ARROW_HEIGHT; + + // + // If not has input highlight statement, just return the first one in this form. + // + if (FormData->HighLightedStatement == NULL) { + *TopOfScreen = gMenuOption.ForwardLink; + *HighlightMenu = gMenuOption.ForwardLink; + if (!IsListEmpty (&gMenuOption)) { + MoveToNextStatement (FALSE, HighlightMenu, BottomRow - TopRow); + } + *SkipValue = 0; + return; + } + + // + // Now base on the input highlight menu to find the top menu in this page. + // Will base on the highlight menu show at the bottom to find the top menu. + // + NewPos = gMenuOption.ForwardLink; + SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos); + + while ((SavedMenuOption->ThisTag != FormData->HighLightedStatement) || + (SavedMenuOption->Sequence != gSequence)) { + NewPos = NewPos->ForwardLink; + if (NewPos == &gMenuOption) { + // + // Not Found it, break + // + break; + } + SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos); + } + ASSERT (SavedMenuOption->ThisTag == FormData->HighLightedStatement); + + *HighlightMenu = NewPos; + + AdjustDateAndTimePosition(FALSE, &NewPos); + SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos); + UpdateOptionSkipLines (SavedMenuOption); + + // + // If highlight opcode is date/time, keep the highlight row info not change. + // + if ((SavedMenuOption->ThisTag->OpCode->OpCode == EFI_IFR_DATE_OP || SavedMenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TIME_OP) && + (gHighligthMenuInfo.QuestionId != 0) && + (gHighligthMenuInfo.QuestionId == GetQuestionIdInfo(SavedMenuOption->ThisTag->OpCode))) { + // + // Still show the highlight menu before exit from display engine. + // + EndRow = gHighligthMenuInfo.DisplayRow + SavedMenuOption->Skip; + } else { + EndRow = BottomRow; + } + + // + // Base on the selected menu will show at the bottome of next page, + // select the menu show at the top of the next page. + // + Link = NewPos; + for (Index = TopRow + SavedMenuOption->Skip; Index <= EndRow; ) { + Link = Link->BackLink; + // + // Already find the first menu in this form, means highlight menu + // will show in first page of this form. + // + if (Link == &gMenuOption) { + *TopOfScreen = gMenuOption.ForwardLink; + *SkipValue = 0; + return; + } + SavedMenuOption = MENU_OPTION_FROM_LINK (Link); + UpdateOptionSkipLines (SavedMenuOption); + Index += SavedMenuOption->Skip; + } + + // + // Found the menu which will show at the top of the page. + // + if (Link == NewPos) { + // + // The menu can show more than one pages, just show the menu at the top of the page. + // + *SkipValue = 0; + *TopOfScreen = Link; + } else { + // + // Check whether need to skip some line for menu shows at the top of the page. + // + *SkipValue = Index - EndRow; + if (*SkipValue > 0 && *SkipValue < (INTN) SavedMenuOption->Skip) { + *TopOfScreen = Link; + } else { + *SkipValue = 0; + *TopOfScreen = Link->ForwardLink; + } + } +} + +/** + 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. + + @param FormData The current form data info. + + @retval EFI_SUCESSS Process the user selection success. + @retval EFI_NOT_FOUND Process option string for orderedlist/Oneof fail. + +**/ +EFI_STATUS +UiDisplayMenu ( + IN FORM_DISPLAY_ENGINE_FORM *FormData + ) +{ + INTN SkipValue; + INTN Difference; + UINTN DistanceValue; + UINTN Row; + UINTN Col; + UINTN TempRightCol; + UINTN Temp; + UINTN Temp2; + UINTN Temp3; + UINTN TopRow; + UINTN BottomRow; + UINTN OriginalRow; + UINTN Index; + UINT16 Width; + CHAR16 *StringPtr; + CHAR16 *OptionString; + CHAR16 *OutputString; + CHAR16 *HelpString; + CHAR16 *HelpHeaderString; + CHAR16 *HelpBottomString; + BOOLEAN NewLine; + BOOLEAN Repaint; + BOOLEAN UpArrow; + BOOLEAN DownArrow; + EFI_STATUS Status; + EFI_INPUT_KEY Key; + LIST_ENTRY *Link; + LIST_ENTRY *NewPos; + LIST_ENTRY *TopOfScreen; + LIST_ENTRY *SavedListEntry; + UI_MENU_OPTION *MenuOption; + UI_MENU_OPTION *NextMenuOption; + UI_MENU_OPTION *SavedMenuOption; + UI_MENU_OPTION *PreviousMenuOption; + UI_CONTROL_FLAG ControlFlag; + UI_SCREEN_OPERATION ScreenOperation; + UINT16 DefaultId; + FORM_DISPLAY_ENGINE_STATEMENT *Statement; + UINTN ModalSkipColumn; + BROWSER_HOT_KEY *HotKey; + UINTN HelpPageIndex; + UINTN HelpPageCount; + UINTN RowCount; + UINTN HelpLine; + UINTN HelpHeaderLine; + UINTN HelpBottomLine; + BOOLEAN MultiHelpPage; + UINT16 GlyphWidth; + UINT16 EachLineWidth; + UINT16 HeaderLineWidth; + UINT16 BottomLineWidth; + EFI_STRING_ID HelpInfo; + UI_EVENT_TYPE EventType; + FORM_DISPLAY_ENGINE_STATEMENT *InitialHighlight; + + EventType = UIEventNone; + Status = EFI_SUCCESS; + HelpString = NULL; + HelpHeaderString = NULL; + HelpBottomString = NULL; + OptionString = NULL; + ScreenOperation = UiNoOperation; + NewLine = TRUE; + DefaultId = 0; + HelpPageCount = 0; + HelpLine = 0; + RowCount = 0; + HelpBottomLine = 0; + HelpHeaderLine = 0; + HelpPageIndex = 0; + MultiHelpPage = FALSE; + EachLineWidth = 0; + HeaderLineWidth = 0; + BottomLineWidth = 0; + OutputString = NULL; + UpArrow = FALSE; + DownArrow = FALSE; + SkipValue = 0; + + NextMenuOption = NULL; + PreviousMenuOption = NULL; + SavedMenuOption = NULL; + HotKey = NULL; + Repaint = TRUE; + MenuOption = NULL; + ModalSkipColumn = (gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn) / 6; + InitialHighlight = gFormData->HighLightedStatement; + + ZeroMem (&Key, sizeof (EFI_INPUT_KEY)); + + gOptionBlockWidth = (CHAR16) ((gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn) / 3); + gPromptBlockWidth = (CHAR16) (gOptionBlockWidth + LEFT_SKIPPED_COLUMNS); + gHelpBlockWidth = (CHAR16) (gOptionBlockWidth - LEFT_SKIPPED_COLUMNS); + + TopRow = gStatementDimensions.TopRow + SCROLL_ARROW_HEIGHT; + BottomRow = gStatementDimensions.BottomRow - SCROLL_ARROW_HEIGHT - 1; + + Row = TopRow; + if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) { + Col = gStatementDimensions.LeftColumn + LEFT_SKIPPED_COLUMNS + ModalSkipColumn; + } else { + Col = gStatementDimensions.LeftColumn + LEFT_SKIPPED_COLUMNS; + } + + FindTopMenu(FormData, &TopOfScreen, &NewPos, &SkipValue); + + gST->ConOut->EnableCursor (gST->ConOut, FALSE); + + ControlFlag = CfInitialization; + while (TRUE) { + switch (ControlFlag) { + case CfInitialization: + if (IsListEmpty (&gMenuOption)) { + + if ((FormData->Attribute & HII_DISPLAY_MODAL) == 0) { + // + // Clear Statement range. + // + ClearLines ( + gStatementDimensions.LeftColumn, + gStatementDimensions.RightColumn, + TopRow - SCROLL_ARROW_HEIGHT, + BottomRow + SCROLL_ARROW_HEIGHT, + GetFieldTextColor () + ); + + // + // Clear Key Range + // + RefreshKeyHelp (gFormData, NULL, FALSE); + } + + ControlFlag = CfReadKey; + } else { + ControlFlag = CfRepaint; + } + break; + + case CfRepaint: + ControlFlag = CfRefreshHighLight; + + if (Repaint) { + // + // Display menu + // + DownArrow = FALSE; + UpArrow = FALSE; + Row = TopRow; + + Temp = (UINTN) SkipValue; + Temp2 = (UINTN) SkipValue; + Temp3 = (UINTN) SkipValue; + + // + // 1. Clear the screen. + // + if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) { + ClearLines ( + gStatementDimensions.LeftColumn + ModalSkipColumn, + gStatementDimensions.LeftColumn + ModalSkipColumn + gPromptBlockWidth + gOptionBlockWidth, + TopRow - SCROLL_ARROW_HEIGHT, + BottomRow + SCROLL_ARROW_HEIGHT, + GetFieldTextColor () + ); + } else { + TempRightCol = gStatementDimensions.RightColumn; + if (!mStatementLayoutIsChanged) { + TempRightCol = gStatementDimensions.RightColumn - gHelpBlockWidth; + } + ClearLines ( + gStatementDimensions.LeftColumn, + gStatementDimensions.RightColumn, + TopRow - SCROLL_ARROW_HEIGHT, + TopRow - 1, + GetFieldTextColor () + ); + ClearLines ( + gStatementDimensions.LeftColumn, + TempRightCol, + TopRow, + BottomRow, + GetFieldTextColor () + ); + ClearLines ( + gStatementDimensions.LeftColumn, + gStatementDimensions.RightColumn, + BottomRow + 1, + BottomRow + SCROLL_ARROW_HEIGHT, + GetFieldTextColor () + ); + } + + // + // 2.Paint the menu. + // + for (Link = TopOfScreen; Link != &gMenuOption; Link = Link->ForwardLink) { + MenuOption = MENU_OPTION_FROM_LINK (Link); + MenuOption->Row = Row; + MenuOption->Col = Col; + if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) { + MenuOption->OptCol = gPromptBlockWidth + 1 + gStatementDimensions.LeftColumn + ModalSkipColumn; + } else { + MenuOption->OptCol = gPromptBlockWidth + 1 + gStatementDimensions.LeftColumn; + } + + Statement = MenuOption->ThisTag; + if (MenuOption->NestInStatement) { + MenuOption->Col += SUBTITLE_INDENT; + } + + if (MenuOption->GrayOut) { + gST->ConOut->SetAttribute (gST->ConOut, GetGrayedTextColor ()); + } else { + if (Statement->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) { + gST->ConOut->SetAttribute (gST->ConOut, GetSubTitleTextColor ()); + } + } + + Width = GetWidth (Statement); + OriginalRow = Row; + GlyphWidth = 1; + + if (Statement->OpCode->OpCode == EFI_IFR_REF_OP && MenuOption->Col >= 2) { + // + // Print Arrow for Goto button. + // + PrintCharAt ( + MenuOption->Col - 2, + Row, + GEOMETRICSHAPE_RIGHT_TRIANGLE + ); + } + + // + // 2.1. Paint the description. + // + for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) { + // + // Temp means need to skip how many lines from the start. + // + if ((Temp == 0) && (Row <= BottomRow)) { + PrintStringAt (MenuOption->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]) != 0) { + if (Temp == 0) { + Row++; + } + } + + FreePool (OutputString); + if (Temp != 0) { + Temp--; + } + } + + Temp = 0; + Row = OriginalRow; + + // + // 2.2. Paint the option string. + // + Status = ProcessOptions (MenuOption, FALSE, &OptionString, FALSE); + // + // If Error occur, question value update in ProcessOptions. + // Exit current FormDisplay with new question value. + // + if (EFI_ERROR (Status)) { + return Status; + } + + if (OptionString != NULL) { + if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP || Statement->OpCode->OpCode == EFI_IFR_TIME_OP) { + ProcessStringForDateTime(MenuOption, OptionString, TRUE); + } + + Width = (UINT16) gOptionBlockWidth; + OriginalRow = Row; + GlyphWidth = 1; + + for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &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]) != 0) { + 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 (OptionString); + } + Temp2 = 0; + + // + // If this is a text op with secondary text information + // + if ((Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) && (((EFI_IFR_TEXT*)Statement->OpCode)->TextTwo != 0)) { + StringPtr = GetToken (((EFI_IFR_TEXT*)Statement->OpCode)->TextTwo, gFormData->HiiHandle); + + Width = (UINT16) gOptionBlockWidth; + OriginalRow = Row; + GlyphWidth = 1; + + for (Index = 0; GetLineByWidth (StringPtr, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) { + if ((Temp3 == 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]) != 0) { + if (Temp3 == 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 (Temp3 != 0) { + Temp3--; + } + } + + Row = OriginalRow; + FreePool (StringPtr); + } + Temp3 = 0; + + gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ()); + + // + // 3. Update the row info which will be used by next menu. + // + if (Link == TopOfScreen) { + Row += MenuOption->Skip - SkipValue; + } else { + Row += MenuOption->Skip; + } + + if (Row > BottomRow) { + if (!ValueIsScroll (FALSE, Link)) { + DownArrow = TRUE; + } + + Row = BottomRow + 1; + break; + } + } + + if (!ValueIsScroll (TRUE, TopOfScreen)) { + UpArrow = TRUE; + } + + if (UpArrow) { + gST->ConOut->SetAttribute (gST->ConOut, GetArrowColor ()); + PrintCharAt ( + gStatementDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1, + TopRow - SCROLL_ARROW_HEIGHT, + ARROW_UP + ); + gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ()); + } + + if (DownArrow) { + gST->ConOut->SetAttribute (gST->ConOut, GetArrowColor ()); + PrintCharAt ( + gStatementDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1, + BottomRow + SCROLL_ARROW_HEIGHT, + ARROW_DOWN + ); + gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ()); + } + + MenuOption = NULL; + } + break; + + case CfRefreshHighLight: + + // + // MenuOption: Last menu option that need to remove hilight + // MenuOption is set to NULL in Repaint + // NewPos: Current menu option that need to hilight + // + ControlFlag = CfUpdateHelpString; + + if (MenuOption != NULL && TopOfScreen == &MenuOption->Link) { + Temp = SkipValue; + } else { + Temp = 0; + } + if (NewPos == TopOfScreen) { + Temp2 = SkipValue; + } else { + Temp2 = 0; + } + + if (NewPos != NULL && (MenuOption == NULL || NewPos != &MenuOption->Link)) { + if (MenuOption != NULL) { + // + // Remove highlight on last Menu Option + // + gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row); + ProcessOptions (MenuOption, FALSE, &OptionString, TRUE); + gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ()); + if (OptionString != NULL) { + if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_DATE_OP) || + (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TIME_OP) + ) { + ProcessStringForDateTime(MenuOption, OptionString, FALSE); + } + + Width = (UINT16) gOptionBlockWidth; + OriginalRow = MenuOption->Row; + GlyphWidth = 1; + + for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) { + if ((Temp == 0) && (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]) != 0) { + if (Temp == 0) { + MenuOption->Row++; + } + } + + FreePool (OutputString); + if (Temp != 0) { + Temp--; + } + } + + MenuOption->Row = OriginalRow; + + FreePool (OptionString); + } else { + if (NewLine) { + if (MenuOption->GrayOut) { + gST->ConOut->SetAttribute (gST->ConOut, GetGrayedTextColor ()); + } else if (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) { + gST->ConOut->SetAttribute (gST->ConOut, GetSubTitleTextColor ()); + } + + OriginalRow = MenuOption->Row; + Width = GetWidth (MenuOption->ThisTag); + GlyphWidth = 1; + + for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) { + if ((Temp == 0) && (MenuOption->Row >= TopRow) && (MenuOption->Row <= BottomRow)) { + PrintStringAt (MenuOption->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]) != 0) { + if (Temp == 0) { + MenuOption->Row++; + } + } + + FreePool (OutputString); + if (Temp != 0) { + Temp--; + } + } + + MenuOption->Row = OriginalRow; + gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ()); + } + } + } + + // + // This is the current selected statement + // + MenuOption = MENU_OPTION_FROM_LINK (NewPos); + Statement = MenuOption->ThisTag; + + // + // Get the highlight statement. + // + gUserInput->SelectedStatement = Statement; + gSequence = (UINT16) MenuOption->Sequence; + + // + // Record highlight row info for date/time opcode. + // + if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP || Statement->OpCode->OpCode == EFI_IFR_TIME_OP) { + gHighligthMenuInfo.QuestionId = GetQuestionIdInfo(Statement->OpCode); + gHighligthMenuInfo.DisplayRow = (UINT16) MenuOption->Row; + } else { + gHighligthMenuInfo.QuestionId = 0; + gHighligthMenuInfo.DisplayRow = 0; + } + + if (!IsSelectable (MenuOption)) { + RefreshKeyHelp(gFormData, Statement, FALSE); + break; + } + + // + // Set reverse attribute + // + gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ()); + gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row); + + ProcessOptions (MenuOption, FALSE, &OptionString, TRUE); + if (OptionString != NULL) { + if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP || Statement->OpCode->OpCode == EFI_IFR_TIME_OP) { + ProcessStringForDateTime(MenuOption, OptionString, FALSE); + } + Width = (UINT16) gOptionBlockWidth; + + OriginalRow = MenuOption->Row; + GlyphWidth = 1; + + for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) { + if ((Temp2 == 0) && (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]) != 0) { + if (Temp2 == 0) { + MenuOption->Row++; + } + } + + FreePool (OutputString); + if (Temp2 != 0) { + Temp2--; + } + } + + MenuOption->Row = OriginalRow; + + FreePool (OptionString); + } else { + if (NewLine) { + OriginalRow = MenuOption->Row; + + Width = GetWidth (Statement); + GlyphWidth = 1; + + for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) { + if ((Temp2 == 0) && (MenuOption->Row >= TopRow) && (MenuOption->Row <= BottomRow) ) { + PrintStringAt (MenuOption->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]) != 0) { + if (Temp2 == 0) { + MenuOption->Row++; + } + } + + FreePool (OutputString); + if (Temp2 != 0) { + Temp2--; + } + } + + MenuOption->Row = OriginalRow; + + } + } + + RefreshKeyHelp(gFormData, MenuOption->ThisTag, FALSE); + + // + // Clear reverse attribute + // + gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ()); + } + break; + + case CfUpdateHelpString: + ControlFlag = CfPrepareToReadKey; + if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) { + break; + } + + if (Repaint || NewLine) { + // + // Don't print anything if it is a NULL help token + // + ASSERT(MenuOption != NULL); + HelpInfo = ((EFI_IFR_STATEMENT_HEADER *) ((CHAR8 *)MenuOption->ThisTag->OpCode + sizeof (EFI_IFR_OP_HEADER)))->Help; + if (HelpInfo == 0 || !IsSelectable (MenuOption)) { + StringPtr = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle); + } else { + StringPtr = GetToken (HelpInfo, gFormData->HiiHandle); + } + + RowCount = BottomRow - TopRow + 1; + HelpPageIndex = 0; + // + // 1.Calculate how many line the help string need to print. + // + if (HelpString != NULL) { + FreePool (HelpString); + HelpString = NULL; + } + HelpLine = ProcessHelpString (StringPtr, &HelpString, &EachLineWidth, RowCount); + FreePool (StringPtr); + + if (HelpLine > RowCount) { + MultiHelpPage = TRUE; + StringPtr = GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_UP), gHiiHandle); + if (HelpHeaderString != NULL) { + FreePool (HelpHeaderString); + HelpHeaderString = NULL; + } + HelpHeaderLine = ProcessHelpString (StringPtr, &HelpHeaderString, &HeaderLineWidth, 0); + FreePool (StringPtr); + StringPtr = GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_DOWN), gHiiHandle); + if (HelpBottomString != NULL) { + FreePool (HelpBottomString); + HelpBottomString = NULL; + } + HelpBottomLine = ProcessHelpString (StringPtr, &HelpBottomString, &BottomLineWidth, 0); + FreePool (StringPtr); + // + // Calculate the help page count. + // + if (HelpLine > 2 * RowCount - 2) { + HelpPageCount = (HelpLine - RowCount + 1) / (RowCount - 2) + 1; + if ((HelpLine - RowCount + 1) % (RowCount - 2) > 1) { + HelpPageCount += 1; + } + } else { + HelpPageCount = 2; + } + } else { + MultiHelpPage = FALSE; + } + } + + // + // Check whether need to show the 'More(U/u)' at the begin. + // Base on current direct info, here shows aligned to the right side of the column. + // If the direction is multi line and aligned to right side may have problem, so + // add ASSERT code here. + // + if (HelpPageIndex > 0) { + gST->ConOut->SetAttribute (gST->ConOut, GetInfoTextColor ()); + for (Index = 0; Index < HelpHeaderLine; Index++) { + ASSERT (HelpHeaderLine == 1); + ASSERT (GetStringWidth (HelpHeaderString) / 2 < (UINTN) (gHelpBlockWidth - 1)); + PrintStringAtWithWidth ( + gStatementDimensions.RightColumn - gHelpBlockWidth, + Index + TopRow, + gEmptyString, + gHelpBlockWidth + ); + PrintStringAt ( + gStatementDimensions.RightColumn - GetStringWidth (HelpHeaderString) / 2 - 1, + Index + TopRow, + &HelpHeaderString[Index * HeaderLineWidth] + ); + } + } + + gST->ConOut->SetAttribute (gST->ConOut, GetHelpTextColor ()); + // + // Print the help string info. + // + if (!MultiHelpPage) { + for (Index = 0; Index < HelpLine; Index++) { + PrintStringAtWithWidth ( + gStatementDimensions.RightColumn - gHelpBlockWidth, + Index + TopRow, + &HelpString[Index * EachLineWidth], + gHelpBlockWidth + ); + } + for (; Index < RowCount; Index ++) { + PrintStringAtWithWidth ( + gStatementDimensions.RightColumn - gHelpBlockWidth, + Index + TopRow, + gEmptyString, + gHelpBlockWidth + ); + } + gST->ConOut->SetCursorPosition(gST->ConOut, gStatementDimensions.RightColumn-1, BottomRow); + } else { + if (HelpPageIndex == 0) { + for (Index = 0; Index < RowCount - HelpBottomLine; Index++) { + PrintStringAtWithWidth ( + gStatementDimensions.RightColumn - gHelpBlockWidth, + Index + TopRow, + &HelpString[Index * EachLineWidth], + gHelpBlockWidth + ); + } + } else { + for (Index = 0; (Index < RowCount - HelpBottomLine - HelpHeaderLine) && + (Index + HelpPageIndex * (RowCount - 2) + 1 < HelpLine); Index++) { + PrintStringAtWithWidth ( + gStatementDimensions.RightColumn - gHelpBlockWidth, + Index + TopRow + HelpHeaderLine, + &HelpString[(Index + HelpPageIndex * (RowCount - 2) + 1)* EachLineWidth], + gHelpBlockWidth + ); + } + if (HelpPageIndex == HelpPageCount - 1) { + for (; Index < RowCount - HelpHeaderLine; Index ++) { + PrintStringAtWithWidth ( + gStatementDimensions.RightColumn - gHelpBlockWidth, + Index + TopRow + HelpHeaderLine, + gEmptyString, + gHelpBlockWidth + ); + } + gST->ConOut->SetCursorPosition(gST->ConOut, gStatementDimensions.RightColumn-1, BottomRow); + } + } + } + + // + // Check whether need to print the 'More(D/d)' at the bottom. + // Base on current direct info, here shows aligned to the right side of the column. + // If the direction is multi line and aligned to right side may have problem, so + // add ASSERT code here. + // + if (HelpPageIndex < HelpPageCount - 1 && MultiHelpPage) { + gST->ConOut->SetAttribute (gST->ConOut, GetInfoTextColor ()); + for (Index = 0; Index < HelpBottomLine; Index++) { + ASSERT (HelpBottomLine == 1); + ASSERT (GetStringWidth (HelpBottomString) / 2 < (UINTN) (gHelpBlockWidth - 1)); + PrintStringAtWithWidth ( + gStatementDimensions.RightColumn - gHelpBlockWidth, + BottomRow + Index - HelpBottomLine + 1, + gEmptyString, + gHelpBlockWidth + ); + PrintStringAt ( + gStatementDimensions.RightColumn - GetStringWidth (HelpBottomString) / 2 - 1, + BottomRow + Index - HelpBottomLine + 1, + &HelpBottomString[Index * BottomLineWidth] + ); + } + } + // + // Reset this flag every time we finish using it. + // + Repaint = FALSE; + NewLine = FALSE; + break; + + case CfPrepareToReadKey: + ControlFlag = CfReadKey; + ScreenOperation = UiNoOperation; + break; + + case CfReadKey: + ControlFlag = CfScreenOperation; + + // + // Wait for user's selection + // + while (TRUE) { + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + if (!EFI_ERROR (Status)) { + EventType = UIEventKey; + break; + } + + // + // If we encounter error, continue to read another key in. + // + if (Status != EFI_NOT_READY) { + continue; + } + + EventType = UiWaitForEvent(gST->ConIn->WaitForKey); + if (EventType == UIEventKey) { + gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + } + break; + } + + if (EventType == UIEventDriver) { + gUserInput->Action = BROWSER_ACTION_NONE; + ControlFlag = CfExit; + break; + } + + if (EventType == UIEventTimeOut) { + gUserInput->Action = BROWSER_ACTION_FORM_EXIT; + ControlFlag = CfExit; + break; + } + + switch (Key.UnicodeChar) { + case CHAR_CARRIAGE_RETURN: + if(MenuOption == NULL || MenuOption->GrayOut || MenuOption->ReadOnly) { + ControlFlag = CfReadKey; + break; + } + + ScreenOperation = UiSelect; + gDirection = 0; + break; + + // + // We will push the adjustment of these numeric values directly to the input handler + // NOTE: we won't handle manual input numeric + // + case '+': + case '-': + // + // If the screen has no menu items, and the user didn't select UiReset + // ignore the selection and go back to reading keys. + // + if(IsListEmpty (&gMenuOption) || MenuOption->GrayOut || MenuOption->ReadOnly) { + ControlFlag = CfReadKey; + break; + } + + ASSERT(MenuOption != NULL); + Statement = MenuOption->ThisTag; + if ((Statement->OpCode->OpCode == EFI_IFR_DATE_OP) + || (Statement->OpCode->OpCode == EFI_IFR_TIME_OP) + || ((Statement->OpCode->OpCode == EFI_IFR_NUMERIC_OP) && (GetFieldFromNum(Statement->OpCode) != 0)) + ){ + if (Key.UnicodeChar == '+') { + gDirection = SCAN_RIGHT; + } else { + gDirection = SCAN_LEFT; + } + + Status = ProcessOptions (MenuOption, TRUE, &OptionString, TRUE); + if (OptionString != NULL) { + FreePool (OptionString); + } + if (EFI_ERROR (Status)) { + // + // Repaint to clear possible error prompt pop-up + // + Repaint = TRUE; + NewLine = TRUE; + } else { + ControlFlag = CfExit; + } + } + break; + + case '^': + ScreenOperation = UiUp; + break; + + case 'V': + case 'v': + ScreenOperation = UiDown; + break; + + case ' ': + if(IsListEmpty (&gMenuOption)) { + ControlFlag = CfReadKey; + break; + } + + ASSERT(MenuOption != NULL); + if (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_CHECKBOX_OP && !MenuOption->GrayOut && !MenuOption->ReadOnly) { + ScreenOperation = UiSelect; + } + break; + + case 'D': + case 'd': + if (!MultiHelpPage) { + ControlFlag = CfReadKey; + break; + } + ControlFlag = CfUpdateHelpString; + HelpPageIndex = HelpPageIndex < HelpPageCount - 1 ? HelpPageIndex + 1 : HelpPageCount - 1; + break; + + case 'U': + case 'u': + if (!MultiHelpPage) { + ControlFlag = CfReadKey; + break; + } + ControlFlag = CfUpdateHelpString; + HelpPageIndex = HelpPageIndex > 0 ? HelpPageIndex - 1 : 0; + break; + + case CHAR_NULL: + for (Index = 0; Index < mScanCodeNumber; Index++) { + if (Key.ScanCode == gScanCodeToOperation[Index].ScanCode) { + ScreenOperation = gScanCodeToOperation[Index].ScreenOperation; + break; + } + } + + if (((FormData->Attribute & HII_DISPLAY_MODAL) != 0) && (Key.ScanCode == SCAN_ESC || Index == mScanCodeNumber)) { + // + // ModalForm has no ESC key and Hot Key. + // + ControlFlag = CfReadKey; + } else if (Index == mScanCodeNumber) { + // + // Check whether Key matches the registered hot key. + // + HotKey = NULL; + HotKey = GetHotKeyFromRegisterList (&Key); + if (HotKey != NULL) { + ScreenOperation = UiHotKey; + } + } + break; + } + break; + + case CfScreenOperation: + if (ScreenOperation != UiReset) { + // + // If the screen has no menu items, and the user didn't select UiReset + // ignore the selection and go back to reading keys. + // + if (IsListEmpty (&gMenuOption)) { + ControlFlag = CfReadKey; + break; + } + } + + for (Index = 0; + Index < sizeof (gScreenOperationToControlFlag) / sizeof (gScreenOperationToControlFlag[0]); + Index++ + ) { + if (ScreenOperation == gScreenOperationToControlFlag[Index].ScreenOperation) { + ControlFlag = gScreenOperationToControlFlag[Index].ControlFlag; + break; + } + } + break; + + case CfUiSelect: + ControlFlag = CfRepaint; + + ASSERT(MenuOption != NULL); + Statement = MenuOption->ThisTag; + if (Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) { + break; + } + + switch (Statement->OpCode->OpCode) { + case EFI_IFR_REF_OP: + case EFI_IFR_ACTION_OP: + case EFI_IFR_RESET_BUTTON_OP: + ControlFlag = CfExit; + break; + + default: + // + // Editable Questions: oneof, ordered list, checkbox, numeric, string, password + // + RefreshKeyHelp (gFormData, Statement, TRUE); + Status = ProcessOptions (MenuOption, TRUE, &OptionString, TRUE); + + if (OptionString != NULL) { + FreePool (OptionString); + } + + if (EFI_ERROR (Status)) { + Repaint = TRUE; + NewLine = TRUE; + RefreshKeyHelp (gFormData, Statement, FALSE); + break; + } else { + ControlFlag = CfExit; + break; + } + } + break; + + case CfUiReset: + // + // We come here when someone press ESC + // If the policy is not exit front page when user press ESC, process here. + // + if (!FormExitPolicy()) { + Repaint = TRUE; + NewLine = TRUE; + ControlFlag = CfRepaint; + break; + } + + // + // When user press ESC, it will try to show another menu, should clean the gSequence info. + // + if (gSequence != 0) { + gSequence = 0; + } + + gUserInput->Action = BROWSER_ACTION_FORM_EXIT; + ControlFlag = CfExit; + break; + + case CfUiHotKey: + ControlFlag = CfRepaint; + + gUserInput->Action = HotKey->Action; + ControlFlag = CfExit; + break; + + case CfUiLeft: + ControlFlag = CfRepaint; + ASSERT(MenuOption != NULL); + if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TIME_OP)) { + if (MenuOption->Sequence != 0) { + // + // In the middle or tail of the Date/Time op-code set, go left. + // + ASSERT(NewPos != NULL); + NewPos = NewPos->BackLink; + } + } + break; + + case CfUiRight: + ControlFlag = CfRepaint; + ASSERT(MenuOption != NULL); + if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TIME_OP)) { + if (MenuOption->Sequence != 2) { + // + // In the middle or tail of the Date/Time op-code set, go left. + // + ASSERT(NewPos != NULL); + NewPos = NewPos->ForwardLink; + } + } + break; + + case CfUiUp: + ControlFlag = CfRepaint; + + SavedListEntry = NewPos; + + ASSERT(NewPos != NULL); + // + // Adjust Date/Time position before we advance forward. + // + AdjustDateAndTimePosition (TRUE, &NewPos); + if (NewPos->BackLink != &gMenuOption) { + MenuOption = MENU_OPTION_FROM_LINK (NewPos); + ASSERT (MenuOption != NULL); + NewLine = TRUE; + NewPos = NewPos->BackLink; + + PreviousMenuOption = MENU_OPTION_FROM_LINK (NewPos); + if (PreviousMenuOption->Row == 0) { + UpdateOptionSkipLines (PreviousMenuOption); + } + DistanceValue = PreviousMenuOption->Skip; + Difference = 0; + if (MenuOption->Row >= DistanceValue + TopRow) { + Difference = MoveToNextStatement (TRUE, &NewPos, MenuOption->Row - TopRow - DistanceValue); + } + NextMenuOption = MENU_OPTION_FROM_LINK (NewPos); + + if (Difference < 0) { + // + // We hit the begining MenuOption that can be focused + // so we simply scroll to the top. + // + if (TopOfScreen != gMenuOption.ForwardLink) { + TopOfScreen = gMenuOption.ForwardLink; + Repaint = TRUE; + } else { + // + // Scroll up to the last page when we have arrived at top page. + // + NewPos = &gMenuOption; + TopOfScreen = &gMenuOption; + MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry); + ScreenOperation = UiPageUp; + ControlFlag = CfScreenOperation; + break; + } + } else if (MenuOption->Row < TopRow + DistanceValue + Difference) { + // + // Previous focus MenuOption is above the TopOfScreen, so we need to scroll + // + TopOfScreen = NewPos; + Repaint = TRUE; + SkipValue = 0; + } else if (!IsSelectable (NextMenuOption)) { + // + // Continue to go up until scroll to next page or the selectable option is found. + // + ScreenOperation = UiUp; + ControlFlag = CfScreenOperation; + } + + // + // If we encounter a Date/Time op-code set, rewind to the first op-code of the set. + // + AdjustDateAndTimePosition (TRUE, &TopOfScreen); + AdjustDateAndTimePosition (TRUE, &NewPos); + MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry); + UpdateStatusBar (INPUT_ERROR, FALSE); + } else { + // + // Scroll up to the last page. + // + NewPos = &gMenuOption; + TopOfScreen = &gMenuOption; + MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry); + ScreenOperation = UiPageUp; + ControlFlag = CfScreenOperation; + } + break; + + case CfUiPageUp: + // + // SkipValue means lines is skipped when show the top menu option. + // + ControlFlag = CfRepaint; + + ASSERT(NewPos != NULL); + // + // Already at the first menu option, Check the skip value. + // + if (NewPos->BackLink == &gMenuOption) { + if (SkipValue == 0) { + NewLine = FALSE; + Repaint = FALSE; + } else { + NewLine = TRUE; + Repaint = TRUE; + SkipValue = 0; + } + break; + } + + NewLine = TRUE; + Repaint = TRUE; + + // + // SkipValue > (BottomRow - TopRow + 1) means current menu has more than one + // form of options to be show, so just update the SkipValue to show the next + // parts of options. + // + if (SkipValue > (INTN) (BottomRow - TopRow + 1)) { + SkipValue -= BottomRow - TopRow + 1; + break; + } + + Link = TopOfScreen; + // + // First minus the menu of the top screen, it's value is SkipValue. + // + Index = (BottomRow + 1) - SkipValue; + while ((Index > TopRow) && (Link->BackLink != &gMenuOption)) { + Link = Link->BackLink; + PreviousMenuOption = MENU_OPTION_FROM_LINK (Link); + if (PreviousMenuOption->Row == 0) { + UpdateOptionSkipLines (PreviousMenuOption); + } + if (Index < PreviousMenuOption->Skip) { + break; + } + Index = Index - PreviousMenuOption->Skip; + } + + if ((Link->BackLink == &gMenuOption) && (Index >= TopRow)) { + SkipValue = 0; + if (TopOfScreen == &gMenuOption) { + TopOfScreen = gMenuOption.ForwardLink; + NewPos = gMenuOption.BackLink; + MoveToNextStatement (TRUE, &NewPos, BottomRow - TopRow); + Repaint = FALSE; + } else if (TopOfScreen != Link) { + TopOfScreen = Link; + NewPos = Link; + MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow); + } else { + // + // Finally we know that NewPos is the last MenuOption can be focused. + // + Repaint = FALSE; + NewPos = Link; + MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow); + } + } else { + if (Index > TopRow) { + // + // At here, only case "Index < PreviousMenuOption->Skip" can reach here. + // + SkipValue = PreviousMenuOption->Skip - (Index - TopRow); + } else if (Index == TopRow) { + SkipValue = 0; + } else { + SkipValue = TopRow - Index; + } + + // + // Move to the option in Next page. + // + if (TopOfScreen == &gMenuOption) { + NewPos = gMenuOption.BackLink; + MoveToNextStatement (TRUE, &NewPos, BottomRow - TopRow); + } else { + NewPos = Link; + MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow); + } + + // + // There are more MenuOption needing scrolling up. + // + TopOfScreen = Link; + MenuOption = NULL; + } + + // + // 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. + // + AdjustDateAndTimePosition (TRUE, &TopOfScreen); + AdjustDateAndTimePosition (TRUE, &NewPos); + break; + + case CfUiPageDown: + // + // SkipValue means lines is skipped when show the top menu option. + // + ControlFlag = CfRepaint; + + ASSERT (NewPos != NULL); + if (NewPos->ForwardLink == &gMenuOption) { + NewLine = FALSE; + Repaint = FALSE; + break; + } + + NewLine = TRUE; + Repaint = TRUE; + Link = TopOfScreen; + NextMenuOption = MENU_OPTION_FROM_LINK (Link); + Index = TopRow + NextMenuOption->Skip - SkipValue; + // + // Count to the menu option which will show at the top of the next form. + // + while ((Index <= BottomRow + 1) && (Link->ForwardLink != &gMenuOption)) { + Link = Link->ForwardLink; + NextMenuOption = MENU_OPTION_FROM_LINK (Link); + Index = Index + NextMenuOption->Skip; + } + + if ((Link->ForwardLink == &gMenuOption) && (Index <= BottomRow + 1)) { + // + // Finally we know that NewPos is the last MenuOption can be focused. + // + Repaint = FALSE; + MoveToNextStatement (TRUE, &Link, Index - TopRow); + } else { + // + // Calculate the skip line for top of screen menu. + // + if (Link == TopOfScreen) { + // + // The top of screen menu option occupies the entire form. + // + SkipValue += BottomRow - TopRow + 1; + } else { + SkipValue = NextMenuOption->Skip - (Index - (BottomRow + 1)); + } + + TopOfScreen = Link; + MenuOption = NULL; + // + // Move to the Next selectable menu. + // + MoveToNextStatement (FALSE, &Link, BottomRow - TopRow); + } + + // + // Save the menu as the next highlight menu. + // + 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 last page. + // + AdjustDateAndTimePosition (TRUE, &TopOfScreen); + AdjustDateAndTimePosition (TRUE, &NewPos); + break; + + case CfUiDown: + // + // SkipValue means lines is skipped when show the top menu option. + // NewPos points to the menu which is highlighted now. + // + ControlFlag = CfRepaint; + + // + // 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. + // + SavedListEntry = NewPos; + AdjustDateAndTimePosition (FALSE, &NewPos); + + if (NewPos->ForwardLink != &gMenuOption) { + MenuOption = MENU_OPTION_FROM_LINK (NewPos); + NewLine = TRUE; + NewPos = NewPos->ForwardLink; + + Difference = 0; + // + // Current menu not at the bottom of the form. + // + if (BottomRow >= MenuOption->Row + MenuOption->Skip) { + // + // Find the next selectable menu. + // + Difference = MoveToNextStatement (FALSE, &NewPos, BottomRow - MenuOption->Row - MenuOption->Skip); + // + // We hit the end of MenuOption that can be focused + // so we simply scroll to the first page. + // + if (Difference < 0) { + // + // Scroll to the first page. + // + if (TopOfScreen != gMenuOption.ForwardLink) { + TopOfScreen = gMenuOption.ForwardLink; + Repaint = TRUE; + MenuOption = NULL; + } else { + MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry); + } + NewPos = gMenuOption.ForwardLink; + MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow); + + SkipValue = 0; + // + // If we are at the end of the list and sitting on a Date/Time op, rewind to the head. + // + AdjustDateAndTimePosition (TRUE, &TopOfScreen); + AdjustDateAndTimePosition (TRUE, &NewPos); + break; + } + } + NextMenuOption = MENU_OPTION_FROM_LINK (NewPos); + if (NextMenuOption->Row == 0) { + UpdateOptionSkipLines (NextMenuOption); + } + DistanceValue = Difference + NextMenuOption->Skip; + + Temp = MenuOption->Row + MenuOption->Skip + DistanceValue - 1; + if ((MenuOption->Row + MenuOption->Skip == BottomRow + 1) && + (NextMenuOption->ThisTag->OpCode->OpCode == EFI_IFR_DATE_OP || + NextMenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TIME_OP) + ) { + Temp ++; + } + + // + // If we are going to scroll, update TopOfScreen + // + 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 = MENU_OPTION_FROM_LINK (TopOfScreen); + + // + // If bottom op-code is more than one line or top op-code is more than one line + // + if ((DistanceValue > 1) || (SavedMenuOption->Skip > 1)) { + // + // Is the bottom op-code greater than or equal in size to the top op-code? + // + if ((Temp - BottomRow) >= (SavedMenuOption->Skip - SkipValue)) { + // + // Skip the top op-code + // + TopOfScreen = TopOfScreen->ForwardLink; + Difference = (Temp - BottomRow) - (SavedMenuOption->Skip - SkipValue); + + SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen); + + // + // If we have a remainder, skip that many more op-codes until we drain the remainder + // + while (Difference >= (INTN) SavedMenuOption->Skip) { + // + // Since the Difference is greater than or equal to this op-code's skip value, skip it + // + Difference = Difference - (INTN) SavedMenuOption->Skip; + TopOfScreen = TopOfScreen->ForwardLink; + SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen); + } + // + // 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 += (Temp - BottomRow) - 1; + } + } else { + if ((SkipValue + 1) == (INTN) SavedMenuOption->Skip) { + TopOfScreen = TopOfScreen->ForwardLink; + break; + } + } + // + // 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 if (SavedMenuOption->Skip == 1) { + SkipValue = 0; + } else { + SkipValue = 0; + TopOfScreen = TopOfScreen->ForwardLink; + } + } while (SavedMenuOption->Skip == 0); + + Repaint = TRUE; + } else if (!IsSelectable (NextMenuOption)) { + // + // Continue to go down until scroll to next page or the selectable option is found. + // + ScreenOperation = UiDown; + ControlFlag = CfScreenOperation; + } + + MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry); + + UpdateStatusBar (INPUT_ERROR, FALSE); + + } else { + // + // Scroll to the first page. + // + if (TopOfScreen != gMenuOption.ForwardLink) { + TopOfScreen = gMenuOption.ForwardLink; + Repaint = TRUE; + MenuOption = NULL; + } else { + // + // Need to remove the current highlight menu. + // MenuOption saved the last highlight menu info. + // + MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry); + } + + SkipValue = 0; + NewLine = TRUE; + // + // Get the next highlight menu. + // + NewPos = gMenuOption.ForwardLink; + MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow); + } + + // + // If we are at the end of the list and sitting on a Date/Time op, rewind to the head. + // + AdjustDateAndTimePosition (TRUE, &TopOfScreen); + AdjustDateAndTimePosition (TRUE, &NewPos); + break; + + case CfUiNoOperation: + ControlFlag = CfRepaint; + break; + + case CfExit: + if (HelpString != NULL) { + FreePool (HelpString); + } + if (HelpHeaderString != NULL) { + FreePool (HelpHeaderString); + } + if (HelpBottomString != NULL) { + FreePool (HelpBottomString); + } + return EFI_SUCCESS; + + default: + break; + } + } +} + +/** + + Base on the browser status info to show an pop up message. + +**/ +VOID +BrowserStatusProcess ( + VOID + ) +{ + CHAR16 *ErrorInfo; + EFI_INPUT_KEY Key; + + if (gFormData->BrowserStatus == BROWSER_SUCCESS) { + return; + } + + if (gFormData->ErrorString != NULL) { + ErrorInfo = gFormData->ErrorString; + } else { + switch (gFormData->BrowserStatus) { + case BROWSER_SUBMIT_FAIL: + ErrorInfo = gSaveFailed; + break; + + case BROWSER_NO_SUBMIT_IF: + ErrorInfo = gNoSubmitIf; + break; + + case BROWSER_FORM_NOT_FOUND: + ErrorInfo = gFormNotFound; + break; + + case BROWSER_FORM_SUPPRESS: + ErrorInfo = gFormSuppress; + break; + + case BROWSER_PROTOCOL_NOT_FOUND: + ErrorInfo = gProtocolNotFound; + break; + + default: + ErrorInfo = gBrwoserError; + break; + } + } + + // + // Error occur, prompt error message. + // + do { + CreateDialog (&Key, gEmptyString, ErrorInfo, gPressEnter, gEmptyString, NULL); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); +} + +/** + Display one form, and return user input. + + @param FormData Form Data to be shown. + @param UserInputData User input data. + + @retval EFI_SUCCESS 1.Form Data is shown, and user input is got. + 2.Error info has show and return. + @retval EFI_INVALID_PARAMETER The input screen dimension is not valid + @retval EFI_NOT_FOUND New form data has some error. +**/ +EFI_STATUS +EFIAPI +FormDisplay ( + IN FORM_DISPLAY_ENGINE_FORM *FormData, + OUT USER_INPUT *UserInputData + ) +{ + EFI_STATUS Status; + + ASSERT (FormData != NULL); + if (FormData == NULL) { + return EFI_INVALID_PARAMETER; + } + + gUserInput = UserInputData; + gFormData = FormData; + + // + // Process the status info first. + // + BrowserStatusProcess(); + if (UserInputData == NULL) { + // + // UserInputData == NULL, means only need to print the error info, return here. + // + return EFI_SUCCESS; + } + + ConvertStatementToMenu(); + + Status = DisplayPageFrame (FormData, &gStatementDimensions); + if (EFI_ERROR (Status)) { + return Status; + } + + if (CompareMem (&gOldStatementDimensions, &gStatementDimensions, sizeof (gStatementDimensions)) == 0) { + mStatementLayoutIsChanged = FALSE; + } else { + mStatementLayoutIsChanged = TRUE; + CopyMem (&gOldStatementDimensions, &gStatementDimensions, sizeof (gStatementDimensions)); + } + + Status = UiDisplayMenu(FormData); + + return Status; +} + +/** + Initialize Setup Browser driver. + + @param ImageHandle The image handle. + @param SystemTable The system table. + + @retval EFI_SUCCESS The Setup Browser module is initialized correctly.. + @return Other value if failed to initialize the Setup Browser module. + +**/ +EFI_STATUS +EFIAPI +InitializeDisplayEngine ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_INPUT_KEY HotKey; + EFI_STRING NewString; + EDKII_FORM_BROWSER_EXTENSION2_PROTOCOL *FormBrowserEx2; + + // + // Publish our HII data + // + gHiiHandle = HiiAddPackages ( + &gDisplayEngineGuid, + ImageHandle, + DisplayEngineStrings, + NULL + ); + ASSERT (gHiiHandle != NULL); + + // + // Install Form Display protocol + // + Status = gBS->InstallProtocolInterface ( + &mPrivateData.Handle, + &gEdkiiFormDisplayEngineProtocolGuid, + EFI_NATIVE_INTERFACE, + &mPrivateData.FromDisplayProt + ); + ASSERT_EFI_ERROR (Status); + + InitializeDisplayStrings(); + + // + // Use BrowserEx2 protocol to register HotKey. + // + Status = gBS->LocateProtocol (&gEdkiiFormBrowserEx2ProtocolGuid, NULL, (VOID **) &FormBrowserEx2); + if (!EFI_ERROR (Status)) { + // + // Register the default HotKey F9 and F10 again. + // + HotKey.UnicodeChar = CHAR_NULL; + HotKey.ScanCode = SCAN_F10; + NewString = HiiGetString (gHiiHandle, STRING_TOKEN (FUNCTION_TEN_STRING), NULL); + ASSERT (NewString != NULL); + FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_SUBMIT, 0, NewString); + + HotKey.ScanCode = SCAN_F9; + NewString = HiiGetString (gHiiHandle, STRING_TOKEN (FUNCTION_NINE_STRING), NULL); + ASSERT (NewString != NULL); + FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_DEFAULT, EFI_HII_DEFAULT_CLASS_STANDARD, NewString); + } + + return EFI_SUCCESS; +} + +/** + This is the default unload handle for display core drivers. + + @param[in] ImageHandle The drivers' driver image. + + @retval EFI_SUCCESS The image is unloaded. + @retval Others Failed to unload the image. + +**/ +EFI_STATUS +EFIAPI +UnloadDisplayEngine ( + IN EFI_HANDLE ImageHandle + ) +{ + HiiRemovePackages(gHiiHandle); + + FreeDisplayStrings (); + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplay.h b/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplay.h new file mode 100644 index 0000000000..ad4684efc3 --- /dev/null +++ b/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplay.h @@ -0,0 +1,580 @@ +/** @file + FormDiplay protocol to show Form + +Copyright (c) 2013, 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 that 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. + +**/ + +#ifndef __FORM_DISPLAY_H__ +#define __FORM_DISPLAY_H__ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +// +// This is the generated header file which includes whatever needs to be exported (strings + IFR) +// +extern UINT8 DisplayEngineStrings[]; +extern EFI_SCREEN_DESCRIPTOR gStatementDimensions; +extern USER_INPUT *gUserInput; +extern FORM_DISPLAY_ENGINE_FORM *gFormData; +extern EFI_HII_HANDLE gHiiHandle; +extern UINT16 gDirection; +extern LIST_ENTRY gMenuOption; + +// +// Browser Global Strings +// +extern CHAR16 *gSaveFailed; +extern CHAR16 *gPromptForData; +extern CHAR16 *gPromptForPassword; +extern CHAR16 *gPromptForNewPassword; +extern CHAR16 *gConfirmPassword; +extern CHAR16 *gConfirmError; +extern CHAR16 *gPassowordInvalid; +extern CHAR16 *gPressEnter; +extern CHAR16 *gEmptyString; +extern CHAR16 *gMiniString; +extern CHAR16 *gOptionMismatch; +extern CHAR16 *gFormSuppress; +extern CHAR16 *gProtocolNotFound; + +extern CHAR16 gPromptBlockWidth; +extern CHAR16 gOptionBlockWidth; +extern CHAR16 gHelpBlockWidth; +extern CHAR16 *mUnknownString; + +// +// Screen definitions +// + +#define LEFT_SKIPPED_COLUMNS 3 +#define SCROLL_ARROW_HEIGHT 1 +#define POPUP_PAD_SPACE_COUNT 5 +#define POPUP_FRAME_WIDTH 2 + +// +// Display definitions +// +#define LEFT_ONEOF_DELIMITER L'<' +#define RIGHT_ONEOF_DELIMITER L'>' + +#define LEFT_NUMERIC_DELIMITER L'[' +#define RIGHT_NUMERIC_DELIMITER L']' + +#define LEFT_CHECKBOX_DELIMITER L'[' +#define RIGHT_CHECKBOX_DELIMITER L']' + +#define CHECK_ON L'X' +#define CHECK_OFF L' ' + +#define TIME_SEPARATOR L':' +#define DATE_SEPARATOR L'/' + +#define SUBTITLE_INDENT 2 + +// +// This is the Input Error Message +// +#define INPUT_ERROR 1 + +// +// This is the NV RAM update required Message +// +#define NV_UPDATE_REQUIRED 2 +// +// Time definitions +// +#define ONE_SECOND 10000000 + +// +// It take 23 characters including the NULL to print a 64 bits number with "[" and "]". +// pow(2, 64) = [18446744073709551616] +// +#define MAX_NUMERIC_INPUT_WIDTH 23 + +#define EFI_HII_EXPRESSION_INCONSISTENT_IF 0 +#define EFI_HII_EXPRESSION_NO_SUBMIT_IF 1 +#define EFI_HII_EXPRESSION_GRAY_OUT_IF 2 +#define EFI_HII_EXPRESSION_SUPPRESS_IF 3 +#define EFI_HII_EXPRESSION_DISABLE_IF 4 + +// +// Character definitions +// +#define CHAR_SPACE 0x0020 + +#define FORM_DISPLAY_DRIVER_SIGNATURE SIGNATURE_32 ('F', 'D', 'D', 'V') +typedef struct { + UINT32 Signature; + + EFI_HANDLE Handle; + + // + // Produced protocol + // + EDKII_FORM_DISPLAY_ENGINE_PROTOCOL FromDisplayProt; +} FORM_DISPLAY_DRIVER_PRIVATE_DATA; + + +typedef enum { + UiNoOperation, + UiSelect, + UiUp, + UiDown, + UiLeft, + UiRight, + UiReset, + UiPrevious, + UiPageUp, + UiPageDown, + UiHotKey, + UiMaxOperation +} UI_SCREEN_OPERATION; + +typedef enum { + CfInitialization, + CfCheckSelection, + CfRepaint, + CfRefreshHighLight, + CfUpdateHelpString, + CfPrepareToReadKey, + CfReadKey, + CfScreenOperation, + CfUiSelect, + CfUiReset, + CfUiLeft, + CfUiRight, + CfUiUp, + CfUiPageUp, + CfUiPageDown, + CfUiDown, + CfUiDefault, + CfUiNoOperation, + CfExit, + CfUiHotKey, + CfMaxControlFlag +} UI_CONTROL_FLAG; + +typedef enum { + UIEventNone, + UIEventKey, + UIEventTimeOut, + UIEventDriver +} UI_EVENT_TYPE; + +typedef struct { + UINT16 ScanCode; + UI_SCREEN_OPERATION ScreenOperation; +} SCAN_CODE_TO_SCREEN_OPERATION; + +typedef struct { + UI_SCREEN_OPERATION ScreenOperation; + UI_CONTROL_FLAG ControlFlag; +} SCREEN_OPERATION_T0_CONTROL_FLAG; + +typedef struct { + EFI_QUESTION_ID QuestionId; + UINT16 DisplayRow; +} DISPLAY_HIGHLIGHT_MENU_INFO; + +#define UI_MENU_OPTION_SIGNATURE SIGNATURE_32 ('u', 'i', 'm', 'm') + +typedef struct { + UINTN Signature; + LIST_ENTRY Link; + + EFI_HII_HANDLE Handle; + FORM_DISPLAY_ENGINE_STATEMENT *ThisTag; + UINT16 EntryNumber; + + UINTN Row; + UINTN Col; + UINTN OptCol; + CHAR16 *Description; + UINTN Skip; // Number of lines + + // + // Display item sequence for date/time + // Date: Month/Day/Year + // Sequence: 0 1 2 + // + // Time: Hour : Minute : Second + // Sequence: 0 1 2 + // + // + UINTN Sequence; + + BOOLEAN GrayOut; + BOOLEAN ReadOnly; + + // + // Whether user could change value of this item + // + BOOLEAN IsQuestion; + BOOLEAN NestInStatement; +} UI_MENU_OPTION; + +#define MENU_OPTION_FROM_LINK(a) CR (a, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE) + +/** + Print Question Value according to it's storage width and display attributes. + + @param Question The Question to be printed. + @param FormattedNumber Buffer for output string. + @param BufferSize The FormattedNumber buffer size in bytes. + + @retval EFI_SUCCESS Print success. + @retval EFI_BUFFER_TOO_SMALL Buffer size is not enough for formatted number. + +**/ +EFI_STATUS +PrintFormattedNumber ( + IN FORM_DISPLAY_ENGINE_STATEMENT *Question, + IN OUT CHAR16 *FormattedNumber, + IN UINTN BufferSize + ); + +/** + Set value of a data element in an Array by its Index. + + @param Array The data array. + @param Type Type of the data in this array. + @param Index Zero based index for data in this array. + @param Value The value to be set. + +**/ +VOID +SetArrayData ( + IN VOID *Array, + IN UINT8 Type, + IN UINTN Index, + IN UINT64 Value + ); + +/** + Return data element in an Array by its Index. + + @param Array The data array. + @param Type Type of the data in this array. + @param Index Zero based index for data in this array. + + @retval Value The data to be returned + +**/ +UINT64 +GetArrayData ( + IN VOID *Array, + IN UINT8 Type, + IN UINTN Index + ); + +/** + Search an Option of a Question by its value. + + @param Question The Question + @param OptionValue Value for Option to be searched. + + @retval Pointer Pointer to the found Option. + @retval NULL Option not found. + +**/ +DISPLAY_QUESTION_OPTION * +ValueToOption ( + IN FORM_DISPLAY_ENGINE_STATEMENT *Question, + IN EFI_HII_VALUE *OptionValue + ); + +/** + Compare two Hii value. + + @param Value1 Expression value to compare on left-hand. + @param Value2 Expression value to compare on right-hand. + @param Result Return value after compare. + retval 0 Two operators equal. + return Positive value if Value1 is greater than Value2. + retval Negative value if Value1 is less than Value2. + @param HiiHandle Only required for string compare. + + @retval other Could not perform compare on two values. + @retval EFI_SUCCESS Compare the value success. + +**/ +EFI_STATUS +CompareHiiValue ( + IN EFI_HII_VALUE *Value1, + IN EFI_HII_VALUE *Value2, + OUT INTN *Result, + IN EFI_HII_HANDLE HiiHandle OPTIONAL + ); + +/** + Draw a pop up windows based on the dimension, number of lines and + strings specified. + + @param RequestedWidth The width of the pop-up. + @param NumberOfLines The number of lines. + @param ... A series of text strings that displayed in the pop-up. + +**/ +VOID +EFIAPI +CreateMultiStringPopUp ( + IN UINTN RequestedWidth, + IN UINTN NumberOfLines, + ... + ); + +/** + 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. + The output string format is: + Glyph Info + String info + '\0'. + + In the code, it deals \r,\n,\r\n same as \n\r, also it not process the \r or \g. + + @param InputString String description for this option. + @param LineWidth Width of the desired string to extract in CHAR16 + characters + @param GlyphWidth The glyph width of the begin of the char in the string. + @param Index Where in InputString to start the copy process + @param OutputString Buffer to copy the string into + + @return Returns the number of CHAR16 characters that were copied into the OutputString + buffer, include extra glyph info and '\0' info. + +**/ +UINT16 +GetLineByWidth ( + IN CHAR16 *InputString, + IN UINT16 LineWidth, + IN OUT UINT16 *GlyphWidth, + IN OUT UINTN *Index, + OUT CHAR16 **OutputString + ); + + +/** + Get the string based on the StringId and HII Package List Handle. + + @param Token The String's ID. + @param HiiHandle The Hii handle for this string package. + + @return The output string. + +**/ +CHAR16 * +GetToken ( + IN EFI_STRING_ID Token, + IN EFI_HII_HANDLE HiiHandle + ); + +/** + Count the storage space of a Unicode string. + + This function handles the Unicode string with NARROW_CHAR + and WIDE_CHAR control characters. NARROW_HCAR and WIDE_CHAR + does not count in the resultant output. If a WIDE_CHAR is + hit, then 2 Unicode character will consume an output storage + space with size of CHAR16 till a NARROW_CHAR is hit. + + If String is NULL, then ASSERT (). + + @param String The input string to be counted. + + @return Storage space for the input string. + +**/ +UINTN +GetStringWidth ( + IN CHAR16 *String + ); + +/** + This routine reads a numeric value from the user input. + + @param MenuOption Pointer to the current input menu. + + @retval EFI_SUCCESS If numerical input is read successfully + @retval EFI_DEVICE_ERROR If operation fails + +**/ +EFI_STATUS +GetNumericInput ( + IN UI_MENU_OPTION *MenuOption + ); + +/** + Get string or password input from user. + + @param MenuOption Pointer to the current input menu. + @param Prompt The prompt string shown on popup window. + @param StringPtr Old user input and destination for use input string. + + @retval EFI_SUCCESS If string input is read successfully + @retval EFI_DEVICE_ERROR If operation fails + +**/ +EFI_STATUS +ReadString ( + IN UI_MENU_OPTION *MenuOption, + IN CHAR16 *Prompt, + IN OUT CHAR16 *StringPtr + ); + +/** + Draw a pop up windows based on the dimension, number of lines and + strings specified. + + @param RequestedWidth The width of the pop-up. + @param NumberOfLines The number of lines. + @param Marker The variable argument list for the list of string to be printed. + +**/ +VOID +CreateSharedPopUp ( + IN UINTN RequestedWidth, + IN UINTN NumberOfLines, + IN VA_LIST Marker + ); + +/** + Wait for a key to be pressed by user. + + @param Key The key which is pressed by user. + + @retval EFI_SUCCESS The function always completed successfully. + +**/ +EFI_STATUS +WaitForKeyStroke ( + OUT EFI_INPUT_KEY *Key + ); + +/** + Get selection for OneOf and OrderedList (Left/Right will be ignored). + + @param MenuOption Pointer to the current input menu. + + @retval EFI_SUCCESS If Option input is processed successfully + @retval EFI_DEVICE_ERROR If operation fails + +**/ +EFI_STATUS +GetSelectionInputPopUp ( + IN UI_MENU_OPTION *MenuOption + ); + +/** + Process the help string: Split StringPtr to several lines of strings stored in + FormattedString and the glyph width of each line cannot exceed gHelpBlockWidth. + + @param StringPtr The entire help string. + @param FormattedString The oupput formatted string. + @param EachLineWidth The max string length of each line in the formatted string. + @param RowCount TRUE: if Question is selected. + +**/ +UINTN +ProcessHelpString ( + IN CHAR16 *StringPtr, + OUT CHAR16 **FormattedString, + OUT UINT16 *EachLineWidth, + IN UINTN RowCount + ); + +/** + Process a Question's Option (whether selected or un-selected). + + @param MenuOption The MenuOption for this Question. + @param Selected TRUE: if Question is selected. + @param OptionString Pointer of the Option String to be displayed. + @param SkipErrorValue Whether need to return when value without option for it. + + @retval EFI_SUCCESS Question Option process success. + @retval Other Question Option process fail. + +**/ +EFI_STATUS +ProcessOptions ( + IN UI_MENU_OPTION *MenuOption, + IN BOOLEAN Selected, + OUT CHAR16 **OptionString, + IN BOOLEAN SkipErrorValue + ); + +/** + Set Buffer to Value for Size bytes. + + @param Buffer Memory to set. + @param Size Number of bytes to set + @param Value Value of the set operation. + +**/ +VOID +SetUnicodeMem ( + IN VOID *Buffer, + IN UINTN Size, + IN CHAR16 Value + ); + +/** + Display one form, and return user input. + + @param FormData Form Data to be shown. + @param UserInputData User input data. + + @retval EFI_SUCCESS Form Data is shown, and user input is got. +**/ +EFI_STATUS +EFIAPI +FormDisplay ( + IN FORM_DISPLAY_ENGINE_FORM *FormData, + OUT USER_INPUT *UserInputData + ); + +/** + Exit Display and Clear Screen to the original state. + +**/ +VOID +EFIAPI +ExitDisplay ( + VOID + ); + +/** + Process validate for one question. + + @param Question The question which need to validate. + + @retval EFI_SUCCESS Question Option process success. + @retval Other Question Option process fail. + +**/ +EFI_STATUS +ValidateQuestion ( + IN FORM_DISPLAY_ENGINE_STATEMENT *Question + ); + +#endif diff --git a/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplayStr.uni b/MdeModulePkg/Universal/DisplayEngineDxe/FormDisplayStr.uni new file mode 100644 index 0000000000000000000000000000000000000000..0ee7f46302f0f9cbf66981dc309c0094322b0a77 GIT binary patch literal 8660 zcmdU!&r%yl5XR@6s=UJnA7VQO;v|>2;#3Kc62%~h1tvLB0`W&xuuv-jA#a$s@GbEr zkC5d1b!*s_Si#CBC0r`)&hE_4^nCqwPfyRk|15^Zkc6k<3;jL~pXh1v`ksX4a2n3S zRmegw9EPJXP}|30FFeuxtMH|M->5V{e0mhEy&0!?DWrkf{%@9lh z)KpK4yzjm}?T5a;&BBGQUA6o!EQQzM`+`)PlBB0riTcgd-=Xz=qUVnK?&>~Ji+y|E zleDMdL@lYi(s!xaCwhZ4jMG!;SiQyB(@SPwAFFOprPL&%-`GE2cZVZfsN7Qzov^Db zqn?`-^qouMW1X1Pu@@vm-rhAX%;_{76uw&u?XaTf)e%{8SuYKXJzbG*bhbT7Lyw^0 zcrjLA&&+yPau)TG1aL+E1J$^0koq^Z8kcGm8AtO~fY=XI4}GS@lX>6c#TbYHS(o}OLH zmV|B9BiFi($a762Az4fBo9dN5)@}6HdO~*IG3KUP(S}jIRYPt0@>ktosRqfh!1{0$ zpG7_Va@|kfdsA3!h*ylcB0Gz?#e1UqQ{fF4U`0#1JW=UINy|j_ZPg9b`b;y&Dleri zY@n^Tnfk{ndL`f1Os}+ozk^%)ciy`FVtohdk3J)d`;y|o_yhVy8OmliFD5-5dOPOq z^L`l~S&64dnmM+5Xts`3!q$0d4ix8v8+EX;F9y!pcQ?N|kR-^;Vb>u#gSIn(U{KFmY59QxBqfE&Nb4ZBp;O?`9B+uetS>T)|QQ2#Gp9x zBOy@KDszP6AiN_TVz=8W!{Z&h@&?N+`<0Dc)EFa=@&eBVYZJ-AD&Z()#+}S8iIo6W zXVTg1@VXDFr^C8oIIjtH7`UlBZ`?P6eHE-5W{a%LsY;-Xb^7XvmwFi3zK@jCVF`lR z@0QrNW}bORSA4hss$kg~B1o)qtn#IJ>5pO|-yz(-F?cdK3M2)ZqQx%j6sPoHylrYtp?N zN|(!aH+DokRacfW*~WcqBRtoR;H8+}vFP2`w)?>*_EILtO^jTQKNL?8VdL3ZYl%kU zDm}jL)WREMQA6ubQ&Ew}f^pQ_?`1OG#Kw0CMXQ}PPT+GSbj}T#B)k=q-c`n|CY$)l zR%%uUBJEYhlRNs?6ro^|EAo59E@kLd!HoFRcVds#*P{I0lbJ;rAv2|7UyH!lD z4PXD*CuibK(FQ&?ZH&&ub8AJbxL>`n-@I+7IqIdV>1CAl*m}$}BD>qW=imCOVJ|wX z_-R#RBcFR3N}lVCeiJVQe|*BtPOgLC^7xGIG*WHKu|Y>mzwYvW`B2 z?}OY9$+)GO#Vk46G1f&{VWUe+zBCy|vALX+BLDLBaYbAt6Ez9P_`VvR)uNKF7{Q7^ zE32O_8gZ|{?kd%u0lt*;wQgDFO=YO3&0vCY6?|ufSNVL&?~s#tpV?NM?`XWmKGV;Z zJikkH!bu$#a3nUxtU4=Z)KO&b{oKoRG`=H$BZi#Ynt(0*F6r23atek0vJ&ENyvEm> zy@Kq2S*s}h_-e9lJBpYO0{^x+hy5;VS9;eR%cLLQyZ>ySj*NkyUJ|d9hr|EjzsW85 zd7_`gUFr>IZk*$sm3}q5O!B0;pda=|UDFJ+i>C&j%h^-ivGyyR*^T4i-^MwO>nG17 zlL6h(&rh_->fg22SF?ybgNr6S8+s-=^R`ECq)eEjlj1p##}Zijg|xvLyGM?0HRM@| jAJN&e%BgrqABUztvaz(|8+vfNOw}&(a(4QA2($hV4k4JE literal 0 HcmV?d00001 diff --git a/MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c b/MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c new file mode 100644 index 0000000000..607f0df83c --- /dev/null +++ b/MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c @@ -0,0 +1,1531 @@ +/** @file +Implementation for handling user input from the User Interfaces. + +Copyright (c) 2004 - 2012, 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 "FormDisplay.h" + +/** + Get maximum and minimum info from this opcode. + + @param OpCode Pointer to the current input opcode. + @param Minimum The minimum size info for this opcode. + @param Maximum The maximum size info for this opcode. + +**/ +VOID +GetFieldFromOp ( + IN EFI_IFR_OP_HEADER *OpCode, + OUT UINTN *Minimum, + OUT UINTN *Maximum + ) +{ + EFI_IFR_STRING *StringOp; + EFI_IFR_PASSWORD *PasswordOp; + if (OpCode->OpCode == EFI_IFR_STRING_OP) { + StringOp = (EFI_IFR_STRING *) OpCode; + *Minimum = StringOp->MinSize; + *Maximum = StringOp->MaxSize; + } else if (OpCode->OpCode == EFI_IFR_PASSWORD_OP) { + PasswordOp = (EFI_IFR_PASSWORD *) OpCode; + *Minimum = PasswordOp->MinSize; + *Maximum = PasswordOp->MaxSize; + } else { + *Minimum = 0; + *Maximum = 0; + } +} + +/** + Get string or password input from user. + + @param MenuOption Pointer to the current input menu. + @param Prompt The prompt string shown on popup window. + @param StringPtr Old user input and destination for use input string. + + @retval EFI_SUCCESS If string input is read successfully + @retval EFI_DEVICE_ERROR If operation fails + +**/ +EFI_STATUS +ReadString ( + IN UI_MENU_OPTION *MenuOption, + IN CHAR16 *Prompt, + IN OUT CHAR16 *StringPtr + ) +{ + EFI_STATUS Status; + EFI_INPUT_KEY Key; + CHAR16 NullCharacter; + UINTN ScreenSize; + CHAR16 Space[2]; + CHAR16 KeyPad[2]; + CHAR16 *TempString; + CHAR16 *BufferedString; + UINTN Index; + UINTN Index2; + UINTN Count; + UINTN Start; + UINTN Top; + UINTN DimensionsWidth; + UINTN DimensionsHeight; + UINTN CurrentCursor; + BOOLEAN CursorVisible; + UINTN Minimum; + UINTN Maximum; + FORM_DISPLAY_ENGINE_STATEMENT *Question; + BOOLEAN IsPassword; + + DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn; + DimensionsHeight = gStatementDimensions.BottomRow - gStatementDimensions.TopRow; + + NullCharacter = CHAR_NULL; + ScreenSize = GetStringWidth (Prompt) / sizeof (CHAR16); + Space[0] = L' '; + Space[1] = CHAR_NULL; + + Question = MenuOption->ThisTag; + GetFieldFromOp(Question->OpCode, &Minimum, &Maximum); + + if (Question->OpCode->OpCode == EFI_IFR_PASSWORD_OP) { + IsPassword = TRUE; + } else { + IsPassword = FALSE; + } + + TempString = AllocateZeroPool ((Maximum + 1)* sizeof (CHAR16)); + ASSERT (TempString); + + if (ScreenSize < (Maximum + 1)) { + ScreenSize = Maximum + 1; + } + + if ((ScreenSize + 2) > DimensionsWidth) { + ScreenSize = DimensionsWidth - 2; + } + + BufferedString = AllocateZeroPool (ScreenSize * 2); + ASSERT (BufferedString); + + Start = (DimensionsWidth - ScreenSize - 2) / 2 + gStatementDimensions.LeftColumn + 1; + Top = ((DimensionsHeight - 6) / 2) + gStatementDimensions.TopRow - 1; + + // + // Display prompt for string + // + // CreateDialog (NULL, "", Prompt, Space, "", NULL); + CreateMultiStringPopUp (ScreenSize, 4, &NullCharacter, Prompt, Space, &NullCharacter); + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY)); + + CursorVisible = gST->ConOut->Mode->CursorVisible; + gST->ConOut->EnableCursor (gST->ConOut, TRUE); + + CurrentCursor = GetStringWidth (StringPtr) / 2 - 1; + if (CurrentCursor != 0) { + // + // Show the string which has beed saved before. + // + SetUnicodeMem (BufferedString, ScreenSize - 1, L' '); + PrintStringAt (Start + 1, Top + 3, BufferedString); + + if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) { + Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2; + } else { + Index = 0; + } + + if (IsPassword) { + gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3); + } + + for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) { + BufferedString[Count] = StringPtr[Index]; + + if (IsPassword) { + PrintCharAt ((UINTN)-1, (UINTN)-1, L'*'); + } + } + + if (!IsPassword) { + PrintStringAt (Start + 1, Top + 3, BufferedString); + } + + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + gST->ConOut->SetCursorPosition (gST->ConOut, Start + GetStringWidth (StringPtr) / 2, Top + 3); + } + + do { + Status = WaitForKeyStroke (&Key); + ASSERT_EFI_ERROR (Status); + + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY)); + switch (Key.UnicodeChar) { + case CHAR_NULL: + switch (Key.ScanCode) { + case SCAN_LEFT: + if (CurrentCursor > 0) { + CurrentCursor--; + } + break; + + case SCAN_RIGHT: + if (CurrentCursor < (GetStringWidth (StringPtr) / 2 - 1)) { + CurrentCursor++; + } + break; + + case SCAN_ESC: + FreePool (TempString); + FreePool (BufferedString); + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + gST->ConOut->EnableCursor (gST->ConOut, CursorVisible); + return EFI_DEVICE_ERROR; + + default: + break; + } + + break; + + case CHAR_CARRIAGE_RETURN: + if (GetStringWidth (StringPtr) >= ((Minimum + 1) * sizeof (CHAR16))) { + + FreePool (TempString); + FreePool (BufferedString); + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + gST->ConOut->EnableCursor (gST->ConOut, CursorVisible); + return EFI_SUCCESS; + } else { + // + // Simply create a popup to tell the user that they had typed in too few characters. + // To save code space, we can then treat this as an error and return back to the menu. + // + do { + CreateDialog (&Key, &NullCharacter, gMiniString, gPressEnter, &NullCharacter, NULL); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + FreePool (TempString); + FreePool (BufferedString); + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + gST->ConOut->EnableCursor (gST->ConOut, CursorVisible); + return EFI_DEVICE_ERROR; + } + + break; + + case CHAR_BACKSPACE: + if (StringPtr[0] != CHAR_NULL && CurrentCursor != 0) { + for (Index = 0; Index < CurrentCursor - 1; Index++) { + TempString[Index] = StringPtr[Index]; + } + Count = GetStringWidth (StringPtr) / 2 - 1; + if (Count >= CurrentCursor) { + for (Index = CurrentCursor - 1, Index2 = CurrentCursor; Index2 < Count; Index++, Index2++) { + TempString[Index] = StringPtr[Index2]; + } + TempString[Index] = CHAR_NULL; + } + // + // Effectively truncate string by 1 character + // + StrCpy (StringPtr, TempString); + CurrentCursor --; + } + + default: + // + // If it is the beginning of the string, don't worry about checking maximum limits + // + if ((StringPtr[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) { + StrnCpy (StringPtr, &Key.UnicodeChar, 1); + CurrentCursor++; + } else if ((GetStringWidth (StringPtr) < ((Maximum + 1) * sizeof (CHAR16))) && (Key.UnicodeChar != CHAR_BACKSPACE)) { + KeyPad[0] = Key.UnicodeChar; + KeyPad[1] = CHAR_NULL; + Count = GetStringWidth (StringPtr) / 2 - 1; + if (CurrentCursor < Count) { + for (Index = 0; Index < CurrentCursor; Index++) { + TempString[Index] = StringPtr[Index]; + } + TempString[Index] = CHAR_NULL; + StrCat (TempString, KeyPad); + StrCat (TempString, StringPtr + CurrentCursor); + StrCpy (StringPtr, TempString); + } else { + StrCat (StringPtr, KeyPad); + } + CurrentCursor++; + } + + // + // If the width of the input string is now larger than the screen, we nee to + // adjust the index to start printing portions of the string + // + SetUnicodeMem (BufferedString, ScreenSize - 1, L' '); + PrintStringAt (Start + 1, Top + 3, BufferedString); + + if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) { + Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2; + } else { + Index = 0; + } + + if (IsPassword) { + gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3); + } + + for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) { + BufferedString[Count] = StringPtr[Index]; + + if (IsPassword) { + PrintCharAt ((UINTN)-1, (UINTN)-1, L'*'); + } + } + + if (!IsPassword) { + PrintStringAt (Start + 1, Top + 3, BufferedString); + } + break; + } + + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + gST->ConOut->SetCursorPosition (gST->ConOut, Start + CurrentCursor + 1, Top + 3); + } while (TRUE); + +} + +/** + Adjust the value to the correct one. Rules follow the sample: + like: Year change: 2012.02.29 -> 2013.02.29 -> 2013.02.01 + Month change: 2013.03.29 -> 2013.02.29 -> 2013.02.28 + + @param QuestionValue Pointer to current question. + @param Sequence The sequence of the field in the question. +**/ +VOID +AdjustQuestionValue ( + IN EFI_HII_VALUE *QuestionValue, + IN UINT8 Sequence + ) +{ + UINT8 Month; + UINT16 Year; + UINT8 Maximum; + UINT8 Minimum; + + Month = QuestionValue->Value.date.Month; + Year = QuestionValue->Value.date.Year; + Minimum = 1; + + switch (Month) { + case 2: + if ((Year % 4) == 0 && ((Year % 100) != 0 || (Year % 400) == 0)) { + Maximum = 29; + } else { + Maximum = 28; + } + break; + case 4: + case 6: + case 9: + case 11: + Maximum = 30; + break; + default: + Maximum = 31; + break; + } + + // + // Change the month area. + // + if (Sequence == 0) { + if (QuestionValue->Value.date.Day > Maximum) { + QuestionValue->Value.date.Day = Maximum; + } + } + + // + // Change the Year area. + // + if (Sequence == 2) { + if (QuestionValue->Value.date.Day > Maximum) { + QuestionValue->Value.date.Day = Minimum; + } + } +} + +/** + Get field info from numeric opcode. + + @param OpCode Pointer to the current input opcode. + @param Minimum The minimum size info for this opcode. + @param Maximum The maximum size info for this opcode. + @param Step The step size info for this opcode. + @param StorageWidth The storage width info for this opcode. + +**/ +VOID +GetValueFromNum ( + IN EFI_IFR_OP_HEADER *OpCode, + OUT UINT64 *Minimum, + OUT UINT64 *Maximum, + OUT UINT64 *Step, + OUT UINT16 *StorageWidth +) +{ + EFI_IFR_NUMERIC *NumericOp; + + NumericOp = (EFI_IFR_NUMERIC *) OpCode; + + switch (NumericOp->Flags & EFI_IFR_NUMERIC_SIZE) { + case EFI_IFR_NUMERIC_SIZE_1: + *Minimum = NumericOp->data.u8.MinValue; + *Maximum = NumericOp->data.u8.MaxValue; + *Step = NumericOp->data.u8.Step; + *StorageWidth = (UINT16) sizeof (UINT8); + break; + + case EFI_IFR_NUMERIC_SIZE_2: + *Minimum = NumericOp->data.u16.MinValue; + *Maximum = NumericOp->data.u16.MaxValue; + *Step = NumericOp->data.u16.Step; + *StorageWidth = (UINT16) sizeof (UINT16); + break; + + case EFI_IFR_NUMERIC_SIZE_4: + *Minimum = NumericOp->data.u32.MinValue; + *Maximum = NumericOp->data.u32.MaxValue; + *Step = NumericOp->data.u32.Step; + *StorageWidth = (UINT16) sizeof (UINT32); + break; + + case EFI_IFR_NUMERIC_SIZE_8: + *Minimum = NumericOp->data.u64.MinValue; + *Maximum = NumericOp->data.u64.MaxValue; + *Step = NumericOp->data.u64.Step; + *StorageWidth = (UINT16) sizeof (UINT64); + break; + + default: + break; + } + + if (*Maximum == 0) { + *Maximum = (UINT64) -1; + } +} + +/** + This routine reads a numeric value from the user input. + + @param MenuOption Pointer to the current input menu. + + @retval EFI_SUCCESS If numerical input is read successfully + @retval EFI_DEVICE_ERROR If operation fails + +**/ +EFI_STATUS +GetNumericInput ( + IN UI_MENU_OPTION *MenuOption + ) +{ + EFI_STATUS Status; + UINTN Column; + UINTN Row; + CHAR16 InputText[MAX_NUMERIC_INPUT_WIDTH]; + CHAR16 FormattedNumber[MAX_NUMERIC_INPUT_WIDTH - 1]; + UINT64 PreviousNumber[MAX_NUMERIC_INPUT_WIDTH - 3]; + UINTN Count; + UINTN Loop; + BOOLEAN ManualInput; + BOOLEAN HexInput; + BOOLEAN DateOrTime; + UINTN InputWidth; + UINT64 EditValue; + UINT64 Step; + UINT64 Minimum; + UINT64 Maximum; + UINTN EraseLen; + UINT8 Digital; + EFI_INPUT_KEY Key; + EFI_HII_VALUE *QuestionValue; + FORM_DISPLAY_ENGINE_STATEMENT *Question; + EFI_IFR_NUMERIC *NumericOp; + UINT16 StorageWidth; + + Column = MenuOption->OptCol; + Row = MenuOption->Row; + PreviousNumber[0] = 0; + Count = 0; + InputWidth = 0; + Digital = 0; + StorageWidth = 0; + Minimum = 0; + Maximum = 0; + NumericOp = NULL; + + Question = MenuOption->ThisTag; + QuestionValue = &Question->CurrentValue; + + // + // Only two case, user can enter to this function: Enter and +/- case. + // In Enter case, gDirection = 0; in +/- case, gDirection = SCAN_LEFT/SCAN_WRIGHT + // + ManualInput = (BOOLEAN)(gDirection == 0 ? TRUE : FALSE); + + if ((Question->OpCode->OpCode == EFI_IFR_DATE_OP) || (Question->OpCode->OpCode == EFI_IFR_TIME_OP)) { + DateOrTime = TRUE; + } else { + DateOrTime = FALSE; + } + + // + // Prepare Value to be edit + // + EraseLen = 0; + EditValue = 0; + if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) { + Step = 1; + Minimum = 1; + + switch (MenuOption->Sequence) { + case 0: + Maximum = 12; + EraseLen = 4; + EditValue = QuestionValue->Value.date.Month; + break; + + case 1: + switch (QuestionValue->Value.date.Month) { + case 2: + if ((QuestionValue->Value.date.Year % 4) == 0 && + ((QuestionValue->Value.date.Year % 100) != 0 || + (QuestionValue->Value.date.Year % 400) == 0)) { + Maximum = 29; + } else { + Maximum = 28; + } + break; + case 4: + case 6: + case 9: + case 11: + Maximum = 30; + break; + default: + Maximum = 31; + break; + } + + EraseLen = 3; + EditValue = QuestionValue->Value.date.Day; + break; + + case 2: + Maximum = 0xffff; + EraseLen = 5; + EditValue = QuestionValue->Value.date.Year; + break; + + default: + break; + } + } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) { + Step = 1; + Minimum = 0; + + switch (MenuOption->Sequence) { + case 0: + Maximum = 23; + EraseLen = 4; + EditValue = QuestionValue->Value.time.Hour; + break; + + case 1: + Maximum = 59; + EraseLen = 3; + EditValue = QuestionValue->Value.time.Minute; + break; + + case 2: + Maximum = 59; + EraseLen = 3; + EditValue = QuestionValue->Value.time.Second; + break; + + default: + break; + } + } else { + ASSERT (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP); + NumericOp = (EFI_IFR_NUMERIC *) Question->OpCode; + GetValueFromNum(Question->OpCode, &Minimum, &Maximum, &Step, &StorageWidth); + EditValue = QuestionValue->Value.u64; + EraseLen = gOptionBlockWidth; + } + + if ((Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) && (NumericOp != NULL) && + ((NumericOp->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX)) { + HexInput = TRUE; + } else { + HexInput = FALSE; + } + + // + // Enter from "Enter" input, clear the old word showing. + // + if (ManualInput) { + if (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) { + if (HexInput) { + InputWidth = StorageWidth * 2; + } else { + switch (StorageWidth) { + case 1: + InputWidth = 3; + break; + + case 2: + InputWidth = 5; + break; + + case 4: + InputWidth = 10; + break; + + case 8: + InputWidth = 20; + break; + + default: + InputWidth = 0; + break; + } + } + + InputText[0] = LEFT_NUMERIC_DELIMITER; + SetUnicodeMem (InputText + 1, InputWidth, L' '); + ASSERT (InputWidth + 2 < MAX_NUMERIC_INPUT_WIDTH); + InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER; + InputText[InputWidth + 2] = L'\0'; + + PrintStringAt (Column, Row, InputText); + Column++; + } + + if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) { + if (MenuOption->Sequence == 2) { + InputWidth = 4; + } else { + InputWidth = 2; + } + + if (MenuOption->Sequence == 0) { + InputText[0] = LEFT_NUMERIC_DELIMITER; + SetUnicodeMem (InputText + 1, InputWidth, L' '); + } else { + SetUnicodeMem (InputText, InputWidth, L' '); + } + + if (MenuOption->Sequence == 2) { + InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER; + } else { + InputText[InputWidth + 1] = DATE_SEPARATOR; + } + InputText[InputWidth + 2] = L'\0'; + + PrintStringAt (Column, Row, InputText); + if (MenuOption->Sequence == 0) { + Column++; + } + } + + if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) { + InputWidth = 2; + + if (MenuOption->Sequence == 0) { + InputText[0] = LEFT_NUMERIC_DELIMITER; + SetUnicodeMem (InputText + 1, InputWidth, L' '); + } else { + SetUnicodeMem (InputText, InputWidth, L' '); + } + + if (MenuOption->Sequence == 2) { + InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER; + } else { + InputText[InputWidth + 1] = TIME_SEPARATOR; + } + InputText[InputWidth + 2] = L'\0'; + + PrintStringAt (Column, Row, InputText); + if (MenuOption->Sequence == 0) { + Column++; + } + } + } + + // + // First time we enter this handler, we need to check to see if + // we were passed an increment or decrement directive + // + do { + Key.UnicodeChar = CHAR_NULL; + if (gDirection != 0) { + Key.ScanCode = gDirection; + gDirection = 0; + goto TheKey2; + } + + Status = WaitForKeyStroke (&Key); + +TheKey2: + switch (Key.UnicodeChar) { + + case '+': + case '-': + if (Key.UnicodeChar == '+') { + Key.ScanCode = SCAN_RIGHT; + } else { + Key.ScanCode = SCAN_LEFT; + } + Key.UnicodeChar = CHAR_NULL; + goto TheKey2; + + case CHAR_NULL: + switch (Key.ScanCode) { + case SCAN_LEFT: + case SCAN_RIGHT: + if (DateOrTime && !ManualInput) { + // + // By setting this value, we will return back to the caller. + // We need to do this since an auto-refresh will destroy the adjustment + // based on what the real-time-clock is showing. So we always commit + // upon changing the value. + // + gDirection = SCAN_DOWN; + } + + if ((Step != 0) && !ManualInput) { + if (Key.ScanCode == SCAN_LEFT) { + if (EditValue >= Minimum + Step) { + EditValue = EditValue - Step; + } else if (EditValue > Minimum){ + EditValue = Minimum; + } else { + EditValue = Maximum; + } + } else if (Key.ScanCode == SCAN_RIGHT) { + if (EditValue + Step <= Maximum) { + EditValue = EditValue + Step; + } else if (EditValue < Maximum) { + EditValue = Maximum; + } else { + EditValue = Minimum; + } + } + + ZeroMem (FormattedNumber, 21 * sizeof (CHAR16)); + if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) { + if (MenuOption->Sequence == 2) { + // + // Year + // + UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINT16) EditValue); + } else { + // + // Month/Day + // + UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue); + } + + if (MenuOption->Sequence == 0) { + ASSERT (EraseLen >= 2); + FormattedNumber[EraseLen - 2] = DATE_SEPARATOR; + } else if (MenuOption->Sequence == 1) { + ASSERT (EraseLen >= 1); + FormattedNumber[EraseLen - 1] = DATE_SEPARATOR; + } + } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) { + UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue); + + if (MenuOption->Sequence == 0) { + ASSERT (EraseLen >= 2); + FormattedNumber[EraseLen - 2] = TIME_SEPARATOR; + } else if (MenuOption->Sequence == 1) { + ASSERT (EraseLen >= 1); + FormattedNumber[EraseLen - 1] = TIME_SEPARATOR; + } + } else { + QuestionValue->Value.u64 = EditValue; + PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16)); + } + + gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ()); + for (Loop = 0; Loop < EraseLen; Loop++) { + PrintStringAt (MenuOption->OptCol + Loop, MenuOption->Row, L" "); + } + gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ()); + + if (MenuOption->Sequence == 0) { + PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER); + Column = MenuOption->OptCol + 1; + } + + PrintStringAt (Column, Row, FormattedNumber); + + if (!DateOrTime || MenuOption->Sequence == 2) { + PrintCharAt ((UINTN)-1, (UINTN)-1, RIGHT_NUMERIC_DELIMITER); + } + } + + goto EnterCarriageReturn; + break; + + case SCAN_UP: + case SCAN_DOWN: + goto EnterCarriageReturn; + + case SCAN_ESC: + return EFI_DEVICE_ERROR; + + default: + break; + } + + break; + +EnterCarriageReturn: + + case CHAR_CARRIAGE_RETURN: + // + // Validate input value with Minimum value. + // + if (EditValue < Minimum) { + UpdateStatusBar (INPUT_ERROR, TRUE); + break; + } else { + UpdateStatusBar (INPUT_ERROR, FALSE); + } + + CopyMem (&gUserInput->InputValue, &Question->CurrentValue, sizeof (EFI_HII_VALUE)); + QuestionValue = &gUserInput->InputValue; + // + // Store Edit value back to Question + // + if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) { + switch (MenuOption->Sequence) { + case 0: + QuestionValue->Value.date.Month = (UINT8) EditValue; + break; + + case 1: + QuestionValue->Value.date.Day = (UINT8) EditValue; + break; + + case 2: + QuestionValue->Value.date.Year = (UINT16) EditValue; + break; + + default: + break; + } + } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) { + switch (MenuOption->Sequence) { + case 0: + QuestionValue->Value.time.Hour = (UINT8) EditValue; + break; + + case 1: + QuestionValue->Value.time.Minute = (UINT8) EditValue; + break; + + case 2: + QuestionValue->Value.time.Second = (UINT8) EditValue; + break; + + default: + break; + } + } else { + // + // Numeric + // + QuestionValue->Value.u64 = EditValue; + } + + // + // Adjust the value to the correct one. + // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01 + // 2013.03.29 -> 2013.02.29 -> 2013.02.28 + // + if (Question->OpCode->OpCode == EFI_IFR_DATE_OP && + (MenuOption->Sequence == 0 || MenuOption->Sequence == 2)) { + AdjustQuestionValue (QuestionValue, (UINT8)MenuOption->Sequence); + } + + return ValidateQuestion (Question); + break; + + case CHAR_BACKSPACE: + if (ManualInput) { + if (Count == 0) { + break; + } + // + // Remove a character + // + EditValue = PreviousNumber[Count - 1]; + UpdateStatusBar (INPUT_ERROR, FALSE); + Count--; + Column--; + PrintStringAt (Column, Row, L" "); + } + break; + + default: + if (ManualInput) { + if (HexInput) { + if ((Key.UnicodeChar >= L'0') && (Key.UnicodeChar <= L'9')) { + Digital = (UINT8) (Key.UnicodeChar - L'0'); + } else if ((Key.UnicodeChar >= L'A') && (Key.UnicodeChar <= L'F')) { + Digital = (UINT8) (Key.UnicodeChar - L'A' + 0x0A); + } else if ((Key.UnicodeChar >= L'a') && (Key.UnicodeChar <= L'f')) { + Digital = (UINT8) (Key.UnicodeChar - L'a' + 0x0A); + } else { + UpdateStatusBar (INPUT_ERROR, TRUE); + break; + } + } else { + if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') { + UpdateStatusBar (INPUT_ERROR, TRUE); + break; + } + } + + // + // If Count exceed input width, there is no way more is valid + // + if (Count >= InputWidth) { + break; + } + // + // Someone typed something valid! + // + if (Count != 0) { + if (HexInput) { + EditValue = LShiftU64 (EditValue, 4) + Digital; + } else { + EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0'); + } + } else { + if (HexInput) { + EditValue = Digital; + } else { + EditValue = Key.UnicodeChar - L'0'; + } + } + + if (EditValue > Maximum) { + UpdateStatusBar (INPUT_ERROR, TRUE); + ASSERT (Count < sizeof (PreviousNumber) / sizeof (PreviousNumber[0])); + EditValue = PreviousNumber[Count]; + break; + } else { + UpdateStatusBar (INPUT_ERROR, FALSE); + } + + Count++; + ASSERT (Count < (sizeof (PreviousNumber) / sizeof (PreviousNumber[0]))); + PreviousNumber[Count] = EditValue; + + gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ()); + PrintCharAt (Column, Row, Key.UnicodeChar); + Column++; + } + break; + } + } while (TRUE); +} + +/** + Adjust option order base on the question value. + + @param Question Pointer to current question. + @param PopUpMenuLines The line number of the pop up menu. + + @retval EFI_SUCCESS If Option input is processed successfully + @retval EFI_DEVICE_ERROR If operation fails + +**/ +EFI_STATUS +AdjustOptionOrder ( + IN FORM_DISPLAY_ENGINE_STATEMENT *Question, + OUT UINTN *PopUpMenuLines + ) +{ + UINTN Index; + EFI_IFR_ORDERED_LIST *OrderList; + UINT8 *ValueArray; + UINT8 ValueType; + LIST_ENTRY *Link; + DISPLAY_QUESTION_OPTION *OneOfOption; + EFI_HII_VALUE *HiiValueArray; + + Link = GetFirstNode (&Question->OptionListHead); + OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + ValueArray = Question->CurrentValue.Buffer; + ValueType = OneOfOption->OptionOpCode->Type; + OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode; + + for (Index = 0; Index < OrderList->MaxContainers; Index++) { + if (GetArrayData (ValueArray, ValueType, Index) == 0) { + break; + } + } + + *PopUpMenuLines = Index; + + // + // Prepare HiiValue array + // + HiiValueArray = AllocateZeroPool (*PopUpMenuLines * sizeof (EFI_HII_VALUE)); + ASSERT (HiiValueArray != NULL); + + for (Index = 0; Index < *PopUpMenuLines; Index++) { + HiiValueArray[Index].Type = ValueType; + HiiValueArray[Index].Value.u64 = GetArrayData (ValueArray, ValueType, Index); + } + + for (Index = 0; Index < *PopUpMenuLines; Index++) { + OneOfOption = ValueToOption (Question, &HiiValueArray[*PopUpMenuLines - Index - 1]); + if (OneOfOption == NULL) { + return EFI_NOT_FOUND; + } + + RemoveEntryList (&OneOfOption->Link); + + // + // Insert to head. + // + InsertHeadList (&Question->OptionListHead, &OneOfOption->Link); + } + + FreePool (HiiValueArray); + + return EFI_SUCCESS; +} + +/** + Base on the type to compare the value. + + @param Value1 The first value need to compare. + @param Value2 The second value need to compare. + @param Type The value type for above two values. + + @retval TRUE The two value are same. + @retval FALSE The two value are different. + +**/ +BOOLEAN +IsValuesEqual ( + IN EFI_IFR_TYPE_VALUE *Value1, + IN EFI_IFR_TYPE_VALUE *Value2, + IN UINT8 Type + ) +{ + switch (Type) { + case EFI_IFR_TYPE_BOOLEAN: + case EFI_IFR_TYPE_NUM_SIZE_8: + return Value1->u8 == Value2->u8; + + case EFI_IFR_TYPE_NUM_SIZE_16: + return Value1->u16 == Value2->u16; + + case EFI_IFR_TYPE_NUM_SIZE_32: + return Value1->u32 == Value2->u32; + + case EFI_IFR_TYPE_NUM_SIZE_64: + return Value1->u64 == Value2->u64; + + default: + ASSERT (FALSE); + return FALSE; + } +} + +/** + Base on the type to set the value. + + @param Dest The dest value. + @param Source The source value. + @param Type The value type for above two values. + +**/ +VOID +SetValuesByType ( + OUT EFI_IFR_TYPE_VALUE *Dest, + IN EFI_IFR_TYPE_VALUE *Source, + IN UINT8 Type + ) +{ + switch (Type) { + case EFI_IFR_TYPE_BOOLEAN: + Dest->b = Source->b; + break; + + case EFI_IFR_TYPE_NUM_SIZE_8: + Dest->u8 = Source->u8; + break; + + case EFI_IFR_TYPE_NUM_SIZE_16: + Dest->u16 = Source->u16; + break; + + case EFI_IFR_TYPE_NUM_SIZE_32: + Dest->u32 = Source->u32; + break; + + case EFI_IFR_TYPE_NUM_SIZE_64: + Dest->u64 = Source->u64; + break; + + default: + ASSERT (FALSE); + break; + } +} + +/** + Get selection for OneOf and OrderedList (Left/Right will be ignored). + + @param MenuOption Pointer to the current input menu. + + @retval EFI_SUCCESS If Option input is processed successfully + @retval EFI_DEVICE_ERROR If operation fails + +**/ +EFI_STATUS +GetSelectionInputPopUp ( + IN UI_MENU_OPTION *MenuOption + ) +{ + EFI_STATUS Status; + EFI_INPUT_KEY Key; + UINTN Index; + CHAR16 *StringPtr; + CHAR16 *TempStringPtr; + UINTN Index2; + UINTN TopOptionIndex; + UINTN HighlightOptionIndex; + UINTN Start; + UINTN End; + UINTN Top; + UINTN Bottom; + UINTN PopUpMenuLines; + UINTN MenuLinesInView; + UINTN PopUpWidth; + CHAR16 Character; + INT32 SavedAttribute; + BOOLEAN ShowDownArrow; + BOOLEAN ShowUpArrow; + UINTN DimensionsWidth; + LIST_ENTRY *Link; + BOOLEAN OrderedList; + UINT8 *ValueArray; + UINT8 *ReturnValue; + UINT8 ValueType; + EFI_HII_VALUE HiiValue; + DISPLAY_QUESTION_OPTION *OneOfOption; + DISPLAY_QUESTION_OPTION *CurrentOption; + FORM_DISPLAY_ENGINE_STATEMENT *Question; + INTN Result; + EFI_IFR_ORDERED_LIST *OrderList; + + DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn; + + ValueArray = NULL; + ValueType = 0; + CurrentOption = NULL; + ShowDownArrow = FALSE; + ShowUpArrow = FALSE; + + StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2); + ASSERT (StringPtr); + + ZeroMem (&HiiValue, sizeof (EFI_HII_VALUE)); + + Question = MenuOption->ThisTag; + if (Question->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP) { + Link = GetFirstNode (&Question->OptionListHead); + OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + ValueArray = Question->CurrentValue.Buffer; + ValueType = OneOfOption->OptionOpCode->Type; + OrderedList = TRUE; + OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode; + } else { + OrderedList = FALSE; + OrderList = NULL; + } + + // + // Calculate Option count + // + PopUpMenuLines = 0; + if (OrderedList) { + AdjustOptionOrder(Question, &PopUpMenuLines); + } else { + Link = GetFirstNode (&Question->OptionListHead); + while (!IsNull (&Question->OptionListHead, Link)) { + OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + PopUpMenuLines++; + Link = GetNextNode (&Question->OptionListHead, Link); + } + } + + // + // Get the number of one of options present and its size + // + PopUpWidth = 0; + HighlightOptionIndex = 0; + Link = GetFirstNode (&Question->OptionListHead); + for (Index = 0; Index < PopUpMenuLines; Index++) { + OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + + StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle); + if (StrLen (StringPtr) > PopUpWidth) { + PopUpWidth = StrLen (StringPtr); + } + FreePool (StringPtr); + HiiValue.Type = OneOfOption->OptionOpCode->Type; + SetValuesByType (&HiiValue.Value, &OneOfOption->OptionOpCode->Value, HiiValue.Type); + if (!OrderedList && (CompareHiiValue (&Question->CurrentValue, &HiiValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) { + // + // Find current selected Option for OneOf + // + HighlightOptionIndex = Index; + } + + Link = GetNextNode (&Question->OptionListHead, Link); + } + + // + // Perform popup menu initialization. + // + PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT; + + SavedAttribute = gST->ConOut->Mode->Attribute; + gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ()); + + if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) { + PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH; + } + + Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gStatementDimensions.LeftColumn; + End = Start + PopUpWidth + POPUP_FRAME_WIDTH; + Top = gStatementDimensions.TopRow; + Bottom = gStatementDimensions.BottomRow - 1; + + MenuLinesInView = Bottom - Top - 1; + if (MenuLinesInView >= PopUpMenuLines) { + Top = Top + (MenuLinesInView - PopUpMenuLines) / 2; + Bottom = Top + PopUpMenuLines + 1; + } else { + ShowDownArrow = TRUE; + } + + if (HighlightOptionIndex > (MenuLinesInView - 1)) { + TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1; + } else { + TopOptionIndex = 0; + } + + do { + // + // Clear that portion of the screen + // + ClearLines (Start, End, Top, Bottom, GetPopupColor ()); + + // + // Draw "One of" pop-up menu + // + Character = BOXDRAW_DOWN_RIGHT; + PrintCharAt (Start, Top, Character); + for (Index = Start; Index + 2 < End; Index++) { + if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) { + Character = GEOMETRICSHAPE_UP_TRIANGLE; + } else { + Character = BOXDRAW_HORIZONTAL; + } + + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); + } + + Character = BOXDRAW_DOWN_LEFT; + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); + Character = BOXDRAW_VERTICAL; + for (Index = Top + 1; Index < Bottom; Index++) { + PrintCharAt (Start, Index, Character); + PrintCharAt (End - 1, Index, Character); + } + + // + // Move to top Option + // + Link = GetFirstNode (&Question->OptionListHead); + for (Index = 0; Index < TopOptionIndex; Index++) { + Link = GetNextNode (&Question->OptionListHead, Link); + } + + // + // Display the One of options + // + Index2 = Top + 1; + for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) { + OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + Link = GetNextNode (&Question->OptionListHead, Link); + + StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle); + ASSERT (StringPtr != NULL); + // + // If the string occupies multiple lines, truncate it to fit in one line, + // and append a "..." for indication. + // + if (StrLen (StringPtr) > (PopUpWidth - 1)) { + TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1)); + ASSERT ( TempStringPtr != NULL ); + CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5))); + FreePool (StringPtr); + StringPtr = TempStringPtr; + StrCat (StringPtr, L"..."); + } + + if (Index == HighlightOptionIndex) { + // + // Highlight the selected one + // + CurrentOption = OneOfOption; + + gST->ConOut->SetAttribute (gST->ConOut, GetPickListColor ()); + PrintStringAt (Start + 2, Index2, StringPtr); + gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ()); + } else { + gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ()); + PrintStringAt (Start + 2, Index2, StringPtr); + } + + Index2++; + FreePool (StringPtr); + } + + Character = BOXDRAW_UP_RIGHT; + PrintCharAt (Start, Bottom, Character); + for (Index = Start; Index + 2 < End; Index++) { + if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) { + Character = GEOMETRICSHAPE_DOWN_TRIANGLE; + } else { + Character = BOXDRAW_HORIZONTAL; + } + + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); + } + + Character = BOXDRAW_UP_LEFT; + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); + + // + // Get User selection + // + Key.UnicodeChar = CHAR_NULL; + if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) { + Key.ScanCode = gDirection; + gDirection = 0; + goto TheKey; + } + + Status = WaitForKeyStroke (&Key); + +TheKey: + switch (Key.UnicodeChar) { + case '+': + if (OrderedList) { + if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) { + // + // Highlight reaches the top of the popup window, scroll one menu item. + // + TopOptionIndex--; + ShowDownArrow = TRUE; + } + + if (TopOptionIndex == 0) { + ShowUpArrow = FALSE; + } + + if (HighlightOptionIndex > 0) { + HighlightOptionIndex--; + + ASSERT (CurrentOption != NULL); + SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link); + } + } + break; + + case '-': + // + // If an ordered list op-code, we will allow for a popup of +/- keys + // to create an ordered list of items + // + if (OrderedList) { + if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) && + (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) { + // + // Highlight reaches the bottom of the popup window, scroll one menu item. + // + TopOptionIndex++; + ShowUpArrow = TRUE; + } + + if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) { + ShowDownArrow = FALSE; + } + + if (HighlightOptionIndex < (PopUpMenuLines - 1)) { + HighlightOptionIndex++; + + ASSERT (CurrentOption != NULL); + SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink); + } + } + break; + + case CHAR_NULL: + switch (Key.ScanCode) { + case SCAN_UP: + case SCAN_DOWN: + if (Key.ScanCode == SCAN_UP) { + if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) { + // + // Highlight reaches the top of the popup window, scroll one menu item. + // + TopOptionIndex--; + ShowDownArrow = TRUE; + } + + if (TopOptionIndex == 0) { + ShowUpArrow = FALSE; + } + + if (HighlightOptionIndex > 0) { + HighlightOptionIndex--; + } + } else { + if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) && + (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) { + // + // Highlight reaches the bottom of the popup window, scroll one menu item. + // + TopOptionIndex++; + ShowUpArrow = TRUE; + } + + if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) { + ShowDownArrow = FALSE; + } + + if (HighlightOptionIndex < (PopUpMenuLines - 1)) { + HighlightOptionIndex++; + } + } + break; + + case SCAN_ESC: + gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute); + + // + // Restore link list order for orderedlist + // + if (OrderedList) { + HiiValue.Type = ValueType; + HiiValue.Value.u64 = 0; + for (Index = 0; Index < OrderList->MaxContainers; Index++) { + HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index); + if (HiiValue.Value.u64 == 0) { + break; + } + + OneOfOption = ValueToOption (Question, &HiiValue); + if (OneOfOption == NULL) { + return EFI_NOT_FOUND; + } + + RemoveEntryList (&OneOfOption->Link); + InsertTailList (&Question->OptionListHead, &OneOfOption->Link); + } + } + + return EFI_DEVICE_ERROR; + + default: + break; + } + + break; + + case CHAR_CARRIAGE_RETURN: + // + // return the current selection + // + if (OrderedList) { + ReturnValue = AllocateZeroPool (Question->CurrentValue.BufferLen); + ASSERT (ReturnValue != NULL); + Index = 0; + Link = GetFirstNode (&Question->OptionListHead); + while (!IsNull (&Question->OptionListHead, Link)) { + OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + Link = GetNextNode (&Question->OptionListHead, Link); + + SetArrayData (ReturnValue, ValueType, Index, OneOfOption->OptionOpCode->Value.u64); + + Index++; + if (Index > OrderList->MaxContainers) { + break; + } + } + if (CompareMem (ReturnValue, ValueArray, Question->CurrentValue.BufferLen) == 0) { + FreePool (ReturnValue); + return EFI_DEVICE_ERROR; + } else { + gUserInput->InputValue.Buffer = ReturnValue; + gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen; + Status = EFI_SUCCESS; + } + } else { + ASSERT (CurrentOption != NULL); + gUserInput->InputValue.Type = CurrentOption->OptionOpCode->Type; + if (IsValuesEqual (&Question->CurrentValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type)) { + return EFI_DEVICE_ERROR; + } else { + SetValuesByType (&gUserInput->InputValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type); + Status = EFI_SUCCESS; + } + } + + gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute); + + return ValidateQuestion (Question); + + default: + break; + } + } while (TRUE); + +} + diff --git a/MdeModulePkg/Universal/DisplayEngineDxe/Print.c b/MdeModulePkg/Universal/DisplayEngineDxe/Print.c new file mode 100644 index 0000000000..f5a95c4ab1 --- /dev/null +++ b/MdeModulePkg/Universal/DisplayEngineDxe/Print.c @@ -0,0 +1,54 @@ +/** @file +Basic Ascii AvSPrintf() function named VSPrint(). VSPrint() enables very +simple implemenation of SPrint() and Print() to support debug. + +You can not Print more than EFI_DRIVER_LIB_MAX_PRINT_BUFFER characters at a +time. This makes the implementation very simple. + +VSPrint, Print, SPrint format specification has the follwoing form + +%type + +type: + 'S','s' - argument is an Unicode string + 'c' - argument is an ascii character + '%' - Print a % + + +Copyright (c) 2004 - 2012, 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 "FormDisplay.h" + + +/** + Set Buffer to Value for Size bytes. + + @param Buffer Memory to set. + @param Size Number of bytes to set + @param Value Value of the set operation. + +**/ +VOID +SetUnicodeMem ( + IN VOID *Buffer, + IN UINTN Size, + IN CHAR16 Value + ) +{ + CHAR16 *Ptr; + + Ptr = Buffer; + while ((Size--) != 0) { + *(Ptr++) = Value; + } +} + diff --git a/MdeModulePkg/Universal/DisplayEngineDxe/ProcessOptions.c b/MdeModulePkg/Universal/DisplayEngineDxe/ProcessOptions.c new file mode 100644 index 0000000000..aca043a8c2 --- /dev/null +++ b/MdeModulePkg/Universal/DisplayEngineDxe/ProcessOptions.c @@ -0,0 +1,1286 @@ +/** @file +Implementation for handling the User Interface option processing. + + +Copyright (c) 2004 - 2012, 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 "FormDisplay.h" + +/** + Concatenate a narrow string to another string. + + @param Destination The destination string. + @param Source The source string. The string to be concatenated. + to the end of Destination. + +**/ +VOID +NewStrCat ( + IN OUT CHAR16 *Destination, + IN CHAR16 *Source + ) +{ + UINTN Length; + + for (Length = 0; Destination[Length] != 0; Length++) + ; + + // + // We now have the length of the original string + // We can safely assume for now that we are concatenating a narrow value to this string. + // For instance, the string is "XYZ" and cat'ing ">" + // If this assumption changes, we need to make this routine a bit more complex + // + Destination[Length] = NARROW_CHAR; + Length++; + + StrCpy (Destination + Length, Source); +} + +/** + Compare two Hii value. + + @param Value1 Expression value to compare on left-hand. + @param Value2 Expression value to compare on right-hand. + @param Result Return value after compare. + retval 0 Two operators equal. + return Positive value if Value1 is greater than Value2. + retval Negative value if Value1 is less than Value2. + @param HiiHandle Only required for string compare. + + @retval other Could not perform compare on two values. + @retval EFI_SUCCESS Compare the value success. + +**/ +EFI_STATUS +CompareHiiValue ( + IN EFI_HII_VALUE *Value1, + IN EFI_HII_VALUE *Value2, + OUT INTN *Result, + IN EFI_HII_HANDLE HiiHandle OPTIONAL + ) +{ + INT64 Temp64; + CHAR16 *Str1; + CHAR16 *Str2; + UINTN Len; + + if (Value1->Type >= EFI_IFR_TYPE_OTHER || Value2->Type >= EFI_IFR_TYPE_OTHER ) { + if (Value1->Type != EFI_IFR_TYPE_BUFFER && Value2->Type != EFI_IFR_TYPE_BUFFER) { + return EFI_UNSUPPORTED; + } + } + + if (Value1->Type == EFI_IFR_TYPE_STRING || Value2->Type == EFI_IFR_TYPE_STRING ) { + if (Value1->Type != Value2->Type) { + // + // Both Operator should be type of String + // + return EFI_UNSUPPORTED; + } + + if (Value1->Value.string == 0 || Value2->Value.string == 0) { + // + // StringId 0 is reserved + // + return EFI_INVALID_PARAMETER; + } + + if (Value1->Value.string == Value2->Value.string) { + *Result = 0; + return EFI_SUCCESS; + } + + Str1 = GetToken (Value1->Value.string, HiiHandle); + if (Str1 == NULL) { + // + // String not found + // + return EFI_NOT_FOUND; + } + + Str2 = GetToken (Value2->Value.string, HiiHandle); + if (Str2 == NULL) { + FreePool (Str1); + return EFI_NOT_FOUND; + } + + *Result = StrCmp (Str1, Str2); + + FreePool (Str1); + FreePool (Str2); + + return EFI_SUCCESS; + } + + if (Value1->Type == EFI_IFR_TYPE_BUFFER || Value2->Type == EFI_IFR_TYPE_BUFFER ) { + if (Value1->Type != Value2->Type) { + // + // Both Operator should be type of Buffer. + // + return EFI_UNSUPPORTED; + } + Len = Value1->BufferLen > Value2->BufferLen ? Value2->BufferLen : Value1->BufferLen; + *Result = CompareMem (Value1->Buffer, Value2->Buffer, Len); + if ((*Result == 0) && (Value1->BufferLen != Value2->BufferLen)) + { + // + // In this case, means base on samll number buffer, the data is same + // So which value has more data, which value is bigger. + // + *Result = Value1->BufferLen > Value2->BufferLen ? 1 : -1; + } + return EFI_SUCCESS; + } + + // + // Take remain types(integer, boolean, date/time) as integer + // + Temp64 = (INT64) (Value1->Value.u64 - Value2->Value.u64); + if (Temp64 > 0) { + *Result = 1; + } else if (Temp64 < 0) { + *Result = -1; + } else { + *Result = 0; + } + + return EFI_SUCCESS; +} + +/** + Search an Option of a Question by its value. + + @param Question The Question + @param OptionValue Value for Option to be searched. + + @retval Pointer Pointer to the found Option. + @retval NULL Option not found. + +**/ +DISPLAY_QUESTION_OPTION * +ValueToOption ( + IN FORM_DISPLAY_ENGINE_STATEMENT *Question, + IN EFI_HII_VALUE *OptionValue + ) +{ + LIST_ENTRY *Link; + DISPLAY_QUESTION_OPTION *Option; + INTN Result; + EFI_HII_VALUE Value; + + Link = GetFirstNode (&Question->OptionListHead); + while (!IsNull (&Question->OptionListHead, Link)) { + Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + + ZeroMem (&Value, sizeof (EFI_HII_VALUE)); + Value.Type = Option->OptionOpCode->Type; + CopyMem (&Value.Value, &Option->OptionOpCode->Value, Option->OptionOpCode->Header.Length - OFFSET_OF (EFI_IFR_ONE_OF_OPTION, Value)); + + if ((CompareHiiValue (&Value, OptionValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) { + return Option; + } + + Link = GetNextNode (&Question->OptionListHead, Link); + } + + return NULL; +} + + +/** + Return data element in an Array by its Index. + + @param Array The data array. + @param Type Type of the data in this array. + @param Index Zero based index for data in this array. + + @retval Value The data to be returned + +**/ +UINT64 +GetArrayData ( + IN VOID *Array, + IN UINT8 Type, + IN UINTN Index + ) +{ + UINT64 Data; + + ASSERT (Array != NULL); + + Data = 0; + switch (Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + Data = (UINT64) *(((UINT8 *) Array) + Index); + break; + + case EFI_IFR_TYPE_NUM_SIZE_16: + Data = (UINT64) *(((UINT16 *) Array) + Index); + break; + + case EFI_IFR_TYPE_NUM_SIZE_32: + Data = (UINT64) *(((UINT32 *) Array) + Index); + break; + + case EFI_IFR_TYPE_NUM_SIZE_64: + Data = (UINT64) *(((UINT64 *) Array) + Index); + break; + + default: + break; + } + + return Data; +} + + +/** + Set value of a data element in an Array by its Index. + + @param Array The data array. + @param Type Type of the data in this array. + @param Index Zero based index for data in this array. + @param Value The value to be set. + +**/ +VOID +SetArrayData ( + IN VOID *Array, + IN UINT8 Type, + IN UINTN Index, + IN UINT64 Value + ) +{ + + ASSERT (Array != NULL); + + switch (Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + *(((UINT8 *) Array) + Index) = (UINT8) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_16: + *(((UINT16 *) Array) + Index) = (UINT16) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_32: + *(((UINT32 *) Array) + Index) = (UINT32) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_64: + *(((UINT64 *) Array) + Index) = (UINT64) Value; + break; + + default: + break; + } +} + +/** + Check whether this value already in the array, if yes, return the index. + + @param Array The data array. + @param Type Type of the data in this array. + @param Value The value to be find. + @param Index The index in the array which has same value with Value. + + @retval TRUE Found the value in the array. + @retval FALSE Not found the value. + +**/ +BOOLEAN +FindArrayData ( + IN VOID *Array, + IN UINT8 Type, + IN UINT64 Value, + OUT UINTN *Index OPTIONAL + ) +{ + UINTN Count; + UINT64 TmpValue; + UINT64 ValueComp; + + ASSERT (Array != NULL); + + Count = 0; + TmpValue = 0; + + switch (Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + ValueComp = (UINT8) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_16: + ValueComp = (UINT16) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_32: + ValueComp = (UINT32) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_64: + ValueComp = (UINT64) Value; + break; + + default: + ValueComp = 0; + break; + } + + while ((TmpValue = GetArrayData (Array, Type, Count)) != 0) { + if (ValueComp == TmpValue) { + if (Index != NULL) { + *Index = Count; + } + return TRUE; + } + + Count ++; + } + + return FALSE; +} + +/** + Print Question Value according to it's storage width and display attributes. + + @param Question The Question to be printed. + @param FormattedNumber Buffer for output string. + @param BufferSize The FormattedNumber buffer size in bytes. + + @retval EFI_SUCCESS Print success. + @retval EFI_BUFFER_TOO_SMALL Buffer size is not enough for formatted number. + +**/ +EFI_STATUS +PrintFormattedNumber ( + IN FORM_DISPLAY_ENGINE_STATEMENT *Question, + IN OUT CHAR16 *FormattedNumber, + IN UINTN BufferSize + ) +{ + INT64 Value; + CHAR16 *Format; + EFI_HII_VALUE *QuestionValue; + EFI_IFR_NUMERIC *NumericOp; + + if (BufferSize < (21 * sizeof (CHAR16))) { + return EFI_BUFFER_TOO_SMALL; + } + + QuestionValue = &Question->CurrentValue; + NumericOp = (EFI_IFR_NUMERIC *) Question->OpCode; + + Value = (INT64) QuestionValue->Value.u64; + switch (NumericOp->Flags & EFI_IFR_DISPLAY) { + case EFI_IFR_DISPLAY_INT_DEC: + switch (QuestionValue->Type) { + case EFI_IFR_NUMERIC_SIZE_1: + Value = (INT64) ((INT8) QuestionValue->Value.u8); + break; + + case EFI_IFR_NUMERIC_SIZE_2: + Value = (INT64) ((INT16) QuestionValue->Value.u16); + break; + + case EFI_IFR_NUMERIC_SIZE_4: + Value = (INT64) ((INT32) QuestionValue->Value.u32); + break; + + case EFI_IFR_NUMERIC_SIZE_8: + default: + break; + } + + if (Value < 0) { + Value = -Value; + Format = L"-%ld"; + } else { + Format = L"%ld"; + } + break; + + case EFI_IFR_DISPLAY_UINT_DEC: + Format = L"%ld"; + break; + + case EFI_IFR_DISPLAY_UINT_HEX: + Format = L"%lx"; + break; + + default: + return EFI_UNSUPPORTED; + break; + } + + UnicodeSPrint (FormattedNumber, BufferSize, Format, Value); + + return EFI_SUCCESS; +} + + +/** + Draw a pop up windows based on the dimension, number of lines and + strings specified. + + @param RequestedWidth The width of the pop-up. + @param NumberOfLines The number of lines. + @param Marker The variable argument list for the list of string to be printed. + +**/ +VOID +CreateSharedPopUp ( + IN UINTN RequestedWidth, + IN UINTN NumberOfLines, + IN VA_LIST Marker + ) +{ + UINTN Index; + UINTN Count; + CHAR16 Character; + UINTN Start; + UINTN End; + UINTN Top; + UINTN Bottom; + CHAR16 *String; + UINTN DimensionsWidth; + UINTN DimensionsHeight; + + DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn; + DimensionsHeight = gStatementDimensions.BottomRow - gStatementDimensions.TopRow; + + gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ()); + + 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 + gStatementDimensions.LeftColumn + 1; + End = Start + RequestedWidth + 1; + + Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gStatementDimensions.TopRow - 1; + Bottom = Top + NumberOfLines + 2; + + Character = BOXDRAW_DOWN_RIGHT; + PrintCharAt (Start, Top, Character); + Character = BOXDRAW_HORIZONTAL; + for (Index = Start; Index + 2 < End; Index++) { + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); + } + + Character = BOXDRAW_DOWN_LEFT; + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); + Character = BOXDRAW_VERTICAL; + + Count = 0; + for (Index = Top; Index + 2 < Bottom; Index++, Count++) { + String = VA_ARG (Marker, CHAR16*); + + // + // 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, GetPopupColor ()); + } + + // + // 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, GetPopupInverseColor ()); + } + + // + // Passing in a NULL results in a blank space + // + if (String[0] == CHAR_NULL) { + ClearLines (Start, End, Index + 1, Index + 1, GetPopupColor ()); + } + + PrintStringAt ( + ((DimensionsWidth - GetStringWidth (String) / 2) / 2) + gStatementDimensions.LeftColumn + 1, + Index + 1, + String + ); + gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ()); + PrintCharAt (Start, Index + 1, Character); + PrintCharAt (End - 1, Index + 1, Character); + } + + Character = BOXDRAW_UP_RIGHT; + PrintCharAt (Start, Bottom - 1, Character); + Character = BOXDRAW_HORIZONTAL; + for (Index = Start; Index + 2 < End; Index++) { + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); + } + + Character = BOXDRAW_UP_LEFT; + PrintCharAt ((UINTN)-1, (UINTN)-1, Character); +} + +/** + Draw a pop up windows based on the dimension, number of lines and + strings specified. + + @param RequestedWidth The width of the pop-up. + @param NumberOfLines The number of lines. + @param ... A series of text strings that displayed in the pop-up. + +**/ +VOID +EFIAPI +CreateMultiStringPopUp ( + IN UINTN RequestedWidth, + IN UINTN NumberOfLines, + ... + ) +{ + VA_LIST Marker; + + VA_START (Marker, NumberOfLines); + + CreateSharedPopUp (RequestedWidth, NumberOfLines, Marker); + + VA_END (Marker); +} + +/** + Process validate for one question. + + @param Question The question need to be validate. + + @retval EFI_SUCCESS Question Option process success. + @retval EFI_INVALID_PARAMETER Question Option process fail. + +**/ +EFI_STATUS +ValidateQuestion ( + IN FORM_DISPLAY_ENGINE_STATEMENT *Question + ) +{ + CHAR16 *ErrorInfo; + EFI_INPUT_KEY Key; + EFI_STATUS Status; + STATEMENT_ERROR_INFO RetInfo; + UINT32 RetVal; + + if (Question->ValidateQuestion == NULL) { + return EFI_SUCCESS; + } + + Status = EFI_SUCCESS; + RetVal = Question->ValidateQuestion(gFormData, Question, &gUserInput->InputValue, &RetInfo); + + switch (RetVal) { + case INCOSISTENT_IF_TRUE: + // + // Condition meet, show up error message + // + ASSERT (RetInfo.StringId != 0); + ErrorInfo = GetToken (RetInfo.StringId, gFormData->HiiHandle); + do { + CreateDialog (&Key, gEmptyString, ErrorInfo, gPressEnter, gEmptyString, NULL); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + FreePool (ErrorInfo); + + Status = EFI_INVALID_PARAMETER; + break; + + default: + break; + } + + return Status; +} + +/** + Display error message for invalid password. + +**/ +VOID +PasswordInvalid ( + VOID + ) +{ + EFI_INPUT_KEY Key; + + // + // Invalid password, prompt error message + // + do { + CreateDialog (&Key, gEmptyString, gPassowordInvalid, gPressEnter, gEmptyString, NULL); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); +} + +/** + Process password op code. + + @param MenuOption The menu for current password op code. + + @retval EFI_SUCCESS Question Option process success. + @retval Other Question Option process fail. + +**/ +EFI_STATUS +PasswordProcess ( + IN UI_MENU_OPTION *MenuOption + ) +{ + CHAR16 *StringPtr; + CHAR16 *TempString; + UINTN Maximum; + EFI_STATUS Status; + EFI_IFR_PASSWORD *PasswordInfo; + FORM_DISPLAY_ENGINE_STATEMENT *Question; + EFI_INPUT_KEY Key; + + Question = MenuOption->ThisTag; + PasswordInfo = (EFI_IFR_PASSWORD *) Question->OpCode; + Maximum = PasswordInfo->MaxSize; + Status = EFI_SUCCESS; + + StringPtr = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16)); + ASSERT (StringPtr); + + // + // Use a NULL password to test whether old password is required + // + *StringPtr = 0; + Status = Question->PasswordCheck (gFormData, Question, StringPtr); + if (Status == EFI_NOT_AVAILABLE_YET || Status == EFI_UNSUPPORTED) { + // + // Password can't be set now. + // + FreePool (StringPtr); + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + // + // Old password exist, ask user for the old password + // + Status = ReadString (MenuOption, gPromptForPassword, StringPtr); + if (EFI_ERROR (Status)) { + FreePool (StringPtr); + return Status; + } + + // + // Check user input old password + // + Status = Question->PasswordCheck (gFormData, Question, StringPtr); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_READY) { + // + // Typed in old password incorrect + // + PasswordInvalid (); + } else { + Status = EFI_SUCCESS; + } + + FreePool (StringPtr); + return Status; + } + } + + // + // Ask for new password + // + ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16)); + Status = ReadString (MenuOption, gPromptForNewPassword, StringPtr); + if (EFI_ERROR (Status)) { + // + // Reset state machine for password + // + Question->PasswordCheck (gFormData, Question, NULL); + FreePool (StringPtr); + return Status; + } + + // + // Confirm new password + // + TempString = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16)); + ASSERT (TempString); + Status = ReadString (MenuOption, gConfirmPassword, TempString); + if (EFI_ERROR (Status)) { + // + // Reset state machine for password + // + Question->PasswordCheck (gFormData, Question, NULL); + FreePool (StringPtr); + FreePool (TempString); + return Status; + } + + // + // Compare two typed-in new passwords + // + if (StrCmp (StringPtr, TempString) == 0) { + gUserInput->InputValue.Buffer = AllocateCopyPool (Question->CurrentValue.BufferLen, StringPtr); + gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen; + gUserInput->InputValue.Type = Question->CurrentValue.Type; + gUserInput->InputValue.Value.string = HiiSetString(gFormData->HiiHandle, gUserInput->InputValue.Value.string, StringPtr, NULL); + FreePool (StringPtr); + + Status = ValidateQuestion (Question); + + if (EFI_ERROR (Status)) { + // + // Reset state machine for password + // + Question->PasswordCheck (gFormData, Question, NULL); + } + + return Status; + } else { + // + // Reset state machine for password + // + Question->PasswordCheck (gFormData, Question, NULL); + + // + // Two password mismatch, prompt error message + // + do { + CreateDialog (&Key, gEmptyString, gConfirmError, gPressEnter, gEmptyString, NULL); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + Status = EFI_INVALID_PARAMETER; + } + + FreePool (TempString); + FreePool (StringPtr); + + return Status; +} + +/** + Process a Question's Option (whether selected or un-selected). + + @param MenuOption The MenuOption for this Question. + @param Selected TRUE: if Question is selected. + @param OptionString Pointer of the Option String to be displayed. + @param SkipErrorValue Whether need to return when value without option for it. + + @retval EFI_SUCCESS Question Option process success. + @retval Other Question Option process fail. + +**/ +EFI_STATUS +ProcessOptions ( + IN UI_MENU_OPTION *MenuOption, + IN BOOLEAN Selected, + OUT CHAR16 **OptionString, + IN BOOLEAN SkipErrorValue + ) +{ + EFI_STATUS Status; + CHAR16 *StringPtr; + UINTN Index; + FORM_DISPLAY_ENGINE_STATEMENT *Question; + CHAR16 FormattedNumber[21]; + UINT16 Number; + CHAR16 Character[2]; + EFI_INPUT_KEY Key; + UINTN BufferSize; + DISPLAY_QUESTION_OPTION *OneOfOption; + LIST_ENTRY *Link; + EFI_HII_VALUE HiiValue; + EFI_HII_VALUE *QuestionValue; + DISPLAY_QUESTION_OPTION *Option; + UINTN Index2; + UINT8 *ValueArray; + UINT8 ValueType; + EFI_STRING_ID StringId; + EFI_IFR_ORDERED_LIST *OrderList; + BOOLEAN ValueInvalid; + + Status = EFI_SUCCESS; + + StringPtr = NULL; + Character[1] = L'\0'; + *OptionString = NULL; + StringId = 0; + ValueInvalid = FALSE; + + ZeroMem (FormattedNumber, 21 * sizeof (CHAR16)); + BufferSize = (gOptionBlockWidth + 1) * 2 * gStatementDimensions.BottomRow; + + Question = MenuOption->ThisTag; + QuestionValue = &Question->CurrentValue; + + switch (Question->OpCode->OpCode) { + case EFI_IFR_ORDERED_LIST_OP: + + // + // Check whether there are Options of this OrderedList + // + if (IsListEmpty (&Question->OptionListHead)) { + break; + } + + OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode; + + Link = GetFirstNode (&Question->OptionListHead); + OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + + ValueType = OneOfOption->OptionOpCode->Type; + ValueArray = Question->CurrentValue.Buffer; + + if (Selected) { + // + // Go ask for input + // + Status = GetSelectionInputPopUp (MenuOption); + } else { + // + // We now know how many strings we will have, so we can allocate the + // space required for the array or strings. + // + *OptionString = AllocateZeroPool (OrderList->MaxContainers * BufferSize); + ASSERT (*OptionString); + + HiiValue.Type = ValueType; + HiiValue.Value.u64 = 0; + for (Index = 0; Index < OrderList->MaxContainers; Index++) { + HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index); + if (HiiValue.Value.u64 == 0) { + // + // Values for the options in ordered lists should never be a 0 + // + break; + } + + OneOfOption = ValueToOption (Question, &HiiValue); + if (OneOfOption == NULL) { + if (SkipErrorValue) { + // + // Just try to get the option string, skip the value which not has option. + // + continue; + } + + // + // Show error message + // + do { + CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + // + // The initial value of the orderedlist is invalid, force to be valid value + // Exit current DisplayForm with new value. + // + gUserInput->SelectedStatement = Question; + + ValueArray = AllocateZeroPool (Question->CurrentValue.BufferLen); + ASSERT (ValueArray != NULL); + gUserInput->InputValue.Buffer = ValueArray; + gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen; + gUserInput->InputValue.Type = Question->CurrentValue.Type; + + Link = GetFirstNode (&Question->OptionListHead); + Index2 = 0; + while (!IsNull (&Question->OptionListHead, Link) && Index2 < OrderList->MaxContainers) { + Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + Link = GetNextNode (&Question->OptionListHead, Link); + SetArrayData (ValueArray, ValueType, Index2, Option->OptionOpCode->Value.u64); + Index2++; + } + SetArrayData (ValueArray, ValueType, Index2, 0); + + FreePool (*OptionString); + *OptionString = NULL; + return EFI_NOT_FOUND; + } + + Character[0] = LEFT_ONEOF_DELIMITER; + NewStrCat (OptionString[0], Character); + StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle); + ASSERT (StringPtr != NULL); + NewStrCat (OptionString[0], StringPtr); + Character[0] = RIGHT_ONEOF_DELIMITER; + NewStrCat (OptionString[0], Character); + Character[0] = CHAR_CARRIAGE_RETURN; + NewStrCat (OptionString[0], Character); + FreePool (StringPtr); + } + + // + // If valid option more than the max container, skip these options. + // + if (Index >= OrderList->MaxContainers) { + break; + } + + // + // Search the other options, try to find the one not in the container. + // + Link = GetFirstNode (&Question->OptionListHead); + while (!IsNull (&Question->OptionListHead, Link)) { + OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + Link = GetNextNode (&Question->OptionListHead, Link); + + if (FindArrayData (ValueArray, ValueType, OneOfOption->OptionOpCode->Value.u64, NULL)) { + continue; + } + + if (SkipErrorValue) { + // + // Not report error, just get the correct option string info. + // + Character[0] = LEFT_ONEOF_DELIMITER; + NewStrCat (OptionString[0], Character); + StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle); + ASSERT (StringPtr != NULL); + NewStrCat (OptionString[0], StringPtr); + Character[0] = RIGHT_ONEOF_DELIMITER; + NewStrCat (OptionString[0], Character); + Character[0] = CHAR_CARRIAGE_RETURN; + NewStrCat (OptionString[0], Character); + FreePool (StringPtr); + + continue; + } + + if (!ValueInvalid) { + ValueInvalid = TRUE; + // + // Show error message + // + do { + CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + // + // The initial value of the orderedlist is invalid, force to be valid value + // Exit current DisplayForm with new value. + // + gUserInput->SelectedStatement = Question; + + ValueArray = AllocateCopyPool (Question->CurrentValue.BufferLen, Question->CurrentValue.Buffer); + ASSERT (ValueArray != NULL); + gUserInput->InputValue.Buffer = ValueArray; + gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen; + gUserInput->InputValue.Type = Question->CurrentValue.Type; + } + + SetArrayData (ValueArray, ValueType, Index++, OneOfOption->OptionOpCode->Value.u64); + } + + if (ValueInvalid) { + FreePool (*OptionString); + *OptionString = NULL; + return EFI_NOT_FOUND; + } + } + break; + + case EFI_IFR_ONE_OF_OP: + // + // Check whether there are Options of this OneOf + // + if (IsListEmpty (&Question->OptionListHead)) { + break; + } + if (Selected) { + // + // Go ask for input + // + Status = GetSelectionInputPopUp (MenuOption); + } else { + *OptionString = AllocateZeroPool (BufferSize); + ASSERT (*OptionString); + + OneOfOption = ValueToOption (Question, QuestionValue); + if (OneOfOption == NULL) { + if (SkipErrorValue) { + // + // Not report error, just get the correct option string info. + // + Link = GetFirstNode (&Question->OptionListHead); + OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + } else { + // + // Show error message + // + do { + CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + // + // Force the Question value to be valid + // Exit current DisplayForm with new value. + // + Link = GetFirstNode (&Question->OptionListHead); + Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); + + CopyMem (&gUserInput->InputValue.Value, &Option->OptionOpCode->Value, sizeof (EFI_IFR_TYPE_VALUE)); + gUserInput->InputValue.Type = Option->OptionOpCode->Type; + gUserInput->SelectedStatement = Question; + + FreePool (*OptionString); + *OptionString = NULL; + return EFI_NOT_FOUND; + } + } + + Character[0] = LEFT_ONEOF_DELIMITER; + NewStrCat (OptionString[0], Character); + StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle); + ASSERT (StringPtr != NULL); + NewStrCat (OptionString[0], StringPtr); + Character[0] = RIGHT_ONEOF_DELIMITER; + NewStrCat (OptionString[0], Character); + + FreePool (StringPtr); + } + break; + + case EFI_IFR_CHECKBOX_OP: + if (Selected) { + // + // Since this is a BOOLEAN operation, flip it upon selection + // + gUserInput->InputValue.Type = QuestionValue->Type; + gUserInput->InputValue.Value.b = (BOOLEAN) (QuestionValue->Value.b ? FALSE : TRUE); + + // + // Perform inconsistent check + // + return ValidateQuestion (Question); + } else { + *OptionString = AllocateZeroPool (BufferSize); + ASSERT (*OptionString); + + *OptionString[0] = LEFT_CHECKBOX_DELIMITER; + + if (QuestionValue->Value.b) { + *(OptionString[0] + 1) = CHECK_ON; + } else { + *(OptionString[0] + 1) = CHECK_OFF; + } + *(OptionString[0] + 2) = RIGHT_CHECKBOX_DELIMITER; + } + break; + + case EFI_IFR_NUMERIC_OP: + if (Selected) { + // + // Go ask for input + // + Status = GetNumericInput (MenuOption); + } else { + *OptionString = AllocateZeroPool (BufferSize); + ASSERT (*OptionString); + + *OptionString[0] = LEFT_NUMERIC_DELIMITER; + + // + // Formatted print + // + PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16)); + Number = (UINT16) GetStringWidth (FormattedNumber); + CopyMem (OptionString[0] + 1, FormattedNumber, Number); + + *(OptionString[0] + Number / 2) = RIGHT_NUMERIC_DELIMITER; + } + break; + + case EFI_IFR_DATE_OP: + if (Selected) { + // + // This is similar to numerics + // + Status = GetNumericInput (MenuOption); + } else { + *OptionString = AllocateZeroPool (BufferSize); + ASSERT (*OptionString); + + switch (MenuOption->Sequence) { + case 0: + *OptionString[0] = LEFT_NUMERIC_DELIMITER; + UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Month); + *(OptionString[0] + 3) = DATE_SEPARATOR; + break; + + case 1: + SetUnicodeMem (OptionString[0], 4, L' '); + UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Day); + *(OptionString[0] + 6) = DATE_SEPARATOR; + break; + + case 2: + SetUnicodeMem (OptionString[0], 7, L' '); + UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%04d", QuestionValue->Value.date.Year); + *(OptionString[0] + 11) = RIGHT_NUMERIC_DELIMITER; + break; + } + } + break; + + case EFI_IFR_TIME_OP: + if (Selected) { + // + // This is similar to numerics + // + Status = GetNumericInput (MenuOption); + } else { + *OptionString = AllocateZeroPool (BufferSize); + ASSERT (*OptionString); + + switch (MenuOption->Sequence) { + case 0: + *OptionString[0] = LEFT_NUMERIC_DELIMITER; + UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Hour); + *(OptionString[0] + 3) = TIME_SEPARATOR; + break; + + case 1: + SetUnicodeMem (OptionString[0], 4, L' '); + UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Minute); + *(OptionString[0] + 6) = TIME_SEPARATOR; + break; + + case 2: + SetUnicodeMem (OptionString[0], 7, L' '); + UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Second); + *(OptionString[0] + 9) = RIGHT_NUMERIC_DELIMITER; + break; + } + } + break; + + case EFI_IFR_STRING_OP: + if (Selected) { + StringPtr = AllocateZeroPool (Question->CurrentValue.BufferLen + sizeof (CHAR16)); + ASSERT (StringPtr); + CopyMem(StringPtr, Question->CurrentValue.Buffer, Question->CurrentValue.BufferLen); + + Status = ReadString (MenuOption, gPromptForData, StringPtr); + if (EFI_ERROR (Status)) { + FreePool (StringPtr); + return Status; + } + + gUserInput->InputValue.Buffer = AllocateCopyPool (Question->CurrentValue.BufferLen, StringPtr); + gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen; + gUserInput->InputValue.Type = Question->CurrentValue.Type; + gUserInput->InputValue.Value.string = HiiSetString(gFormData->HiiHandle, gUserInput->InputValue.Value.string, StringPtr, NULL); + FreePool (StringPtr); + return ValidateQuestion (Question); + } else { + *OptionString = AllocateZeroPool (BufferSize); + ASSERT (*OptionString); + + if (((CHAR16 *) Question->CurrentValue.Buffer)[0] == 0x0000) { + *(OptionString[0]) = '_'; + } else { + if (Question->CurrentValue.BufferLen < BufferSize) { + BufferSize = Question->CurrentValue.BufferLen; + } + CopyMem (OptionString[0], (CHAR16 *) Question->CurrentValue.Buffer, BufferSize); + } + } + break; + + case EFI_IFR_PASSWORD_OP: + if (Selected) { + Status = PasswordProcess (MenuOption); + } + break; + + default: + break; + } + + return Status; +} + + +/** + Process the help string: Split StringPtr to several lines of strings stored in + FormattedString and the glyph width of each line cannot exceed gHelpBlockWidth. + + @param StringPtr The entire help string. + @param FormattedString The oupput formatted string. + @param EachLineWidth The max string length of each line in the formatted string. + @param RowCount TRUE: if Question is selected. + +**/ +UINTN +ProcessHelpString ( + IN CHAR16 *StringPtr, + OUT CHAR16 **FormattedString, + OUT UINT16 *EachLineWidth, + IN UINTN RowCount + ) +{ + UINTN Index; + CHAR16 *OutputString; + UINTN TotalRowNum; + UINTN CheckedNum; + UINT16 GlyphWidth; + UINT16 LineWidth; + UINT16 MaxStringLen; + UINT16 StringLen; + + TotalRowNum = 0; + CheckedNum = 0; + GlyphWidth = 1; + Index = 0; + MaxStringLen = 0; + StringLen = 0; + + // + // Set default help string width. + // + LineWidth = (UINT16) (gHelpBlockWidth - 1); + + // + // Get row number of the String. + // + while ((StringLen = GetLineByWidth (StringPtr, LineWidth, &GlyphWidth, &Index, &OutputString)) != 0) { + if (StringLen > MaxStringLen) { + MaxStringLen = StringLen; + } + + TotalRowNum ++; + FreePool (OutputString); + } + *EachLineWidth = MaxStringLen; + + *FormattedString = AllocateZeroPool (TotalRowNum * MaxStringLen * sizeof (CHAR16)); + ASSERT (*FormattedString != NULL); + + // + // Generate formatted help string array. + // + GlyphWidth = 1; + Index = 0; + while((StringLen = GetLineByWidth (StringPtr, LineWidth, &GlyphWidth, &Index, &OutputString)) != 0) { + CopyMem (*FormattedString + CheckedNum * MaxStringLen, OutputString, StringLen * sizeof (CHAR16)); + CheckedNum ++; + FreePool (OutputString); + } + + return TotalRowNum; +} diff --git a/MdeModulePkg/Universal/SetupBrowserDxe/Expression.c b/MdeModulePkg/Universal/SetupBrowserDxe/Expression.c index cd29e29438..44dae1ba03 100644 --- a/MdeModulePkg/Universal/SetupBrowserDxe/Expression.c +++ b/MdeModulePkg/Universal/SetupBrowserDxe/Expression.c @@ -787,7 +787,7 @@ FORM_BROWSER_FORM * IdToForm ( IN FORM_BROWSER_FORMSET *FormSet, IN UINT16 FormId -) + ) { LIST_ENTRY *Link; FORM_BROWSER_FORM *Form; @@ -2105,7 +2105,7 @@ GetQuestionValueFromForm ( // FormSet = AllocateZeroPool (sizeof (FORM_BROWSER_FORMSET)); ASSERT (FormSet != NULL); - Status = InitializeFormSet(HiiHandle, FormSetGuid, FormSet, FALSE); + Status = InitializeFormSet(HiiHandle, FormSetGuid, FormSet); if (EFI_ERROR (Status)) { GetTheVal = FALSE; goto Done; @@ -2800,7 +2800,7 @@ EvaluateExpression ( for (Index = 0; Index < OpCode->ValueWidth; Index ++, TempBuffer --) { StrPtr += UnicodeValueToString (StrPtr, PREFIX_ZERO | RADIX_HEX, *TempBuffer, 2); } - Status = SetValueByName (OpCode->VarStorage, OpCode->ValueName, NameValue, GetSetValueWithEditBuffer); + Status = SetValueByName (OpCode->VarStorage, OpCode->ValueName, NameValue, GetSetValueWithEditBuffer, NULL); FreePool (NameValue); if (!EFI_ERROR (Status)) { Data1.Value.b = TRUE; diff --git a/MdeModulePkg/Universal/SetupBrowserDxe/Expression.h b/MdeModulePkg/Universal/SetupBrowserDxe/Expression.h new file mode 100644 index 0000000000..5660a997b8 --- /dev/null +++ b/MdeModulePkg/Universal/SetupBrowserDxe/Expression.h @@ -0,0 +1,265 @@ +/** @file +Private structure, MACRO and function definitions for User Interface related functionalities. + +Copyright (c) 2004 - 2013, 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. + +**/ + +#ifndef _EXPRESSION_H_ +#define _EXPRESSION_H_ + +/** + Get the expression list count. + + @param Level Which type this expression belong to. Form, + statement or option? + + @retval >=0 The expression count + @retval -1 Input parameter error. + +**/ +INTN +GetConditionalExpressionCount ( + IN EXPRESS_LEVEL Level + ); + +/** + Reset stack pointer to begin of the stack. + +**/ +VOID +ResetCurrentExpressionStack ( + VOID + ); + +/** + Reset stack pointer to begin of the stack. + +**/ +VOID +ResetMapExpressionListStack ( + VOID + ); + +/** + Reset stack pointer to begin of the stack. + +**/ +VOID +ResetScopeStack ( + VOID + ); + +/** + Push an Operand onto the Stack + + @param Operand Operand to push. + + @retval EFI_SUCCESS The value was pushed onto the stack. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the + stack. + +**/ +EFI_STATUS +PushScope ( + IN UINT8 Operand + ); + +/** + Get the expression Buffer pointer. + + @param Level Which type this expression belong to. Form, + statement or option? + + @retval The start pointer of the expression buffer or NULL. + +**/ +FORM_EXPRESSION ** +GetConditionalExpressionList ( + IN EXPRESS_LEVEL Level + ); + +/** + Pop an Operand from the Stack + + @param Operand Operand to pop. + + @retval EFI_SUCCESS The value was pushed onto the stack. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the + stack. + +**/ +EFI_STATUS +PopScope ( + OUT UINT8 *Operand + ); + +/** + Push the list of map expression onto the Stack + + @param Pointer Pointer to the list of map expression to be pushed. + + @retval EFI_SUCCESS The value was pushed onto the stack. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack. + +**/ +EFI_STATUS +PushMapExpressionList ( + IN VOID *Pointer + ); + +/** + Push current expression onto the Stack + + @param Pointer Pointer to current expression. + + @retval EFI_SUCCESS The value was pushed onto the stack. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack. + +**/ +EFI_STATUS +PushCurrentExpression ( + IN VOID *Pointer + ); + +/** + Zero extend integer/boolean/date/time to UINT64 for comparing. + + @param Value HII Value to be converted. + +**/ +VOID +ExtendValueToU64 ( + IN EFI_HII_VALUE *Value + ); + +/** + Push the expression options onto the Stack. + + @param Pointer Pointer to the current expression. + @param Level Which type this expression belong to. Form, + statement or option? + + @retval EFI_SUCCESS The value was pushed onto the stack. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack. + +**/ +EFI_STATUS +PushConditionalExpression ( + IN FORM_EXPRESSION *Pointer, + IN EXPRESS_LEVEL Level + ); + +/** + Pop the expression options from the Stack + + @param Level Which type this expression belong to. Form, + statement or option? + + @retval EFI_SUCCESS The value was pushed onto the stack. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack. + +**/ +EFI_STATUS +PopConditionalExpression ( + IN EXPRESS_LEVEL Level + ); + +/** + Pop the list of map expression from the Stack + + @param Pointer Pointer to the list of map expression to be pop. + + @retval EFI_SUCCESS The value was pushed onto the stack. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack. + +**/ +EFI_STATUS +PopMapExpressionList ( + OUT VOID **Pointer + ); + +/** + Pop current expression from the Stack + + @param Pointer Pointer to current expression to be pop. + + @retval EFI_SUCCESS The value was pushed onto the stack. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack. + +**/ +EFI_STATUS +PopCurrentExpression ( + OUT VOID **Pointer + ); + +/** + Evaluate the result of a HII expression. + + If Expression is NULL, then ASSERT. + + @param FormSet FormSet associated with this expression. + @param Form Form associated with this expression. + @param Expression Expression to be evaluated. + + @retval EFI_SUCCESS The expression evaluated successfuly + @retval EFI_NOT_FOUND The Question which referenced by a QuestionId + could not be found. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the + stack. + @retval EFI_ACCESS_DENIED The pop operation underflowed the stack + @retval EFI_INVALID_PARAMETER Syntax error with the Expression + +**/ +EFI_STATUS +EvaluateExpression ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form, + IN OUT FORM_EXPRESSION *Expression + ); +/** + Return the result of the expression list. Check the expression list and + return the highest priority express result. + Priority: DisableIf > SuppressIf > GrayOutIf > FALSE + + @param ExpList The input expression list. + @param Evaluate Whether need to evaluate the expression first. + @param FormSet FormSet associated with this expression. + @param Form Form associated with this expression. + + @retval EXPRESS_RESULT Return the higher priority express result. + DisableIf > SuppressIf > GrayOutIf > FALSE + +**/ +EXPRESS_RESULT +EvaluateExpressionList ( + IN FORM_EXPRESSION_LIST *ExpList, + IN BOOLEAN Evaluate, + IN FORM_BROWSER_FORMSET *FormSet, OPTIONAL + IN FORM_BROWSER_FORM *Form OPTIONAL + ); + +/** + Get Form given its FormId. + + @param FormSet The formset which contains this form. + @param FormId Id of this form. + + @retval Pointer The form. + @retval NULL Specified Form is not found in the formset. + +**/ +FORM_BROWSER_FORM * +IdToForm ( + IN FORM_BROWSER_FORMSET *FormSet, + IN UINT16 FormId + ); + +#endif // _EXPRESSION_H diff --git a/MdeModulePkg/Universal/SetupBrowserDxe/IfrParse.c b/MdeModulePkg/Universal/SetupBrowserDxe/IfrParse.c index 2464aebd09..1c3ab2bedc 100644 --- a/MdeModulePkg/Universal/SetupBrowserDxe/IfrParse.c +++ b/MdeModulePkg/Universal/SetupBrowserDxe/IfrParse.c @@ -16,7 +16,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. UINT16 mStatementIndex; UINT16 mExpressionOpCodeIndex; - +EFI_QUESTION_ID mUsedQuestionId; BOOLEAN mInScopeSubtitle; extern LIST_ENTRY gBrowserStorageList; /** @@ -42,9 +42,9 @@ CreateStatement ( if (Form == NULL) { // - // We are currently not in a Form Scope, so just skip this Statement + // Only guid op may out side the form level. // - return NULL; + ASSERT (((EFI_IFR_OP_HEADER *) OpCodeData)->OpCode == EFI_IFR_GUID_OP); } Statement = &FormSet->StatementBuffer[mStatementIndex]; @@ -58,6 +58,7 @@ CreateStatement ( Statement->Signature = FORM_BROWSER_STATEMENT_SIGNATURE; Statement->Operand = ((EFI_IFR_OP_HEADER *) OpCodeData)->OpCode; + Statement->OpCode = (EFI_IFR_OP_HEADER *) OpCodeData; StatementHdr = (EFI_IFR_STATEMENT_HEADER *) (OpCodeData + sizeof (EFI_IFR_OP_HEADER)); CopyMem (&Statement->Prompt, &StatementHdr->Prompt, sizeof (EFI_STRING_ID)); @@ -82,8 +83,11 @@ CreateStatement ( // // Insert this Statement into current Form // - InsertTailList (&Form->StatementListHead, &Statement->Link); - + if (Form == NULL) { + InsertTailList (&FormSet->StatementListOSF, &Statement->Link); + } else { + InsertTailList (&Form->StatementListHead, &Statement->Link); + } return Statement; } @@ -1133,6 +1137,7 @@ ParseOpCodes ( CountOpCodes (FormSet, &NumberOfStatement, &NumberOfExpression); mStatementIndex = 0; + mUsedQuestionId = 1; FormSet->StatementBuffer = AllocateZeroPool (NumberOfStatement * sizeof (FORM_BROWSER_STATEMENT)); if (FormSet->StatementBuffer == NULL) { return EFI_OUT_OF_RESOURCES; @@ -1144,6 +1149,7 @@ ParseOpCodes ( return EFI_OUT_OF_RESOURCES; } + InitializeListHead (&FormSet->StatementListOSF); InitializeListHead (&FormSet->StorageListHead); InitializeListHead (&FormSet->DefaultStoreListHead); InitializeListHead (&FormSet->FormListHead); @@ -1502,7 +1508,6 @@ ParseOpCodes ( InitializeListHead (&CurrentForm->ConfigRequestHead); CurrentForm->FormType = STANDARD_MAP_FORM_TYPE; - CurrentForm->NvUpdateRequired = FALSE; CopyMem (&CurrentForm->FormId, &((EFI_IFR_FORM *) OpCodeData)->FormId, sizeof (UINT16)); CopyMem (&CurrentForm->FormTitle, &((EFI_IFR_FORM *) OpCodeData)->FormTitle, sizeof (EFI_STRING_ID)); @@ -1539,7 +1544,6 @@ ParseOpCodes ( CurrentForm = AllocateZeroPool (sizeof (FORM_BROWSER_FORM)); ASSERT (CurrentForm != NULL); CurrentForm->Signature = FORM_BROWSER_FORM_SIGNATURE; - CurrentForm->NvUpdateRequired = FALSE; InitializeListHead (&CurrentForm->ExpressionListHead); InitializeListHead (&CurrentForm->StatementListHead); InitializeListHead (&CurrentForm->ConfigRequestHead); @@ -1653,7 +1657,7 @@ ParseOpCodes ( ASSERT (CurrentStatement != NULL); CurrentStatement->Flags = ((EFI_IFR_SUBTITLE *) OpCodeData)->Flags; - + CurrentStatement->FakeQuestionId = mUsedQuestionId++; if (Scope != 0) { mInScopeSubtitle = TRUE; } @@ -1662,13 +1666,14 @@ ParseOpCodes ( case EFI_IFR_TEXT_OP: CurrentStatement = CreateStatement (OpCodeData, FormSet, CurrentForm); ASSERT (CurrentStatement != NULL); - + CurrentStatement->FakeQuestionId = mUsedQuestionId++; CopyMem (&CurrentStatement->TextTwo, &((EFI_IFR_TEXT *) OpCodeData)->TextTwo, sizeof (EFI_STRING_ID)); break; case EFI_IFR_RESET_BUTTON_OP: CurrentStatement = CreateStatement (OpCodeData, FormSet, CurrentForm); ASSERT (CurrentStatement != NULL); + CurrentStatement->FakeQuestionId = mUsedQuestionId++; CopyMem (&CurrentStatement->DefaultId, &((EFI_IFR_RESET_BUTTON *) OpCodeData)->DefaultId, sizeof (EFI_DEFAULT_ID)); break; @@ -1913,6 +1918,7 @@ ParseOpCodes ( CurrentOption = AllocateZeroPool (sizeof (QUESTION_OPTION)); ASSERT (CurrentOption != NULL); CurrentOption->Signature = QUESTION_OPTION_SIGNATURE; + CurrentOption->OpCode = (EFI_IFR_ONE_OF_OPTION *) OpCodeData; CurrentOption->Flags = ((EFI_IFR_ONE_OF_OPTION *) OpCodeData)->Flags; CurrentOption->Value.Type = ((EFI_IFR_ONE_OF_OPTION *) OpCodeData)->Type; @@ -2270,45 +2276,8 @@ ParseOpCodes ( // // Vendor specific // - case EFI_IFR_GUID_OP: - if (CompareGuid (&gEfiIfrTianoGuid, (EFI_GUID *)(OpCodeData + sizeof (EFI_IFR_OP_HEADER)))) { - // - // Tiano specific GUIDed opcodes - // - switch (((EFI_IFR_GUID_LABEL *) OpCodeData)->ExtendOpCode) { - case EFI_IFR_EXTEND_OP_LABEL: - // - // just ignore label - // - break; - - case EFI_IFR_EXTEND_OP_BANNER: - // - // By SubClass to get Banner Data from Front Page - // - if (FormSet->SubClass == EFI_FRONT_PAGE_SUBCLASS) { - CopyMem ( - &gBannerData->Banner[((EFI_IFR_GUID_BANNER *) OpCodeData)->LineNumber][ - ((EFI_IFR_GUID_BANNER *) OpCodeData)->Alignment], - &((EFI_IFR_GUID_BANNER *) OpCodeData)->Title, - sizeof (EFI_STRING_ID) - ); - } - break; - - case EFI_IFR_EXTEND_OP_CLASS: - CopyMem (&FormSet->Class, &((EFI_IFR_GUID_CLASS *) OpCodeData)->Class, sizeof (UINT16)); - break; - - case EFI_IFR_EXTEND_OP_SUBCLASS: - CopyMem (&FormSet->SubClass, &((EFI_IFR_GUID_SUBCLASS *) OpCodeData)->SubClass, sizeof (UINT16)); - break; - - default: - break; - } - } - + case EFI_IFR_GUID_OP: + CurrentStatement = CreateStatement (OpCodeData, FormSet, CurrentForm); break; // diff --git a/MdeModulePkg/Universal/SetupBrowserDxe/Presentation.c b/MdeModulePkg/Universal/SetupBrowserDxe/Presentation.c index 4cd71e5d44..def18fd9f9 100644 --- a/MdeModulePkg/Universal/SetupBrowserDxe/Presentation.c +++ b/MdeModulePkg/Universal/SetupBrowserDxe/Presentation.c @@ -19,863 +19,1658 @@ UI_MENU_SELECTION *gCurrentSelection; EFI_HII_HANDLE mCurrentHiiHandle = NULL; EFI_GUID mCurrentFormSetGuid = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; UINT16 mCurrentFormId = 0; +EFI_EVENT mValueChangedEvent = NULL; +LIST_ENTRY mRefreshEventList = INITIALIZE_LIST_HEAD_VARIABLE (mRefreshEventList); +UINT32 gBrowserStatus = BROWSER_SUCCESS; +CHAR16 *gErrorInfo; +UINT16 mCurFakeQestId; +FORM_DISPLAY_ENGINE_FORM gDisplayFormData; + +/** + Evaluate all expressions in a Form. + + @param FormSet FormSet this Form belongs to. + @param Form The Form. + + @retval EFI_SUCCESS The expression evaluated successfuly + +**/ +EFI_STATUS +EvaluateFormExpressions ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + FORM_EXPRESSION *Expression; + + Link = GetFirstNode (&Form->ExpressionListHead); + while (!IsNull (&Form->ExpressionListHead, Link)) { + Expression = FORM_EXPRESSION_FROM_LINK (Link); + Link = GetNextNode (&Form->ExpressionListHead, Link); + + if (Expression->Type == EFI_HII_EXPRESSION_INCONSISTENT_IF || + Expression->Type == EFI_HII_EXPRESSION_NO_SUBMIT_IF || + Expression->Type == EFI_HII_EXPRESSION_WRITE || + (Expression->Type == EFI_HII_EXPRESSION_READ && Form->FormType != STANDARD_MAP_FORM_TYPE)) { + // + // Postpone Form validation to Question editing or Form submitting or Question Write or Question Read for nonstandard form. + // + continue; + } + + Status = EvaluateExpression (FormSet, Form, Expression); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} + +/** + Add empty function for event process function. + + @param Event The Event need to be process + @param Context The context of the event. + +**/ +VOID +EFIAPI +SetupBrowserEmptyFunction ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ +} + +/** + Base on the opcode buffer info to get the display statement. + + @param OpCode The input opcode buffer for this statement. + + @retval Statement The statement use this opcode buffer. + +**/ +FORM_DISPLAY_ENGINE_STATEMENT * +GetDisplayStatement ( + IN EFI_IFR_OP_HEADER *OpCode + ) +{ + FORM_DISPLAY_ENGINE_STATEMENT *DisplayStatement; + LIST_ENTRY *Link; + + Link = GetFirstNode (&gDisplayFormData.StatementListHead); + while (!IsNull (&gDisplayFormData.StatementListHead, Link)) { + DisplayStatement = FORM_DISPLAY_ENGINE_STATEMENT_FROM_LINK (Link); + + if (DisplayStatement->OpCode == OpCode) { + return DisplayStatement; + } + Link = GetNextNode (&gDisplayFormData.StatementListHead, Link); + } + + return NULL; +} + +/** + Free the refresh event list. + +**/ +VOID +FreeRefreshEvent ( + VOID + ) +{ + LIST_ENTRY *Link; + FORM_BROWSER_REFRESH_EVENT_NODE *EventNode; + + while (!IsListEmpty (&mRefreshEventList)) { + Link = GetFirstNode (&mRefreshEventList); + EventNode = FORM_BROWSER_REFRESH_EVENT_FROM_LINK (Link); + RemoveEntryList (&EventNode->Link); + + gBS->CloseEvent (EventNode->RefreshEvent); + + FreePool (EventNode); + } +} + +/** + Check whether this statement value is changed. If yes, update the statement value and return TRUE; + else return FALSE. + + @param Statement The statement need to check. + +**/ +VOID +UpdateStatement ( + IN OUT FORM_BROWSER_STATEMENT *Statement + ) +{ + GetQuestionValue (gCurrentSelection->FormSet, gCurrentSelection->Form, Statement, GetSetValueWithHiiDriver); + + // + // Reset FormPackage update flag + // + mHiiPackageListUpdated = FALSE; + + // + // Question value may be changed, need invoke its Callback() + // + ProcessCallBackFunction (gCurrentSelection, Statement, EFI_BROWSER_ACTION_CHANGING, FALSE); + + if (mHiiPackageListUpdated) { + // + // Package list is updated, force to reparse IFR binary of target Formset + // + mHiiPackageListUpdated = FALSE; + gCurrentSelection->Action = UI_ACTION_REFRESH_FORMSET; + } +} + +/** + Refresh the question which has refresh guid event attribute. + + @param Event The event which has this function related. + @param Context The input context info related to this event or the status code return to the caller. +**/ +VOID +EFIAPI +RefreshEventNotify( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + FORM_BROWSER_STATEMENT *Statement; + + Statement = (FORM_BROWSER_STATEMENT *)Context; + UpdateStatement(Statement); + gBS->SignalEvent (mValueChangedEvent); +} + + +/** + Create refresh hook event for statement which has refresh event or interval. + + @param Statement The statement need to check. + +**/ +VOID +CreateRefreshEvent ( + IN FORM_BROWSER_STATEMENT *Statement + ) +{ + EFI_STATUS Status; + EFI_EVENT RefreshEvent; + FORM_BROWSER_REFRESH_EVENT_NODE *EventNode; + + // + // If question has refresh guid, create the notify function. + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + RefreshEventNotify, + Statement, + &Statement->RefreshGuid, + &RefreshEvent); + ASSERT_EFI_ERROR (Status); + + EventNode = AllocateZeroPool (sizeof (FORM_BROWSER_REFRESH_EVENT_NODE)); + ASSERT (EventNode != NULL); + EventNode->RefreshEvent = RefreshEvent; + InsertTailList(&mRefreshEventList, &EventNode->Link); +} + +/** + Perform value check for a question. + + @param Question The question need to do check. + @param ErrorInfo Return info about the error. + + @retval The check result. +**/ +UINT32 +InConsistentIfCheck ( + IN FORM_BROWSER_STATEMENT *Question, + OUT STATEMENT_ERROR_INFO *ErrorInfo + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + FORM_EXPRESSION *Expression; + LIST_ENTRY *ListHead; + UINT32 RetVal; + + RetVal = STATEMENT_VALID; + ListHead = &Question->InconsistentListHead; + + Link = GetFirstNode (ListHead); + while (!IsNull (ListHead, Link)) { + Expression = FORM_EXPRESSION_FROM_LINK (Link); + Link = GetNextNode (ListHead, Link); + + // + // Evaluate the expression + // + Status = EvaluateExpression (gCurrentSelection->FormSet, gCurrentSelection->Form, Expression); + if (EFI_ERROR (Status)) { + continue; + } + + if ((Expression->Result.Type == EFI_IFR_TYPE_BOOLEAN) && Expression->Result.Value.b) { + ErrorInfo->StringId = Expression->Error; + ErrorInfo->TimeOut = 0; + RetVal = INCOSISTENT_IF_TRUE; + break; + } + } + + return RetVal; +} + +/** + Perform value check for a question. + + @param Form Form where Statement is in. + @param Statement Value will check for it. + @param InputValue New value will be checked. + @param ErrorInfo Return the error info for this check. + + @retval TRUE Input Value is valid. + @retval FALSE Input Value is invalid. +**/ +UINT32 +EFIAPI +QuestionCheck ( + IN FORM_DISPLAY_ENGINE_FORM *Form, + IN FORM_DISPLAY_ENGINE_STATEMENT *Statement, + IN EFI_HII_VALUE *InputValue, + OUT STATEMENT_ERROR_INFO *ErrorInfo + ) +{ + FORM_BROWSER_STATEMENT *Question; + EFI_HII_VALUE BackUpValue; + UINT8 *BackUpBuffer; + UINT32 RetVal; + + BackUpBuffer = NULL; + RetVal = STATEMENT_VALID; + + ASSERT (Form != NULL && Statement != NULL && InputValue != NULL && ErrorInfo != NULL); + + Question = GetBrowserStatement(Statement); + ASSERT (Question != NULL); + + // + // Back up the quesion value. + // + switch (Question->Operand) { + case EFI_IFR_ORDERED_LIST_OP: + BackUpBuffer = AllocateCopyPool (Question->StorageWidth, Question->BufferValue); + ASSERT (BackUpBuffer != NULL); + CopyMem (Question->BufferValue, InputValue->Buffer, Question->StorageWidth); + break; + + default: + CopyMem (&BackUpValue, &Question->HiiValue, sizeof (EFI_HII_VALUE)); + CopyMem (&Question->HiiValue, InputValue, sizeof (EFI_HII_VALUE)); + break; + } + + // + // Do the inconsistentif check. + // + if (!IsListEmpty (&Question->InconsistentListHead)) { + RetVal = InConsistentIfCheck(Question, ErrorInfo); + } + + // + // Restore the quesion value. + // + switch (Question->Operand) { + case EFI_IFR_ORDERED_LIST_OP: + CopyMem (Question->BufferValue, BackUpBuffer, Question->StorageWidth); + break; + + default: + CopyMem (&Question->HiiValue, &BackUpValue, sizeof (EFI_HII_VALUE)); + break; + } + + return RetVal; +} + +/** + + Initialize the Display statement structure data. + + @param DisplayStatement Pointer to the display Statement data strucure. + @param Statement The statement need to check. + @param HostDisplayStatement Pointer to the display Statement data strucure which is an host statement. +**/ +VOID +InitializeDisplayStatement ( + IN OUT FORM_DISPLAY_ENGINE_STATEMENT *DisplayStatement, + IN FORM_BROWSER_STATEMENT *Statement, + IN FORM_DISPLAY_ENGINE_STATEMENT *HostDisplayStatement + ) +{ + LIST_ENTRY *Link; + QUESTION_OPTION *Option; + DISPLAY_QUESTION_OPTION *DisplayOption; + + DisplayStatement->Signature = FORM_DISPLAY_ENGINE_STATEMENT_SIGNATURE; + DisplayStatement->Version = FORM_DISPLAY_ENGINE_STATEMENT_VERSION_1; + DisplayStatement->OpCode = Statement->OpCode; + InitializeListHead (&DisplayStatement->NestStatementList); + InitializeListHead (&DisplayStatement->OptionListHead); + + if ((EvaluateExpressionList(Statement->Expression, FALSE, NULL, NULL) == ExpressGrayOut) || Statement->Locked) { + DisplayStatement->Attribute |= HII_DISPLAY_GRAYOUT; + } + if ((Statement->ValueExpression != NULL) || ((Statement->QuestionFlags & EFI_IFR_FLAG_READ_ONLY) != 0)) { + DisplayStatement->Attribute |= HII_DISPLAY_READONLY; + } + + // + // Initilize the option list in statement. + // + Link = GetFirstNode (&Statement->OptionListHead); + while (!IsNull (&Statement->OptionListHead, Link)) { + Option = QUESTION_OPTION_FROM_LINK (Link); + Link = GetNextNode (&Statement->OptionListHead, Link); + if ((Option->SuppressExpression != NULL) && + ((EvaluateExpressionList(Option->SuppressExpression, FALSE, NULL, NULL) == ExpressSuppress))) { + continue; + } + + DisplayOption = AllocateZeroPool (sizeof (DISPLAY_QUESTION_OPTION)); + ASSERT (DisplayOption != NULL); + + DisplayOption->ImageId = Option->ImageId; + DisplayOption->Signature = DISPLAY_QUESTION_OPTION_SIGNATURE; + DisplayOption->OptionOpCode = Option->OpCode; + InsertTailList(&DisplayStatement->OptionListHead, &DisplayOption->Link); + } + + CopyMem (&DisplayStatement->CurrentValue, &Statement->HiiValue, sizeof (EFI_HII_VALUE)); + + // + // Some special op code need an extra buffer to save the data. + // Such as string, password, orderedlist... + // + if (Statement->BufferValue != NULL) { + // + // Ordered list opcode may not initilized, get default value here. + // + if (Statement->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP && GetArrayData (Statement->BufferValue, Statement->ValueType, 0) == 0) { + GetQuestionDefault (gCurrentSelection->FormSet, gCurrentSelection->Form, Statement, 0); + } + + DisplayStatement->CurrentValue.Buffer = AllocateCopyPool(Statement->StorageWidth,Statement->BufferValue); + DisplayStatement->CurrentValue.BufferLen = Statement->StorageWidth; + } + + DisplayStatement->SettingChangedFlag = Statement->ValueChanged; + + // + // Get the highlight statement for current form. + // + if (((gCurrentSelection->QuestionId != 0) && (Statement->QuestionId == gCurrentSelection->QuestionId)) || + ((mCurFakeQestId != 0) && (Statement->FakeQuestionId == mCurFakeQestId))) { + gDisplayFormData.HighLightedStatement = DisplayStatement; + } + + // + // Create the refresh event process function. + // + if (!CompareGuid (&Statement->RefreshGuid, &gZeroGuid)) { + CreateRefreshEvent (Statement); + } + + // + // For RTC type of date/time, set default refresh interval to be 1 second. + // + if ((Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) && Statement->Storage == NULL) { + Statement->RefreshInterval = 1; + } + + // + // Create the refresh guid hook event. + // If the statement in this form has refresh event or refresh interval, browser will create this event for display engine. + // + if ((!CompareGuid (&Statement->RefreshGuid, &gZeroGuid)) || (Statement->RefreshInterval != 0)) { + gDisplayFormData.FormRefreshEvent = mValueChangedEvent; + } + + // + // Save the password check function for later use. + // + if (Statement->Operand == EFI_IFR_PASSWORD_OP) { + DisplayStatement->PasswordCheck = PasswordCheck; + } + + // + // Save the validate check question for later use. + // + if (!IsListEmpty (&Statement->InconsistentListHead)) { + DisplayStatement->ValidateQuestion = QuestionCheck; + } + + // + // If this statement is nest in the subtitle, insert to the host statement. + // else insert to the form it belongs to. + // + if (Statement->InSubtitle) { + InsertTailList(&HostDisplayStatement->NestStatementList, &DisplayStatement->DisplayLink); + } else { + InsertTailList(&gDisplayFormData.StatementListHead, &DisplayStatement->DisplayLink); + } +} + +/** + Process for the refresh interval statement. + + @param Event The Event need to be process + @param Context The context of the event. + +**/ +VOID +EFIAPI +RefreshIntervalProcess ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + FORM_BROWSER_STATEMENT *Statement; + LIST_ENTRY *Link; + + Link = GetFirstNode (&gCurrentSelection->Form->StatementListHead); + while (!IsNull (&gCurrentSelection->Form->StatementListHead, Link)) { + Statement = FORM_BROWSER_STATEMENT_FROM_LINK (Link); + Link = GetNextNode (&gCurrentSelection->Form->StatementListHead, Link); + + if (Statement->RefreshInterval == 0) { + continue; + } + + UpdateStatement(Statement); + } + + gBS->SignalEvent (mValueChangedEvent); +} + +/** + + Make a copy of the global hotkey info. + +**/ +VOID +UpdateHotkeyList ( + VOID + ) +{ + BROWSER_HOT_KEY *HotKey; + BROWSER_HOT_KEY *CopyKey; + LIST_ENTRY *Link; + + Link = GetFirstNode (&gBrowserHotKeyList); + while (!IsNull (&gBrowserHotKeyList, Link)) { + HotKey = BROWSER_HOT_KEY_FROM_LINK (Link); + + CopyKey = AllocateCopyPool(sizeof (BROWSER_HOT_KEY), HotKey); + CopyKey->KeyData = AllocateCopyPool(sizeof (EFI_INPUT_KEY), HotKey->KeyData); + CopyKey->HelpString = AllocateCopyPool(StrSize (HotKey->HelpString), HotKey->HelpString); + + InsertTailList(&gDisplayFormData.HotKeyListHead, &CopyKey->Link); + + Link = GetNextNode (&gBrowserHotKeyList, Link); + } +} /** - Clear retangle with specified text attribute. - @param LeftColumn Left column of retangle. - @param RightColumn Right column of retangle. - @param TopRow Start row of retangle. - @param BottomRow End row of retangle. - @param TextAttribute The character foreground and background. + Enum all statement in current form, find all the statement can be display and + add to the display form. **/ VOID -ClearLines ( - IN UINTN LeftColumn, - IN UINTN RightColumn, - IN UINTN TopRow, - IN UINTN BottomRow, - IN UINTN TextAttribute +AddStatementToDisplayForm ( + VOID + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + FORM_BROWSER_STATEMENT *Statement; + FORM_DISPLAY_ENGINE_STATEMENT *DisplayStatement; + FORM_DISPLAY_ENGINE_STATEMENT *HostDisplayStatement; + UINT8 MinRefreshInterval; + EFI_EVENT RefreshIntervalEvent; + FORM_BROWSER_REFRESH_EVENT_NODE *EventNode; + BOOLEAN FormEditable; + + HostDisplayStatement = NULL; + MinRefreshInterval = 0; + FormEditable = FALSE; + + // + // Process the statement outside the form, these statements are not recognized + // by browser core. + // + Link = GetFirstNode (&gCurrentSelection->FormSet->StatementListOSF); + while (!IsNull (&gCurrentSelection->FormSet->StatementListOSF, Link)) { + Statement = FORM_BROWSER_STATEMENT_FROM_LINK (Link); + Link = GetNextNode (&gCurrentSelection->FormSet->StatementListOSF, Link); + + DisplayStatement = AllocateZeroPool (sizeof (FORM_DISPLAY_ENGINE_STATEMENT)); + ASSERT (DisplayStatement != NULL); + DisplayStatement->Signature = FORM_DISPLAY_ENGINE_STATEMENT_SIGNATURE; + DisplayStatement->Version = FORM_DISPLAY_ENGINE_STATEMENT_VERSION_1; + DisplayStatement->OpCode = Statement->OpCode; + + InitializeListHead (&DisplayStatement->NestStatementList); + InitializeListHead (&DisplayStatement->OptionListHead); + + InsertTailList(&gDisplayFormData.StatementListOSF, &DisplayStatement->DisplayLink); + } + + // + // Process the statement in this form. + // + Link = GetFirstNode (&gCurrentSelection->Form->StatementListHead); + while (!IsNull (&gCurrentSelection->Form->StatementListHead, Link)) { + Statement = FORM_BROWSER_STATEMENT_FROM_LINK (Link); + Link = GetNextNode (&gCurrentSelection->Form->StatementListHead, Link); + + // + // This statement can't be show, skip it. + // + if (EvaluateExpressionList(Statement->Expression, FALSE, NULL, NULL) > ExpressGrayOut) { + continue; + } + + DisplayStatement = AllocateZeroPool (sizeof (FORM_DISPLAY_ENGINE_STATEMENT)); + ASSERT (DisplayStatement != NULL); + + // + // Initialize this statement and add it to the display form. + // + InitializeDisplayStatement(DisplayStatement, Statement, HostDisplayStatement); + + // + // Save the Host statement info. + // Host statement may has nest statement follow it. + // + if (!Statement->InSubtitle) { + HostDisplayStatement = DisplayStatement; + } + + if (Statement->Storage != NULL) { + FormEditable = TRUE; + } + + // + // Get the minimal refresh interval value for later use. + // + if ((Statement->RefreshInterval != 0) && + (MinRefreshInterval == 0 || Statement->RefreshInterval < MinRefreshInterval)) { + MinRefreshInterval = Statement->RefreshInterval; + } + } + + // + // Create the periodic timer for refresh interval statement. + // + if (MinRefreshInterval != 0) { + Status = gBS->CreateEvent (EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, RefreshIntervalProcess, NULL, &RefreshIntervalEvent); + ASSERT_EFI_ERROR (Status); + Status = gBS->SetTimer (RefreshIntervalEvent, TimerPeriodic, MinRefreshInterval * ONE_SECOND); + ASSERT_EFI_ERROR (Status); + + EventNode = AllocateZeroPool (sizeof (FORM_BROWSER_REFRESH_EVENT_NODE)); + ASSERT (EventNode != NULL); + EventNode->RefreshEvent = RefreshIntervalEvent; + InsertTailList(&mRefreshEventList, &EventNode->Link); + } + + // + // Update hotkey list field. + // + if (gBrowserSettingScope == SystemLevel || FormEditable) { + UpdateHotkeyList(); + } +} + +/** + + Initialize the SettingChangedFlag variable in the display form. + +**/ +VOID +UpdateDataChangedFlag ( + VOID + ) +{ + LIST_ENTRY *Link; + FORM_BROWSER_FORMSET *LocalFormSet; + + gDisplayFormData.SettingChangedFlag = FALSE; + + if (IsNvUpdateRequiredForForm (gCurrentSelection->Form)) { + gDisplayFormData.SettingChangedFlag = TRUE; + return; + } + + // + // Base on the system level to check whether need to show the NV flag. + // + switch (gBrowserSettingScope) { + case SystemLevel: + // + // Check the maintain list to see whether there is any change. + // + Link = GetFirstNode (&gBrowserFormSetList); + while (!IsNull (&gBrowserFormSetList, Link)) { + LocalFormSet = FORM_BROWSER_FORMSET_FROM_LINK (Link); + if (IsNvUpdateRequiredForFormSet(LocalFormSet)) { + gDisplayFormData.SettingChangedFlag = TRUE; + return; + } + Link = GetNextNode (&gBrowserFormSetList, Link); + } + break; + + case FormSetLevel: + if (IsNvUpdateRequiredForFormSet(gCurrentSelection->FormSet)) { + gDisplayFormData.SettingChangedFlag = TRUE; + return; + } + break; + + default: + break; + } +} + +/** + + Initialize the Display form structure data. + +**/ +VOID +InitializeDisplayFormData ( + VOID + ) +{ + EFI_STATUS Status; + + gDisplayFormData.Signature = FORM_DISPLAY_ENGINE_FORM_SIGNATURE; + gDisplayFormData.Version = FORM_DISPLAY_ENGINE_VERSION_1; + gDisplayFormData.ImageId = 0; + gDisplayFormData.AnimationId = 0; + + InitializeListHead (&gDisplayFormData.StatementListHead); + InitializeListHead (&gDisplayFormData.StatementListOSF); + InitializeListHead (&gDisplayFormData.HotKeyListHead); + + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_CALLBACK, + SetupBrowserEmptyFunction, + NULL, + &mValueChangedEvent + ); + ASSERT_EFI_ERROR (Status); +} + +/** + + Free the kotkey info saved in form data. + +**/ +VOID +FreeHotkeyList ( + VOID + ) +{ + BROWSER_HOT_KEY *HotKey; + LIST_ENTRY *Link; + + while (!IsListEmpty (&gDisplayFormData.HotKeyListHead)) { + Link = GetFirstNode (&gDisplayFormData.HotKeyListHead); + HotKey = BROWSER_HOT_KEY_FROM_LINK (Link); + + RemoveEntryList (&HotKey->Link); + + FreePool (HotKey->KeyData); + FreePool (HotKey->HelpString); + FreePool (HotKey); + } +} + +/** + + Update the Display form structure data. + +**/ +VOID +UpdateDisplayFormData ( + VOID + ) +{ + gDisplayFormData.FormTitle = gCurrentSelection->Form->FormTitle; + gDisplayFormData.FormId = gCurrentSelection->FormId; + gDisplayFormData.HiiHandle = gCurrentSelection->Handle; + CopyGuid (&gDisplayFormData.FormSetGuid, &gCurrentSelection->FormSetGuid); + + gDisplayFormData.Attribute = 0; + gDisplayFormData.Attribute |= gCurrentSelection->Form->ModalForm ? HII_DISPLAY_MODAL : 0; + gDisplayFormData.Attribute |= gCurrentSelection->Form->Locked ? HII_DISPLAY_LOCK : 0; + + gDisplayFormData.FormRefreshEvent = NULL; + gDisplayFormData.HighLightedStatement = NULL; + + gDisplayFormData.BrowserStatus = gBrowserStatus; + gDisplayFormData.ErrorString = gErrorInfo; + + gBrowserStatus = BROWSER_SUCCESS; + gErrorInfo = NULL; + + UpdateDataChangedFlag (); + + AddStatementToDisplayForm (); +} + +/** + + Free the Display Statement structure data. + + @param StatementList Point to the statement list which need to be free. + +**/ +VOID +FreeStatementData ( + LIST_ENTRY *StatementList + ) +{ + LIST_ENTRY *Link; + LIST_ENTRY *OptionLink; + FORM_DISPLAY_ENGINE_STATEMENT *Statement; + DISPLAY_QUESTION_OPTION *Option; + + // + // Free Statements/Questions + // + while (!IsListEmpty (StatementList)) { + Link = GetFirstNode (StatementList); + Statement = FORM_DISPLAY_ENGINE_STATEMENT_FROM_LINK (Link); + + // + // Free Options List + // + while (!IsListEmpty (&Statement->OptionListHead)) { + OptionLink = GetFirstNode (&Statement->OptionListHead); + Option = DISPLAY_QUESTION_OPTION_FROM_LINK (OptionLink); + RemoveEntryList (&Option->Link); + FreePool (Option); + } + + // + // Free nest statement List + // + if (!IsListEmpty (&Statement->NestStatementList)) { + FreeStatementData(&Statement->NestStatementList); + } + + RemoveEntryList (&Statement->DisplayLink); + FreePool (Statement); + } +} + +/** + + Free the Display form structure data. + +**/ +VOID +FreeDisplayFormData ( + VOID + ) +{ + FreeStatementData (&gDisplayFormData.StatementListHead); + FreeStatementData (&gDisplayFormData.StatementListOSF); + + FreeRefreshEvent(); + + FreeHotkeyList(); +} + +/** + + Get FORM_BROWSER_STATEMENT from FORM_DISPLAY_ENGINE_STATEMENT based on the OpCode info. + + @param DisplayStatement The input FORM_DISPLAY_ENGINE_STATEMENT. + + @retval FORM_BROWSER_STATEMENT The return FORM_BROWSER_STATEMENT info. + +**/ +FORM_BROWSER_STATEMENT * +GetBrowserStatement ( + IN FORM_DISPLAY_ENGINE_STATEMENT *DisplayStatement + ) +{ + FORM_BROWSER_STATEMENT *Statement; + LIST_ENTRY *Link; + + Link = GetFirstNode (&gCurrentSelection->Form->StatementListHead); + while (!IsNull (&gCurrentSelection->Form->StatementListHead, Link)) { + Statement = FORM_BROWSER_STATEMENT_FROM_LINK (Link); + + if (Statement->OpCode == DisplayStatement->OpCode) { + return Statement; + } + + Link = GetNextNode (&gCurrentSelection->Form->StatementListHead, Link); + } + + return NULL; +} + +/** + + Process the action request in user input. + + @param Action The user input action request info. + @param DefaultId The user input default Id info. + + @retval EFI_SUCESSS This function always return successfully for now. + +**/ +EFI_STATUS +ProcessAction ( + IN UINT32 Action, + IN UINT16 DefaultId + ) +{ + EFI_STATUS Status; + + // + // This is caused by use press ESC, and it should not combine with other action type. + // + if ((Action & BROWSER_ACTION_FORM_EXIT) == BROWSER_ACTION_FORM_EXIT) { + FindNextMenu (gCurrentSelection, FormLevel); + return EFI_SUCCESS; + } + + // + // Below is normal hotkey trigged action, these action maybe combine with each other. + // + if ((Action & BROWSER_ACTION_DISCARD) == BROWSER_ACTION_DISCARD) { + DiscardForm (gCurrentSelection->FormSet, gCurrentSelection->Form, gBrowserSettingScope); + } + + if ((Action & BROWSER_ACTION_DEFAULT) == BROWSER_ACTION_DEFAULT) { + ExtractDefault (gCurrentSelection->FormSet, gCurrentSelection->Form, DefaultId, gBrowserSettingScope, GetDefaultForAll, NULL, FALSE); + } + + if ((Action & BROWSER_ACTION_SUBMIT) == BROWSER_ACTION_SUBMIT) { + Status = SubmitForm (gCurrentSelection->FormSet, gCurrentSelection->Form, gBrowserSettingScope); + if (EFI_ERROR (Status)) { + gBrowserStatus = BROWSER_SUBMIT_FAIL; + } + } + + if ((Action & BROWSER_ACTION_RESET) == BROWSER_ACTION_RESET) { + gResetRequired = TRUE; + } + + if ((Action & BROWSER_ACTION_EXIT) == BROWSER_ACTION_EXIT) { + // + // Form Exit without saving, Similar to ESC Key. + // FormSet Exit without saving, Exit SendForm. + // System Exit without saving, CallExitHandler and Exit SendForm. + // + DiscardForm (gCurrentSelection->FormSet, gCurrentSelection->Form, gBrowserSettingScope); + if (gBrowserSettingScope == FormLevel || gBrowserSettingScope == FormSetLevel) { + FindNextMenu (gCurrentSelection, gBrowserSettingScope); + } else if (gBrowserSettingScope == SystemLevel) { + if (ExitHandlerFunction != NULL) { + ExitHandlerFunction (); + } + gCurrentSelection->Action = UI_ACTION_EXIT; + } + } + + return EFI_SUCCESS; +} + + +/** + Find HII Handle in the HII database associated with given Device Path. + + If DevicePath is NULL, then ASSERT. + + @param DevicePath Device Path associated with the HII package list + handle. + + @retval Handle HII package list Handle associated with the Device + Path. + @retval NULL Hii Package list handle is not found. + +**/ +EFI_HII_HANDLE +EFIAPI +DevicePathToHiiHandle ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath ) { - CHAR16 *Buffer; - UINTN Row; + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath; + UINTN BufferSize; + UINTN HandleCount; + UINTN Index; + EFI_HANDLE Handle; + EFI_HANDLE DriverHandle; + EFI_HII_HANDLE *HiiHandles; + EFI_HII_HANDLE HiiHandle; + + ASSERT (DevicePath != NULL); + + TmpDevicePath = DevicePath; + // + // Locate Device Path Protocol handle buffer + // + Status = gBS->LocateDevicePath ( + &gEfiDevicePathProtocolGuid, + &TmpDevicePath, + &DriverHandle + ); + if (EFI_ERROR (Status) || !IsDevicePathEnd (TmpDevicePath)) { + return NULL; + } // - // For now, allocate an arbitrarily long buffer + // Retrieve all HII Handles from HII database // - Buffer = AllocateZeroPool (0x10000); - ASSERT (Buffer != NULL); + BufferSize = 0x1000; + HiiHandles = AllocatePool (BufferSize); + ASSERT (HiiHandles != NULL); + Status = mHiiDatabase->ListPackageLists ( + mHiiDatabase, + EFI_HII_PACKAGE_TYPE_ALL, + NULL, + &BufferSize, + HiiHandles + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (HiiHandles); + HiiHandles = AllocatePool (BufferSize); + ASSERT (HiiHandles != NULL); + + Status = mHiiDatabase->ListPackageLists ( + mHiiDatabase, + EFI_HII_PACKAGE_TYPE_ALL, + NULL, + &BufferSize, + HiiHandles + ); + } + + if (EFI_ERROR (Status)) { + FreePool (HiiHandles); + return NULL; + } // - // Set foreground and background as defined + // Search Hii Handle by Driver Handle // - gST->ConOut->SetAttribute (gST->ConOut, TextAttribute); + HiiHandle = NULL; + HandleCount = BufferSize / sizeof (EFI_HII_HANDLE); + for (Index = 0; Index < HandleCount; Index++) { + Status = mHiiDatabase->GetPackageListHandle ( + mHiiDatabase, + HiiHandles[Index], + &Handle + ); + if (!EFI_ERROR (Status) && (Handle == DriverHandle)) { + HiiHandle = HiiHandles[Index]; + break; + } + } + + FreePool (HiiHandles); + return HiiHandle; +} + +/** + Find HII Handle in the HII database associated with given form set guid. + + If FormSetGuid is NULL, then ASSERT. + + @param ComparingGuid FormSet Guid associated with the HII package list + handle. + + @retval Handle HII package list Handle associated with the Device + Path. + @retval NULL Hii Package list handle is not found. +**/ +EFI_HII_HANDLE +FormSetGuidToHiiHandle ( + EFI_GUID *ComparingGuid + ) +{ + EFI_HII_HANDLE *HiiHandles; + UINTN Index; + EFI_HII_PACKAGE_LIST_HEADER *HiiPackageList; + UINTN BufferSize; + UINT32 Offset; + UINT32 Offset2; + UINT32 PackageListLength; + EFI_HII_PACKAGE_HEADER PackageHeader; + UINT8 *Package; + UINT8 *OpCodeData; + EFI_STATUS Status; + EFI_HII_HANDLE HiiHandle; + + ASSERT (ComparingGuid != NULL); + + HiiHandle = NULL; // - // Much faster to buffer the long string instead of print it a character at a time + // Get all the Hii handles // - SetUnicodeMem (Buffer, RightColumn - LeftColumn, L' '); + HiiHandles = HiiGetHiiHandles (NULL); + ASSERT (HiiHandles != NULL); // - // Clear the desired area with the appropriate foreground/background + // Search for formset of each class type // - for (Row = TopRow; Row <= BottomRow; Row++) { - PrintStringAt (LeftColumn, Row, Buffer); + for (Index = 0; HiiHandles[Index] != NULL; Index++) { + BufferSize = 0; + HiiPackageList = NULL; + Status = mHiiDatabase->ExportPackageLists (mHiiDatabase, HiiHandles[Index], &BufferSize, HiiPackageList); + if (Status == EFI_BUFFER_TOO_SMALL) { + HiiPackageList = AllocatePool (BufferSize); + ASSERT (HiiPackageList != NULL); + + Status = mHiiDatabase->ExportPackageLists (mHiiDatabase, HiiHandles[Index], &BufferSize, HiiPackageList); + } + if (EFI_ERROR (Status) || HiiPackageList == NULL) { + return NULL; + } + + // + // Get Form package from this HII package List + // + Offset = sizeof (EFI_HII_PACKAGE_LIST_HEADER); + Offset2 = 0; + CopyMem (&PackageListLength, &HiiPackageList->PackageLength, sizeof (UINT32)); + + while (Offset < PackageListLength) { + Package = ((UINT8 *) HiiPackageList) + Offset; + CopyMem (&PackageHeader, Package, sizeof (EFI_HII_PACKAGE_HEADER)); + + if (PackageHeader.Type == EFI_HII_PACKAGE_FORMS) { + // + // Search FormSet in this Form Package + // + Offset2 = sizeof (EFI_HII_PACKAGE_HEADER); + while (Offset2 < PackageHeader.Length) { + OpCodeData = Package + Offset2; + + if (((EFI_IFR_OP_HEADER *) OpCodeData)->OpCode == EFI_IFR_FORM_SET_OP) { + // + // Try to compare against formset GUID + // + if (CompareGuid (ComparingGuid, (EFI_GUID *)(OpCodeData + sizeof (EFI_IFR_OP_HEADER)))) { + HiiHandle = HiiHandles[Index]; + break; + } + } + + Offset2 += ((EFI_IFR_OP_HEADER *) OpCodeData)->Length; + } + } + if (HiiHandle != NULL) { + break; + } + Offset += PackageHeader.Length; + } + + FreePool (HiiPackageList); + if (HiiHandle != NULL) { + break; + } } - gST->ConOut->SetCursorPosition (gST->ConOut, LeftColumn, TopRow); + FreePool (HiiHandles); - FreePool (Buffer); - return ; + return HiiHandle; } /** - Concatenate a narrow string to another string. + check how to process the changed data in current form or form set. + + @param Selection On input, Selection tell setup browser the information + about the Selection, form and formset to be displayed. + On output, Selection return the screen item that is selected + by user. - @param Destination The destination string. - @param Source The source string. The string to be concatenated. - to the end of Destination. + @param Scope Data save or discard scope, form or formset. + @retval TRUE Success process the changed data, will return to the parent form. + @retval FALSE Reject to process the changed data, will stay at current form. **/ -VOID -NewStrCat ( - IN OUT CHAR16 *Destination, - IN CHAR16 *Source +BOOLEAN +ProcessChangedData ( + IN OUT UI_MENU_SELECTION *Selection, + IN BROWSER_SETTING_SCOPE Scope ) { - UINTN Length; + BOOLEAN RetValue; - for (Length = 0; Destination[Length] != 0; Length++) - ; + RetValue = TRUE; + switch (mFormDisplay->ConfirmDataChange()) { + case BROWSER_ACTION_DISCARD: + DiscardForm (Selection->FormSet, Selection->Form, Scope); + break; + + case BROWSER_ACTION_SUBMIT: + SubmitForm (Selection->FormSet, Selection->Form, Scope); + break; - // - // We now have the length of the original string - // We can safely assume for now that we are concatenating a narrow value to this string. - // For instance, the string is "XYZ" and cat'ing ">" - // If this assumption changes, we need to make this routine a bit more complex - // - Destination[Length] = NARROW_CHAR; - Length++; + case BROWSER_ACTION_NONE: + RetValue = FALSE; + break; - StrCpy (Destination + Length, Source); + default: + // + // if Invalid value return, process same as BROWSER_ACTION_NONE. + // + RetValue = FALSE; + break; + } + + return RetValue; } /** - Count the storage space of a Unicode string. - - This function handles the Unicode string with NARROW_CHAR - and WIDE_CHAR control characters. NARROW_HCAR and WIDE_CHAR - does not count in the resultant output. If a WIDE_CHAR is - hit, then 2 Unicode character will consume an output storage - space with size of CHAR16 till a NARROW_CHAR is hit. - - If String is NULL, then ASSERT (). - - @param String The input string to be counted. + Find parent formset menu(the first menu which has different formset) for current menu. + If not find, just return to the first menu. - @return Storage space for the input string. + @param Selection The selection info. **/ -UINTN -GetStringWidth ( - IN CHAR16 *String +VOID +FindParentFormSet ( + IN OUT UI_MENU_SELECTION *Selection ) { - UINTN Index; - UINTN Count; - UINTN IncrementValue; - - ASSERT (String != NULL); - if (String == NULL) { - return 0; - } - - Index = 0; - Count = 0; - IncrementValue = 1; - - do { - // - // Advance to the null-terminator or to the first width directive - // - for (; - (String[Index] != NARROW_CHAR) && (String[Index] != WIDE_CHAR) && (String[Index] != 0); - Index++, Count = Count + IncrementValue - ) - ; + FORM_ENTRY_INFO *CurrentMenu; + FORM_ENTRY_INFO *ParentMenu; - // - // We hit the null-terminator, we now have a count - // - if (String[Index] == 0) { - break; - } - // - // We encountered a narrow directive - strip it from the size calculation since it doesn't get printed - // and also set the flag that determines what we increment by.(if narrow, increment by 1, if wide increment by 2) - // - if (String[Index] == NARROW_CHAR) { - // - // Skip to the next character - // - Index++; - IncrementValue = 1; - } else { - // - // Skip to the next character - // - Index++; - IncrementValue = 2; - } - } while (String[Index] != 0); + CurrentMenu = Selection->CurrentMenu; + ParentMenu = UiFindParentMenu(CurrentMenu); // - // Increment by one to include the null-terminator in the size + // Find a menu which has different formset guid with current. // - Count++; + while (ParentMenu != NULL && CompareGuid (&CurrentMenu->FormSetGuid, &ParentMenu->FormSetGuid)) { + CurrentMenu = ParentMenu; + ParentMenu = UiFindParentMenu(CurrentMenu); + } + + if (ParentMenu != NULL) { + CopyMem (&Selection->FormSetGuid, &ParentMenu->FormSetGuid, sizeof (EFI_GUID)); + Selection->Handle = ParentMenu->HiiHandle; + Selection->FormId = ParentMenu->FormId; + Selection->QuestionId = ParentMenu->QuestionId; + } else { + Selection->FormId = CurrentMenu->FormId; + Selection->QuestionId = CurrentMenu->QuestionId; + } - return Count * sizeof (CHAR16); + Selection->Statement = NULL; } /** - This function displays the page frame. + Process the goto op code, update the info in the selection structure. - @param Selection Selection contains the information about - the Selection, form and formset to be displayed. - Selection action may be updated in retrieve callback. + @param Statement The statement belong to goto op code. + @param Selection The selection info. + + @retval EFI_SUCCESS The menu process successfully. + @return Other value if the process failed. **/ -VOID -DisplayPageFrame ( - IN UI_MENU_SELECTION *Selection +EFI_STATUS +ProcessGotoOpCode ( + IN OUT FORM_BROWSER_STATEMENT *Statement, + IN OUT UI_MENU_SELECTION *Selection ) { - UINTN Index; - UINT8 Line; - UINT8 Alignment; - CHAR16 Character; - CHAR16 *Buffer; - CHAR16 *StrFrontPageBanner; - UINTN Row; - EFI_SCREEN_DESCRIPTOR LocalScreen; - UINT8 RowIdx; - UINT8 ColumnIdx; - - ZeroMem (&LocalScreen, sizeof (EFI_SCREEN_DESCRIPTOR)); - gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &LocalScreen.RightColumn, &LocalScreen.BottomRow); - ClearLines (0, LocalScreen.RightColumn, 0, LocalScreen.BottomRow, KEYHELP_BACKGROUND); - - if (Selection->Form->ModalForm) { - return; - } - - CopyMem (&LocalScreen, &gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR)); + CHAR16 *StringPtr; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + FORM_BROWSER_FORM *RefForm; + EFI_STATUS Status; + EFI_HII_HANDLE HiiHandle; + + Status = EFI_SUCCESS; + StringPtr = NULL; + HiiHandle = NULL; // - // For now, allocate an arbitrarily long buffer + // Prepare the device path check, get the device path info first. // - Buffer = AllocateZeroPool (0x10000); - ASSERT (Buffer != NULL); - - Character = BOXDRAW_HORIZONTAL; - - for (Index = 0; Index + 2 < (LocalScreen.RightColumn - LocalScreen.LeftColumn); Index++) { - Buffer[Index] = Character; + if (Statement->HiiValue.Value.ref.DevicePath != 0) { + StringPtr = GetToken (Statement->HiiValue.Value.ref.DevicePath, Selection->FormSet->HiiHandle); } - if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) == FORMSET_CLASS_FRONT_PAGE) { - // - // ClearLines(0, LocalScreen.RightColumn, 0, BANNER_HEIGHT-1, BANNER_TEXT | BANNER_BACKGROUND); - // - ClearLines ( - LocalScreen.LeftColumn, - LocalScreen.RightColumn, - LocalScreen.TopRow, - FRONT_PAGE_HEADER_HEIGHT - 1 + LocalScreen.TopRow, - BANNER_TEXT | BANNER_BACKGROUND - ); + // + // Check whether the device path string is a valid string. + // + if (Statement->HiiValue.Value.ref.DevicePath != 0 && StringPtr != NULL) { + if (Selection->Form->ModalForm) { + return Status; + } + // - // for (Line = 0; Line < BANNER_HEIGHT; Line++) { + // Goto another Hii Package list // - for (Line = (UINT8) LocalScreen.TopRow; Line < BANNER_HEIGHT + (UINT8) LocalScreen.TopRow; Line++) { + if (mPathFromText != NULL) { + DevicePath = mPathFromText->ConvertTextToDevicePath(StringPtr); + if (DevicePath != NULL) { + HiiHandle = DevicePathToHiiHandle (DevicePath); + FreePool (DevicePath); + } + FreePool (StringPtr); + } else { // - // for (Alignment = 0; Alignment < BANNER_COLUMNS; Alignment++) { + // Not found the EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL protocol. // - for (Alignment = (UINT8) LocalScreen.LeftColumn; - Alignment < BANNER_COLUMNS + (UINT8) LocalScreen.LeftColumn; - Alignment++ - ) { - RowIdx = (UINT8) (Line - (UINT8) LocalScreen.TopRow); - ColumnIdx = (UINT8) (Alignment - (UINT8) LocalScreen.LeftColumn); - - ASSERT (RowIdx < BANNER_HEIGHT); - ASSERT (ColumnIdx < BANNER_COLUMNS); - - if (gBannerData->Banner[RowIdx][ColumnIdx] != 0x0000) { - StrFrontPageBanner = GetToken ( - gBannerData->Banner[RowIdx][ColumnIdx], - gFrontPageHandle - ); - } else { - continue; - } - - switch (Alignment - LocalScreen.LeftColumn) { - case 0: - // - // Handle left column - // - PrintStringAt (LocalScreen.LeftColumn + BANNER_LEFT_COLUMN_INDENT, Line, StrFrontPageBanner); - break; - - case 1: - // - // Handle center column - // - PrintStringAt ( - LocalScreen.LeftColumn + (LocalScreen.RightColumn - LocalScreen.LeftColumn) / 3, - Line, - StrFrontPageBanner - ); - break; + gBrowserStatus = BROWSER_PROTOCOL_NOT_FOUND; + FreePool (StringPtr); + return Status; + } - case 2: - // - // Handle right column - // - PrintStringAt ( - LocalScreen.LeftColumn + (LocalScreen.RightColumn - LocalScreen.LeftColumn) * 2 / 3, - Line, - StrFrontPageBanner - ); - break; + if (HiiHandle != Selection->Handle) { + // + // Goto another Formset, check for uncommitted data + // + if ((gBrowserSettingScope == FormLevel || gBrowserSettingScope == FormSetLevel) && + IsNvUpdateRequiredForFormSet(Selection->FormSet)) { + if (!ProcessChangedData(Selection, FormSetLevel)) { + return EFI_SUCCESS; } - - FreePool (StrFrontPageBanner); } } - } - ClearLines ( - LocalScreen.LeftColumn, - LocalScreen.RightColumn, - LocalScreen.BottomRow - STATUS_BAR_HEIGHT - gFooterHeight, - LocalScreen.BottomRow - STATUS_BAR_HEIGHT - 1, - KEYHELP_TEXT | KEYHELP_BACKGROUND - ); - - if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) != FORMSET_CLASS_FRONT_PAGE) { - ClearLines ( - LocalScreen.LeftColumn, - LocalScreen.RightColumn, - LocalScreen.TopRow, - LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT - 1, - TITLE_TEXT | TITLE_BACKGROUND - ); - // - // Print Top border line - // +------------------------------------------------------------------------------+ - // ? ? - // +------------------------------------------------------------------------------+ - // - Character = BOXDRAW_DOWN_RIGHT; - - PrintChar (Character); - PrintString (Buffer); - - Character = BOXDRAW_DOWN_LEFT; - PrintChar (Character); - - Character = BOXDRAW_VERTICAL; - for (Row = LocalScreen.TopRow + 1; Row <= LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT - 2; Row++) { - PrintCharAt (LocalScreen.LeftColumn, Row, Character); - PrintCharAt (LocalScreen.RightColumn - 1, Row, Character); + Selection->Action = UI_ACTION_REFRESH_FORMSET; + Selection->Handle = HiiHandle; + if (Selection->Handle == NULL) { + // + // If target Hii Handle not found, exit current formset. + // + FindParentFormSet(Selection); + return EFI_SUCCESS; } - Character = BOXDRAW_UP_RIGHT; - PrintCharAt (LocalScreen.LeftColumn, LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT - 1, Character); - PrintString (Buffer); - - Character = BOXDRAW_UP_LEFT; - PrintChar (Character); - - if ((gClassOfVfr & FORMSET_CLASS_PLATFORM_SETUP) == FORMSET_CLASS_PLATFORM_SETUP) { + CopyMem (&Selection->FormSetGuid,&Statement->HiiValue.Value.ref.FormSetGuid, sizeof (EFI_GUID)); + Selection->FormId = Statement->HiiValue.Value.ref.FormId; + Selection->QuestionId = Statement->HiiValue.Value.ref.QuestionId; + } else if (!CompareGuid (&Statement->HiiValue.Value.ref.FormSetGuid, &gZeroGuid)) { + if (Selection->Form->ModalForm) { + return Status; + } + if (!CompareGuid (&Statement->HiiValue.Value.ref.FormSetGuid, &Selection->FormSetGuid)) { // - // Print Bottom border line - // +------------------------------------------------------------------------------+ - // ? ? - // +------------------------------------------------------------------------------+ + // Goto another Formset, check for uncommitted data // - Character = BOXDRAW_DOWN_RIGHT; - PrintCharAt (LocalScreen.LeftColumn, LocalScreen.BottomRow - STATUS_BAR_HEIGHT - gFooterHeight, Character); - - PrintString (Buffer); - - Character = BOXDRAW_DOWN_LEFT; - PrintChar (Character); - Character = BOXDRAW_VERTICAL; - for (Row = LocalScreen.BottomRow - STATUS_BAR_HEIGHT - gFooterHeight + 1; - Row <= LocalScreen.BottomRow - STATUS_BAR_HEIGHT - 2; - Row++ - ) { - PrintCharAt (LocalScreen.LeftColumn, Row, Character); - PrintCharAt (LocalScreen.RightColumn - 1, Row, Character); + if ((gBrowserSettingScope == FormLevel || gBrowserSettingScope == FormSetLevel) && + IsNvUpdateRequiredForFormSet(Selection->FormSet)) { + if (!ProcessChangedData(Selection, FormSetLevel)) { + return EFI_SUCCESS; + } } + } - Character = BOXDRAW_UP_RIGHT; - PrintCharAt (LocalScreen.LeftColumn, LocalScreen.BottomRow - STATUS_BAR_HEIGHT - 1, Character); + Selection->Action = UI_ACTION_REFRESH_FORMSET; + Selection->Handle = FormSetGuidToHiiHandle(&Statement->HiiValue.Value.ref.FormSetGuid); + if (Selection->Handle == NULL) { + // + // If target Hii Handle not found, exit current formset. + // + FindParentFormSet(Selection); + return EFI_SUCCESS; + } - PrintString (Buffer); + CopyMem (&Selection->FormSetGuid, &Statement->HiiValue.Value.ref.FormSetGuid, sizeof (EFI_GUID)); + Selection->FormId = Statement->HiiValue.Value.ref.FormId; + Selection->QuestionId = Statement->HiiValue.Value.ref.QuestionId; + } else if (Statement->HiiValue.Value.ref.FormId != 0) { + // + // Goto another Form, check for uncommitted data + // + if (Statement->HiiValue.Value.ref.FormId != Selection->FormId) { + if ((gBrowserSettingScope == FormLevel && IsNvUpdateRequiredForForm(Selection->Form))) { + if (!ProcessChangedData (Selection, FormLevel)) { + return EFI_SUCCESS; + } + } + } - Character = BOXDRAW_UP_LEFT; - PrintChar (Character); + RefForm = IdToForm (Selection->FormSet, Statement->HiiValue.Value.ref.FormId); + if ((RefForm != NULL) && (RefForm->SuppressExpression != NULL)) { + if (EvaluateExpressionList(RefForm->SuppressExpression, TRUE, Selection->FormSet, RefForm) != ExpressFalse) { + // + // Form is suppressed. + // + gBrowserStatus = BROWSER_FORM_SUPPRESS; + return EFI_SUCCESS; + } } - } - FreePool (Buffer); + Selection->FormId = Statement->HiiValue.Value.ref.FormId; + Selection->QuestionId = Statement->HiiValue.Value.ref.QuestionId; + } else if (Statement->HiiValue.Value.ref.QuestionId != 0) { + Selection->QuestionId = Statement->HiiValue.Value.ref.QuestionId; + } + return Status; } /** - Evaluate all expressions in a Form. + Process Question Config. - @param FormSet FormSet this Form belongs to. - @param Form The Form. + @param Selection The UI menu selection. + @param Question The Question to be peocessed. - @retval EFI_SUCCESS The expression evaluated successfuly + @retval EFI_SUCCESS Question Config process success. + @retval Other Question Config process fail. **/ EFI_STATUS -EvaluateFormExpressions ( - IN FORM_BROWSER_FORMSET *FormSet, - IN FORM_BROWSER_FORM *Form +ProcessQuestionConfig ( + IN UI_MENU_SELECTION *Selection, + IN FORM_BROWSER_STATEMENT *Question ) { - EFI_STATUS Status; - LIST_ENTRY *Link; - FORM_EXPRESSION *Expression; + EFI_STATUS Status; + CHAR16 *ConfigResp; + CHAR16 *Progress; + EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess; - Link = GetFirstNode (&Form->ExpressionListHead); - while (!IsNull (&Form->ExpressionListHead, Link)) { - Expression = FORM_EXPRESSION_FROM_LINK (Link); - Link = GetNextNode (&Form->ExpressionListHead, Link); + if (Question->QuestionConfig == 0) { + return EFI_SUCCESS; + } - if (Expression->Type == EFI_HII_EXPRESSION_INCONSISTENT_IF || - Expression->Type == EFI_HII_EXPRESSION_NO_SUBMIT_IF || - Expression->Type == EFI_HII_EXPRESSION_WRITE || - (Expression->Type == EFI_HII_EXPRESSION_READ && Form->FormType != STANDARD_MAP_FORM_TYPE)) { - // - // Postpone Form validation to Question editing or Form submitting or Question Write or Question Read for nonstandard form. - // - continue; - } + // + // Get + // + ConfigResp = GetToken (Question->QuestionConfig, Selection->FormSet->HiiHandle); + if (ConfigResp == NULL) { + return EFI_NOT_FOUND; + } - Status = EvaluateExpression (FormSet, Form, Expression); - if (EFI_ERROR (Status)) { - return Status; - } + // + // Send config to Configuration Driver + // + ConfigAccess = Selection->FormSet->ConfigAccess; + if (ConfigAccess == NULL) { + return EFI_UNSUPPORTED; } + Status = ConfigAccess->RouteConfig ( + ConfigAccess, + ConfigResp, + &Progress + ); - return EFI_SUCCESS; + return Status; } -/* -+------------------------------------------------------------------------------+ -? Setup Page ? -+------------------------------------------------------------------------------+ - - - - - - - - - - - - - - - - - -+------------------------------------------------------------------------------+ -?F1=Scroll Help F9=Reset to Defaults F10=Save and Exit ? -| ^"=Move Highlight Toggles Checkbox Esc=Discard Changes | -+------------------------------------------------------------------------------+ -*/ - /** + Process the user input data. - Display form and wait for user to select one menu option, then return it. + @param UserInput The user input data. + @param ChangeHighlight Whether need to change the highlight statement. - @param Selection On input, Selection tell setup browser the information - about the Selection, form and formset to be displayed. - On output, Selection return the screen item that is selected - by user. @retval EFI_SUCESSS This function always return successfully for now. **/ EFI_STATUS -DisplayForm ( - IN OUT UI_MENU_SELECTION *Selection +ProcessUserInput ( + IN USER_INPUT *UserInput, + IN BOOLEAN ChangeHighlight ) { - CHAR16 *StringPtr; - UINT16 MenuItemCount; - EFI_HII_HANDLE Handle; - EFI_SCREEN_DESCRIPTOR LocalScreen; - UINT16 Width; - UINTN ArrayEntry; - CHAR16 *OutputString; - LIST_ENTRY *Link; - FORM_BROWSER_STATEMENT *Statement; - UINT16 NumberOfLines; - EFI_STATUS Status; - UI_MENU_OPTION *MenuOption; - UINT16 GlyphWidth; - - Handle = Selection->Handle; - MenuItemCount = 0; - ArrayEntry = 0; - OutputString = NULL; - - UiInitMenu (); - - CopyMem (&LocalScreen, &gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR)); - - StringPtr = GetToken (Selection->Form->FormTitle, Handle); + EFI_STATUS Status; + FORM_BROWSER_STATEMENT *Statement; - if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) != FORMSET_CLASS_FRONT_PAGE) { - if (Selection->Form->ModalForm) { - gST->ConOut->SetAttribute (gST->ConOut, TITLE_TEXT | EFI_BACKGROUND_BLACK); - } else { - gST->ConOut->SetAttribute (gST->ConOut, TITLE_TEXT | TITLE_BACKGROUND); - } - PrintStringAt ( - (LocalScreen.RightColumn + LocalScreen.LeftColumn - GetStringWidth (StringPtr) / 2) / 2, - LocalScreen.TopRow + 1, - StringPtr - ); - } + Status = EFI_SUCCESS; // - // Remove Buffer allocated for StringPtr after it has been used. + // When Exit from FormDisplay function, one of the below two cases must be true. // - FreePool (StringPtr); + ASSERT (UserInput->Action != 0 || UserInput->SelectedStatement != NULL); // - // Evaluate all the Expressions in this Form + // Remove the last highligh question id, this id will update when show next form. // - Status = EvaluateFormExpressions (Selection->FormSet, Selection->Form); - if (EFI_ERROR (Status)) { - return Status; - } + gCurrentSelection->QuestionId = 0; - Selection->FormEditable = FALSE; - Link = GetFirstNode (&Selection->Form->StatementListHead); - while (!IsNull (&Selection->Form->StatementListHead, Link)) { - Statement = FORM_BROWSER_STATEMENT_FROM_LINK (Link); + // + // First process the Action field in USER_INPUT. + // + if (UserInput->Action != 0) { + Status = ProcessAction (UserInput->Action, UserInput->DefaultId); + if (EFI_ERROR (Status)) { + return Status; + } - if (EvaluateExpressionList(Statement->Expression, FALSE, NULL, NULL) <= ExpressGrayOut) { - StringPtr = GetToken (Statement->Prompt, Handle); - ASSERT (StringPtr != NULL); + // + // Clear the highlight info. + // + gCurrentSelection->Statement = NULL; - Width = GetWidth (Statement, Handle); + if (UserInput->SelectedStatement != NULL) { + Statement = GetBrowserStatement(UserInput->SelectedStatement); + ASSERT (Statement != NULL); + // + // Save the current highlight menu in the menu history data. + // which will be used when later browse back to this form. + // + gCurrentSelection->CurrentMenu->QuestionId = Statement->QuestionId; + // + // For statement like text, actio, it not has question id. + // So use FakeQuestionId to save the question. + // + if (gCurrentSelection->CurrentMenu->QuestionId == 0) { + mCurFakeQestId = Statement->FakeQuestionId; + } else { + mCurFakeQestId = 0; + } + } + } else { + Statement = GetBrowserStatement(UserInput->SelectedStatement); + ASSERT (Statement != NULL); - NumberOfLines = 1; - ArrayEntry = 0; - GlyphWidth = 1; - for (; GetLineByWidth (StringPtr, Width, &GlyphWidth,&ArrayEntry, &OutputString) != 0x0000;) { - // - // If there is more string to process print on the next row and increment the Skip value - // - if (StrLen (&StringPtr[ArrayEntry]) != 0) { - NumberOfLines++; - } + gCurrentSelection->Statement = Statement; - FreePool (OutputString); + if (ChangeHighlight) { + // + // This question is the current user select one,record it and later + // show it as the highlight question. + // + gCurrentSelection->CurrentMenu->QuestionId = Statement->QuestionId; + // + // For statement like text, actio, it not has question id. + // So use FakeQuestionId to save the question. + // + if (gCurrentSelection->CurrentMenu->QuestionId == 0) { + mCurFakeQestId = Statement->FakeQuestionId; + } else { + mCurFakeQestId = 0; } + } + switch (Statement->Operand) { + case EFI_IFR_REF_OP: + Status = ProcessGotoOpCode(Statement, gCurrentSelection); + break; + + case EFI_IFR_ACTION_OP: + // + // Process the Config string + // + Status = ProcessQuestionConfig (gCurrentSelection, Statement); + break; + + case EFI_IFR_RESET_BUTTON_OP: // - // We are NOT!! removing this StringPtr buffer via FreePool since it is being used in the menuoptions, we will do - // it in UiFreeMenu. + // Reset Question to default value specified by DefaultId // - MenuOption = UiAddMenuOption (StringPtr, Selection->Handle, Selection->Form, Statement, NumberOfLines, MenuItemCount); - MenuItemCount++; + Status = ExtractDefault (gCurrentSelection->FormSet, NULL, Statement->DefaultId, FormSetLevel, GetDefaultForAll, NULL, FALSE); + break; + + default: + switch (Statement->Operand) { + case EFI_IFR_STRING_OP: + DeleteString(Statement->HiiValue.Value.string, gCurrentSelection->FormSet->HiiHandle); + Statement->HiiValue.Value.string = UserInput->InputValue.Value.string; + CopyMem (Statement->BufferValue, UserInput->InputValue.Buffer, (UINTN) UserInput->InputValue.BufferLen); + FreePool (UserInput->InputValue.Buffer); + break; + + case EFI_IFR_PASSWORD_OP: + if (UserInput->InputValue.Buffer == NULL) { + // + // User not input new password, just return. + // + break; + } - if (MenuOption->IsQuestion && !MenuOption->ReadOnly) { + DeleteString(Statement->HiiValue.Value.string, gCurrentSelection->FormSet->HiiHandle); + Statement->HiiValue.Value.string = UserInput->InputValue.Value.string; + CopyMem (Statement->BufferValue, UserInput->InputValue.Buffer, (UINTN) UserInput->InputValue.BufferLen); + FreePool (UserInput->InputValue.Buffer); // - // At least one item is not readonly, this Form is considered as editable + // Two password match, send it to Configuration Driver // - Selection->FormEditable = TRUE; + if ((Statement->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) { + PasswordCheck (NULL, UserInput->SelectedStatement, (CHAR16 *) Statement->BufferValue); + // + // Clean the value after saved it. + // + ZeroMem (Statement->BufferValue, (UINTN) UserInput->InputValue.BufferLen); + HiiSetString (gCurrentSelection->FormSet->HiiHandle, Statement->HiiValue.Value.string, (CHAR16*)Statement->BufferValue, NULL); + } else { + SetQuestionValue (gCurrentSelection->FormSet, gCurrentSelection->Form, Statement, GetSetValueWithHiiDriver); + } + break; + + case EFI_IFR_ORDERED_LIST_OP: + CopyMem (Statement->BufferValue, UserInput->InputValue.Buffer, UserInput->InputValue.BufferLen); + break; + + default: + CopyMem (&Statement->HiiValue, &UserInput->InputValue, sizeof (EFI_HII_VALUE)); + break; + } + if (Statement->Operand != EFI_IFR_PASSWORD_OP) { + SetQuestionValue (gCurrentSelection->FormSet, gCurrentSelection->Form, Statement, GetSetValueWithEditBuffer); } + break; } - - Link = GetNextNode (&Selection->Form->StatementListHead, Link); } - Status = UiDisplayMenu (Selection); - - UiFreeMenu (); - return Status; } /** - Initialize the HII String Token to the correct values. - -**/ -VOID -InitializeBrowserStrings ( - VOID - ) -{ - gEnterString = GetToken (STRING_TOKEN (ENTER_STRING), gHiiHandle); - gEnterCommitString = GetToken (STRING_TOKEN (ENTER_COMMIT_STRING), gHiiHandle); - gEnterEscapeString = GetToken (STRING_TOKEN (ENTER_ESCAPE_STRING), gHiiHandle); - gEscapeString = GetToken (STRING_TOKEN (ESCAPE_STRING), gHiiHandle); - gMoveHighlight = GetToken (STRING_TOKEN (MOVE_HIGHLIGHT), gHiiHandle); - gMakeSelection = GetToken (STRING_TOKEN (MAKE_SELECTION), gHiiHandle); - gDecNumericInput = GetToken (STRING_TOKEN (DEC_NUMERIC_INPUT), gHiiHandle); - gHexNumericInput = GetToken (STRING_TOKEN (HEX_NUMERIC_INPUT), gHiiHandle); - gToggleCheckBox = GetToken (STRING_TOKEN (TOGGLE_CHECK_BOX), gHiiHandle); - gPromptForData = GetToken (STRING_TOKEN (PROMPT_FOR_DATA), gHiiHandle); - gPromptForPassword = GetToken (STRING_TOKEN (PROMPT_FOR_PASSWORD), gHiiHandle); - gPromptForNewPassword = GetToken (STRING_TOKEN (PROMPT_FOR_NEW_PASSWORD), gHiiHandle); - gConfirmPassword = GetToken (STRING_TOKEN (CONFIRM_PASSWORD), gHiiHandle); - gConfirmError = GetToken (STRING_TOKEN (CONFIRM_ERROR), gHiiHandle); - gPassowordInvalid = GetToken (STRING_TOKEN (PASSWORD_INVALID), gHiiHandle); - gPressEnter = GetToken (STRING_TOKEN (PRESS_ENTER), gHiiHandle); - gEmptyString = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle); - gAreYouSure = GetToken (STRING_TOKEN (ARE_YOU_SURE), gHiiHandle); - gYesResponse = GetToken (STRING_TOKEN (ARE_YOU_SURE_YES), gHiiHandle); - gNoResponse = GetToken (STRING_TOKEN (ARE_YOU_SURE_NO), gHiiHandle); - gMiniString = GetToken (STRING_TOKEN (MINI_STRING), gHiiHandle); - gPlusString = GetToken (STRING_TOKEN (PLUS_STRING), gHiiHandle); - gMinusString = GetToken (STRING_TOKEN (MINUS_STRING), gHiiHandle); - gAdjustNumber = GetToken (STRING_TOKEN (ADJUST_NUMBER), gHiiHandle); - gSaveChanges = GetToken (STRING_TOKEN (SAVE_CHANGES), gHiiHandle); - gOptionMismatch = GetToken (STRING_TOKEN (OPTION_MISMATCH), gHiiHandle); - gFormSuppress = GetToken (STRING_TOKEN (FORM_SUPPRESSED), gHiiHandle); - gProtocolNotFound = GetToken (STRING_TOKEN (PROTOCOL_NOT_FOUND), gHiiHandle); - return ; -} - -/** - Free up the resource allocated for all strings required - by Setup Browser. -**/ -VOID -FreeBrowserStrings ( - VOID - ) -{ - FreePool (gEnterString); - FreePool (gEnterCommitString); - FreePool (gEnterEscapeString); - FreePool (gEscapeString); - FreePool (gMoveHighlight); - FreePool (gMakeSelection); - FreePool (gDecNumericInput); - FreePool (gHexNumericInput); - FreePool (gToggleCheckBox); - FreePool (gPromptForData); - FreePool (gPromptForPassword); - FreePool (gPromptForNewPassword); - FreePool (gConfirmPassword); - FreePool (gPassowordInvalid); - FreePool (gConfirmError); - FreePool (gPressEnter); - FreePool (gEmptyString); - FreePool (gAreYouSure); - FreePool (gYesResponse); - FreePool (gNoResponse); - FreePool (gMiniString); - FreePool (gPlusString); - FreePool (gMinusString); - FreePool (gAdjustNumber); - FreePool (gSaveChanges); - FreePool (gOptionMismatch); - FreePool (gFormSuppress); - FreePool (gProtocolNotFound); - return ; -} + Display form and wait for user to select one menu option, then return it. -/** - Show all registered HotKey help strings on bottom Rows. + @retval EFI_SUCESSS This function always return successfully for now. **/ -VOID -PrintHotKeyHelpString ( +EFI_STATUS +DisplayForm ( VOID ) { - UINTN CurrentCol; - UINTN CurrentRow; - UINTN BottomRowOfHotKeyHelp; - UINTN ColumnWidth; - UINTN Index; - EFI_SCREEN_DESCRIPTOR LocalScreen; - LIST_ENTRY *Link; - BROWSER_HOT_KEY *HotKey; + EFI_STATUS Status; + USER_INPUT UserInput; + FORM_ENTRY_INFO *CurrentMenu; + BOOLEAN ChangeHighlight; - CopyMem (&LocalScreen, &gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR)); - ColumnWidth = (LocalScreen.RightColumn - LocalScreen.LeftColumn) / 3; - BottomRowOfHotKeyHelp = LocalScreen.BottomRow - STATUS_BAR_HEIGHT - 3; + ZeroMem (&UserInput, sizeof (USER_INPUT)); // - // Calculate total number of Register HotKeys. + // Update the menu history data. // - Index = 0; - Link = GetFirstNode (&gBrowserHotKeyList); - while (!IsNull (&gBrowserHotKeyList, Link)) { - HotKey = BROWSER_HOT_KEY_FROM_LINK (Link); - // - // Help string can't exceed ColumnWidth. One Row will show three Help information. + CurrentMenu = UiFindMenuList (gCurrentSelection->Handle, &gCurrentSelection->FormSetGuid, gCurrentSelection->FormId); + if (CurrentMenu == NULL) { // - if (StrLen (HotKey->HelpString) > ColumnWidth) { - HotKey->HelpString[ColumnWidth] = L'\0'; - } - // - // Calculate help information Column and Row. - // - if ((Index % 3) != 2) { - CurrentCol = LocalScreen.LeftColumn + (2 - Index % 3) * ColumnWidth; - } else { - CurrentCol = LocalScreen.LeftColumn + 2; - } - CurrentRow = BottomRowOfHotKeyHelp - Index / 3; - // - // Print HotKey help string on bottom Row. + // Current menu not found, add it to the menu tree // - PrintStringAt (CurrentCol, CurrentRow, HotKey->HelpString); + CurrentMenu = UiAddMenuList (gCurrentSelection->Handle, &gCurrentSelection->FormSetGuid, + gCurrentSelection->FormId, gCurrentSelection->QuestionId); + ASSERT (CurrentMenu != NULL); + } + gCurrentSelection->CurrentMenu = CurrentMenu; + // + // Find currrent highlight statement. + // + if (gCurrentSelection->QuestionId == 0) { // - // Get Next Hot Key. + // Highlight not specified, fetch it from cached menu // - Link = GetNextNode (&gBrowserHotKeyList, Link); - Index ++; + gCurrentSelection->QuestionId = CurrentMenu->QuestionId; } - - return; -} - -/** - Update key's help imformation. - - @param Selection Tell setup browser the information about the Selection - @param MenuOption The Menu option - @param Selected Whether or not a tag be selected - -**/ -VOID -UpdateKeyHelp ( - IN UI_MENU_SELECTION *Selection, - IN UI_MENU_OPTION *MenuOption, - IN BOOLEAN Selected - ) -{ - UINTN SecCol; - UINTN ThdCol; - UINTN LeftColumnOfHelp; - UINTN RightColumnOfHelp; - UINTN TopRowOfHelp; - UINTN BottomRowOfHelp; - UINTN StartColumnOfHelp; - EFI_SCREEN_DESCRIPTOR LocalScreen; - FORM_BROWSER_STATEMENT *Statement; - - gST->ConOut->SetAttribute (gST->ConOut, KEYHELP_TEXT | KEYHELP_BACKGROUND); - if (Selection->Form->ModalForm) { - return; + // + // Evaluate all the Expressions in this Form + // + Status = EvaluateFormExpressions (gCurrentSelection->FormSet, gCurrentSelection->Form); + if (EFI_ERROR (Status)) { + return Status; } - CopyMem (&LocalScreen, &gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR)); - - SecCol = LocalScreen.LeftColumn + (LocalScreen.RightColumn - LocalScreen.LeftColumn) / 3; - ThdCol = LocalScreen.LeftColumn + (LocalScreen.RightColumn - LocalScreen.LeftColumn) / 3 * 2; - - StartColumnOfHelp = LocalScreen.LeftColumn + 2; - LeftColumnOfHelp = LocalScreen.LeftColumn + 1; - RightColumnOfHelp = LocalScreen.RightColumn - 2; - TopRowOfHelp = LocalScreen.BottomRow - STATUS_BAR_HEIGHT - gFooterHeight + 1; - BottomRowOfHelp = LocalScreen.BottomRow - STATUS_BAR_HEIGHT - 2; - - Statement = MenuOption->ThisTag; - switch (Statement->Operand) { - case EFI_IFR_ORDERED_LIST_OP: - case EFI_IFR_ONE_OF_OP: - case EFI_IFR_NUMERIC_OP: - case EFI_IFR_TIME_OP: - case EFI_IFR_DATE_OP: - ClearLines (LeftColumnOfHelp, RightColumnOfHelp, TopRowOfHelp, BottomRowOfHelp, KEYHELP_TEXT | KEYHELP_BACKGROUND); - - if (!Selected) { - // - // On system setting, HotKey will show on every form. - // - if (gBrowserSettingScope == SystemLevel || - (Selection->FormEditable && gFunctionKeySetting != NONE_FUNCTION_KEY_SETTING)) { - PrintHotKeyHelpString (); - } - - if ((gClassOfVfr & FORMSET_CLASS_PLATFORM_SETUP) == FORMSET_CLASS_PLATFORM_SETUP) { - PrintStringAt (ThdCol, BottomRowOfHelp, gEscapeString); - } - - if ((Statement->Operand == EFI_IFR_DATE_OP) || - (Statement->Operand == EFI_IFR_TIME_OP)) { - PrintAt ( - StartColumnOfHelp, - BottomRowOfHelp, - L"%c%c%c%c%s", - ARROW_UP, - ARROW_DOWN, - ARROW_RIGHT, - ARROW_LEFT, - gMoveHighlight - ); - PrintStringAt (SecCol, BottomRowOfHelp, gEnterString); - PrintStringAt (StartColumnOfHelp, TopRowOfHelp, gAdjustNumber); - } else { - PrintAt (StartColumnOfHelp, BottomRowOfHelp, L"%c%c%s", ARROW_UP, ARROW_DOWN, gMoveHighlight); - if (Statement->Operand == EFI_IFR_NUMERIC_OP && Statement->Step != 0) { - PrintStringAt (StartColumnOfHelp, TopRowOfHelp, gAdjustNumber); - } - PrintStringAt (SecCol, BottomRowOfHelp, gEnterString); - } - } else { - PrintStringAt (SecCol, BottomRowOfHelp, gEnterCommitString); - - // - // If it is a selected numeric with manual input, display different message - // - if ((Statement->Operand == EFI_IFR_NUMERIC_OP) || - (Statement->Operand == EFI_IFR_DATE_OP) || - (Statement->Operand == EFI_IFR_TIME_OP)) { - PrintStringAt ( - SecCol, - TopRowOfHelp, - ((Statement->Flags & EFI_IFR_DISPLAY_UINT_HEX) == EFI_IFR_DISPLAY_UINT_HEX) ? gHexNumericInput : gDecNumericInput - ); - } else if (Statement->Operand != EFI_IFR_ORDERED_LIST_OP) { - PrintAt (StartColumnOfHelp, BottomRowOfHelp, L"%c%c%s", ARROW_UP, ARROW_DOWN, gMoveHighlight); - } - - if (Statement->Operand == EFI_IFR_ORDERED_LIST_OP) { - PrintStringAt (StartColumnOfHelp, TopRowOfHelp, gPlusString); - PrintStringAt (ThdCol, TopRowOfHelp, gMinusString); - } - - PrintStringAt (ThdCol, BottomRowOfHelp, gEnterEscapeString); - } - break; - - case EFI_IFR_CHECKBOX_OP: - ClearLines (LeftColumnOfHelp, RightColumnOfHelp, TopRowOfHelp, BottomRowOfHelp, KEYHELP_TEXT | KEYHELP_BACKGROUND); - - // - // On system setting, HotKey will show on every form. - // - if (gBrowserSettingScope == SystemLevel || - (Selection->FormEditable && gFunctionKeySetting != NONE_FUNCTION_KEY_SETTING)) { - PrintHotKeyHelpString (); - } - if ((gClassOfVfr & FORMSET_CLASS_PLATFORM_SETUP) == FORMSET_CLASS_PLATFORM_SETUP) { - PrintStringAt (ThdCol, BottomRowOfHelp, gEscapeString); - } + UpdateDisplayFormData (); - PrintAt (StartColumnOfHelp, BottomRowOfHelp, L"%c%c%s", ARROW_UP, ARROW_DOWN, gMoveHighlight); - PrintStringAt (SecCol, BottomRowOfHelp, gToggleCheckBox); - break; + // + // Three possible status maybe return. + // + // EFI_INVALID_PARAMETER: The input dimension info is not valid. + // EFI_NOT_FOUND: The input value for oneof/orderedlist opcode is not valid + // and an valid value has return. + // EFI_SUCCESS: Success shows form and get user input in UserInput paramenter. + // + Status = mFormDisplay->FormDisplay (&gDisplayFormData, &UserInput); + if (EFI_ERROR (Status) && Status != EFI_NOT_FOUND) { + FreeDisplayFormData(); + return Status; + } - case EFI_IFR_REF_OP: - case EFI_IFR_PASSWORD_OP: - case EFI_IFR_STRING_OP: - case EFI_IFR_TEXT_OP: - case EFI_IFR_ACTION_OP: - case EFI_IFR_RESET_BUTTON_OP: - case EFI_IFR_SUBTITLE_OP: - ClearLines (LeftColumnOfHelp, RightColumnOfHelp, TopRowOfHelp, BottomRowOfHelp, KEYHELP_TEXT | KEYHELP_BACKGROUND); + // + // If status is EFI_SUCCESS, means user has change the highlight menu and new user input return. + // in this case, browser need to change the highlight menu. + // If status is EFI_NOT_FOUND, means the input DisplayFormData has error for oneof/orderedlist + // opcode and new valid value has return, browser core need to adjust + // value for this opcode and shows this form again. + // + ChangeHighlight = (Status == EFI_SUCCESS ? TRUE :FALSE); - if (!Selected) { - // - // On system setting, HotKey will show on every form. - // - if (gBrowserSettingScope == SystemLevel || - (Selection->FormEditable && gFunctionKeySetting != NONE_FUNCTION_KEY_SETTING)) { - PrintHotKeyHelpString (); - } - if ((gClassOfVfr & FORMSET_CLASS_PLATFORM_SETUP) == FORMSET_CLASS_PLATFORM_SETUP) { - PrintStringAt (ThdCol, BottomRowOfHelp, gEscapeString); - } + Status = ProcessUserInput (&UserInput, ChangeHighlight); - PrintAt (StartColumnOfHelp, BottomRowOfHelp, L"%c%c%s", ARROW_UP, ARROW_DOWN, gMoveHighlight); - if (Statement->Operand != EFI_IFR_TEXT_OP && Statement->Operand != EFI_IFR_SUBTITLE_OP) { - PrintStringAt (SecCol, BottomRowOfHelp, gEnterString); - } - } else { - if (Statement->Operand != EFI_IFR_REF_OP) { - PrintStringAt ( - (LocalScreen.RightColumn - GetStringWidth (gEnterCommitString) / 2) / 2, - BottomRowOfHelp, - gEnterCommitString - ); - PrintStringAt (ThdCol, BottomRowOfHelp, gEnterEscapeString); - } - } - break; + FreeDisplayFormData(); - default: - break; - } + return Status; } /** @@ -920,60 +1715,136 @@ FormUpdateNotify ( } /** - check whether the formset need to update the NV. + Update the NV flag info for this form set. @param FormSet FormSet data structure. - @retval TRUE Need to update the NV. - @retval FALSE No need to update the NV. **/ -BOOLEAN -IsNvUpdateRequired ( +BOOLEAN +IsNvUpdateRequiredForFormSet ( IN FORM_BROWSER_FORMSET *FormSet ) { LIST_ENTRY *Link; FORM_BROWSER_FORM *Form; + BOOLEAN RetVal; + + // + // Not finished question initialization, return FALSE. + // + if (!FormSet->QuestionInited) { + return FALSE; + } + + RetVal = FALSE; Link = GetFirstNode (&FormSet->FormListHead); while (!IsNull (&FormSet->FormListHead, Link)) { Form = FORM_BROWSER_FORM_FROM_LINK (Link); - if (Form->NvUpdateRequired ) { - return TRUE; + RetVal = IsNvUpdateRequiredForForm(Form); + if (RetVal) { + break; } Link = GetNextNode (&FormSet->FormListHead, Link); } + return RetVal; +} + +/** + Update the NvUpdateRequired flag for a form. + + @param Form Form data structure. + +**/ +BOOLEAN +IsNvUpdateRequiredForForm ( + IN FORM_BROWSER_FORM *Form + ) +{ + LIST_ENTRY *Link; + FORM_BROWSER_STATEMENT *Statement; + + Link = GetFirstNode (&Form->StatementListHead); + while (!IsNull (&Form->StatementListHead, Link)) { + Statement = FORM_BROWSER_STATEMENT_FROM_LINK (Link); + + if (Statement->ValueChanged) { + return TRUE; + } + + Link = GetNextNode (&Form->StatementListHead, Link); + } + return FALSE; } /** - check whether the formset need to update the NV. + Check whether the storage data for current form set is changed. - @param FormSet FormSet data structure. - @param SetValue Whether set new value or clear old value. + @param FormSet FormSet data structure. + @retval TRUE Data is changed. + @retval FALSE Data is not changed. **/ -VOID -UpdateNvInfoInForm ( - IN FORM_BROWSER_FORMSET *FormSet, - IN BOOLEAN SetValue +BOOLEAN +IsStorageDataChangedForFormSet ( + IN FORM_BROWSER_FORMSET *FormSet ) { LIST_ENTRY *Link; - FORM_BROWSER_FORM *Form; - - Link = GetFirstNode (&FormSet->FormListHead); - while (!IsNull (&FormSet->FormListHead, Link)) { - Form = FORM_BROWSER_FORM_FROM_LINK (Link); + FORMSET_STORAGE *Storage; + BROWSER_STORAGE *BrowserStorage; + CHAR16 *ConfigRespNew; + CHAR16 *ConfigRespOld; + BOOLEAN RetVal; - Form->NvUpdateRequired = SetValue; + RetVal = FALSE; + ConfigRespNew = NULL; + ConfigRespOld = NULL; - Link = GetNextNode (&FormSet->FormListHead, Link); + // + // Request current settings from Configuration Driver + // + Link = GetFirstNode (&FormSet->StorageListHead); + while (!IsNull (&FormSet->StorageListHead, Link)) { + Storage = FORMSET_STORAGE_FROM_LINK (Link); + Link = GetNextNode (&FormSet->StorageListHead, Link); + + BrowserStorage = Storage->BrowserStorage; + + if (BrowserStorage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { + continue; + } + + if (Storage->ElementCount == 0) { + continue; + } + + StorageToConfigResp (BrowserStorage, &ConfigRespNew, Storage->ConfigRequest, TRUE); + StorageToConfigResp (BrowserStorage, &ConfigRespOld, Storage->ConfigRequest, FALSE); + ASSERT (ConfigRespNew != NULL && ConfigRespOld != NULL); + + if (StrCmp (ConfigRespNew, ConfigRespOld) != 0) { + RetVal = TRUE; + } + + FreePool (ConfigRespNew); + ConfigRespNew = NULL; + + FreePool (ConfigRespOld); + ConfigRespOld = NULL; + + if (RetVal) { + break; + } } + + return RetVal; } + /** Find menu which will show next time. @@ -981,170 +1852,92 @@ UpdateNvInfoInForm ( about the Selection, form and formset to be displayed. On output, Selection return the screen item that is selected by user. - @param Repaint Whether need to repaint the menu. - @param NewLine Whether need to show at new line. + @param SettingLevel Input Settting level, if it is FormLevel, just exit current form. + else, we need to exit current formset. - @retval TRUE Need return. - @retval FALSE No need to return. + @retval TRUE Exit current form. + @retval FALSE User press ESC and keep in current form. **/ BOOLEAN FindNextMenu ( - IN OUT UI_MENU_SELECTION *Selection, - IN BOOLEAN *Repaint, - IN BOOLEAN *NewLine + IN OUT UI_MENU_SELECTION *Selection, + IN BROWSER_SETTING_SCOPE SettingLevel ) { - UI_MENU_LIST *CurrentMenu; - CHAR16 YesResponse; - CHAR16 NoResponse; - EFI_INPUT_KEY Key; - BROWSER_SETTING_SCOPE Scope; + FORM_ENTRY_INFO *CurrentMenu; + FORM_ENTRY_INFO *ParentMenu; + BROWSER_SETTING_SCOPE Scope; CurrentMenu = Selection->CurrentMenu; + ParentMenu = NULL; + Scope = FormSetLevel; - if (CurrentMenu != NULL && CurrentMenu->Parent != NULL) { + if (CurrentMenu != NULL && (ParentMenu = UiFindParentMenu(CurrentMenu)) != NULL) { // // we have a parent, so go to the parent menu // - if (CompareGuid (&CurrentMenu->FormSetGuid, &CurrentMenu->Parent->FormSetGuid)) { - // - // The parent menu and current menu are in the same formset - // - Selection->Action = UI_ACTION_REFRESH_FORM; - Scope = FormLevel; - } else { - Selection->Action = UI_ACTION_REFRESH_FORMSET; - CopyMem (&Selection->FormSetGuid, &CurrentMenu->Parent->FormSetGuid, sizeof (EFI_GUID)); - Selection->Handle = CurrentMenu->Parent->HiiHandle; - Scope = FormSetLevel; - } - - // - // Form Level Check whether the data is changed. - // - if ((gBrowserSettingScope == FormLevel && Selection->Form->NvUpdateRequired) || - (gBrowserSettingScope == FormSetLevel && IsNvUpdateRequired(Selection->FormSet) && Scope == FormSetLevel)) { - gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); - - YesResponse = gYesResponse[0]; - NoResponse = gNoResponse[0]; - - // - // If NV flag is up, prompt user - // - do { - CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gSaveChanges, 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 (Key.ScanCode == SCAN_ESC) { + if (CompareGuid (&CurrentMenu->FormSetGuid, &ParentMenu->FormSetGuid)) { + if (SettingLevel == FormSetLevel) { // - // User hits the ESC key, Ingore. + // Find a menu which has different formset guid with current. // - if (Repaint != NULL) { - *Repaint = TRUE; - } - if (NewLine != NULL) { - *NewLine = TRUE; + while (CompareGuid (&CurrentMenu->FormSetGuid, &ParentMenu->FormSetGuid)) { + CurrentMenu = ParentMenu; + if ((ParentMenu = UiFindParentMenu(CurrentMenu)) == NULL) { + break; + } } - Selection->Action = UI_ACTION_NONE; - return FALSE; - } - - if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (YesResponse | UPPER_LOWER_CASE_OFFSET)) { - // - // If the user hits the YesResponse key - // - SubmitForm (Selection->FormSet, Selection->Form, Scope); + if (ParentMenu != NULL) { + Scope = FormSetLevel; + } } else { - // - // If the user hits the NoResponse key - // - DiscardForm (Selection->FormSet, Selection->Form, Scope); + Scope = FormLevel; } + } else { + Scope = FormSetLevel; } - - Selection->Statement = NULL; - - Selection->FormId = CurrentMenu->Parent->FormId; - Selection->QuestionId = CurrentMenu->Parent->QuestionId; - - // - // Clear highlight record for this menu - // - CurrentMenu->QuestionId = 0; - return FALSE; - } - - if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) == FORMSET_CLASS_FRONT_PAGE) { - // - // We never exit FrontPage, so skip the ESC - // - Selection->Action = UI_ACTION_NONE; - return FALSE; } // - // We are going to leave current FormSet, so check uncommited data in this FormSet + // Form Level Check whether the data is changed. // - if (gBrowserSettingScope != SystemLevel && IsNvUpdateRequired(Selection->FormSet)) { - gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); - - YesResponse = gYesResponse[0]; - NoResponse = gNoResponse[0]; - - // - // If NV flag is up, prompt user - // - do { - CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gSaveChanges, 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 (Key.ScanCode == SCAN_ESC) { - // - // User hits the ESC key - // - if (Repaint != NULL) { - *Repaint = TRUE; - } - - if (NewLine != NULL) { - *NewLine = TRUE; - } - - Selection->Action = UI_ACTION_NONE; + if ((gBrowserSettingScope == FormLevel && IsNvUpdateRequiredForForm (Selection->Form)) || + (gBrowserSettingScope == FormSetLevel && IsNvUpdateRequiredForFormSet(Selection->FormSet) && Scope == FormSetLevel)) { + if (!ProcessChangedData(Selection, Scope)) { return FALSE; } + } - if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (YesResponse | UPPER_LOWER_CASE_OFFSET)) { - // - // If the user hits the YesResponse key - // - SubmitForm (Selection->FormSet, Selection->Form, FormSetLevel); + if (ParentMenu != NULL) { + // + // ParentMenu is found. Then, go to it. + // + if (Scope == FormLevel) { + Selection->Action = UI_ACTION_REFRESH_FORM; } else { - // - // If the user hits the NoResponse key - // - DiscardForm (Selection->FormSet, Selection->Form, FormSetLevel); + Selection->Action = UI_ACTION_REFRESH_FORMSET; + CopyMem (&Selection->FormSetGuid, &ParentMenu->FormSetGuid, sizeof (EFI_GUID)); + Selection->Handle = ParentMenu->HiiHandle; } - } - Selection->Statement = NULL; - if (CurrentMenu != NULL) { + Selection->Statement = NULL; + + Selection->FormId = ParentMenu->FormId; + Selection->QuestionId = ParentMenu->QuestionId; + + // + // Clear highlight record for this menu + // CurrentMenu->QuestionId = 0; + return FALSE; } + // + // Current in root page, exit the SendForm + // Selection->Action = UI_ACTION_EXIT; + return TRUE; } @@ -1246,17 +2039,17 @@ ProcessCallBackFunction ( case EFI_BROWSER_ACTION_REQUEST_RESET: DiscardFormIsRequired = TRUE; gResetRequired = TRUE; - Selection->Action = UI_ACTION_EXIT; + NeedExit = TRUE; break; case EFI_BROWSER_ACTION_REQUEST_SUBMIT: SubmitFormIsRequired = TRUE; - Selection->Action = UI_ACTION_EXIT; + NeedExit = TRUE; break; case EFI_BROWSER_ACTION_REQUEST_EXIT: DiscardFormIsRequired = TRUE; - Selection->Action = UI_ACTION_EXIT; + NeedExit = TRUE; break; case EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT: @@ -1267,7 +2060,7 @@ ProcessCallBackFunction ( case EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT: DiscardFormIsRequired = TRUE; - SettingLevel = FormLevel; + SettingLevel = FormLevel; NeedExit = TRUE; break; @@ -1320,7 +2113,7 @@ ProcessCallBackFunction ( } if (NeedExit) { - FindNextMenu (Selection, NULL, NULL); + FindNextMenu (Selection, SettingLevel); } return Status; @@ -1400,9 +2193,7 @@ SetupBrowser ( EFI_HANDLE NotifyHandle; FORM_BROWSER_STATEMENT *Statement; EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess; - EFI_INPUT_KEY Key; - gMenuRefreshHead = NULL; ConfigAccess = Selection->FormSet->ConfigAccess; // @@ -1423,21 +2214,17 @@ SetupBrowser ( // // Initialize current settings of Questions in this FormSet // - Status = InitializeCurrentSetting (Selection->FormSet); - if (EFI_ERROR (Status)) { - goto Done; - } + InitializeCurrentSetting (Selection->FormSet); // - // Update gOldFormSet on maintain back up FormSet list. - // And, make gOldFormSet point to current FormSet. + // Initilize Action field. // - if (gOldFormSet != NULL) { - RemoveEntryList (&gOldFormSet->Link); - DestroyFormSet (gOldFormSet); - } - gOldFormSet = Selection->FormSet; - InsertTailList (&gBrowserFormSetList, &gOldFormSet->Link); + Selection->Action = UI_ACTION_REFRESH_FORM; + + // + // Clean the mCurFakeQestId value is formset refreshed. + // + mCurFakeQestId = 0; do { // @@ -1480,10 +2267,7 @@ SetupBrowser ( // // Form is suppressed. // - do { - CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gFormSuppress, gPressEnter, gEmptyString); - } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); - + gBrowserStatus = BROWSER_FORM_SUPPRESS; Status = EFI_NOT_FOUND; goto Done; } @@ -1498,7 +2282,6 @@ SetupBrowser ( ((Selection->Handle != mCurrentHiiHandle) || (!CompareGuid (&Selection->FormSetGuid, &mCurrentFormSetGuid)) || (Selection->FormId != mCurrentFormId))) { - // // Keep current form information // @@ -1511,12 +2294,6 @@ SetupBrowser ( goto Done; } - // - // EXIT requests to close form. - // - if (Selection->Action == UI_ACTION_EXIT) { - goto Done; - } // // IFR is updated during callback of open form, force to reparse the IFR binary // @@ -1535,12 +2312,6 @@ SetupBrowser ( goto Done; } - // - // EXIT requests to close form. - // - if (Selection->Action == UI_ACTION_EXIT) { - goto Done; - } // // IFR is updated during callback of read value, force to reparse the IFR binary // @@ -1550,15 +2321,10 @@ SetupBrowser ( break; } - // - // Displays the Header and Footer borders - // - DisplayPageFrame (Selection); - // // Display form // - Status = DisplayForm (Selection); + Status = DisplayForm (); if (EFI_ERROR (Status)) { goto Done; } @@ -1568,20 +2334,16 @@ SetupBrowser ( // Statement = Selection->Statement; if (Statement != NULL) { - if ((Statement->QuestionFlags & EFI_IFR_FLAG_RESET_REQUIRED) == EFI_IFR_FLAG_RESET_REQUIRED) { - gResetRequired = TRUE; - } - if ((ConfigAccess != NULL) && ((Statement->QuestionFlags & EFI_IFR_FLAG_CALLBACK) == EFI_IFR_FLAG_CALLBACK) && (Statement->Operand != EFI_IFR_PASSWORD_OP)) { - Status = ProcessCallBackFunction(Selection, Statement, EFI_BROWSER_ACTION_CHANGING, FALSE); - if (Statement->Operand == EFI_IFR_REF_OP && Selection->Action != UI_ACTION_EXIT) { + Status = ProcessCallBackFunction(Selection, Statement, EFI_BROWSER_ACTION_CHANGING, FALSE); + if (Statement->Operand == EFI_IFR_REF_OP) { // // Process dynamic update ref opcode. // if (!EFI_ERROR (Status)) { - Status = ProcessGotoOpCode(Statement, Selection, NULL, NULL); + Status = ProcessGotoOpCode(Statement, Selection); } // @@ -1602,6 +2364,27 @@ SetupBrowser ( } } + // + // Check whether Exit flag is TRUE. + // + if (gExitRequired) { + switch (gBrowserSettingScope) { + case SystemLevel: + Selection->Action = UI_ACTION_EXIT; + break; + + case FormSetLevel: + case FormLevel: + FindNextMenu (Selection, gBrowserSettingScope); + break; + + default: + break; + } + + gExitRequired = FALSE; + } + // // Before exit the form, invoke ConfigAccess.Callback() with EFI_BROWSER_ACTION_FORM_CLOSE // for each question with callback flag. diff --git a/MdeModulePkg/Universal/SetupBrowserDxe/Setup.c b/MdeModulePkg/Universal/SetupBrowserDxe/Setup.c index ed904161b6..c4dd18a04f 100644 --- a/MdeModulePkg/Universal/SetupBrowserDxe/Setup.c +++ b/MdeModulePkg/Universal/SetupBrowserDxe/Setup.c @@ -26,13 +26,21 @@ SETUP_DRIVER_PRIVATE_DATA mPrivateData = { RegisterHotKey, RegiserExitHandler, SaveReminder + }, + { + BROWSER_EXTENSION2_VERSION_1, + SetScope, + RegisterHotKey, + RegiserExitHandler, + IsBrowserDataModified, + ExecuteAction, } }; EFI_HII_DATABASE_PROTOCOL *mHiiDatabase; -EFI_HII_STRING_PROTOCOL *mHiiString; EFI_HII_CONFIG_ROUTING_PROTOCOL *mHiiConfigRouting; EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *mPathFromText; +EDKII_FORM_DISPLAY_ENGINE_PROTOCOL *mFormDisplay; UINTN gBrowserContextCount = 0; LIST_ENTRY gBrowserContextList = INITIALIZE_LIST_HEAD_VARIABLE (gBrowserContextList); @@ -40,153 +48,261 @@ LIST_ENTRY gBrowserFormSetList = INITIALIZE_LIST_HEAD_VARIABLE (gBrowserFor LIST_ENTRY gBrowserHotKeyList = INITIALIZE_LIST_HEAD_VARIABLE (gBrowserHotKeyList); LIST_ENTRY gBrowserStorageList = INITIALIZE_LIST_HEAD_VARIABLE (gBrowserStorageList); -BANNER_DATA *gBannerData; -EFI_HII_HANDLE gFrontPageHandle; -UINTN gClassOfVfr; -UINTN gFunctionKeySetting; BOOLEAN gResetRequired; -EFI_HII_HANDLE gHiiHandle; -UINT16 gDirection; -EFI_SCREEN_DESCRIPTOR gScreenDimensions; +BOOLEAN gExitRequired; BROWSER_SETTING_SCOPE gBrowserSettingScope = FormSetLevel; BOOLEAN mBrowserScopeFirstSet = TRUE; EXIT_HANDLER ExitHandlerFunction = NULL; -UINTN gFooterHeight; // // Browser Global Strings // -CHAR16 *gSaveFailed; -CHAR16 *gDiscardFailed; -CHAR16 *gDefaultFailed; -CHAR16 *gEnterString; -CHAR16 *gEnterCommitString; -CHAR16 *gEnterEscapeString; -CHAR16 *gEscapeString; -CHAR16 *gMoveHighlight; -CHAR16 *gMakeSelection; -CHAR16 *gDecNumericInput; -CHAR16 *gHexNumericInput; -CHAR16 *gToggleCheckBox; -CHAR16 *gPromptForData; -CHAR16 *gPromptForPassword; -CHAR16 *gPromptForNewPassword; -CHAR16 *gConfirmPassword; -CHAR16 *gConfirmError; -CHAR16 *gPassowordInvalid; -CHAR16 *gPressEnter; CHAR16 *gEmptyString; -CHAR16 *gAreYouSure; -CHAR16 *gYesResponse; -CHAR16 *gNoResponse; -CHAR16 *gMiniString; -CHAR16 *gPlusString; -CHAR16 *gMinusString; -CHAR16 *gAdjustNumber; -CHAR16 *gSaveChanges; -CHAR16 *gOptionMismatch; -CHAR16 *gFormSuppress; -CHAR16 *gProtocolNotFound; CHAR16 *mUnknownString = L"!"; -CHAR16 gPromptBlockWidth; -CHAR16 gOptionBlockWidth; -CHAR16 gHelpBlockWidth; - EFI_GUID gZeroGuid = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; EFI_GUID gSetupBrowserGuid = { 0xab368524, 0xb60c, 0x495b, {0xa0, 0x9, 0x12, 0xe8, 0x5b, 0x1a, 0xea, 0x32} }; FORM_BROWSER_FORMSET *gOldFormSet = NULL; +extern UINT32 gBrowserStatus; +extern CHAR16 *gErrorInfo; +extern FORM_DISPLAY_ENGINE_FORM gDisplayFormData; + +/** + Create a menu with specified formset GUID and form ID, and add it as a child + of the given parent menu. + + @param HiiHandle Hii handle related to this formset. + @param FormSetGuid The Formset Guid of menu to be added. + @param FormId The Form ID of menu to be added. + @param QuestionId The question id of this menu to be added. + + @return A pointer to the newly added menu or NULL if memory is insufficient. + +**/ +FORM_ENTRY_INFO * +UiAddMenuList ( + IN EFI_HII_HANDLE HiiHandle, + IN EFI_GUID *FormSetGuid, + IN UINT16 FormId, + IN UINT16 QuestionId + ) +{ + FORM_ENTRY_INFO *MenuList; + + MenuList = AllocateZeroPool (sizeof (FORM_ENTRY_INFO)); + if (MenuList == NULL) { + return NULL; + } + + MenuList->Signature = FORM_ENTRY_INFO_SIGNATURE; + + MenuList->HiiHandle = HiiHandle; + CopyMem (&MenuList->FormSetGuid, FormSetGuid, sizeof (EFI_GUID)); + MenuList->FormId = FormId; + MenuList->QuestionId = QuestionId; -FUNCTIION_KEY_SETTING gFunctionKeySettingTable[] = { // - // Boot Manager + // If parent is not specified, it is the root Form of a Formset // - { - { - 0x847bc3fe, - 0xb974, - 0x446d, - { - 0x94, - 0x49, - 0x5a, - 0xd5, - 0x41, - 0x2e, - 0x99, - 0x3b + InsertTailList (&mPrivateData.FormBrowserEx2.FormViewHistoryHead, &MenuList->Link); + + return MenuList; +} + +/** + Return the form id for the input hiihandle and formset. + + @param HiiHandle HiiHandle for FormSet. + @param FormSetGuid The Formset GUID of the menu to search. + + @return First form's id for this form set. + +**/ +EFI_FORM_ID +GetFirstFormId ( + IN EFI_HII_HANDLE HiiHandle, + IN EFI_GUID *FormSetGuid + ) +{ + LIST_ENTRY *Link; + FORM_BROWSER_FORM *Form; + + Link = GetFirstNode (&gCurrentSelection->FormSet->FormListHead); + Form = FORM_BROWSER_FORM_FROM_LINK (Link); + + return Form->FormId; +} + +/** + Search Menu with given FormSetGuid and FormId in all cached menu list. + + @param HiiHandle HiiHandle for FormSet. + @param FormSetGuid The Formset GUID of the menu to search. + @param FormId The Form ID of menu to search. + + @return A pointer to menu found or NULL if not found. + +**/ +FORM_ENTRY_INFO * +UiFindMenuList ( + IN EFI_HII_HANDLE HiiHandle, + IN EFI_GUID *FormSetGuid, + IN UINT16 FormId + ) +{ + LIST_ENTRY *Link; + FORM_ENTRY_INFO *MenuList; + FORM_ENTRY_INFO *RetMenu; + EFI_FORM_ID FirstFormId; + + RetMenu = NULL; + + Link = GetFirstNode (&mPrivateData.FormBrowserEx2.FormViewHistoryHead); + while (!IsNull (&mPrivateData.FormBrowserEx2.FormViewHistoryHead, Link)) { + MenuList = FORM_ENTRY_INFO_FROM_LINK (Link); + Link = GetNextNode (&mPrivateData.FormBrowserEx2.FormViewHistoryHead, Link); + + // + // If already find the menu, free the menus behind it. + // + if (RetMenu != NULL) { + RemoveEntryList (&MenuList->Link); + FreePool (MenuList); + continue; + } + + // + // Find the same FromSet. + // + if (MenuList->HiiHandle == HiiHandle) { + if (CompareGuid (&MenuList->FormSetGuid, &gZeroGuid)) { + // + // FormSetGuid is not specified. + // + RetMenu = MenuList; + } else if (CompareGuid (&MenuList->FormSetGuid, FormSetGuid)) { + if (MenuList->FormId == FormId) { + RetMenu = MenuList; + } else if (FormId == 0 || MenuList->FormId == 0 ) { + FirstFormId = GetFirstFormId (HiiHandle, FormSetGuid); + if ((FormId == 0 && FirstFormId == MenuList->FormId) || (MenuList->FormId ==0 && FirstFormId == FormId)) { + RetMenu = MenuList; + } + } } - }, - NONE_FUNCTION_KEY_SETTING - }, + } + } + + return RetMenu; +} + +/** + Find parent menu for current menu. + + @param CurrentMenu Current Menu + + @retval The parent menu for current menu. +**/ +FORM_ENTRY_INFO * +UiFindParentMenu ( + IN FORM_ENTRY_INFO *CurrentMenu + ) +{ + FORM_ENTRY_INFO *ParentMenu; + + ParentMenu = NULL; + if (CurrentMenu->Link.BackLink != &mPrivateData.FormBrowserEx2.FormViewHistoryHead) { + ParentMenu = FORM_ENTRY_INFO_FROM_LINK (CurrentMenu->Link.BackLink); + } + + return ParentMenu; +} + +/** + Free Menu list linked list. + + @param MenuListHead One Menu list point in the menu list. + +**/ +VOID +UiFreeMenuList ( + LIST_ENTRY *MenuListHead + ) +{ + FORM_ENTRY_INFO *MenuList; + + while (!IsListEmpty (MenuListHead)) { + MenuList = FORM_ENTRY_INFO_FROM_LINK (MenuListHead->ForwardLink); + RemoveEntryList (&MenuList->Link); + + FreePool (MenuList); + } +} + +/** + Load all hii formset to the browser. + +**/ +VOID +LoadAllHiiFormset ( + VOID + ) +{ + FORM_BROWSER_FORMSET *LocalFormSet; + EFI_HII_HANDLE *HiiHandles; + UINTN Index; + EFI_GUID ZeroGuid; + EFI_STATUS Status; + // - // Device Manager + // Get all the Hii handles // - { - { - 0x3ebfa8e6, - 0x511d, - 0x4b5b, - { - 0xa9, - 0x5f, - 0xfb, - 0x38, - 0x26, - 0xf, - 0x1c, - 0x27 - } - }, - NONE_FUNCTION_KEY_SETTING - }, + HiiHandles = HiiGetHiiHandles (NULL); + ASSERT (HiiHandles != NULL); + // - // BMM FormSet. + // Search for formset of each class type // - { - { - 0x642237c7, - 0x35d4, - 0x472d, - { - 0x83, - 0x65, - 0x12, - 0xe0, - 0xcc, - 0xf2, - 0x7a, - 0x22 - } - }, - NONE_FUNCTION_KEY_SETTING - }, + for (Index = 0; HiiHandles[Index] != NULL; Index++) { + // + // Check HiiHandles[Index] does exist in global maintain list. + // + if (GetFormSetFromHiiHandle (HiiHandles[Index]) != NULL) { + continue; + } + + // + // Initilize FormSet Setting + // + LocalFormSet = AllocateZeroPool (sizeof (FORM_BROWSER_FORMSET)); + ASSERT (LocalFormSet != NULL); + ZeroMem (&ZeroGuid, sizeof (ZeroGuid)); + Status = InitializeFormSet (HiiHandles[Index], &ZeroGuid, LocalFormSet); + if (EFI_ERROR (Status) || IsListEmpty (&LocalFormSet->FormListHead)) { + DestroyFormSet (LocalFormSet); + continue; + } + InitializeCurrentSetting (LocalFormSet); + + // + // Initilize Questions' Value + // + Status = LoadFormSetConfig (NULL, LocalFormSet); + if (EFI_ERROR (Status)) { + DestroyFormSet (LocalFormSet); + continue; + } + } + // - // BMM File Explorer FormSet. + // Free resources, and restore gOldFormSet and gClassOfVfr // - { - { - 0x1f2d63e1, - 0xfebd, - 0x4dc7, - { - 0x9c, - 0xc5, - 0xba, - 0x2b, - 0x1c, - 0xef, - 0x9c, - 0x5b - } - }, - NONE_FUNCTION_KEY_SETTING - }, -}; + FreePool (HiiHandles); +} /** This is the routine which an external caller uses to direct the browser @@ -229,26 +345,14 @@ SendForm ( UI_MENU_SELECTION *Selection; UINTN Index; FORM_BROWSER_FORMSET *FormSet; - LIST_ENTRY *Link; + FORM_ENTRY_INFO *MenuList; // - // Calculate total number of Register HotKeys. + // If EDKII_FORM_DISPLAY_ENGINE_PROTOCOL not found, return EFI_UNSUPPORTED. // - Index = 0; - Link = GetFirstNode (&gBrowserHotKeyList); - while (!IsNull (&gBrowserHotKeyList, Link)) { - Link = GetNextNode (&gBrowserHotKeyList, Link); - Index ++; + if (mFormDisplay == NULL) { + return EFI_UNSUPPORTED; } - // - // Show three HotKeys help information on one ROW. - // - gFooterHeight = FOOTER_HEIGHT + (Index / 3); - - // - // Clean the history menu list. - // - InitializeListHead (&gMenuList); // // Save globals used by SendForm() @@ -256,65 +360,10 @@ SendForm ( SaveBrowserContext (); gResetRequired = FALSE; - Status = EFI_SUCCESS; - ZeroMem (&gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR)); - - // - // Seed the dimensions in the global - // - gST->ConOut->QueryMode ( - gST->ConOut, - gST->ConOut->Mode->Mode, - &gScreenDimensions.RightColumn, - &gScreenDimensions.BottomRow - ); - - if (ScreenDimensions != NULL) { - // - // Check local dimension vs. global dimension. - // - if ((gScreenDimensions.RightColumn < ScreenDimensions->RightColumn) || - (gScreenDimensions.BottomRow < ScreenDimensions->BottomRow) - ) { - Status = EFI_INVALID_PARAMETER; - goto Done; - } else { - // - // Local dimension validation. - // - if ((ScreenDimensions->RightColumn > ScreenDimensions->LeftColumn) && - (ScreenDimensions->BottomRow > ScreenDimensions->TopRow) && - ((ScreenDimensions->RightColumn - ScreenDimensions->LeftColumn) > 2) && - ( - (ScreenDimensions->BottomRow - ScreenDimensions->TopRow) > STATUS_BAR_HEIGHT + - SCROLL_ARROW_HEIGHT * - 2 + - FRONT_PAGE_HEADER_HEIGHT + - gFooterHeight + - 1 - ) - ) { - CopyMem (&gScreenDimensions, (VOID *) ScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR)); - } else { - Status = EFI_INVALID_PARAMETER; - goto Done; - } - } - } - - gOptionBlockWidth = (CHAR16) ((gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn) / 3); - gPromptBlockWidth = (CHAR16) (gOptionBlockWidth + LEFT_SKIPPED_COLUMNS); - gHelpBlockWidth = (CHAR16) (gOptionBlockWidth - LEFT_SKIPPED_COLUMNS); - - // - // Initialize the strings for the browser, upon exit of the browser, the strings will be freed - // - InitializeBrowserStrings (); - - // - // Ensure we are in Text mode - // - gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + gExitRequired = FALSE; + Status = EFI_SUCCESS; + gEmptyString = L""; + gDisplayFormData.ScreenDimensions = (EFI_SCREEN_DESCRIPTOR *) ScreenDimensions; for (Index = 0; Index < HandleCount; Index++) { Selection = AllocateZeroPool (sizeof (UI_MENU_SELECTION)); @@ -335,18 +384,13 @@ SendForm ( // // Initialize internal data structures of FormSet // - Status = InitializeFormSet (Selection->Handle, &Selection->FormSetGuid, FormSet, TRUE); + Status = InitializeFormSet (Selection->Handle, &Selection->FormSetGuid, FormSet); if (EFI_ERROR (Status) || IsListEmpty (&FormSet->FormListHead)) { DestroyFormSet (FormSet); break; } Selection->FormSet = FormSet; - // - // Try to find pre FormSet in the maintain backup list. - // - gOldFormSet = GetFormSetFromHiiHandle (Selection->Handle); - // // Display this formset // @@ -356,27 +400,36 @@ SendForm ( gCurrentSelection = NULL; - if (EFI_ERROR (Status)) { - break; - } - - } while (Selection->Action == UI_ACTION_REFRESH_FORMSET); - - if (gOldFormSet != NULL) { // // If no data is changed, don't need to save current FormSet into the maintain list. // - if (!IsNvUpdateRequired (gOldFormSet)) { - CleanBrowserStorage(gOldFormSet); - RemoveEntryList (&gOldFormSet->Link); - DestroyFormSet (gOldFormSet); + if (!IsNvUpdateRequiredForFormSet (FormSet) && !IsStorageDataChangedForFormSet(FormSet)) { + CleanBrowserStorage(FormSet); + RemoveEntryList (&FormSet->Link); + DestroyFormSet (FormSet); } - gOldFormSet = NULL; - } + + if (EFI_ERROR (Status)) { + break; + } + } while (Selection->Action == UI_ACTION_REFRESH_FORMSET); FreePool (Selection); } + // + // Still has error info, pop up a message. + // + if (gBrowserStatus != BROWSER_SUCCESS) { + gDisplayFormData.BrowserStatus = gBrowserStatus; + gDisplayFormData.ErrorString = gErrorInfo; + + gBrowserStatus = BROWSER_SUCCESS; + gErrorInfo = NULL; + + mFormDisplay->FormDisplay (&gDisplayFormData, NULL); + } + if (ActionRequest != NULL) { *ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE; if (gResetRequired) { @@ -384,13 +437,17 @@ SendForm ( } } - FreeBrowserStrings (); - UiFreeMenuList(&gMenuList); + mFormDisplay->ExitDisplay(); - gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); - gST->ConOut->ClearScreen (gST->ConOut); + // + // Clear the menu history data. + // + while (!IsListEmpty (&mPrivateData.FormBrowserEx2.FormViewHistoryHead)) { + MenuList = FORM_ENTRY_INFO_FROM_LINK (mPrivateData.FormBrowserEx2.FormViewHistoryHead.ForwardLink); + RemoveEntryList (&MenuList->Link); + FreePool (MenuList); + } -Done: // // Restore globals used by SendForm() // @@ -399,10 +456,93 @@ Done: return Status; } +/** + Get or set data to the storage. + + @param ResultsDataSize The size of the buffer associatedwith ResultsData. + @param ResultsData A string returned from an IFR browser or + equivalent. The results string will have no + routing information in them. + @param RetrieveData A BOOLEAN field which allows an agent to retrieve + (if RetrieveData = TRUE) data from the uncommitted + browser state information or set (if RetrieveData + = FALSE) data in the uncommitted browser state + information. + @param Storage The pointer to the storage. + + @retval EFI_SUCCESS The results have been distributed or are awaiting + distribution. + +**/ +EFI_STATUS +ProcessStorage ( + IN OUT UINTN *ResultsDataSize, + IN OUT EFI_STRING *ResultsData, + IN BOOLEAN RetrieveData, + IN BROWSER_STORAGE *Storage + ) +{ + CHAR16 *ConfigResp; + EFI_STATUS Status; + CHAR16 *StrPtr; + UINTN BufferSize; + UINTN TmpSize; + + if (RetrieveData) { + // + // Generate + // + Status = StorageToConfigResp (Storage, &ConfigResp, Storage->ConfigRequest, TRUE); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Skip and '&' to point to when first copy the configbody. + // Also need to consider add "\0" at first time. + // + StrPtr = ConfigResp + StrLen (Storage->ConfigHdr) + 1; + BufferSize = StrSize (StrPtr); + + + // + // Copy the data if the input buffer is bigger enough. + // + if (*ResultsDataSize >= BufferSize) { + StrCpy (*ResultsData, StrPtr); + } + + *ResultsDataSize = BufferSize; + FreePool (ConfigResp); + } else { + // + // Prepare + // + TmpSize = StrLen (*ResultsData); + BufferSize = (TmpSize + StrLen (Storage->ConfigHdr) + 2) * sizeof (CHAR16); + ConfigResp = AllocateZeroPool (BufferSize); + ASSERT (ConfigResp != NULL); + + StrCpy (ConfigResp, Storage->ConfigHdr); + StrCat (ConfigResp, L"&"); + StrCat (ConfigResp, *ResultsData); + + // + // Update Browser uncommited data + // + Status = ConfigRespToStorage (Storage, ConfigResp); + FreePool (ConfigResp); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} /** - This function is called by a callback handler to retrieve uncommitted state - data from the browser. + This routine called this service in the browser to retrieve or set certain uncommitted + state information that resides in the open formsets. @param This A pointer to the EFI_FORM_BROWSER2_PROTOCOL instance. @@ -440,60 +580,69 @@ BrowserCallback ( { EFI_STATUS Status; LIST_ENTRY *Link; - FORMSET_STORAGE *Storage; + BROWSER_STORAGE *Storage; + FORMSET_STORAGE *FormsetStorage; FORM_BROWSER_FORMSET *FormSet; + UINTN TotalSize; BOOLEAN Found; - CHAR16 *ConfigResp; - CHAR16 *StrPtr; - UINTN BufferSize; - UINTN TmpSize; if (ResultsDataSize == NULL || ResultsData == NULL) { return EFI_INVALID_PARAMETER; } - if (gCurrentSelection == NULL) { - return EFI_NOT_READY; - } - - Storage = NULL; - ConfigResp = NULL; - FormSet = gCurrentSelection->FormSet; + TotalSize = *ResultsDataSize; + Storage = NULL; + Found = FALSE; + Status = EFI_SUCCESS; // - // Find target storage + // If set browser data, pre load all hii formset to avoid set the varstore which is not + // saved in browser. // - Link = GetFirstNode (&FormSet->StorageListHead); - if (IsNull (&FormSet->StorageListHead, Link)) { - return EFI_UNSUPPORTED; + if (!RetrieveData && (gBrowserSettingScope == SystemLevel)) { + LoadAllHiiFormset(); } if (VariableGuid != NULL) { // - // Try to find target storage + // Try to find target storage in the current formset. // - Found = FALSE; - while (!IsNull (&FormSet->StorageListHead, Link)) { - Storage = FORMSET_STORAGE_FROM_LINK (Link); - Link = GetNextNode (&FormSet->StorageListHead, Link); + Link = GetFirstNode (&gBrowserStorageList); + while (!IsNull (&gBrowserStorageList, Link)) { + Storage = BROWSER_STORAGE_FROM_LINK (Link); + Link = GetNextNode (&gBrowserStorageList, Link); + // + // Check the current storage. + // + if (!CompareGuid (&Storage->Guid, (EFI_GUID *) VariableGuid)) { + continue; + } - if (CompareGuid (&Storage->BrowserStorage->Guid, (EFI_GUID *) VariableGuid)) { - if (Storage->BrowserStorage->Type == EFI_HII_VARSTORE_BUFFER || - Storage->BrowserStorage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER) { - // - // Buffer storage require both GUID and Name - // - if (VariableName == NULL) { - return EFI_NOT_FOUND; - } + if (Storage->Type == EFI_HII_VARSTORE_BUFFER || + Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER) { + // + // Buffer storage require both GUID and Name + // + if (VariableName == NULL) { + return EFI_NOT_FOUND; + } - if (StrCmp (Storage->BrowserStorage->Name, (CHAR16 *) VariableName) != 0) { - continue; - } + if (StrCmp (Storage->Name, (CHAR16 *) VariableName) != 0) { + continue; } - Found = TRUE; - break; } + + Status = ProcessStorage (&TotalSize, &ResultsData, RetrieveData, Storage); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Different formsets may have same varstore, so here just set the flag + // not exit the circle. + // + Found = TRUE; + break; } if (!Found) { @@ -503,65 +652,62 @@ BrowserCallback ( // // GUID/Name is not specified, take the first storage in FormSet // - Storage = FORMSET_STORAGE_FROM_LINK (Link); - } - - if (RetrieveData) { - // - // Skip if there is no RequestElement - // - if (Storage->ElementCount == 0) { - return EFI_SUCCESS; + if (gCurrentSelection == NULL) { + return EFI_NOT_READY; } // // Generate // - Status = StorageToConfigResp (Storage->BrowserStorage, &ConfigResp, Storage->ConfigRequest); + FormSet = gCurrentSelection->FormSet; + Link = GetFirstNode (&FormSet->StorageListHead); + if (IsNull (&FormSet->StorageListHead, Link)) { + return EFI_UNSUPPORTED; + } + + FormsetStorage = FORMSET_STORAGE_FROM_LINK (Link); + + Status = ProcessStorage (&TotalSize, &ResultsData, RetrieveData, FormsetStorage->BrowserStorage); if (EFI_ERROR (Status)) { return Status; } + } - // - // Skip and '&' to point to - // - StrPtr = ConfigResp + StrLen (Storage->BrowserStorage->ConfigHdr) + 1; + if (RetrieveData) { + Status = TotalSize <= *ResultsDataSize ? EFI_SUCCESS : EFI_BUFFER_TOO_SMALL; + *ResultsDataSize = TotalSize; + } + + return Status; - BufferSize = StrSize (StrPtr); - if (*ResultsDataSize < BufferSize) { - *ResultsDataSize = BufferSize; +} - FreePool (ConfigResp); - return EFI_BUFFER_TOO_SMALL; - } - *ResultsDataSize = BufferSize; - CopyMem (ResultsData, StrPtr, BufferSize); +/** + Callback function for SimpleTextInEx protocol install events - FreePool (ConfigResp); - } else { - // - // Prepare - // - TmpSize = StrLen (ResultsData); - BufferSize = (TmpSize + StrLen (Storage->BrowserStorage->ConfigHdr) + 2) * sizeof (CHAR16); - ConfigResp = AllocateZeroPool (BufferSize); - ASSERT (ConfigResp != NULL); + @param Event the event that is signaled. + @param Context not used here. - StrCpy (ConfigResp, Storage->BrowserStorage->ConfigHdr); - StrCat (ConfigResp, L"&"); - StrCat (ConfigResp, ResultsData); +**/ +VOID +EFIAPI +FormDisplayCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; - // - // Update Browser uncommited data - // - Status = ConfigRespToStorage (Storage->BrowserStorage, ConfigResp); - if (EFI_ERROR (Status)) { - return Status; - } + if (mFormDisplay != NULL) { + return; } - return EFI_SUCCESS; + Status = gBS->LocateProtocol ( + &gEdkiiFormDisplayEngineProtocolGuid, + NULL, + (VOID **) &mFormDisplay + ); } /** @@ -582,8 +728,7 @@ InitializeSetup ( ) { EFI_STATUS Status; - EFI_INPUT_KEY DefaultHotKey; - EFI_STRING HelpString; + VOID *Registration; // // Locate required Hii relative protocols @@ -595,13 +740,6 @@ InitializeSetup ( ); ASSERT_EFI_ERROR (Status); - Status = gBS->LocateProtocol ( - &gEfiHiiStringProtocolGuid, - NULL, - (VOID **) &mHiiString - ); - ASSERT_EFI_ERROR (Status); - Status = gBS->LocateProtocol ( &gEfiHiiConfigRoutingProtocolGuid, NULL, @@ -615,30 +753,6 @@ InitializeSetup ( (VOID **) &mPathFromText ); - // - // Publish our HII data - // - gHiiHandle = HiiAddPackages ( - &gSetupBrowserGuid, - ImageHandle, - SetupBrowserStrings, - NULL - ); - ASSERT (gHiiHandle != NULL); - - // - // Initialize Driver private data - // - gBannerData = AllocateZeroPool (sizeof (BANNER_DATA)); - ASSERT (gBannerData != NULL); - - // - // Initialize generic help strings. - // - gSaveFailed = GetToken (STRING_TOKEN (SAVE_FAILED), gHiiHandle); - gDiscardFailed = GetToken (STRING_TOKEN (DISCARD_FAILED), gHiiHandle); - gDefaultFailed = GetToken (STRING_TOKEN (DEFAULT_FAILED), gHiiHandle); - // // Install FormBrowser2 protocol // @@ -652,34 +766,45 @@ InitializeSetup ( ASSERT_EFI_ERROR (Status); // - // Install default HotKey F10 for Save - // - DefaultHotKey.UnicodeChar = CHAR_NULL; - HelpString = GetToken (STRING_TOKEN (FUNCTION_TEN_STRING), gHiiHandle); - DefaultHotKey.ScanCode = SCAN_F10; - RegisterHotKey (&DefaultHotKey, BROWSER_ACTION_SUBMIT, 0, HelpString); - FreePool (HelpString); - // - // Install default HotKey F9 for Reset To Defaults - // - DefaultHotKey.ScanCode = SCAN_F9; - HelpString = GetToken (STRING_TOKEN (FUNCTION_NINE_STRING), gHiiHandle); - RegisterHotKey (&DefaultHotKey, BROWSER_ACTION_DEFAULT, EFI_HII_DEFAULT_CLASS_STANDARD, HelpString); - FreePool (HelpString); - - // - // Install FormBrowserEx protocol + // Install FormBrowserEx2 protocol // + InitializeListHead (&mPrivateData.FormBrowserEx2.FormViewHistoryHead); mPrivateData.Handle = NULL; + Status = gBS->InstallProtocolInterface ( + &mPrivateData.Handle, + &gEdkiiFormBrowserEx2ProtocolGuid, + EFI_NATIVE_INTERFACE, + &mPrivateData.FormBrowserEx2 + ); + ASSERT_EFI_ERROR (Status); + Status = gBS->InstallProtocolInterface ( &mPrivateData.Handle, &gEfiFormBrowserExProtocolGuid, EFI_NATIVE_INTERFACE, &mPrivateData.FormBrowserEx ); - ASSERT_EFI_ERROR (Status); + ASSERT_EFI_ERROR (Status); + + InitializeDisplayFormData (); + + Status = gBS->LocateProtocol ( + &gEdkiiFormDisplayEngineProtocolGuid, + NULL, + (VOID **) &mFormDisplay + ); - return Status; + if (EFI_ERROR (Status)) { + EfiCreateProtocolNotifyEvent ( + &gEdkiiFormDisplayEngineProtocolGuid, + TPL_CALLBACK, + FormDisplayCallback, + NULL, + &Registration + ); + } + + return EFI_SUCCESS; } @@ -815,58 +940,6 @@ NewStringCat ( *Dest = NewString; } - -/** - Synchronize or restore Storage's Edit copy and Shadow copy. - - @param Storage The Storage to be synchronized. - @param SyncOrRestore Sync the buffer to editbuffer or Restore the - editbuffer to buffer - if TRUE, copy the editbuffer to the buffer. - if FALSE, copy the buffer to the editbuffer. - -**/ -VOID -SynchronizeStorage ( - IN BROWSER_STORAGE *Storage, - IN BOOLEAN SyncOrRestore - ) -{ - LIST_ENTRY *Link; - NAME_VALUE_NODE *Node; - - switch (Storage->Type) { - case EFI_HII_VARSTORE_BUFFER: - case EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER: - if (SyncOrRestore) { - CopyMem (Storage->Buffer, Storage->EditBuffer, Storage->Size); - } else { - CopyMem (Storage->EditBuffer, Storage->Buffer, Storage->Size); - } - break; - - case EFI_HII_VARSTORE_NAME_VALUE: - Link = GetFirstNode (&Storage->NameValueListHead); - while (!IsNull (&Storage->NameValueListHead, Link)) { - Node = NAME_VALUE_NODE_FROM_LINK (Link); - - if (SyncOrRestore) { - NewStringCpy (&Node->Value, Node->EditValue); - } else { - NewStringCpy (&Node->EditValue, Node->Value); - } - - Link = GetNextNode (&Storage->NameValueListHead, Link); - } - break; - - case EFI_HII_VARSTORE_EFI_VARIABLE: - default: - break; - } -} - - /** Get Value for given Name from a NameValue Storage. @@ -923,6 +996,7 @@ GetValueByName ( @param Name The Name. @param Value The Value to set. @param SetValueTo Whether update editValue or Value. + @param ReturnNode The node use the input name. @retval EFI_SUCCESS Value found for given Name. @retval EFI_NOT_FOUND No such Name found in NameValue storage. @@ -930,10 +1004,11 @@ GetValueByName ( **/ EFI_STATUS SetValueByName ( - IN BROWSER_STORAGE *Storage, - IN CHAR16 *Name, - IN CHAR16 *Value, - IN GET_SET_QUESTION_VALUE_WITH SetValueTo + IN BROWSER_STORAGE *Storage, + IN CHAR16 *Name, + IN CHAR16 *Value, + IN GET_SET_QUESTION_VALUE_WITH SetValueTo, + OUT NAME_VALUE_NODE **ReturnNode ) { LIST_ENTRY *Link; @@ -964,6 +1039,11 @@ SetValueByName ( } else { Node->Value = Buffer; } + + if (ReturnNode != NULL) { + *ReturnNode = Node; + } + return EFI_SUCCESS; } @@ -980,6 +1060,7 @@ SetValueByName ( @param Storage The Storage to be conveted. @param ConfigResp The returned . @param ConfigRequest The ConfigRequest string. + @param GetEditBuf Get the data from editbuffer or buffer. @retval EFI_SUCCESS Convert success. @retval EFI_INVALID_PARAMETER Incorrect storage type. @@ -989,23 +1070,26 @@ EFI_STATUS StorageToConfigResp ( IN BROWSER_STORAGE *Storage, IN CHAR16 **ConfigResp, - IN CHAR16 *ConfigRequest + IN CHAR16 *ConfigRequest, + IN BOOLEAN GetEditBuf ) { EFI_STATUS Status; EFI_STRING Progress; LIST_ENTRY *Link; NAME_VALUE_NODE *Node; + UINT8 *SourceBuf; Status = EFI_SUCCESS; switch (Storage->Type) { case EFI_HII_VARSTORE_BUFFER: case EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER: + SourceBuf = GetEditBuf ? Storage->EditBuffer : Storage->Buffer; Status = mHiiConfigRouting->BlockToConfig ( mHiiConfigRouting, ConfigRequest, - Storage->EditBuffer, + SourceBuf, Storage->Size, ConfigResp, &Progress @@ -1024,7 +1108,11 @@ StorageToConfigResp ( NewStringCat (ConfigResp, L"&"); NewStringCat (ConfigResp, Node->Name); NewStringCat (ConfigResp, L"="); - NewStringCat (ConfigResp, Node->EditValue); + if (GetEditBuf) { + NewStringCat (ConfigResp, Node->EditValue); + } else { + NewStringCat (ConfigResp, Node->Value); + } } Link = GetNextNode (&Storage->NameValueListHead, Link); } @@ -1105,7 +1193,7 @@ ConfigRespToStorage ( if (StrPtr != NULL) { *StrPtr = 0; } - SetValueByName (Storage, Name, Value, GetSetValueWithEditBuffer); + SetValueByName (Storage, Name, Value, GetSetValueWithEditBuffer, NULL); } break; @@ -1531,7 +1619,7 @@ GetQuestionValue ( if (IsBufferStorage) { CopyMem (Storage->EditBuffer + Question->VarStoreInfo.VarOffset, Dst, StorageWidth); } else { - SetValueByName (Storage, Question->VariableName, Value, GetSetValueWithEditBuffer); + SetValueByName (Storage, Question->VariableName, Value, GetSetValueWithEditBuffer, NULL); } if (Result != NULL) { @@ -1581,8 +1669,10 @@ SetQuestionValue ( CHAR16 *TemName; CHAR16 *TemString; UINTN Index; + NAME_VALUE_NODE *Node; Status = EFI_SUCCESS; + Node = NULL; if (SetValueTo >= GetSetValueWithMax) { return EFI_INVALID_PARAMETER; @@ -1717,6 +1807,14 @@ SetQuestionValue ( // CopyMem (Storage->Buffer + Question->VarStoreInfo.VarOffset, Src, StorageWidth); } + // + // Check whether question value has been changed. + // + if (CompareMem (Storage->Buffer + Question->VarStoreInfo.VarOffset, Storage->EditBuffer + Question->VarStoreInfo.VarOffset, StorageWidth) != 0) { + Question->ValueChanged = TRUE; + } else { + Question->ValueChanged = FALSE; + } } else { if (IsString) { // @@ -1748,8 +1846,19 @@ SetQuestionValue ( } } - Status = SetValueByName (Storage, Question->VariableName, Value, SetValueTo); + Status = SetValueByName (Storage, Question->VariableName, Value, SetValueTo, &Node); FreePool (Value); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check whether question value has been changed. + // + if (StrCmp (Node->Value, Node->EditValue) != 0) { + Question->ValueChanged = TRUE; + } else { + Question->ValueChanged = FALSE; + } } } else if (SetValueTo == GetSetValueWithHiiDriver) { if (Storage->Type == EFI_HII_VARSTORE_BUFFER || Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { @@ -1868,12 +1977,12 @@ SetQuestionValue ( /** - Perform inconsistent check for a Form. + Perform nosubmitif check for a Form. @param FormSet FormSet data structure. @param Form Form data structure. @param Question The Question to be validated. - @param Type Validation type: InConsistent or NoSubmit + @param Type Validation type: NoSubmit @retval EFI_SUCCESS Form validation pass. @retval other Form validation failed. @@ -1891,12 +2000,9 @@ ValidateQuestion ( LIST_ENTRY *Link; LIST_ENTRY *ListHead; EFI_STRING PopUp; - EFI_INPUT_KEY Key; FORM_EXPRESSION *Expression; - if (Type == EFI_HII_EXPRESSION_INCONSISTENT_IF) { - ListHead = &Question->InconsistentListHead; - } else if (Type == EFI_HII_EXPRESSION_NO_SUBMIT_IF) { + if (Type == EFI_HII_EXPRESSION_NO_SUBMIT_IF) { ListHead = &Question->NoSubmitListHead; } else { return EFI_UNSUPPORTED; @@ -1920,10 +2026,10 @@ ValidateQuestion ( // if (Expression->Error != 0) { PopUp = GetToken (Expression->Error, FormSet->HiiHandle); - do { - CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, PopUp, gPressEnter, gEmptyString); - } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); - FreePool (PopUp); + if (Type == EFI_HII_EXPRESSION_NO_SUBMIT_IF) { + gBrowserStatus = BROWSER_NO_SUBMIT_IF; + gErrorInfo = PopUp; + } } return EFI_NOT_READY; @@ -1987,7 +2093,8 @@ NoSubmitCheck ( Fill storage's edit copy with settings requested from Configuration Driver. @param FormSet FormSet data structure. - @param ConfigInfo The config info related to this form. + @param Storage The storage which need to sync. + @param ConfigRequest The config request string which used to sync storage. @param SyncOrRestore Sync the buffer to editbuffer or Restore the editbuffer to buffer if TRUE, copy the editbuffer to the buffer. @@ -1997,10 +2104,11 @@ NoSubmitCheck ( **/ EFI_STATUS -SynchronizeStorageForForm ( - IN FORM_BROWSER_FORMSET *FormSet, - IN FORM_BROWSER_CONFIG_REQUEST *ConfigInfo, - IN BOOLEAN SyncOrRestore +SynchronizeStorage ( + IN FORM_BROWSER_FORMSET *FormSet, + OUT BROWSER_STORAGE *Storage, + IN CHAR16 *ConfigRequest, + IN BOOLEAN SyncOrRestore ) { EFI_STATUS Status; @@ -2014,57 +2122,52 @@ SynchronizeStorageForForm ( Status = EFI_SUCCESS; Result = NULL; - if (FormSet->ConfigAccess == NULL && ConfigInfo->Storage->Type != EFI_HII_VARSTORE_NAME_VALUE) { - return EFI_NOT_FOUND; - } - - if (ConfigInfo->ElementCount == 0) { - // - // Skip if there is no RequestElement - // - return EFI_SUCCESS; - } - if (ConfigInfo->Storage->Type == EFI_HII_VARSTORE_BUFFER || - (ConfigInfo->Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER)) { - BufferSize = ConfigInfo->Storage->Size; + if (Storage->Type == EFI_HII_VARSTORE_BUFFER || + (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER)) { + BufferSize = Storage->Size; if (SyncOrRestore) { - Src = ConfigInfo->Storage->EditBuffer; - Dst = ConfigInfo->Storage->Buffer; + Src = Storage->EditBuffer; + Dst = Storage->Buffer; } else { - Src = ConfigInfo->Storage->Buffer; - Dst = ConfigInfo->Storage->EditBuffer; + Src = Storage->Buffer; + Dst = Storage->EditBuffer; } - Status = mHiiConfigRouting->BlockToConfig( - mHiiConfigRouting, - ConfigInfo->ConfigRequest, - Src, - BufferSize, - &Result, - &Progress - ); - if (EFI_ERROR (Status)) { - return Status; - } + if (ConfigRequest != NULL) { + Status = mHiiConfigRouting->BlockToConfig( + mHiiConfigRouting, + ConfigRequest, + Src, + BufferSize, + &Result, + &Progress + ); + if (EFI_ERROR (Status)) { + return Status; + } - Status = mHiiConfigRouting->ConfigToBlock ( - mHiiConfigRouting, - Result, - Dst, - &BufferSize, - &Progress - ); - if (Result != NULL) { - FreePool (Result); + Status = mHiiConfigRouting->ConfigToBlock ( + mHiiConfigRouting, + Result, + Dst, + &BufferSize, + &Progress + ); + if (Result != NULL) { + FreePool (Result); + } + } else { + CopyMem (Dst, Src, BufferSize); } - } else if (ConfigInfo->Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { - Link = GetFirstNode (&ConfigInfo->Storage->NameValueListHead); - while (!IsNull (&ConfigInfo->Storage->NameValueListHead, Link)) { + } else if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { + Link = GetFirstNode (&Storage->NameValueListHead); + while (!IsNull (&Storage->NameValueListHead, Link)) { Node = NAME_VALUE_NODE_FROM_LINK (Link); - if (StrStr (ConfigInfo->ConfigRequest, Node->Name) != NULL) { + if ((ConfigRequest != NULL && StrStr (ConfigRequest, Node->Name) != NULL) || + (ConfigRequest == NULL)) { if (SyncOrRestore) { NewStringCpy (&Node->Value, Node->EditValue); } else { @@ -2072,7 +2175,7 @@ SynchronizeStorageForForm ( } } - Link = GetNextNode (&ConfigInfo->Storage->NameValueListHead, Link); + Link = GetNextNode (&Storage->NameValueListHead, Link); } } @@ -2095,20 +2198,9 @@ SendDiscardInfoToDriver ( { LIST_ENTRY *Link; FORM_BROWSER_STATEMENT *Question; - EFI_STATUS Status; - EFI_HII_VALUE HiiValue; - UINT8 *BufferValue; - BOOLEAN ValueChanged; EFI_IFR_TYPE_VALUE *TypeValue; EFI_BROWSER_ACTION_REQUEST ActionRequest; - ValueChanged = FALSE; - BufferValue = NULL; - - if(!Form->NvUpdateRequired) { - return; - } - Link = GetFirstNode (&Form->StatementListHead); while (!IsNull (&Form->StatementListHead, Link)) { Question = FORM_BROWSER_STATEMENT_FROM_LINK (Link); @@ -2122,45 +2214,10 @@ SendDiscardInfoToDriver ( continue; } - if (Question->BufferValue != NULL) { - BufferValue = AllocateZeroPool (Question->StorageWidth); - ASSERT (BufferValue != NULL); - CopyMem (BufferValue, Question->BufferValue, Question->StorageWidth); - } else { - HiiValue.Type = Question->HiiValue.Type; - CopyMem (&HiiValue.Value, &Question->HiiValue.Value, sizeof (EFI_IFR_TYPE_VALUE)); - } - - Status = GetQuestionValue (FormSet, Form, Question, GetSetValueWithBuffer); - if (EFI_ERROR (Status)) { - if (BufferValue != NULL) { - FreePool (BufferValue); - BufferValue = NULL; - } - continue; - } - - if (Question->BufferValue != NULL) { - if (CompareMem (BufferValue, Question->BufferValue, Question->StorageWidth)) { - ValueChanged = TRUE; - } - } else { - if (CompareMem (&HiiValue.Value, &Question->HiiValue.Value, sizeof (EFI_IFR_TYPE_VALUE))) { - ValueChanged = TRUE; - } - } - - if (BufferValue != NULL) { - FreePool (BufferValue); - BufferValue = NULL; - } - - if (!ValueChanged) { + if (!Question->ValueChanged) { continue; } - ValueChanged = FALSE; - if (Question->HiiValue.Type == EFI_IFR_TYPE_BUFFER) { TypeValue = (EFI_IFR_TYPE_VALUE *) Question->BufferValue; } else { @@ -2225,6 +2282,78 @@ ValidateFormSet ( return Find; } +/** + Check whether need to enable the reset flag in form level. + Also clean all ValueChanged flag in question. + + @param SetFlag Whether need to set the Reset Flag. + @param Form Form data structure. + +**/ +VOID +UpdateFlagForForm ( + IN BOOLEAN SetFlag, + IN FORM_BROWSER_FORM *Form + ) +{ + LIST_ENTRY *Link; + FORM_BROWSER_STATEMENT *Question; + BOOLEAN FindOne; + + FindOne = FALSE; + Link = GetFirstNode (&Form->StatementListHead); + while (!IsNull (&Form->StatementListHead, Link)) { + Question = FORM_BROWSER_STATEMENT_FROM_LINK (Link); + + if (SetFlag && Question->ValueChanged && ((Question->QuestionFlags & EFI_IFR_FLAG_RESET_REQUIRED) != 0)) { + gResetRequired = TRUE; + } + + if (Question->ValueChanged) { + Question->ValueChanged = FALSE; + } + + Link = GetNextNode (&Form->StatementListHead, Link); + } +} + +/** + Check whether need to enable the reset flag. + Also clean ValueChanged flag for all statements. + + Form level or formset level, only one. + + @param SetFlag Whether need to set the Reset Flag. + @param FormSet FormSet data structure. + @param Form Form data structure. + +**/ +VOID +ValueChangeResetFlagUpdate ( + IN BOOLEAN SetFlag, + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form + ) +{ + FORM_BROWSER_FORM *CurrentForm; + LIST_ENTRY *Link; + + // + // Form != NULL means only check form level. + // + if (Form != NULL) { + UpdateFlagForForm(SetFlag, Form); + return; + } + + Link = GetFirstNode (&FormSet->FormListHead); + while (!IsNull (&FormSet->FormListHead, Link)) { + CurrentForm = FORM_BROWSER_FORM_FROM_LINK (Link); + Link = GetNextNode (&FormSet->FormListHead, Link); + + UpdateFlagForForm(SetFlag, CurrentForm); + } +} /** Discard data based on the input setting scope (Form, FormSet or System). @@ -2256,7 +2385,7 @@ DiscardForm ( return EFI_UNSUPPORTED; } - if (SettingScope == FormLevel && Form->NvUpdateRequired) { + if (SettingScope == FormLevel && IsNvUpdateRequiredForForm (Form)) { ConfigInfo = NULL; Link = GetFirstNode (&Form->ConfigRequestHead); while (!IsNull (&Form->ConfigRequestHead, Link)) { @@ -2277,7 +2406,7 @@ DiscardForm ( // // Prepare // - SynchronizeStorageForForm(FormSet, ConfigInfo, FALSE); + SynchronizeStorage(FormSet, ConfigInfo->Storage, ConfigInfo->ConfigRequest, FALSE); // // Call callback with Changed type to inform the driver. @@ -2285,8 +2414,8 @@ DiscardForm ( SendDiscardInfoToDriver (FormSet, Form); } - Form->NvUpdateRequired = FALSE; - } else if (SettingScope == FormSetLevel && IsNvUpdateRequired(FormSet)) { + ValueChangeResetFlagUpdate (FALSE, NULL, Form); + } else if (SettingScope == FormSetLevel && IsNvUpdateRequiredForFormSet (FormSet)) { // // Discard Buffer storage or Name/Value storage @@ -2307,7 +2436,7 @@ DiscardForm ( continue; } - SynchronizeStorage(Storage->BrowserStorage, FALSE); + SynchronizeStorage(FormSet, Storage->BrowserStorage, Storage->ConfigRequest, FALSE); } Link = GetFirstNode (&FormSet->FormListHead); @@ -2321,7 +2450,7 @@ DiscardForm ( SendDiscardInfoToDriver (FormSet, Form); } - UpdateNvInfoInForm (FormSet, FALSE); + ValueChangeResetFlagUpdate(FALSE, FormSet, NULL); } else if (SettingScope == SystemLevel) { // // System Level Discard. @@ -2401,7 +2530,7 @@ SubmitForm ( return Status; } - if (SettingScope == FormLevel && Form->NvUpdateRequired) { + if (SettingScope == FormLevel && IsNvUpdateRequiredForForm (Form)) { ConfigInfo = NULL; Link = GetFirstNode (&Form->ConfigRequestHead); while (!IsNull (&Form->ConfigRequestHead, Link)) { @@ -2423,7 +2552,7 @@ SubmitForm ( // // 1. Prepare // - Status = StorageToConfigResp (ConfigInfo->Storage, &ConfigResp, ConfigInfo->ConfigRequest); + Status = StorageToConfigResp (ConfigInfo->Storage, &ConfigResp, ConfigInfo->ConfigRequest, TRUE); if (EFI_ERROR (Status)) { return Status; } @@ -2499,14 +2628,14 @@ SubmitForm ( // // 3. Config success, update storage shadow Buffer, only update the data belong to this form. // - SynchronizeStorageForForm(FormSet, ConfigInfo, TRUE); + SynchronizeStorage (FormSet, ConfigInfo->Storage, ConfigInfo->ConfigRequest, TRUE); } // // 4. Update the NV flag. // - Form->NvUpdateRequired = FALSE; - } else if (SettingScope == FormSetLevel && IsNvUpdateRequired(FormSet)) { + ValueChangeResetFlagUpdate(TRUE, NULL, Form); + } else if (SettingScope == FormSetLevel && IsNvUpdateRequiredForFormSet (FormSet)) { // // Submit Buffer storage or Name/Value storage // @@ -2530,7 +2659,7 @@ SubmitForm ( // // 1. Prepare // - Status = StorageToConfigResp (Storage, &ConfigResp, FormSetStorage->ConfigRequest); + Status = StorageToConfigResp (Storage, &ConfigResp, FormSetStorage->ConfigRequest, TRUE); if (EFI_ERROR (Status)) { return Status; } @@ -2602,13 +2731,13 @@ SubmitForm ( // // 3. Config success, update storage shadow Buffer // - SynchronizeStorage (Storage, TRUE); + SynchronizeStorage (FormSet, Storage, FormSetStorage->ConfigRequest, TRUE); } // // 4. Update the NV flag. // - UpdateNvInfoInForm (FormSet, FALSE); + ValueChangeResetFlagUpdate(TRUE, FormSet, NULL); } else if (SettingScope == SystemLevel) { // // System Level Save. @@ -2818,52 +2947,184 @@ GetDefaultValueFromAltCfg ( } } -Done: - if (ConfigRequest != NULL){ - FreePool (ConfigRequest); - } +Done: + if (ConfigRequest != NULL){ + FreePool (ConfigRequest); + } + + if (ConfigResp != NULL) { + FreePool (ConfigResp); + } + + if (Result != NULL) { + FreePool (Result); + } + + return Status; +} + +/** + Get default Id value used for browser. + + @param DefaultId The default id value used by hii. + + @retval Browser used default value. + +**/ +INTN +GetDefaultIdForCallBack ( + UINTN DefaultId + ) +{ + if (DefaultId == EFI_HII_DEFAULT_CLASS_STANDARD) { + return EFI_BROWSER_ACTION_DEFAULT_STANDARD; + } else if (DefaultId == EFI_HII_DEFAULT_CLASS_MANUFACTURING) { + return EFI_BROWSER_ACTION_DEFAULT_MANUFACTURING; + } else if (DefaultId == EFI_HII_DEFAULT_CLASS_SAFE) { + return EFI_BROWSER_ACTION_DEFAULT_SAFE; + } else if (DefaultId >= EFI_HII_DEFAULT_CLASS_PLATFORM_BEGIN && DefaultId < EFI_HII_DEFAULT_CLASS_PLATFORM_BEGIN + 0x1000) { + return EFI_BROWSER_ACTION_DEFAULT_PLATFORM + DefaultId - EFI_HII_DEFAULT_CLASS_PLATFORM_BEGIN; + } else if (DefaultId >= EFI_HII_DEFAULT_CLASS_HARDWARE_BEGIN && DefaultId < EFI_HII_DEFAULT_CLASS_HARDWARE_BEGIN + 0x1000) { + return EFI_BROWSER_ACTION_DEFAULT_HARDWARE + DefaultId - EFI_HII_DEFAULT_CLASS_HARDWARE_BEGIN; + } else if (DefaultId >= EFI_HII_DEFAULT_CLASS_FIRMWARE_BEGIN && DefaultId < EFI_HII_DEFAULT_CLASS_FIRMWARE_BEGIN + 0x1000) { + return EFI_BROWSER_ACTION_DEFAULT_FIRMWARE + DefaultId - EFI_HII_DEFAULT_CLASS_FIRMWARE_BEGIN; + } else { + return -1; + } +} + + + +/** + Return data element in an Array by its Index. + + @param Array The data array. + @param Type Type of the data in this array. + @param Index Zero based index for data in this array. + + @retval Value The data to be returned + +**/ +UINT64 +GetArrayData ( + IN VOID *Array, + IN UINT8 Type, + IN UINTN Index + ) +{ + UINT64 Data; + + ASSERT (Array != NULL); + + Data = 0; + switch (Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + Data = (UINT64) *(((UINT8 *) Array) + Index); + break; + + case EFI_IFR_TYPE_NUM_SIZE_16: + Data = (UINT64) *(((UINT16 *) Array) + Index); + break; + + case EFI_IFR_TYPE_NUM_SIZE_32: + Data = (UINT64) *(((UINT32 *) Array) + Index); + break; + + case EFI_IFR_TYPE_NUM_SIZE_64: + Data = (UINT64) *(((UINT64 *) Array) + Index); + break; + + default: + break; + } + + return Data; +} + + +/** + Set value of a data element in an Array by its Index. + + @param Array The data array. + @param Type Type of the data in this array. + @param Index Zero based index for data in this array. + @param Value The value to be set. + +**/ +VOID +SetArrayData ( + IN VOID *Array, + IN UINT8 Type, + IN UINTN Index, + IN UINT64 Value + ) +{ + + ASSERT (Array != NULL); - if (ConfigResp != NULL) { - FreePool (ConfigResp); - } - - if (Result != NULL) { - FreePool (Result); - } + switch (Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + *(((UINT8 *) Array) + Index) = (UINT8) Value; + break; - return Status; + case EFI_IFR_TYPE_NUM_SIZE_16: + *(((UINT16 *) Array) + Index) = (UINT16) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_32: + *(((UINT32 *) Array) + Index) = (UINT32) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_64: + *(((UINT64 *) Array) + Index) = (UINT64) Value; + break; + + default: + break; + } } /** - Get default Id value used for browser. + Search an Option of a Question by its value. - @param DefaultId The default id value used by hii. + @param Question The Question + @param OptionValue Value for Option to be searched. - @retval Browser used default value. + @retval Pointer Pointer to the found Option. + @retval NULL Option not found. **/ -INTN -GetDefaultIdForCallBack ( - UINTN DefaultId +QUESTION_OPTION * +ValueToOption ( + IN FORM_BROWSER_STATEMENT *Question, + IN EFI_HII_VALUE *OptionValue ) -{ - if (DefaultId == EFI_HII_DEFAULT_CLASS_STANDARD) { - return EFI_BROWSER_ACTION_DEFAULT_STANDARD; - } else if (DefaultId == EFI_HII_DEFAULT_CLASS_MANUFACTURING) { - return EFI_BROWSER_ACTION_DEFAULT_MANUFACTURING; - } else if (DefaultId == EFI_HII_DEFAULT_CLASS_SAFE) { - return EFI_BROWSER_ACTION_DEFAULT_SAFE; - } else if (DefaultId >= EFI_HII_DEFAULT_CLASS_PLATFORM_BEGIN && DefaultId < EFI_HII_DEFAULT_CLASS_PLATFORM_BEGIN + 0x1000) { - return EFI_BROWSER_ACTION_DEFAULT_PLATFORM + DefaultId - EFI_HII_DEFAULT_CLASS_PLATFORM_BEGIN; - } else if (DefaultId >= EFI_HII_DEFAULT_CLASS_HARDWARE_BEGIN && DefaultId < EFI_HII_DEFAULT_CLASS_HARDWARE_BEGIN + 0x1000) { - return EFI_BROWSER_ACTION_DEFAULT_HARDWARE + DefaultId - EFI_HII_DEFAULT_CLASS_HARDWARE_BEGIN; - } else if (DefaultId >= EFI_HII_DEFAULT_CLASS_FIRMWARE_BEGIN && DefaultId < EFI_HII_DEFAULT_CLASS_FIRMWARE_BEGIN + 0x1000) { - return EFI_BROWSER_ACTION_DEFAULT_FIRMWARE + DefaultId - EFI_HII_DEFAULT_CLASS_FIRMWARE_BEGIN; - } else { - return -1; +{ + LIST_ENTRY *Link; + QUESTION_OPTION *Option; + INTN Result; + + Link = GetFirstNode (&Question->OptionListHead); + while (!IsNull (&Question->OptionListHead, Link)) { + Option = QUESTION_OPTION_FROM_LINK (Link); + + if ((CompareHiiValue (&Option->Value, OptionValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) { + // + // Check the suppressif condition, only a valid option can be return. + // + if ((Option->SuppressExpression == NULL) || + ((EvaluateExpressionList(Option->SuppressExpression, FALSE, NULL, NULL) == ExpressFalse))) { + return Option; + } + } + + Link = GetNextNode (&Question->OptionListHead, Link); } + + return NULL; } + /** Reset Question to its default value. @@ -3155,11 +3416,7 @@ ExtractDefault ( LIST_ENTRY *FormLink; LIST_ENTRY *Link; FORM_BROWSER_STATEMENT *Question; - FORM_BROWSER_FORMSET *BackUpFormSet; FORM_BROWSER_FORMSET *LocalFormSet; - EFI_HII_HANDLE *HiiHandles; - UINTN Index; - EFI_GUID ZeroGuid; Status = EFI_SUCCESS; @@ -3229,10 +3486,6 @@ ExtractDefault ( if ((Question->Storage != NULL) && (Question->Storage->Type != EFI_HII_VARSTORE_EFI_VARIABLE)) { SetQuestionValue (FormSet, Form, Question, GetSetValueWithEditBuffer); - // - // Update Form NV flag. - // - Form->NvUpdateRequired = TRUE; } } } else if (SettingScope == FormSetLevel) { @@ -3244,65 +3497,9 @@ ExtractDefault ( } } else if (SettingScope == SystemLevel) { // - // Open all FormSet by locate HII packages. - // Initiliaze the maintain FormSet to store default data as back up data. - // - BackUpFormSet = gOldFormSet; - gOldFormSet = NULL; - - // - // Get all the Hii handles - // - HiiHandles = HiiGetHiiHandles (NULL); - ASSERT (HiiHandles != NULL); - - // - // Search for formset of each class type - // - for (Index = 0; HiiHandles[Index] != NULL; Index++) { - // - // Check HiiHandles[Index] does exist in global maintain list. - // - if (GetFormSetFromHiiHandle (HiiHandles[Index]) != NULL) { - continue; - } - - // - // Initilize FormSet Setting - // - LocalFormSet = AllocateZeroPool (sizeof (FORM_BROWSER_FORMSET)); - ASSERT (LocalFormSet != NULL); - ZeroMem (&ZeroGuid, sizeof (ZeroGuid)); - Status = InitializeFormSet (HiiHandles[Index], &ZeroGuid, LocalFormSet, FALSE); - if (EFI_ERROR (Status) || IsListEmpty (&LocalFormSet->FormListHead)) { - DestroyFormSet (LocalFormSet); - continue; - } - Status = InitializeCurrentSetting (LocalFormSet); - if (EFI_ERROR (Status)) { - DestroyFormSet (LocalFormSet); - continue; - } - // - // Initilize Questions' Value - // - Status = LoadFormSetConfig (NULL, LocalFormSet); - if (EFI_ERROR (Status)) { - DestroyFormSet (LocalFormSet); - continue; - } - - // - // Add FormSet into the maintain list. - // - InsertTailList (&gBrowserFormSetList, &LocalFormSet->Link); - } - - // - // Free resources, and restore gOldFormSet and gClassOfVfr + // Preload all Hii formset. // - FreePool (HiiHandles); - gOldFormSet = BackUpFormSet; + LoadAllHiiFormset(); // // Set Default Value for each FormSet in the maintain list. @@ -3321,6 +3518,81 @@ ExtractDefault ( return EFI_SUCCESS; } + +/** + Validate whether this question's value has changed. + + @param FormSet FormSet data structure. + @param Form Form data structure. + @param Question Question to be initialized. + @param GetValueFrom Where to get value, may from editbuffer, buffer or hii driver. + + @retval TRUE Question's value has changed. + @retval FALSE Question's value has not changed + +**/ +BOOLEAN +IsQuestionValueChanged ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form, + IN OUT FORM_BROWSER_STATEMENT *Question, + IN GET_SET_QUESTION_VALUE_WITH GetValueFrom + ) +{ + EFI_HII_VALUE BackUpValue; + CHAR8 *BackUpBuffer; + EFI_STATUS Status; + BOOLEAN ValueChanged; + UINTN BufferWidth; + + // + // For quetion without storage, always mark it as data not changed. + // + if (Question->Storage == NULL && Question->Operand != EFI_IFR_TIME_OP && Question->Operand != EFI_IFR_DATE_OP) { + return FALSE; + } + + BackUpBuffer = NULL; + ValueChanged = FALSE; + + switch (Question->Operand) { + case EFI_IFR_ORDERED_LIST_OP: + BufferWidth = Question->StorageWidth; + BackUpBuffer = AllocateCopyPool (BufferWidth, Question->BufferValue); + ASSERT (BackUpBuffer != NULL); + break; + + case EFI_IFR_STRING_OP: + case EFI_IFR_PASSWORD_OP: + BufferWidth = (UINTN) Question->Maximum * sizeof (CHAR16); + BackUpBuffer = AllocateCopyPool (BufferWidth, Question->BufferValue); + ASSERT (BackUpBuffer != NULL); + break; + + default: + BufferWidth = 0; + break; + } + CopyMem (&BackUpValue, &Question->HiiValue, sizeof (EFI_HII_VALUE)); + + Status = GetQuestionValue (FormSet, Form, Question, GetValueFrom); + ASSERT_EFI_ERROR(Status); + + if (CompareMem (&BackUpValue, &Question->HiiValue, sizeof (EFI_HII_VALUE)) != 0 || + CompareMem (BackUpBuffer, Question->BufferValue, BufferWidth) != 0) { + ValueChanged = TRUE; + } + + CopyMem (&Question->HiiValue, &BackUpValue, sizeof (EFI_HII_VALUE)); + CopyMem (Question->BufferValue, BackUpBuffer, BufferWidth); + + if (BackUpBuffer != NULL) { + FreePool (BackUpBuffer); + } + + return ValueChanged; +} + /** Initialize Question's Edit copy from Storage. @@ -3354,7 +3626,11 @@ LoadFormConfig ( // // Initialize local copy of Value for each Question // - Status = GetQuestionValue (FormSet, Form, Question, GetSetValueWithEditBuffer); + if (Question->Operand == EFI_IFR_PASSWORD_OP && (Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK)== 0) { + Status = GetQuestionValue (FormSet, Form, Question, GetSetValueWithHiiDriver); + } else { + Status = GetQuestionValue (FormSet, Form, Question, GetSetValueWithEditBuffer); + } if (EFI_ERROR (Status)) { return Status; } @@ -3394,6 +3670,11 @@ LoadFormConfig ( Status = ProcessCallBackFunction(Selection, Question, EFI_BROWSER_ACTION_RETRIEVE, TRUE); } + // + // Update Question Value changed flag. + // + Question->ValueChanged = IsQuestionValueChanged(FormSet, Form, Question, GetSetValueWithBuffer); + Link = GetNextNode (&Form->StatementListHead, Link); } @@ -3437,6 +3718,11 @@ LoadFormSetConfig ( Link = GetNextNode (&FormSet->FormListHead, Link); } + // + // Finished question initialization. + // + FormSet->QuestionInited = TRUE; + return EFI_SUCCESS; } @@ -3491,6 +3777,13 @@ RemoveConfigRequest ( CHAR16 *NextRequestElement; CHAR16 *SearchKey; + // + // No request element in it, just return. + // + if (ConfigRequest == NULL) { + return; + } + if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { // // "&Name1&Name2" section for EFI_HII_VARSTORE_NAME_VALUE storage @@ -3565,6 +3858,7 @@ CleanBrowserStorage ( { LIST_ENTRY *Link; FORMSET_STORAGE *Storage; + CHAR16 *ConfigRequest; Link = GetFirstNode (&FormSet->StorageListHead); while (!IsNull (&FormSet->StorageListHead, Link)) { @@ -3572,7 +3866,8 @@ CleanBrowserStorage ( Link = GetNextNode (&FormSet->StorageListHead, Link); if ((Storage->BrowserStorage->Type != EFI_HII_VARSTORE_BUFFER) && - (Storage->BrowserStorage->Type != EFI_HII_VARSTORE_NAME_VALUE)) { + (Storage->BrowserStorage->Type != EFI_HII_VARSTORE_NAME_VALUE) && + (Storage->BrowserStorage->Type != EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER)) { continue; } @@ -3580,7 +3875,8 @@ CleanBrowserStorage ( continue; } - RemoveConfigRequest (Storage->BrowserStorage, Storage->ConfigRequest); + ConfigRequest = FormSet->QuestionInited ? Storage->ConfigRequest : Storage->ConfigElements; + RemoveConfigRequest (Storage->BrowserStorage, ConfigRequest); } } @@ -3651,7 +3947,6 @@ AppendConfigRequest ( Adjust the config request info, remove the request elements which already in AllConfigRequest string. @param Storage Form set Storage. - @param ConfigRequest Return the ConfigRequest info. @retval TRUE Has element not covered by current used elements, need to continue to call ExtractConfig @retval FALSE All elements covered by current used elements. @@ -3659,8 +3954,7 @@ AppendConfigRequest ( **/ BOOLEAN ConfigRequestAdjust ( - IN FORMSET_STORAGE *Storage, - OUT CHAR16 **ConfigRequest + IN FORMSET_STORAGE *Storage ) { CHAR16 *RequestElement; @@ -3676,7 +3970,10 @@ ConfigRequestAdjust ( if (Storage->BrowserStorage->ConfigRequest == NULL) { Storage->BrowserStorage->ConfigRequest = AllocateCopyPool (StrSize (Storage->ConfigRequest), Storage->ConfigRequest); - *ConfigRequest = AllocateCopyPool (StrSize (Storage->ConfigRequest), Storage->ConfigRequest); + if (Storage->ConfigElements != NULL) { + FreePool (Storage->ConfigElements); + } + Storage->ConfigElements = AllocateCopyPool (StrSize (Storage->ConfigRequest), Storage->ConfigRequest); return TRUE; } @@ -3745,7 +4042,10 @@ ConfigRequestAdjust ( } if (RetVal) { - *ConfigRequest = RetBuf; + if (Storage->ConfigElements != NULL) { + FreePool (Storage->ConfigElements); + } + Storage->ConfigElements = RetBuf; } else { FreePool (RetBuf); } @@ -3753,16 +4053,163 @@ ConfigRequestAdjust ( return RetVal; } +/** + + Base on ConfigRequest info to get default value for current formset. + + ConfigRequest info include the info about which questions in current formset need to + get default value. This function only get these questions default value. + + @param FormSet FormSet data structure. + @param Storage Storage need to update value. + @param ConfigRequest The config request string. + +**/ +VOID +GetDefaultForFormset ( + IN FORM_BROWSER_FORMSET *FormSet, + IN BROWSER_STORAGE *Storage, + IN CHAR16 *ConfigRequest + ) +{ + UINT8 *BackUpBuf; + UINTN BufferSize; + LIST_ENTRY BackUpList; + NAME_VALUE_NODE *Node; + LIST_ENTRY *Link; + LIST_ENTRY *NodeLink; + NAME_VALUE_NODE *TmpNode; + EFI_STATUS Status; + EFI_STRING Progress; + EFI_STRING Result; + + BackUpBuf = NULL; + InitializeListHead(&BackUpList); + + // + // Back update the edit buffer. + // + if (Storage->Type == EFI_HII_VARSTORE_BUFFER || + (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER)) { + BackUpBuf = AllocateCopyPool (Storage->Size, Storage->EditBuffer); + ASSERT (BackUpBuf != NULL); + } else if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { + Link = GetFirstNode (&Storage->NameValueListHead); + while (!IsNull (&Storage->NameValueListHead, Link)) { + Node = NAME_VALUE_NODE_FROM_LINK (Link); + Link = GetNextNode (&Storage->NameValueListHead, Link); + + // + // Only back Node belong to this formset. + // + if (StrStr (Storage->ConfigRequest, Node->Name) == NULL) { + continue; + } + + TmpNode = AllocateCopyPool (sizeof (NAME_VALUE_NODE), Node); + TmpNode->Name = AllocateCopyPool (StrSize(Node->Name) * sizeof (CHAR16), Node->Name); + TmpNode->EditValue = AllocateCopyPool (StrSize(Node->EditValue) * sizeof (CHAR16), Node->EditValue); + + InsertTailList(&BackUpList, &TmpNode->Link); + } + } + + // + // Get default value. + // + ExtractDefault (FormSet, NULL, EFI_HII_DEFAULT_CLASS_STANDARD, FormSetLevel, GetDefaultForStorage, Storage, TRUE); + + // + // Update the question value based on the input ConfigRequest. + // + if (Storage->Type == EFI_HII_VARSTORE_BUFFER || + (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER)) { + ASSERT (BackUpBuf != NULL); + BufferSize = Storage->Size; + Status = mHiiConfigRouting->BlockToConfig( + mHiiConfigRouting, + ConfigRequest, + Storage->EditBuffer, + BufferSize, + &Result, + &Progress + ); + ASSERT_EFI_ERROR (Status); + + Status = mHiiConfigRouting->ConfigToBlock ( + mHiiConfigRouting, + Result, + BackUpBuf, + &BufferSize, + &Progress + ); + ASSERT_EFI_ERROR (Status); + + if (Result != NULL) { + FreePool (Result); + } + + CopyMem (Storage->EditBuffer, BackUpBuf, Storage->Size); + FreePool (BackUpBuf); + } else if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { + // + // Update question value, only element in ConfigReqeust will be update. + // + Link = GetFirstNode (&BackUpList); + while (!IsNull (&BackUpList, Link)) { + Node = NAME_VALUE_NODE_FROM_LINK (Link); + Link = GetNextNode (&BackUpList, Link); + + if (StrStr (ConfigRequest, Node->Name) != NULL) { + continue; + } + + NodeLink = GetFirstNode (&Storage->NameValueListHead); + while (!IsNull (&Storage->NameValueListHead, NodeLink)) { + TmpNode = NAME_VALUE_NODE_FROM_LINK (NodeLink); + NodeLink = GetNextNode (&Storage->NameValueListHead, NodeLink); + + if (StrCmp (Node->Name, TmpNode->Name) != 0) { + continue; + } + + FreePool (TmpNode->EditValue); + TmpNode->EditValue = AllocateCopyPool (StrSize(Node->EditValue) * sizeof (CHAR16), Node->EditValue); + + RemoveEntryList (&Node->Link); + FreePool (Node->EditValue); + FreePool (Node->Name); + FreePool (Node); + } + } + + // + // Restore the Name/Value node. + // + Link = GetFirstNode (&BackUpList); + while (!IsNull (&BackUpList, Link)) { + Node = NAME_VALUE_NODE_FROM_LINK (Link); + Link = GetNextNode (&BackUpList, Link); + + // + // Free this node. + // + RemoveEntryList (&Node->Link); + FreePool (Node->EditValue); + FreePool (Node->Name); + FreePool (Node); + } + } +} + /** Fill storage's edit copy with settings requested from Configuration Driver. @param FormSet FormSet data structure. @param Storage Buffer Storage. - @retval EFI_SUCCESS The function completed successfully. - **/ -EFI_STATUS +VOID LoadStorage ( IN FORM_BROWSER_FORMSET *FormSet, IN FORMSET_STORAGE *Storage @@ -3772,18 +4219,17 @@ LoadStorage ( EFI_STRING Progress; EFI_STRING Result; CHAR16 *StrPtr; - CHAR16 *ConfigRequest; - if (Storage->BrowserStorage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { - return EFI_SUCCESS; - } + switch (Storage->BrowserStorage->Type) { + case EFI_HII_VARSTORE_EFI_VARIABLE: + return; + + case EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER: + if (Storage->BrowserStorage->ReferenceCount > 1) { + ConfigRequestAdjust(Storage); + return; + } - if (Storage->BrowserStorage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER) { - Status = EFI_SUCCESS; - // - // EFI varstore data all get from variable, so no need to get again. - // - if (Storage->BrowserStorage->ReferenceCount == 1) { Status = gRT->GetVariable ( Storage->BrowserStorage->Name, &Storage->BrowserStorage->Guid, @@ -3791,55 +4237,78 @@ LoadStorage ( (UINTN*)&Storage->BrowserStorage->Size, Storage->BrowserStorage->EditBuffer ); - } - return Status; - } + // + // If get variable fail, extract default from IFR binary + // + if (EFI_ERROR (Status)) { + ExtractDefault (FormSet, NULL, EFI_HII_DEFAULT_CLASS_STANDARD, FormSetLevel, GetDefaultForStorage, Storage->BrowserStorage, TRUE); + } + + Storage->BrowserStorage->ConfigRequest = AllocateCopyPool (StrSize (Storage->ConfigRequest), Storage->ConfigRequest); + // + // Input NULL for ConfigRequest field means sync all fields from editbuffer to buffer. + // + SynchronizeStorage(FormSet, Storage->BrowserStorage, NULL, TRUE); + break; - if (FormSet->ConfigAccess == NULL) { - return EFI_NOT_FOUND; - } + case EFI_HII_VARSTORE_BUFFER: + case EFI_HII_VARSTORE_NAME_VALUE: + // + // Skip if there is no RequestElement + // + if (Storage->ElementCount == 0) { + return; + } - if (Storage->ElementCount == 0) { - // - // Skip if there is no RequestElement - // - return EFI_SUCCESS; - } + // + // Adjust the ConfigRequest string, only the field not saved in BrowserStorage->AllConfig + // will used to call ExtractConfig. + // If not elements need to udpate, return. + // + if (!ConfigRequestAdjust(Storage)) { + return; + } + ASSERT (Storage->ConfigElements != NULL); - // - // Adjust the ConfigRequest string, only the field not saved in BrowserStorage->AllConfig - // will used to call ExtractConfig. - // - if (!ConfigRequestAdjust(Storage, &ConfigRequest)) { - return EFI_SUCCESS; - } + Status = EFI_NOT_FOUND; + if (FormSet->ConfigAccess != NULL) { + // + // Request current settings from Configuration Driver + // + Status = FormSet->ConfigAccess->ExtractConfig ( + FormSet->ConfigAccess, + Storage->ConfigElements, + &Progress, + &Result + ); + + if (!EFI_ERROR (Status)) { + // + // Convert Result from to + // + StrPtr = StrStr (Result, L"&GUID="); + if (StrPtr != NULL) { + *StrPtr = L'\0'; + } + + Status = ConfigRespToStorage (Storage->BrowserStorage, Result); + FreePool (Result); + } + } - // - // Request current settings from Configuration Driver - // - Status = FormSet->ConfigAccess->ExtractConfig ( - FormSet->ConfigAccess, - ConfigRequest, - &Progress, - &Result - ); - FreePool (ConfigRequest); + if (EFI_ERROR (Status)) { + // + // Base on the configRequest string to get default value. + // + GetDefaultForFormset (FormSet, Storage->BrowserStorage, Storage->ConfigElements); + } - if (EFI_ERROR (Status)) { - return Status; - } + SynchronizeStorage(FormSet, Storage->BrowserStorage, Storage->ConfigElements, TRUE); + break; - // - // Convert Result from to - // - StrPtr = StrStr (Result, L"&GUID="); - if (StrPtr != NULL) { - *StrPtr = L'\0'; + default: + break; } - - Status = ConfigRespToStorage (Storage->BrowserStorage, Result); - FreePool (Result); - return Status; } /** @@ -3847,22 +4316,15 @@ LoadStorage ( @param FormSet FormSet data structure. - @retval EFI_SUCCESS The function completed successfully. - **/ -EFI_STATUS +VOID InitializeCurrentSetting ( IN OUT FORM_BROWSER_FORMSET *FormSet ) { LIST_ENTRY *Link; - LIST_ENTRY *Link2; FORMSET_STORAGE *Storage; - FORMSET_STORAGE *StorageSrc; - FORMSET_STORAGE *OldStorage; - FORM_BROWSER_FORM *Form; - FORM_BROWSER_FORM *Form2; - EFI_STATUS Status; + FORM_BROWSER_FORMSET *OldFormSet; // // Extract default from IFR binary for no storage questions. @@ -3876,77 +4338,21 @@ InitializeCurrentSetting ( while (!IsNull (&FormSet->StorageListHead, Link)) { Storage = FORMSET_STORAGE_FROM_LINK (Link); - OldStorage = NULL; - if (gOldFormSet != NULL) { - // - // Try to find the Storage in backup formset gOldFormSet - // - Link2 = GetFirstNode (&gOldFormSet->StorageListHead); - while (!IsNull (&gOldFormSet->StorageListHead, Link2)) { - StorageSrc = FORMSET_STORAGE_FROM_LINK (Link2); - - if (StorageSrc->VarStoreId == Storage->VarStoreId) { - OldStorage = StorageSrc; - break; - } - - Link2 = GetNextNode (&gOldFormSet->StorageListHead, Link2); - } - } - - // - // Storage is not found in backup formset and current global storage not has other driver used, - // request it from ConfigDriver - // - if (OldStorage == NULL) { - Status = LoadStorage (FormSet, Storage); - - if (EFI_ERROR (Status)) { - // - // If get last time changed value failed, extract default from IFR binary - // - ExtractDefault (FormSet, NULL, EFI_HII_DEFAULT_CLASS_STANDARD, FormSetLevel, GetDefaultForStorage, Storage->BrowserStorage, TRUE); - // - // ExtractDefault will set the NV flag to TRUE, so need this function to clean the flag - // in current situation. - // - UpdateNvInfoInForm (FormSet, FALSE); - } - - // - // Now Edit Buffer is filled with default values(lower priority) or current - // settings(higher priority), sychronize it to shadow Buffer - // - SynchronizeStorage (Storage->BrowserStorage, TRUE); - } + LoadStorage (FormSet, Storage); Link = GetNextNode (&FormSet->StorageListHead, Link); } // - // If has old formset, get the old nv update status. + // Try to find pre FormSet in the maintain backup list. + // If old formset != NULL, destroy this formset. Add new formset to gBrowserFormSetList. // - if (gOldFormSet != NULL) { - Link = GetFirstNode (&FormSet->FormListHead); - while (!IsNull (&FormSet->FormListHead, Link)) { - Form = FORM_BROWSER_FORM_FROM_LINK (Link); - - Link2 = GetFirstNode (&gOldFormSet->FormListHead); - while (!IsNull (&gOldFormSet->FormListHead, Link2)) { - Form2 = FORM_BROWSER_FORM_FROM_LINK (Link2); - - if (Form->FormId == Form2->FormId) { - Form->NvUpdateRequired = Form2->NvUpdateRequired; - break; - } - - Link2 = GetNextNode (&gOldFormSet->FormListHead, Link2); - } - Link = GetNextNode (&FormSet->FormListHead, Link); - } + OldFormSet = GetFormSetFromHiiHandle (FormSet->HiiHandle); + if (OldFormSet != NULL) { + RemoveEntryList (&OldFormSet->Link); + DestroyFormSet (OldFormSet); } - - return EFI_SUCCESS; + InsertTailList (&gBrowserFormSetList, &FormSet->Link); } @@ -4129,7 +4535,6 @@ GetIfrBinaryData ( found in package list. On output, GUID of the formset found(if not NULL). @param FormSet FormSet data structure. - @param UpdateGlobalVar Whether need to update the global variable. @retval EFI_SUCCESS The function completed successfully. @retval EFI_NOT_FOUND The specified FormSet could not be found. @@ -4139,13 +4544,11 @@ EFI_STATUS InitializeFormSet ( IN EFI_HII_HANDLE Handle, IN OUT EFI_GUID *FormSetGuid, - OUT FORM_BROWSER_FORMSET *FormSet, - IN BOOLEAN UpdateGlobalVar + OUT FORM_BROWSER_FORMSET *FormSet ) { EFI_STATUS Status; EFI_HANDLE DriverHandle; - UINT16 Index; Status = GetIfrBinaryData (Handle, FormSetGuid, &FormSet->IfrBinaryLength, &FormSet->IfrBinaryData); if (EFI_ERROR (Status)) { @@ -4155,6 +4558,7 @@ InitializeFormSet ( FormSet->Signature = FORM_BROWSER_FORMSET_SIGNATURE; FormSet->HiiHandle = Handle; CopyMem (&FormSet->Guid, FormSetGuid, sizeof (EFI_GUID)); + FormSet->QuestionInited = FALSE; // // Retrieve ConfigAccess Protocol associated with this HiiPackageList @@ -4181,55 +4585,8 @@ InitializeFormSet ( // Parse the IFR binary OpCodes // Status = ParseOpCodes (FormSet); - if (EFI_ERROR (Status)) { - return Status; - } - - // - // If not need to update the global variable, just return. - // - if (!UpdateGlobalVar) { - return Status; - } - - // - // Set VFR type by FormSet SubClass field - // - gClassOfVfr = FORMSET_CLASS_PLATFORM_SETUP; - if (FormSet->SubClass == EFI_FRONT_PAGE_SUBCLASS) { - gClassOfVfr = FORMSET_CLASS_FRONT_PAGE; - } - - // - // Set VFR type by FormSet class guid - // - for (Index = 0; Index < 3; Index ++) { - if (CompareGuid (&FormSet->ClassGuid[Index], &gEfiHiiPlatformSetupFormsetGuid)) { - gClassOfVfr |= FORMSET_CLASS_PLATFORM_SETUP; - break; - } - } - - gFunctionKeySetting = ENABLE_FUNCTION_KEY_SETTING; - - if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) == FORMSET_CLASS_FRONT_PAGE) { - gFrontPageHandle = FormSet->HiiHandle; - gFunctionKeySetting = NONE_FUNCTION_KEY_SETTING; - } - - // - // Match GUID to find out the function key setting. If match fail, use the default setting. - // - for (Index = 0; Index < sizeof (gFunctionKeySettingTable) / sizeof (FUNCTIION_KEY_SETTING); Index++) { - if (CompareGuid (&FormSet->Guid, &(gFunctionKeySettingTable[Index].FormSetGuid))) { - // - // Update the function key setting. - // - gFunctionKeySetting = gFunctionKeySettingTable[Index].KeySetting; - } - } - return EFI_SUCCESS; + return Status; } @@ -4245,6 +4602,7 @@ SaveBrowserContext ( ) { BROWSER_CONTEXT *Context; + FORM_ENTRY_INFO *MenuList; gBrowserContextCount++; if (gBrowserContextCount == 1) { @@ -4254,6 +4612,11 @@ SaveBrowserContext ( return; } + // + // Not support SendForm nest in another SendForm, assert here. + // + ASSERT (FALSE); + Context = AllocatePool (sizeof (BROWSER_CONTEXT)); ASSERT (Context != NULL); @@ -4262,47 +4625,20 @@ SaveBrowserContext ( // // Save FormBrowser context // - Context->BannerData = gBannerData; - Context->ClassOfVfr = gClassOfVfr; - Context->FunctionKeySetting = gFunctionKeySetting; Context->ResetRequired = gResetRequired; - Context->Direction = gDirection; - Context->EnterString = gEnterString; - Context->EnterCommitString = gEnterCommitString; - Context->EnterEscapeString = gEnterEscapeString; - Context->EscapeString = gEscapeString; - Context->MoveHighlight = gMoveHighlight; - Context->MakeSelection = gMakeSelection; - Context->DecNumericInput = gDecNumericInput; - Context->HexNumericInput = gHexNumericInput; - Context->ToggleCheckBox = gToggleCheckBox; - Context->PromptForData = gPromptForData; - Context->PromptForPassword = gPromptForPassword; - Context->PromptForNewPassword = gPromptForNewPassword; - Context->ConfirmPassword = gConfirmPassword; - Context->ConfirmError = gConfirmError; - Context->PassowordInvalid = gPassowordInvalid; - Context->PressEnter = gPressEnter; - Context->EmptyString = gEmptyString; - Context->AreYouSure = gAreYouSure; - Context->YesResponse = gYesResponse; - Context->NoResponse = gNoResponse; - Context->MiniString = gMiniString; - Context->PlusString = gPlusString; - Context->MinusString = gMinusString; - Context->AdjustNumber = gAdjustNumber; - Context->SaveChanges = gSaveChanges; - Context->OptionMismatch = gOptionMismatch; - Context->FormSuppress = gFormSuppress; - Context->PromptBlockWidth = gPromptBlockWidth; - Context->OptionBlockWidth = gOptionBlockWidth; - Context->HelpBlockWidth = gHelpBlockWidth; - Context->OldFormSet = gOldFormSet; - Context->MenuRefreshHead = gMenuRefreshHead; - Context->ProtocolNotFound = gProtocolNotFound; - - CopyMem (&Context->ScreenDimensions, &gScreenDimensions, sizeof (gScreenDimensions)); - CopyMem (&Context->MenuOption, &gMenuOption, sizeof (gMenuOption)); + Context->ExitRequired = gExitRequired; + Context->HiiHandle = mCurrentHiiHandle; + + // + // Save the menu history data. + // + InitializeListHead(&Context->FormHistoryList); + while (!IsListEmpty (&mPrivateData.FormBrowserEx2.FormViewHistoryHead)) { + MenuList = FORM_ENTRY_INFO_FROM_LINK (mPrivateData.FormBrowserEx2.FormViewHistoryHead.ForwardLink); + RemoveEntryList (&MenuList->Link); + + InsertTailList(&Context->FormHistoryList, &MenuList->Link); + } // // Insert to FormBrowser context list @@ -4322,6 +4658,7 @@ RestoreBrowserContext ( { LIST_ENTRY *Link; BROWSER_CONTEXT *Context; + FORM_ENTRY_INFO *MenuList; ASSERT (gBrowserContextCount != 0); gBrowserContextCount--; @@ -4340,47 +4677,19 @@ RestoreBrowserContext ( // // Restore FormBrowser context // - gBannerData = Context->BannerData; - gClassOfVfr = Context->ClassOfVfr; - gFunctionKeySetting = Context->FunctionKeySetting; gResetRequired = Context->ResetRequired; - gDirection = Context->Direction; - gEnterString = Context->EnterString; - gEnterCommitString = Context->EnterCommitString; - gEnterEscapeString = Context->EnterEscapeString; - gEscapeString = Context->EscapeString; - gMoveHighlight = Context->MoveHighlight; - gMakeSelection = Context->MakeSelection; - gDecNumericInput = Context->DecNumericInput; - gHexNumericInput = Context->HexNumericInput; - gToggleCheckBox = Context->ToggleCheckBox; - gPromptForData = Context->PromptForData; - gPromptForPassword = Context->PromptForPassword; - gPromptForNewPassword = Context->PromptForNewPassword; - gConfirmPassword = Context->ConfirmPassword; - gConfirmError = Context->ConfirmError; - gPassowordInvalid = Context->PassowordInvalid; - gPressEnter = Context->PressEnter; - gEmptyString = Context->EmptyString; - gAreYouSure = Context->AreYouSure; - gYesResponse = Context->YesResponse; - gNoResponse = Context->NoResponse; - gMiniString = Context->MiniString; - gPlusString = Context->PlusString; - gMinusString = Context->MinusString; - gAdjustNumber = Context->AdjustNumber; - gSaveChanges = Context->SaveChanges; - gOptionMismatch = Context->OptionMismatch; - gFormSuppress = Context->FormSuppress; - gPromptBlockWidth = Context->PromptBlockWidth; - gOptionBlockWidth = Context->OptionBlockWidth; - gHelpBlockWidth = Context->HelpBlockWidth; - gOldFormSet = Context->OldFormSet; - gMenuRefreshHead = Context->MenuRefreshHead; - gProtocolNotFound = Context->ProtocolNotFound; - - CopyMem (&gScreenDimensions, &Context->ScreenDimensions, sizeof (gScreenDimensions)); - CopyMem (&gMenuOption, &Context->MenuOption, sizeof (gMenuOption)); + gExitRequired = Context->ExitRequired; + mCurrentHiiHandle = Context->HiiHandle; + + // + // Restore the menu history data. + // + while (!IsListEmpty (&Context->FormHistoryList)) { + MenuList = FORM_ENTRY_INFO_FROM_LINK (Context->FormHistoryList.ForwardLink); + RemoveEntryList (&MenuList->Link); + + InsertTailList(&mPrivateData.FormBrowserEx2.FormViewHistoryHead, &MenuList->Link); + } // // Remove from FormBrowser context list @@ -4440,7 +4749,7 @@ IsHiiHandleInBrowserContext ( // // HiiHandle is Current FormSet. // - if ((gOldFormSet != NULL) && (gOldFormSet->HiiHandle == Handle)) { + if (mCurrentHiiHandle == Handle) { return TRUE; } @@ -4450,7 +4759,7 @@ IsHiiHandleInBrowserContext ( Link = GetFirstNode (&gBrowserContextList); while (!IsNull (&gBrowserContextList, Link)) { Context = BROWSER_CONTEXT_FROM_LINK (Link); - if (Context->OldFormSet->HiiHandle == Handle) { + if (Context->HiiHandle == Handle) { // // HiiHandle is in BrowserContext // @@ -4462,6 +4771,83 @@ IsHiiHandleInBrowserContext ( return FALSE; } +/** + Perform Password check. + Passwork may be encrypted by driver that requires the specific check. + + @param Form Form where Password Statement is in. + @param Statement Password statement + @param PasswordString Password string to be checked. It may be NULL. + NULL means to restore password. + "" string can be used to checked whether old password does exist. + + @return Status Status of Password check. +**/ +EFI_STATUS +EFIAPI +PasswordCheck ( + IN FORM_DISPLAY_ENGINE_FORM *Form, + IN FORM_DISPLAY_ENGINE_STATEMENT *Statement, + IN EFI_STRING PasswordString OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess; + EFI_BROWSER_ACTION_REQUEST ActionRequest; + EFI_IFR_TYPE_VALUE IfrTypeValue; + FORM_BROWSER_STATEMENT *Question; + + ConfigAccess = gCurrentSelection->FormSet->ConfigAccess; + Question = GetBrowserStatement(Statement); + ASSERT (Question != NULL); + + if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) == EFI_IFR_FLAG_CALLBACK) { + if (ConfigAccess == NULL) { + return EFI_UNSUPPORTED; + } + } else { + if (PasswordString == NULL) { + return EFI_SUCCESS; + } + + if (StrnCmp (PasswordString, (CHAR16 *) Question->BufferValue, Question->StorageWidth/sizeof (CHAR16)) == 0) { + return EFI_SUCCESS; + } else { + return EFI_NOT_READY; + } + } + + // + // Prepare password string in HII database + // + if (PasswordString != NULL) { + IfrTypeValue.string = NewString (PasswordString, gCurrentSelection->FormSet->HiiHandle); + } else { + IfrTypeValue.string = 0; + } + + // + // Send password to Configuration Driver for validation + // + Status = ConfigAccess->Callback ( + ConfigAccess, + EFI_BROWSER_ACTION_CHANGING, + Question->QuestionId, + Question->HiiValue.Type, + &IfrTypeValue, + &ActionRequest + ); + + // + // Remove password string from HII database + // + if (PasswordString != NULL) { + DeleteString (IfrTypeValue.string, gCurrentSelection->FormSet->HiiHandle); + } + + return Status; +} + /** Find the registered HotKey based on KeyData. @@ -4512,7 +4898,7 @@ SetScope ( if (Scope >= MaxLevel) { return EFI_INVALID_PARAMETER; } - + // // When no hot key registered in system or on the first setting, // Scope can be set. @@ -4636,6 +5022,128 @@ RegiserExitHandler ( return; } +/** + Check whether the browser data has been modified. + + @retval TRUE Browser data is modified. + @retval FALSE No browser data is modified. + +**/ +BOOLEAN +EFIAPI +IsBrowserDataModified ( + VOID + ) +{ + LIST_ENTRY *Link; + FORM_BROWSER_FORMSET *FormSet; + + if (gCurrentSelection == NULL) { + return FALSE; + } + + switch (gBrowserSettingScope) { + case FormLevel: + return IsNvUpdateRequiredForForm (gCurrentSelection->Form); + + case FormSetLevel: + return IsNvUpdateRequiredForFormSet (gCurrentSelection->FormSet); + + case SystemLevel: + Link = GetFirstNode (&gBrowserFormSetList); + while (!IsNull (&gBrowserFormSetList, Link)) { + FormSet = FORM_BROWSER_FORMSET_FROM_LINK (Link); + if (IsNvUpdateRequiredForFormSet (FormSet)) { + return TRUE; + } + Link = GetNextNode (&gBrowserFormSetList, Link); + } + return FALSE; + + default: + return FALSE; + } +} + +/** + Execute the action requested by the Action parameter. + + @param[in] Action Execute the request action. + @param[in] DefaultId The default Id info when need to load default value. Only used when Action is BROWSER_ACTION_DEFAULT. + + @retval EFI_SUCCESS Execute the request action succss. + @retval EFI_INVALID_PARAMETER The input action value is invalid. + +**/ +EFI_STATUS +EFIAPI +ExecuteAction ( + IN UINT32 Action, + IN UINT16 DefaultId + ) +{ + EFI_STATUS Status; + + if (gCurrentSelection == NULL) { + return EFI_NOT_READY; + } + + Status = EFI_SUCCESS; + + // + // Executet the discard action. + // + if ((Action & BROWSER_ACTION_DISCARD) != 0) { + Status = DiscardForm (gCurrentSelection->FormSet, gCurrentSelection->Form, gBrowserSettingScope); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Executet the difault action. + // + if ((Action & BROWSER_ACTION_DEFAULT) != 0) { + Status = ExtractDefault (gCurrentSelection->FormSet, gCurrentSelection->Form, DefaultId, gBrowserSettingScope, GetDefaultForAll, NULL, FALSE); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Executet the submit action. + // + if ((Action & BROWSER_ACTION_SUBMIT) != 0) { + Status = SubmitForm (gCurrentSelection->FormSet, gCurrentSelection->Form, gBrowserSettingScope); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Executet the reset action. + // + if ((Action & BROWSER_ACTION_RESET) != 0) { + gResetRequired = TRUE; + } + + // + // Executet the exit action. + // + if ((Action & BROWSER_ACTION_EXIT) != 0) { + DiscardForm (gCurrentSelection->FormSet, gCurrentSelection->Form, gBrowserSettingScope); + if (gBrowserSettingScope == SystemLevel) { + if (ExitHandlerFunction != NULL) { + ExitHandlerFunction (); + } + } + + gExitRequired = TRUE; + } + + return Status; +} + /** Create reminder to let user to choose save or discard the changed browser data. Caller can use it to actively check the changed browser data. @@ -4655,12 +5163,6 @@ SaveReminder ( FORM_BROWSER_FORMSET *FormSet; BOOLEAN IsDataChanged; UINT32 DataSavedAction; - CHAR16 *YesResponse; - CHAR16 *NoResponse; - CHAR16 *EmptyString; - CHAR16 *ChangeReminderString; - CHAR16 *SaveConfirmString; - EFI_INPUT_KEY Key; DataSavedAction = BROWSER_NO_CHANGES; IsDataChanged = FALSE; @@ -4671,7 +5173,7 @@ SaveReminder ( if (!ValidateFormSet(FormSet)) { continue; } - if (IsNvUpdateRequired (FormSet)) { + if (IsNvUpdateRequiredForFormSet (FormSet)) { IsDataChanged = TRUE; break; } @@ -4685,42 +5187,19 @@ SaveReminder ( } // - // If data is changed, prompt user + // If data is changed, prompt user to save or discard it. // - gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); - - YesResponse = GetToken (STRING_TOKEN (ARE_YOU_SURE_YES), gHiiHandle); - ASSERT (YesResponse != NULL); - NoResponse = GetToken (STRING_TOKEN (ARE_YOU_SURE_NO), gHiiHandle); - ASSERT (NoResponse != NULL); - EmptyString = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle); - ChangeReminderString = GetToken (STRING_TOKEN (CHANGE_REMINDER), gHiiHandle); - SaveConfirmString = GetToken (STRING_TOKEN (SAVE_CONFIRM), gHiiHandle); - do { - CreateDialog (4, TRUE, 0, NULL, &Key, EmptyString, ChangeReminderString, SaveConfirmString, EmptyString); - } while - (((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (NoResponse[0] | UPPER_LOWER_CASE_OFFSET)) && - ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (YesResponse[0] | UPPER_LOWER_CASE_OFFSET)) - ); + DataSavedAction = (UINT32) mFormDisplay->ConfirmDataChange(); - // - // If the user hits the YesResponse key - // - if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (YesResponse[0] | UPPER_LOWER_CASE_OFFSET)) { - SubmitForm (NULL, NULL, SystemLevel); - DataSavedAction = BROWSER_SAVE_CHANGES; - } else { - DiscardForm (NULL, NULL, SystemLevel); - DataSavedAction = BROWSER_DISCARD_CHANGES; - gResetRequired = FALSE; - } - - FreePool (YesResponse); - FreePool (NoResponse); - FreePool (EmptyString); - FreePool (SaveConfirmString); - FreePool (ChangeReminderString); + if (DataSavedAction == BROWSER_SAVE_CHANGES) { + SubmitForm (NULL, NULL, SystemLevel); + break; + } else if (DataSavedAction == BROWSER_DISCARD_CHANGES) { + DiscardForm (NULL, NULL, SystemLevel); + break; + } + } while (1); return DataSavedAction; } diff --git a/MdeModulePkg/Universal/SetupBrowserDxe/Setup.h b/MdeModulePkg/Universal/SetupBrowserDxe/Setup.h index 9a0c739094..1be2c0d5a0 100644 --- a/MdeModulePkg/Universal/SetupBrowserDxe/Setup.h +++ b/MdeModulePkg/Universal/SetupBrowserDxe/Setup.h @@ -22,7 +22,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include #include -#include +#include +#include #include #include #include @@ -47,105 +48,28 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include #include +#include -#include "Colors.h" // // This is the generated header file which includes whatever needs to be exported (strings + IFR) // -extern UINT8 SetupBrowserStrings[]; +#define UI_ACTION_NONE 0 +#define UI_ACTION_REFRESH_FORM 1 +#define UI_ACTION_REFRESH_FORMSET 2 +#define UI_ACTION_EXIT 3 // -// Screen definitions -// -#define BANNER_HEIGHT 6 -#define BANNER_COLUMNS 3 -#define BANNER_LEFT_COLUMN_INDENT 1 - -#define FRONT_PAGE_HEADER_HEIGHT 6 -#define NONE_FRONT_PAGE_HEADER_HEIGHT 3 -#define LEFT_SKIPPED_COLUMNS 3 -#define FOOTER_HEIGHT 4 -#define STATUS_BAR_HEIGHT 1 -#define SCROLL_ARROW_HEIGHT 1 -#define POPUP_PAD_SPACE_COUNT 5 -#define POPUP_FRAME_WIDTH 2 - -// -// Definition for function key setting -// -#define NONE_FUNCTION_KEY_SETTING 0 -#define ENABLE_FUNCTION_KEY_SETTING 1 - -typedef struct { - EFI_GUID FormSetGuid; - UINTN KeySetting; -} FUNCTIION_KEY_SETTING; - -// -// Character definitions -// -#define CHAR_SPACE 0x0020 -#define UPPER_LOWER_CASE_OFFSET 0x20 - // // Time definitions // #define ONE_SECOND 10000000 -// -// Display definitions -// -#define LEFT_HYPER_DELIMITER L'<' -#define RIGHT_HYPER_DELIMITER L'>' - -#define LEFT_ONEOF_DELIMITER L'<' -#define RIGHT_ONEOF_DELIMITER L'>' - -#define LEFT_NUMERIC_DELIMITER L'[' -#define RIGHT_NUMERIC_DELIMITER L']' - -#define LEFT_CHECKBOX_DELIMITER L'[' -#define RIGHT_CHECKBOX_DELIMITER L']' - -#define CHECK_ON L'X' -#define CHECK_OFF L' ' - -#define TIME_SEPARATOR L':' -#define DATE_SEPARATOR L'/' - -#define YES_ANSWER L'Y' -#define NO_ANSWER L'N' - -// -// This is the Input Error Message -// -#define INPUT_ERROR 1 - -// -// This is the NV RAM update required Message -// -#define NV_UPDATE_REQUIRED 2 - -// -// Refresh the Status Bar with flags -// -#define REFRESH_STATUS_BAR 0xff - -// // Incremental string lenght of ConfigRequest // #define CONFIG_REQUEST_STRING_INCREMENTAL 1024 -// -// HII value compare result -// -#define HII_VALUE_UNDEFINED 0 -#define HII_VALUE_EQUAL 1 -#define HII_VALUE_LESS_THAN 2 -#define HII_VALUE_GREATER_THAN 3 - // // Incremental size of stack for expression // @@ -163,15 +87,12 @@ typedef struct { // // Produced protocol // - EFI_FORM_BROWSER2_PROTOCOL FormBrowser2; - - EFI_FORM_BROWSER_EXTENSION_PROTOCOL FormBrowserEx; + EFI_FORM_BROWSER2_PROTOCOL FormBrowser2; + EFI_FORM_BROWSER_EXTENSION_PROTOCOL FormBrowserEx; -} SETUP_DRIVER_PRIVATE_DATA; + EDKII_FORM_BROWSER_EXTENSION2_PROTOCOL FormBrowserEx2; -typedef struct { - EFI_STRING_ID Banner[BANNER_HEIGHT][BANNER_COLUMNS]; -} BANNER_DATA; +} SETUP_DRIVER_PRIVATE_DATA; // // IFR relative definition @@ -194,16 +115,6 @@ typedef struct { #define FORM_INCONSISTENT_VALIDATION 0 #define FORM_NO_SUBMIT_VALIDATION 1 -#define FORMSET_CLASS_PLATFORM_SETUP 0x0001 -#define FORMSET_CLASS_FRONT_PAGE 0x0002 - -typedef struct { - UINT8 Type; - UINT8 *Buffer; - UINT16 BufferLen; - EFI_IFR_TYPE_VALUE Value; -} EFI_HII_VALUE; - #define NAME_VALUE_NODE_SIGNATURE SIGNATURE_32 ('N', 'V', 'S', 'T') typedef struct { @@ -255,6 +166,7 @@ typedef struct { BROWSER_STORAGE *BrowserStorage; CHAR16 *ConfigRequest; // = + + CHAR16 *ConfigElements;// Elements need to load initial data. UINTN ElementCount; // Number of in the UINTN SpareStrLen; // Spare length of ConfigRequest string buffer } FORMSET_STORAGE; @@ -344,6 +256,8 @@ typedef struct { typedef struct { UINTN Signature; LIST_ENTRY Link; + + EFI_IFR_ONE_OF_OPTION *OpCode; // OneOfOption Data EFI_STRING_ID Text; UINT8 Flags; @@ -376,6 +290,7 @@ typedef struct { LIST_ENTRY Link; UINT8 Operand; // The operand (first byte) of this Statement or Question + EFI_IFR_OP_HEADER *OpCode; // // Statement Header @@ -384,6 +299,11 @@ typedef struct { EFI_STRING_ID Help; EFI_STRING_ID TextTwo; // For EFI_IFR_TEXT + // + // Fake Question Id, used for statement not has true QuestionId. + // + EFI_QUESTION_ID FakeQuestionId; + // // Question Header // @@ -417,6 +337,7 @@ typedef struct { EFI_DEFAULT_ID DefaultId; // for EFI_IFR_RESET_BUTTON EFI_GUID RefreshGuid; // for EFI_IFR_REFRESH_ID BOOLEAN Locked; // Whether this statement is locked. + BOOLEAN ValueChanged; // Whether this statement's value is changed. // // Get from IFR parsing // @@ -467,8 +388,6 @@ typedef struct { BOOLEAN ModalForm; // Whether this is a modal form. BOOLEAN Locked; // Whether this form is locked. - BOOLEAN NvUpdateRequired; // Whether this form has NV update request. - LIST_ENTRY ExpressionListHead; // List of Expressions (FORM_EXPRESSION) LIST_ENTRY StatementListHead; // List of Statements and Questions (FORM_BROWSER_STATEMENT) LIST_ENTRY ConfigRequestHead; // List of configreques for all storage. @@ -502,6 +421,7 @@ typedef struct { UINTN IfrBinaryLength; UINT8 *IfrBinaryData; + BOOLEAN QuestionInited; // Have finished question initilization? EFI_GUID Guid; EFI_STRING_ID FormSetTitle; EFI_STRING_ID Help; @@ -514,14 +434,21 @@ typedef struct { FORM_BROWSER_STATEMENT *StatementBuffer; // Buffer for all Statements and Questions EXPRESSION_OPCODE *ExpressionBuffer; // Buffer for all Expression OpCode + LIST_ENTRY StatementListOSF; // Statement list out side of the form. LIST_ENTRY StorageListHead; // Storage list (FORMSET_STORAGE) LIST_ENTRY DefaultStoreListHead; // DefaultStore list (FORMSET_DEFAULTSTORE) LIST_ENTRY FormListHead; // Form list (FORM_BROWSER_FORM) LIST_ENTRY ExpressionListHead; // List of Expressions (FORM_EXPRESSION) } FORM_BROWSER_FORMSET; - #define FORM_BROWSER_FORMSET_FROM_LINK(a) CR (a, FORM_BROWSER_FORMSET, Link, FORM_BROWSER_FORMSET_SIGNATURE) +typedef struct { + LIST_ENTRY Link; + EFI_EVENT RefreshEvent; +} FORM_BROWSER_REFRESH_EVENT_NODE; + +#define FORM_BROWSER_REFRESH_EVENT_FROM_LINK(a) BASE_CR (a, FORM_BROWSER_REFRESH_EVENT_NODE, Link) + #define BROWSER_CONTEXT_SIGNATURE SIGNATURE_32 ('B', 'C', 'T', 'X') typedef struct { @@ -531,67 +458,54 @@ typedef struct { // // Globals defined in Setup.c // - BANNER_DATA *BannerData; - UINTN ClassOfVfr; - UINTN FunctionKeySetting; - BOOLEAN ResetRequired; - UINT16 Direction; - EFI_SCREEN_DESCRIPTOR ScreenDimensions; - CHAR16 *EnterString; - CHAR16 *EnterCommitString; - CHAR16 *EnterEscapeString; - CHAR16 *EscapeString; - CHAR16 *MoveHighlight; - CHAR16 *MakeSelection; - CHAR16 *DecNumericInput; - CHAR16 *HexNumericInput; - CHAR16 *ToggleCheckBox; - CHAR16 *PromptForData; - CHAR16 *PromptForPassword; - CHAR16 *PromptForNewPassword; - CHAR16 *ConfirmPassword; - CHAR16 *ConfirmError; - CHAR16 *PassowordInvalid; - CHAR16 *PressEnter; - CHAR16 *EmptyString; - CHAR16 *AreYouSure; - CHAR16 *YesResponse; - CHAR16 *NoResponse; - CHAR16 *MiniString; - CHAR16 *PlusString; - CHAR16 *MinusString; - CHAR16 *AdjustNumber; - CHAR16 *SaveChanges; - CHAR16 *OptionMismatch; - CHAR16 *FormSuppress; - CHAR16 *ProtocolNotFound; - CHAR16 PromptBlockWidth; - CHAR16 OptionBlockWidth; - CHAR16 HelpBlockWidth; - FORM_BROWSER_FORMSET *OldFormSet; + BOOLEAN ResetRequired; + BOOLEAN ExitRequired; + EFI_HII_HANDLE HiiHandle; - // - // Globals defined in Ui.c - // - LIST_ENTRY MenuOption; - VOID *MenuRefreshHead; + LIST_ENTRY FormHistoryList; } BROWSER_CONTEXT; #define BROWSER_CONTEXT_FROM_LINK(a) CR (a, BROWSER_CONTEXT, Link, BROWSER_CONTEXT_SIGNATURE) -#define BROWSER_HOT_KEY_SIGNATURE SIGNATURE_32 ('B', 'H', 'K', 'S') - typedef struct { - UINTN Signature; - LIST_ENTRY Link; - - EFI_INPUT_KEY *KeyData; - IN UINT32 Action; - IN UINT16 DefaultId; - IN EFI_STRING HelpString; -} BROWSER_HOT_KEY; + EFI_HII_HANDLE Handle; -#define BROWSER_HOT_KEY_FROM_LINK(a) CR (a, BROWSER_HOT_KEY, Link, BROWSER_HOT_KEY_SIGNATURE) + // + // Target formset/form/Question information + // + EFI_GUID FormSetGuid; + UINT16 FormId; + UINT16 QuestionId; + UINTN Sequence; // used for time/date only. + + UINTN TopRow; + UINTN BottomRow; + UINTN PromptCol; + UINTN OptionCol; + UINTN CurrentRow; + + // + // Ation for Browser to taken: + // UI_ACTION_NONE - navigation inside a form + // UI_ACTION_REFRESH_FORM - re-evaluate expressions and repaint form + // UI_ACTION_REFRESH_FORMSET - re-parse formset IFR binary + // + UINTN Action; + + // + // Current selected fomset/form/Question + // + FORM_BROWSER_FORMSET *FormSet; + FORM_BROWSER_FORM *Form; + FORM_BROWSER_STATEMENT *Statement; + + // + // Whether the Form is editable + // + BOOLEAN FormEditable; + + FORM_ENTRY_INFO *CurrentMenu; +} UI_MENU_SELECTION; // // Scope for get defaut value. It may be GetDefaultForNoStorage, GetDefaultForStorage or GetDefaultForAll. @@ -614,72 +528,32 @@ typedef enum { } GET_SET_QUESTION_VALUE_WITH; extern EFI_HII_DATABASE_PROTOCOL *mHiiDatabase; -extern EFI_HII_STRING_PROTOCOL *mHiiString; extern EFI_HII_CONFIG_ROUTING_PROTOCOL *mHiiConfigRouting; extern EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *mPathFromText; +extern EDKII_FORM_DISPLAY_ENGINE_PROTOCOL *mFormDisplay; -extern BANNER_DATA *gBannerData; -extern EFI_HII_HANDLE gFrontPageHandle; -extern UINTN gClassOfVfr; -extern UINTN gFunctionKeySetting; extern BOOLEAN gResetRequired; -extern EFI_HII_HANDLE gHiiHandle; -extern UINT16 gDirection; -extern EFI_SCREEN_DESCRIPTOR gScreenDimensions; +extern BOOLEAN gExitRequired; extern FORM_BROWSER_FORMSET *gOldFormSet; extern LIST_ENTRY gBrowserFormSetList; extern LIST_ENTRY gBrowserHotKeyList; extern BROWSER_SETTING_SCOPE gBrowserSettingScope; extern EXIT_HANDLER ExitHandlerFunction; -extern UINTN gFooterHeight; - +extern EFI_HII_HANDLE mCurrentHiiHandle; // // Browser Global Strings // -extern CHAR16 *gDiscardFailed; -extern CHAR16 *gDefaultFailed; -extern CHAR16 *gEnterString; -extern CHAR16 *gEnterCommitString; -extern CHAR16 *gEnterEscapeString; -extern CHAR16 *gEscapeString; -extern CHAR16 *gSaveFailed; -extern CHAR16 *gMoveHighlight; -extern CHAR16 *gMakeSelection; -extern CHAR16 *gDecNumericInput; -extern CHAR16 *gHexNumericInput; -extern CHAR16 *gToggleCheckBox; -extern CHAR16 *gPromptForData; -extern CHAR16 *gPromptForPassword; -extern CHAR16 *gPromptForNewPassword; -extern CHAR16 *gConfirmPassword; -extern CHAR16 *gConfirmError; -extern CHAR16 *gPassowordInvalid; -extern CHAR16 *gPressEnter; extern CHAR16 *gEmptyString; -extern CHAR16 *gAreYouSure; -extern CHAR16 *gYesResponse; -extern CHAR16 *gNoResponse; -extern CHAR16 *gMiniString; -extern CHAR16 *gPlusString; -extern CHAR16 *gMinusString; -extern CHAR16 *gAdjustNumber; -extern CHAR16 *gSaveChanges; -extern CHAR16 *gOptionMismatch; -extern CHAR16 *gFormSuppress; -extern CHAR16 *gProtocolNotFound; - -extern CHAR16 gPromptBlockWidth; -extern CHAR16 gOptionBlockWidth; -extern CHAR16 gHelpBlockWidth; extern EFI_GUID gZeroGuid; -extern EFI_GUID gTianoHiiIfrGuid; -#include "Ui.h" +extern UI_MENU_SELECTION *gCurrentSelection; + // // Global Procedure Defines // +#include "Expression.h" /** Initialize the HII String Token to the correct values. @@ -690,91 +564,6 @@ InitializeBrowserStrings ( VOID ); -/** - Prints a unicode string to the default console, - using L"%s" format. - - @param String String pointer. - - @return Length of string printed to the console - -**/ -UINTN -PrintString ( - IN CHAR16 *String - ); - -/** - Prints a chracter to the default console, - using L"%c" format. - - @param Character Character to print. - - @return Length of string printed to the console. - -**/ -UINTN -PrintChar ( - CHAR16 Character - ); - -/** - Prints a formatted unicode string to the default console, at - the supplied cursor position. - - @param Column The cursor position to print the string at. - @param Row The cursor position to print the string at - @param Fmt Format string - @param ... Variable argument list for formating string. - - @return Length of string printed to the console - -**/ -UINTN -EFIAPI -PrintAt ( - IN UINTN Column, - IN UINTN Row, - IN CHAR16 *Fmt, - ... - ); - -/** - Prints a unicode string to the default console, at - the supplied cursor position, using L"%s" format. - - @param Column The cursor position to print the string at. - @param Row The cursor position to print the string at - @param String String pointer. - - @return Length of string printed to the console - -**/ -UINTN -PrintStringAt ( - IN UINTN Column, - IN UINTN Row, - IN CHAR16 *String - ); - -/** - Prints a chracter to the default console, at - the supplied cursor position, using L"%c" format. - - @param Column The cursor position to print the string at. - @param Row The cursor position to print the string at. - @param Character Character to print. - - @return Length of string printed to the console. - -**/ -UINTN -PrintCharAt ( - IN UINTN Column, - IN UINTN Row, - CHAR16 Character - ); - /** Parse opcodes in the formset IFR binary. @@ -800,17 +589,6 @@ DestroyFormSet ( IN OUT FORM_BROWSER_FORMSET *FormSet ); -/** - This function displays the page frame. - - @param Selection Selection contains the information about - the Selection, form and formset to be displayed. - Selection action may be updated in retrieve callback. -**/ -VOID -DisplayPageFrame ( - IN UI_MENU_SELECTION *Selection - ); /** Create a new string in HII Package List. @@ -859,59 +637,6 @@ GetToken ( IN EFI_HII_HANDLE HiiHandle ); -/** - Draw a pop up windows based on the dimension, number of lines and - strings specified. - - @param RequestedWidth The width of the pop-up. - @param NumberOfLines The number of lines. - @param Marker The variable argument list for the list of string to be printed. - -**/ -VOID -CreateSharedPopUp ( - IN UINTN RequestedWidth, - IN UINTN NumberOfLines, - IN VA_LIST Marker - ); - -/** - Routine used to abstract a generic dialog interface and return the selected key or string - - @param NumberOfLines The number of lines for the dialog box - @param 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 - @param MaximumStringSize The maximum size in bytes of a typed in string - (each character is a CHAR16) and the minimum - string returned is two bytes - @param StringBuffer The passed in pointer to the buffer which will - hold the typed in string if HotKey is FALSE - @param KeyValue The EFI_KEY value returned if HotKey is TRUE.. - @param ... A series of (quantity == NumberOfLines) text - strings which will be used to construct the dialog - box - - @retval EFI_SUCCESS Displayed dialog and received user interaction - @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g. - (StringBuffer == NULL) && (HotKey == FALSE)) - @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine - -**/ -EFI_STATUS -EFIAPI -CreateDialog ( - IN UINTN NumberOfLines, - IN BOOLEAN HotKey, - IN UINTN MaximumStringSize, - OUT CHAR16 *StringBuffer, - OUT EFI_INPUT_KEY *KeyValue, - ... - ); - /** Get Value for given Name from a NameValue Storage. @@ -939,6 +664,7 @@ GetValueByName ( @param Name The Name. @param Value The Value to set. @param SetValueTo Whether update editValue or Value. + @param ReturnNode The node use the input name. @retval EFI_SUCCESS Value found for given Name. @retval EFI_NOT_FOUND No such Name found in NameValue storage. @@ -946,10 +672,31 @@ GetValueByName ( **/ EFI_STATUS SetValueByName ( - IN BROWSER_STORAGE *Storage, - IN CHAR16 *Name, - IN CHAR16 *Value, - IN GET_SET_QUESTION_VALUE_WITH SetValueTo + IN BROWSER_STORAGE *Storage, + IN CHAR16 *Name, + IN CHAR16 *Value, + IN GET_SET_QUESTION_VALUE_WITH SetValueTo, + OUT NAME_VALUE_NODE **ReturnNode + ); + +/** + Validate whether this question's value has changed. + + @param FormSet FormSet data structure. + @param Form Form data structure. + @param Question Question to be initialized. + @param GetValueFrom Where to get value, may from editbuffer, buffer or hii driver. + + @retval TRUE Question's value has changed. + @retval FALSE Question's value has not changed + +**/ +BOOLEAN +IsQuestionValueChanged ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form, + IN OUT FORM_BROWSER_STATEMENT *Question, + IN GET_SET_QUESTION_VALUE_WITH GetValueFrom ); /** @@ -1071,10 +818,8 @@ GetQuestionDefault ( @param FormSet FormSet data structure. - @retval EFI_SUCCESS The function completed successfully. - **/ -EFI_STATUS +VOID InitializeCurrentSetting ( IN OUT FORM_BROWSER_FORMSET *FormSet ); @@ -1087,7 +832,6 @@ InitializeCurrentSetting ( GUID), take the first FormSet found in package list. @param FormSet FormSet data structure. - @param UpdateGlobalVar Whether need to update the global variable. @retval EFI_SUCCESS The function completed successfully. @retval EFI_NOT_FOUND The specified FormSet could not be found. @@ -1097,8 +841,7 @@ EFI_STATUS InitializeFormSet ( IN EFI_HII_HANDLE Handle, IN OUT EFI_GUID *FormSetGuid, - OUT FORM_BROWSER_FORMSET *FormSet, - IN BOOLEAN UpdateGlobalVar + OUT FORM_BROWSER_FORMSET *FormSet ); /** @@ -1176,6 +919,7 @@ LoadFormSetConfig ( @param Storage The Storage to be conveted. @param ConfigResp The returned . @param ConfigRequest The ConfigRequest string. + @param GetEditBuf Get the data from editbuffer or buffer. @retval EFI_SUCCESS Convert success. @retval EFI_INVALID_PARAMETER Incorrect storage type. @@ -1185,7 +929,8 @@ EFI_STATUS StorageToConfigResp ( IN BROWSER_STORAGE *Storage, IN CHAR16 **ConfigResp, - IN CHAR16 *ConfigRequest + IN CHAR16 *ConfigRequest, + IN BOOLEAN GetEditBuf ); /** @@ -1210,10 +955,8 @@ ConfigRespToStorage ( @param FormSet FormSet data structure. @param Storage Buffer Storage. - @retval EFI_SUCCESS The function completed successfully. - **/ -EFI_STATUS +VOID LoadStorage ( IN FORM_BROWSER_FORMSET *FormSet, IN FORMSET_STORAGE *Storage @@ -1349,30 +1092,29 @@ BrowserCallback ( about the Selection, form and formset to be displayed. On output, Selection return the screen item that is selected by user. - @param Repaint Whether need to repaint the menu. - @param NewLine Whether need to show at new line. + @param SettingLevel Input Settting level, if it is FormLevel, just exit current form. + else, we need to exit current formset. - @retval TRUE Need return. - @retval FALSE No need to return. + @retval TRUE Exit current form. + @retval FALSE User press ESC and keep in current form. **/ BOOLEAN FindNextMenu ( - IN OUT UI_MENU_SELECTION *Selection, - IN BOOLEAN *Repaint, - IN BOOLEAN *NewLine + IN OUT UI_MENU_SELECTION *Selection, + IN BROWSER_SETTING_SCOPE SettingLevel ); /** - check whether the formset need to update the NV. + check whether the form need to update the NV. - @param FormSet FormSet data structure. - @param SetValue Whether set new value or clear old value. + @param Form Form data structure. + @retval TRUE Need to update the NV. + @retval FALSE No need to update the NV. **/ -VOID -UpdateNvInfoInForm ( - IN FORM_BROWSER_FORMSET *FormSet, - IN BOOLEAN SetValue +BOOLEAN +IsNvUpdateRequiredForForm ( + IN FORM_BROWSER_FORM *Form ); /** @@ -1383,11 +1125,24 @@ UpdateNvInfoInForm ( @retval TRUE Need to update the NV. @retval FALSE No need to update the NV. **/ -BOOLEAN -IsNvUpdateRequired ( +BOOLEAN +IsNvUpdateRequiredForFormSet ( IN FORM_BROWSER_FORMSET *FormSet ); +/** + Check whether the storage data for current form set is changed. + + @param FormSet FormSet data structure. + + @retval TRUE Data is changed. + @retval FALSE Data is not changed. +**/ +BOOLEAN +IsStorageDataChangedForFormSet ( + IN FORM_BROWSER_FORMSET *FormSet + ); + /** Call the call back function for the question and process the return action. @@ -1516,6 +1271,38 @@ RegiserExitHandler ( IN EXIT_HANDLER Handler ); +/** + + Check whether the browser data has been modified. + + @retval TRUE Browser data is changed. + @retval FALSE No browser data is changed. + +**/ +BOOLEAN +EFIAPI +IsBrowserDataModified ( + VOID + ); + +/** + + Execute the action requested by the Action parameter. + + @param[in] Action Execute the request action. + @param[in] DefaultId The default Id info when need to load default value. + + @retval EFI_SUCCESS Execute the request action succss. + @retval EFI_INVALID_PARAMETER The input action value is invalid. + +**/ +EFI_STATUS +EFIAPI +ExecuteAction ( + IN UINT32 Action, + IN UINT16 DefaultId + ); + /** Create reminder to let user to choose save or discard the changed browser data. Caller can use it to actively check the changed browser data. @@ -1544,6 +1331,267 @@ GetHotKeyFromRegisterList ( IN EFI_INPUT_KEY *KeyData ); +/** + + Get FORM_BROWSER_STATEMENT from FORM_DISPLAY_ENGINE_STATEMENT based on the OpCode info. + + @param DisplayStatement The input FORM_DISPLAY_ENGINE_STATEMENT. + + @retval FORM_BROWSER_STATEMENT The return FORM_BROWSER_STATEMENT info. + +**/ +FORM_BROWSER_STATEMENT * +GetBrowserStatement ( + IN FORM_DISPLAY_ENGINE_STATEMENT *DisplayStatement + ); + +/** + Password may be stored as encrypted by Configuration Driver. When change a + password, user will be challenged with old password. To validate user input old + password, we will send the clear text to Configuration Driver via Callback(). + Configuration driver is responsible to check the passed in password and return + the validation result. If validation pass, state machine in password Callback() + will transit from BROWSER_STATE_VALIDATE_PASSWORD to BROWSER_STATE_SET_PASSWORD. + After user type in new password twice, Callback() will be invoked to send the + new password to Configuration Driver. + + @param Selection Pointer to UI_MENU_SELECTION. + @param MenuOption The MenuOption for this password Question. + @param String The clear text of password. + + @retval EFI_NOT_AVAILABLE_YET Callback() request to terminate password input. + @return In state of BROWSER_STATE_VALIDATE_PASSWORD: + @retval EFI_SUCCESS Password correct, Browser will prompt for new + password. + @retval EFI_NOT_READY Password incorrect, Browser will show error + message. + @retval Other Browser will do nothing. + @return In state of BROWSER_STATE_SET_PASSWORD: + @retval EFI_SUCCESS Set password success. + @retval Other Set password failed. + +**/ +EFI_STATUS +PasswordCallback ( + IN UI_MENU_SELECTION *Selection, + IN FORM_BROWSER_STATEMENT *Question, + IN CHAR16 *String + ); + +/** + Display error message for invalid password. + +**/ +VOID +PasswordInvalid ( + VOID + ); + +/** + The worker function that send the displays to the screen. On output, + the selection made by user is returned. + + @param Selection On input, Selection tell setup browser the information + about the Selection, form and formset to be displayed. + On output, Selection return the screen item that is selected + by user. + + @retval EFI_SUCCESS The page is displayed successfully. + @return Other value if the page failed to be diplayed. + +**/ +EFI_STATUS +SetupBrowser ( + IN OUT UI_MENU_SELECTION *Selection + ); + +/** + Free up the resource allocated for all strings required + by Setup Browser. + +**/ +VOID +FreeBrowserStrings ( + VOID + ); + +/** + Create a menu with specified formset GUID and form ID, and add it as a child + of the given parent menu. + + @param HiiHandle Hii handle related to this formset. + @param FormSetGuid The Formset Guid of menu to be added. + @param FormId The Form ID of menu to be added. + @param QuestionId The question id of this menu to be added. + + @return A pointer to the newly added menu or NULL if memory is insufficient. + +**/ +FORM_ENTRY_INFO * +UiAddMenuList ( + IN EFI_HII_HANDLE HiiHandle, + IN EFI_GUID *FormSetGuid, + IN UINT16 FormId, + IN UINT16 QuestionId + ); + +/** + Search Menu with given FormSetGuid and FormId in all cached menu list. + + @param HiiHandle HiiHandle for FormSet. + @param FormSetGuid The Formset GUID of the menu to search. + @param FormId The Form ID of menu to search. + + @return A pointer to menu found or NULL if not found. + +**/ +FORM_ENTRY_INFO * +UiFindMenuList ( + IN EFI_HII_HANDLE HiiHandle, + IN EFI_GUID *FormSetGuid, + IN UINT16 FormId + ); + +/** + Free Menu list linked list. + + @param MenuListHead One Menu list point in the menu list. + +**/ +VOID +UiFreeMenuList ( + LIST_ENTRY *MenuListHead + ); + +/** + Find parent menu for current menu. + + @param CurrentMenu Current Menu + + @retval The parent menu for current menu. +**/ +FORM_ENTRY_INFO * +UiFindParentMenu ( + IN FORM_ENTRY_INFO *CurrentMenu + ); + +/** + Search an Option of a Question by its value. + + @param Question The Question + @param OptionValue Value for Option to be searched. + + @retval Pointer Pointer to the found Option. + @retval NULL Option not found. + +**/ +QUESTION_OPTION * +ValueToOption ( + IN FORM_BROWSER_STATEMENT *Question, + IN EFI_HII_VALUE *OptionValue + ); +/** + Return data element in an Array by its Index. + + @param Array The data array. + @param Type Type of the data in this array. + @param Index Zero based index for data in this array. + + @retval Value The data to be returned + +**/ +UINT64 +GetArrayData ( + IN VOID *Array, + IN UINT8 Type, + IN UINTN Index + ); + +/** + Set value of a data element in an Array by its Index. + + @param Array The data array. + @param Type Type of the data in this array. + @param Index Zero based index for data in this array. + @param Value The value to be set. + +**/ +VOID +SetArrayData ( + IN VOID *Array, + IN UINT8 Type, + IN UINTN Index, + IN UINT64 Value + ); + +/** + Compare two Hii value. + + @param Value1 Expression value to compare on left-hand. + @param Value2 Expression value to compare on right-hand. + @param Result Return value after compare. + retval 0 Two operators equal. + return Positive value if Value1 is greater than Value2. + retval Negative value if Value1 is less than Value2. + @param HiiHandle Only required for string compare. + + @retval other Could not perform compare on two values. + @retval EFI_SUCCESS Compare the value success. + +**/ +EFI_STATUS +CompareHiiValue ( + IN EFI_HII_VALUE *Value1, + IN EFI_HII_VALUE *Value2, + OUT INTN *Result, + IN EFI_HII_HANDLE HiiHandle OPTIONAL + ); + +/** + Perform Password check. + Passwork may be encrypted by driver that requires the specific check. + + @param Form Form where Password Statement is in. + @param Statement Password statement + @param PasswordString Password string to be checked. It may be NULL. + NULL means to restore password. + "" string can be used to checked whether old password does exist. + + @return Status Status of Password check. +**/ +EFI_STATUS +EFIAPI +PasswordCheck ( + IN FORM_DISPLAY_ENGINE_FORM *Form, + IN FORM_DISPLAY_ENGINE_STATEMENT *Statement, + IN EFI_STRING PasswordString OPTIONAL + ); + +/** + + Get FORM_BROWSER_STATEMENT from FORM_DISPLAY_ENGINE_STATEMENT based on the OpCode info. + + @param DisplayStatement The input FORM_DISPLAY_ENGINE_STATEMENT. + + @retval FORM_BROWSER_STATEMENT The return FORM_BROWSER_STATEMENT info. + +**/ +FORM_BROWSER_STATEMENT * +GetBrowserStatement ( + IN FORM_DISPLAY_ENGINE_STATEMENT *DisplayStatement + ); + +/** + + Initialize the Display form structure data. + +**/ +VOID +InitializeDisplayFormData ( + VOID + ); + + /** Base on the current formset info, clean the ConfigRequest string in browser storage. @@ -1554,5 +1602,5 @@ VOID CleanBrowserStorage ( IN OUT FORM_BROWSER_FORMSET *FormSet ); - + #endif diff --git a/MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf b/MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf index 7e740bc099..395c9383d5 100644 --- a/MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf +++ b/MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf @@ -19,7 +19,7 @@ BASE_NAME = SetupBrowser FILE_GUID = EBf342FE-B1D3-4EF8-957C-8048606FF671 MODULE_TYPE = DXE_DRIVER - VERSION_STRING = 1.0 + VERSION_STRING = 2.0 ENTRY_POINT = InitializeSetup # @@ -29,19 +29,12 @@ # [Sources] - SetupBrowserStr.uni Setup.c Setup.h IfrParse.c Expression.c - InputHandler.c - Print.c Presentation.c - ProcessOptions.c - Ui.c - Ui.h - Colors.h - + Expression.h [Packages] MdePkg/MdePkg.dec @@ -59,36 +52,31 @@ HiiLib DevicePathLib PcdLib + UefiLib [Guids] - gEfiIfrTianoGuid ## CONSUMES ## GUID gEfiIfrFrameworkGuid ## CONSUMES ## GUID gEfiHiiPlatformSetupFormsetGuid gEfiHiiStandardFormGuid ## SOMETIMES_CONSUMES ## GUID [Protocols] gEfiHiiConfigAccessProtocolGuid ## CONSUMES - gEfiHiiStringProtocolGuid ## CONSUMES gEfiFormBrowser2ProtocolGuid ## PRODUCES - gEfiFormBrowserExProtocolGuid ## PRODUCES + gEdkiiFormBrowserEx2ProtocolGuid ## PRODUCES gEfiHiiConfigRoutingProtocolGuid ## CONSUMES gEfiHiiDatabaseProtocolGuid ## CONSUMES gEfiUnicodeCollation2ProtocolGuid ## CONSUMES gEfiUserManagerProtocolGuid ## SOMETIMES_CONSUMES gEfiDevicePathFromTextProtocolGuid ## SOMETIMES_CONSUMES + gEdkiiFormDisplayEngineProtocolGuid ## PRODUCE + gEfiFormBrowserExProtocolGuid ## PRODUCE [FeaturePcd] gEfiMdeModulePkgTokenSpaceGuid.PcdFrameworkCompatibilitySupport ## CONSUMES - gEfiMdeModulePkgTokenSpaceGuid.PcdBrowserGrayOutTextStatement ## CONSUMES - gEfiMdeModulePkgTokenSpaceGuid.PcdBrowerGrayOutReadOnlyMenu ## CONSUMES - -[Pcd] - gEfiMdeModulePkgTokenSpaceGuid.PcdBrowserSubtitleTextColor ## CONSUMES - gEfiMdeModulePkgTokenSpaceGuid.PcdBrowserFieldTextColor ## CONSUMES - gEfiMdeModulePkgTokenSpaceGuid.PcdBrowserFieldTextHighlightColor ## CONSUMES - gEfiMdeModulePkgTokenSpaceGuid.PcdBrowserFieldBackgroundHighlightColor ## CONSUMES [Depex] gEfiHiiDatabaseProtocolGuid AND gEfiHiiConfigRoutingProtocolGuid +[BuildOptions] + MSFT:*_*_*_CC_FLAGS = /Od -- 2.39.2