1 /** @file
2 Entry and initialization module for the browser.
3 
4 Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2014, Hewlett-Packard Development Company, L.P.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution.  The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10 
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include "FormDisplay.h"
17 
18 //
19 // Search table for UiDisplayMenu()
20 //
21 SCAN_CODE_TO_SCREEN_OPERATION     gScanCodeToOperation[] = {
22   {
23     SCAN_UP,
24     UiUp,
25   },
26   {
27     SCAN_DOWN,
28     UiDown,
29   },
30   {
31     SCAN_PAGE_UP,
32     UiPageUp,
33   },
34   {
35     SCAN_PAGE_DOWN,
36     UiPageDown,
37   },
38   {
39     SCAN_ESC,
40     UiReset,
41   },
42   {
43     SCAN_LEFT,
44     UiLeft,
45   },
46   {
47     SCAN_RIGHT,
48     UiRight,
49   }
50 };
51 
52 UINTN mScanCodeNumber = sizeof (gScanCodeToOperation) / sizeof (gScanCodeToOperation[0]);
53 
54 SCREEN_OPERATION_T0_CONTROL_FLAG  gScreenOperationToControlFlag[] = {
55   {
56     UiNoOperation,
57     CfUiNoOperation,
58   },
59   {
60     UiSelect,
61     CfUiSelect,
62   },
63   {
64     UiUp,
65     CfUiUp,
66   },
67   {
68     UiDown,
69     CfUiDown,
70   },
71   {
72     UiLeft,
73     CfUiLeft,
74   },
75   {
76     UiRight,
77     CfUiRight,
78   },
79   {
80     UiReset,
81     CfUiReset,
82   },
83   {
84     UiPageUp,
85     CfUiPageUp,
86   },
87   {
88     UiPageDown,
89     CfUiPageDown
90   },
91   {
92     UiHotKey,
93     CfUiHotKey
94   }
95 };
96 
97 EFI_GUID  gDisplayEngineGuid = {
98   0xE38C1029, 0xE38F, 0x45b9, {0x8F, 0x0D, 0xE2, 0xE6, 0x0B, 0xC9, 0xB2, 0x62}
99 };
100 
101 BOOLEAN                       gMisMatch;
102 EFI_SCREEN_DESCRIPTOR         gStatementDimensions;
103 BOOLEAN                       mStatementLayoutIsChanged = TRUE;
104 USER_INPUT                    *gUserInput;
105 FORM_DISPLAY_ENGINE_FORM      *gFormData;
106 EFI_HII_HANDLE                gHiiHandle;
107 UINT16                        gDirection;
108 LIST_ENTRY                    gMenuOption;
109 DISPLAY_HIGHLIGHT_MENU_INFO   gHighligthMenuInfo = {0};
110 BOOLEAN                       mIsFirstForm = TRUE;
111 FORM_ENTRY_INFO               gOldFormEntry = {0};
112 
113 //
114 // Browser Global Strings
115 //
116 CHAR16            *gFormNotFound;
117 CHAR16            *gNoSubmitIf;
118 CHAR16            *gBrwoserError;
119 CHAR16            *gSaveFailed;
120 CHAR16            *gNoSubmitIfFailed;
121 CHAR16            *gSaveProcess;
122 CHAR16            *gSaveNoSubmitProcess;
123 CHAR16            *gDiscardChange;
124 CHAR16            *gJumpToFormSet;
125 CHAR16            *gCheckError;
126 CHAR16            *gPromptForData;
127 CHAR16            *gPromptForPassword;
128 CHAR16            *gPromptForNewPassword;
129 CHAR16            *gConfirmPassword;
130 CHAR16            *gConfirmError;
131 CHAR16            *gPassowordInvalid;
132 CHAR16            *gPressEnter;
133 CHAR16            *gEmptyString;
134 CHAR16            *gMiniString;
135 CHAR16            *gOptionMismatch;
136 CHAR16            *gFormSuppress;
137 CHAR16            *gProtocolNotFound;
138 CHAR16            *gConfirmDefaultMsg;
139 CHAR16            *gConfirmSubmitMsg;
140 CHAR16            *gConfirmDiscardMsg;
141 CHAR16            *gConfirmResetMsg;
142 CHAR16            *gConfirmExitMsg;
143 CHAR16            *gConfirmSubmitMsg2nd;
144 CHAR16            *gConfirmDefaultMsg2nd;
145 CHAR16            *gConfirmResetMsg2nd;
146 CHAR16            *gConfirmExitMsg2nd;
147 CHAR16            *gConfirmOpt;
148 CHAR16            *gConfirmOptYes;
149 CHAR16            *gConfirmOptNo;
150 CHAR16            *gConfirmMsgConnect;
151 CHAR16            *gConfirmMsgEnd;
152 CHAR16            gModalSkipColumn;
153 CHAR16            gPromptBlockWidth;
154 CHAR16            gOptionBlockWidth;
155 CHAR16            gHelpBlockWidth;
156 CHAR16            *mUnknownString;
157 
158 FORM_DISPLAY_DRIVER_PRIVATE_DATA  mPrivateData = {
159   FORM_DISPLAY_DRIVER_SIGNATURE,
160   NULL,
161   {
162     FormDisplay,
163     DriverClearDisplayPage,
164     ConfirmDataChange
165   }
166 };
167 
168 
169 /**
170   Get the string based on the StringId and HII Package List Handle.
171 
172   @param  Token                  The String's ID.
173   @param  HiiHandle              The package list in the HII database to search for
174                                  the specified string.
175 
176   @return The output string.
177 
178 **/
179 CHAR16 *
GetToken(IN EFI_STRING_ID Token,IN EFI_HII_HANDLE HiiHandle)180 GetToken (
181   IN  EFI_STRING_ID                Token,
182   IN  EFI_HII_HANDLE               HiiHandle
183   )
184 {
185   EFI_STRING  String;
186 
187   String = HiiGetString (HiiHandle, Token, NULL);
188   if (String == NULL) {
189     String = AllocateCopyPool (StrSize (mUnknownString), mUnknownString);
190     ASSERT (String != NULL);
191   }
192 
193   return (CHAR16 *) String;
194 }
195 
196 
197 /**
198   Initialize the HII String Token to the correct values.
199 
200 **/
201 VOID
InitializeDisplayStrings(VOID)202 InitializeDisplayStrings (
203   VOID
204   )
205 {
206   mUnknownString        = GetToken (STRING_TOKEN (UNKNOWN_STRING), gHiiHandle);
207   gSaveFailed           = GetToken (STRING_TOKEN (SAVE_FAILED), gHiiHandle);
208   gNoSubmitIfFailed     = GetToken (STRING_TOKEN (NO_SUBMIT_IF_CHECK_FAILED), gHiiHandle);
209   gSaveProcess          = GetToken (STRING_TOKEN (DISCARD_OR_JUMP), gHiiHandle);
210   gSaveNoSubmitProcess  = GetToken (STRING_TOKEN (DISCARD_OR_CHECK), gHiiHandle);
211   gDiscardChange        = GetToken (STRING_TOKEN (DISCARD_OR_JUMP_DISCARD), gHiiHandle);
212   gJumpToFormSet        = GetToken (STRING_TOKEN (DISCARD_OR_JUMP_JUMP), gHiiHandle);
213   gCheckError           = GetToken (STRING_TOKEN (DISCARD_OR_CHECK_CHECK), gHiiHandle);
214   gPromptForData        = GetToken (STRING_TOKEN (PROMPT_FOR_DATA), gHiiHandle);
215   gPromptForPassword    = GetToken (STRING_TOKEN (PROMPT_FOR_PASSWORD), gHiiHandle);
216   gPromptForNewPassword = GetToken (STRING_TOKEN (PROMPT_FOR_NEW_PASSWORD), gHiiHandle);
217   gConfirmPassword      = GetToken (STRING_TOKEN (CONFIRM_PASSWORD), gHiiHandle);
218   gConfirmError         = GetToken (STRING_TOKEN (CONFIRM_ERROR), gHiiHandle);
219   gPassowordInvalid     = GetToken (STRING_TOKEN (PASSWORD_INVALID), gHiiHandle);
220   gPressEnter           = GetToken (STRING_TOKEN (PRESS_ENTER), gHiiHandle);
221   gEmptyString          = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle);
222   gMiniString           = GetToken (STRING_TOKEN (MINI_STRING), gHiiHandle);
223   gOptionMismatch       = GetToken (STRING_TOKEN (OPTION_MISMATCH), gHiiHandle);
224   gFormSuppress         = GetToken (STRING_TOKEN (FORM_SUPPRESSED), gHiiHandle);
225   gProtocolNotFound     = GetToken (STRING_TOKEN (PROTOCOL_NOT_FOUND), gHiiHandle);
226   gFormNotFound         = GetToken (STRING_TOKEN (STATUS_BROWSER_FORM_NOT_FOUND), gHiiHandle);
227   gNoSubmitIf           = GetToken (STRING_TOKEN (STATUS_BROWSER_NO_SUBMIT_IF), gHiiHandle);
228   gBrwoserError         = GetToken (STRING_TOKEN (STATUS_BROWSER_ERROR), gHiiHandle);
229   gConfirmDefaultMsg    = GetToken (STRING_TOKEN (CONFIRM_DEFAULT_MESSAGE), gHiiHandle);
230   gConfirmDiscardMsg    = GetToken (STRING_TOKEN (CONFIRM_DISCARD_MESSAGE), gHiiHandle);
231   gConfirmSubmitMsg     = GetToken (STRING_TOKEN (CONFIRM_SUBMIT_MESSAGE), gHiiHandle);
232   gConfirmResetMsg      = GetToken (STRING_TOKEN (CONFIRM_RESET_MESSAGE), gHiiHandle);
233   gConfirmExitMsg       = GetToken (STRING_TOKEN (CONFIRM_EXIT_MESSAGE), gHiiHandle);
234   gConfirmDefaultMsg2nd = GetToken (STRING_TOKEN (CONFIRM_DEFAULT_MESSAGE_2ND), gHiiHandle);
235   gConfirmSubmitMsg2nd  = GetToken (STRING_TOKEN (CONFIRM_SUBMIT_MESSAGE_2ND), gHiiHandle);
236   gConfirmResetMsg2nd   = GetToken (STRING_TOKEN (CONFIRM_RESET_MESSAGE_2ND), gHiiHandle);
237   gConfirmExitMsg2nd    = GetToken (STRING_TOKEN (CONFIRM_EXIT_MESSAGE_2ND), gHiiHandle);
238   gConfirmOpt           = GetToken (STRING_TOKEN (CONFIRM_OPTION), gHiiHandle);
239   gConfirmOptYes        = GetToken (STRING_TOKEN (CONFIRM_OPTION_YES), gHiiHandle);
240   gConfirmOptNo         = GetToken (STRING_TOKEN (CONFIRM_OPTION_NO), gHiiHandle);
241   gConfirmMsgConnect    = GetToken (STRING_TOKEN (CONFIRM_OPTION_CONNECT), gHiiHandle);
242   gConfirmMsgEnd        = GetToken (STRING_TOKEN (CONFIRM_OPTION_END), gHiiHandle);
243 }
244 
245 /**
246   Free up the resource allocated for all strings required
247   by Setup Browser.
248 
249 **/
250 VOID
FreeDisplayStrings(VOID)251 FreeDisplayStrings (
252   VOID
253   )
254 {
255   FreePool (mUnknownString);
256   FreePool (gEmptyString);
257   FreePool (gSaveFailed);
258   FreePool (gNoSubmitIfFailed);
259   FreePool (gSaveProcess);
260   FreePool (gSaveNoSubmitProcess);
261   FreePool (gDiscardChange);
262   FreePool (gJumpToFormSet);
263   FreePool (gCheckError);
264   FreePool (gPromptForData);
265   FreePool (gPromptForPassword);
266   FreePool (gPromptForNewPassword);
267   FreePool (gConfirmPassword);
268   FreePool (gConfirmError);
269   FreePool (gPassowordInvalid);
270   FreePool (gPressEnter);
271   FreePool (gMiniString);
272   FreePool (gOptionMismatch);
273   FreePool (gFormSuppress);
274   FreePool (gProtocolNotFound);
275   FreePool (gBrwoserError);
276   FreePool (gNoSubmitIf);
277   FreePool (gFormNotFound);
278   FreePool (gConfirmDefaultMsg);
279   FreePool (gConfirmSubmitMsg);
280   FreePool (gConfirmDiscardMsg);
281   FreePool (gConfirmResetMsg);
282   FreePool (gConfirmExitMsg);
283   FreePool (gConfirmDefaultMsg2nd);
284   FreePool (gConfirmSubmitMsg2nd);
285   FreePool (gConfirmResetMsg2nd);
286   FreePool (gConfirmExitMsg2nd);
287   FreePool (gConfirmOpt);
288   FreePool (gConfirmOptYes);
289   FreePool (gConfirmOptNo);
290   FreePool (gConfirmMsgConnect);
291   FreePool (gConfirmMsgEnd);
292 }
293 
294 /**
295   Get prompt string id from the opcode data buffer.
296 
297   @param  OpCode                 The input opcode buffer.
298 
299   @return The prompt string id.
300 
301 **/
302 EFI_STRING_ID
GetPrompt(IN EFI_IFR_OP_HEADER * OpCode)303 GetPrompt (
304   IN EFI_IFR_OP_HEADER     *OpCode
305   )
306 {
307   EFI_IFR_STATEMENT_HEADER  *Header;
308 
309   if (OpCode->Length <= sizeof (EFI_IFR_OP_HEADER)) {
310     return 0;
311   }
312 
313   Header = (EFI_IFR_STATEMENT_HEADER  *) (OpCode + 1);
314 
315   return Header->Prompt;
316 }
317 
318 /**
319   Get the supported width for a particular op-code
320 
321   @param  MenuOption             The menu option.
322   @param  AdjustWidth            The width which is saved for the space.
323 
324   @return Returns the number of CHAR16 characters that is support.
325 
326 **/
327 UINT16
GetWidth(IN UI_MENU_OPTION * MenuOption,OUT UINT16 * AdjustWidth)328 GetWidth (
329   IN  UI_MENU_OPTION     *MenuOption,
330   OUT UINT16             *AdjustWidth
331   )
332 {
333   CHAR16                        *String;
334   UINTN                         Size;
335   EFI_IFR_TEXT                  *TestOp;
336   UINT16                        ReturnWidth;
337   FORM_DISPLAY_ENGINE_STATEMENT *Statement;
338 
339   Statement = MenuOption->ThisTag;
340 
341   //
342   // For modal form, clean the entire row.
343   //
344   if ((gFormData->Attribute & HII_DISPLAY_MODAL) != 0) {
345     if (AdjustWidth  != NULL) {
346       *AdjustWidth = LEFT_SKIPPED_COLUMNS;
347     }
348     return (UINT16)(gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 * (gModalSkipColumn + LEFT_SKIPPED_COLUMNS));
349   }
350 
351   Size = 0;
352 
353   //
354   // See if the second text parameter is really NULL
355   //
356   if (Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) {
357     TestOp = (EFI_IFR_TEXT *) Statement->OpCode;
358     if (TestOp->TextTwo != 0) {
359       String = GetToken (TestOp->TextTwo, gFormData->HiiHandle);
360       Size   = StrLen (String);
361       FreePool (String);
362     }
363   }
364 
365   if ((Statement->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) ||
366       (Statement->OpCode->OpCode == EFI_IFR_REF_OP) ||
367       (Statement->OpCode->OpCode == EFI_IFR_PASSWORD_OP) ||
368       (Statement->OpCode->OpCode == EFI_IFR_ACTION_OP) ||
369       (Statement->OpCode->OpCode == EFI_IFR_RESET_BUTTON_OP) ||
370       //
371       // Allow a wide display if text op-code and no secondary text op-code
372       //
373       ((Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) && (Size == 0))
374       ) {
375 
376     //
377     // Return the space width.
378     //
379     if (AdjustWidth != NULL) {
380       *AdjustWidth = 2;
381     }
382     //
383     // Keep consistent with current behavior.
384     //
385     ReturnWidth = (UINT16) (gPromptBlockWidth + gOptionBlockWidth - 2);
386   } else {
387     if (AdjustWidth != NULL) {
388       *AdjustWidth = 1;
389     }
390 
391     ReturnWidth =  (UINT16) (gPromptBlockWidth - 1);
392   }
393 
394   //
395   // For nest in statement, should the subtitle indent.
396   //
397   if (MenuOption->NestInStatement) {
398     ReturnWidth -= SUBTITLE_INDENT;
399   }
400 
401   return ReturnWidth;
402 }
403 
404 /**
405   Will copy LineWidth amount of a string in the OutputString buffer and return the
406   number of CHAR16 characters that were copied into the OutputString buffer.
407   The output string format is:
408     Glyph Info + String info + '\0'.
409 
410   In the code, it deals \r,\n,\r\n same as \n\r, also it not process the \r or \g.
411 
412   @param  InputString            String description for this option.
413   @param  LineWidth              Width of the desired string to extract in CHAR16
414                                  characters
415   @param  GlyphWidth             The glyph width of the begin of the char in the string.
416   @param  Index                  Where in InputString to start the copy process
417   @param  OutputString           Buffer to copy the string into
418 
419   @return Returns the number of CHAR16 characters that were copied into the OutputString
420   buffer, include extra glyph info and '\0' info.
421 
422 **/
423 UINT16
GetLineByWidth(IN CHAR16 * InputString,IN UINT16 LineWidth,IN OUT UINT16 * GlyphWidth,IN OUT UINTN * Index,OUT CHAR16 ** OutputString)424 GetLineByWidth (
425   IN      CHAR16                      *InputString,
426   IN      UINT16                      LineWidth,
427   IN OUT  UINT16                      *GlyphWidth,
428   IN OUT  UINTN                       *Index,
429   OUT     CHAR16                      **OutputString
430   )
431 {
432   UINT16          StrOffset;
433   UINT16          GlyphOffset;
434   UINT16          OriginalGlyphWidth;
435   BOOLEAN         ReturnFlag;
436   UINT16          LastSpaceOffset;
437   UINT16          LastGlyphWidth;
438 
439   if (InputString == NULL || Index == NULL || OutputString == NULL) {
440     return 0;
441   }
442 
443   if (LineWidth == 0 || *GlyphWidth == 0) {
444     return 0;
445   }
446 
447   //
448   // Save original glyph width.
449   //
450   OriginalGlyphWidth = *GlyphWidth;
451   LastGlyphWidth     = OriginalGlyphWidth;
452   ReturnFlag         = FALSE;
453   LastSpaceOffset    = 0;
454 
455   //
456   // 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.
457   // To avoid displaying this  empty line in screen,  just skip  the two CHARs here.
458   //
459   if ((InputString[*Index] == NARROW_CHAR) && (InputString[*Index + 1] == CHAR_CARRIAGE_RETURN)) {
460     *Index = *Index + 2;
461   }
462 
463   //
464   // Fast-forward the string and see if there is a carriage-return in the string
465   //
466   for (StrOffset = 0, GlyphOffset = 0; GlyphOffset <= LineWidth; StrOffset++) {
467     switch (InputString[*Index + StrOffset]) {
468       case NARROW_CHAR:
469         *GlyphWidth = 1;
470         break;
471 
472       case WIDE_CHAR:
473         *GlyphWidth = 2;
474         break;
475 
476       case CHAR_CARRIAGE_RETURN:
477       case CHAR_LINEFEED:
478       case CHAR_NULL:
479         ReturnFlag = TRUE;
480         break;
481 
482       default:
483         GlyphOffset = GlyphOffset + *GlyphWidth;
484 
485         //
486         // Record the last space info in this line. Will be used in rewind.
487         //
488         if ((InputString[*Index + StrOffset] == CHAR_SPACE) && (GlyphOffset <= LineWidth)) {
489           LastSpaceOffset = StrOffset;
490           LastGlyphWidth  = *GlyphWidth;
491         }
492         break;
493     }
494 
495     if (ReturnFlag) {
496       break;
497     }
498   }
499 
500   //
501   // Rewind the string from the maximum size until we see a space to break the line
502   //
503   if (GlyphOffset > LineWidth) {
504     //
505     // Rewind the string to last space char in this line.
506     //
507     if (LastSpaceOffset != 0) {
508       StrOffset   = LastSpaceOffset;
509       *GlyphWidth = LastGlyphWidth;
510     } else {
511       //
512       // Roll back to last char in the line width.
513       //
514       StrOffset--;
515     }
516   }
517 
518   //
519   // The CHAR_NULL has process last time, this time just return 0 to stand for the end.
520   //
521   if (StrOffset == 0 && (InputString[*Index + StrOffset] == CHAR_NULL)) {
522     return 0;
523   }
524 
525   //
526   // Need extra glyph info and '\0' info, so +2.
527   //
528   *OutputString = AllocateZeroPool (((UINTN) (StrOffset + 2) * sizeof(CHAR16)));
529   if (*OutputString == NULL) {
530     return 0;
531   }
532 
533   //
534   // Save the glyph info at the begin of the string, will used by Print function.
535   //
536   if (OriginalGlyphWidth == 1) {
537     *(*OutputString) = NARROW_CHAR;
538   } else  {
539     *(*OutputString) = WIDE_CHAR;
540   }
541 
542   CopyMem ((*OutputString) + 1, &InputString[*Index], StrOffset * sizeof(CHAR16));
543 
544   if (InputString[*Index + StrOffset] == CHAR_SPACE) {
545     //
546     // Skip the space info at the begin of next line.
547     //
548     *Index = (UINT16) (*Index + StrOffset + 1);
549   } else if (InputString[*Index + StrOffset] == CHAR_LINEFEED) {
550     //
551     // Skip the /n or /n/r info.
552     //
553     if (InputString[*Index + StrOffset + 1] == CHAR_CARRIAGE_RETURN) {
554       *Index = (UINT16) (*Index + StrOffset + 2);
555     } else {
556       *Index = (UINT16) (*Index + StrOffset + 1);
557     }
558   } else if (InputString[*Index + StrOffset] == CHAR_CARRIAGE_RETURN) {
559     //
560     // Skip the /r or /r/n info.
561     //
562     if (InputString[*Index + StrOffset + 1] == CHAR_LINEFEED) {
563       *Index = (UINT16) (*Index + StrOffset + 2);
564     } else {
565       *Index = (UINT16) (*Index + StrOffset + 1);
566     }
567   } else {
568     *Index = (UINT16) (*Index + StrOffset);
569   }
570 
571   //
572   // Include extra glyph info and '\0' info, so +2.
573   //
574   return StrOffset + 2;
575 }
576 
577 /**
578   Add one menu option by specified description and context.
579 
580   @param  Statement              Statement of this Menu Option.
581   @param  MenuItemCount          The index for this Option in the Menu.
582   @param  NestIn                 Whether this statement is nest in another statement.
583 
584 **/
585 VOID
UiAddMenuOption(IN FORM_DISPLAY_ENGINE_STATEMENT * Statement,IN UINT16 * MenuItemCount,IN BOOLEAN NestIn)586 UiAddMenuOption (
587   IN FORM_DISPLAY_ENGINE_STATEMENT   *Statement,
588   IN UINT16                          *MenuItemCount,
589   IN BOOLEAN                         NestIn
590   )
591 {
592   UI_MENU_OPTION   *MenuOption;
593   UINTN            Index;
594   UINTN            Count;
595   CHAR16           *String;
596   UINT16           NumberOfLines;
597   UINT16           GlyphWidth;
598   UINT16           Width;
599   UINTN            ArrayEntry;
600   CHAR16           *OutputString;
601   EFI_STRING_ID    PromptId;
602 
603   NumberOfLines = 1;
604   ArrayEntry    = 0;
605   GlyphWidth    = 1;
606   Count         = 1;
607   MenuOption    = NULL;
608 
609   PromptId = GetPrompt (Statement->OpCode);
610   ASSERT (PromptId != 0);
611 
612   String = GetToken (PromptId, gFormData->HiiHandle);
613   ASSERT (String != NULL);
614 
615   if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP || Statement->OpCode->OpCode == EFI_IFR_TIME_OP) {
616     Count = 3;
617   }
618 
619   for (Index = 0; Index < Count; Index++) {
620     MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION));
621     ASSERT (MenuOption);
622 
623     MenuOption->Signature   = UI_MENU_OPTION_SIGNATURE;
624     MenuOption->Description = String;
625     MenuOption->Handle      = gFormData->HiiHandle;
626     MenuOption->ThisTag     = Statement;
627     MenuOption->NestInStatement = NestIn;
628     MenuOption->EntryNumber = *MenuItemCount;
629 
630     MenuOption->Sequence = Index;
631 
632     if ((Statement->Attribute & HII_DISPLAY_GRAYOUT) != 0) {
633       MenuOption->GrayOut = TRUE;
634     } else {
635       MenuOption->GrayOut = FALSE;
636     }
637 
638     if ((Statement->Attribute & HII_DISPLAY_LOCK) != 0 || (gFormData->Attribute & HII_DISPLAY_LOCK) != 0) {
639       MenuOption->GrayOut = TRUE;
640     }
641 
642     //
643     // If the form or the question has the lock attribute, deal same as grayout.
644     //
645     if ((gFormData->Attribute & HII_DISPLAY_LOCK) != 0 || (Statement->Attribute & HII_DISPLAY_LOCK) != 0) {
646       MenuOption->GrayOut = TRUE;
647     }
648 
649     switch (Statement->OpCode->OpCode) {
650     case EFI_IFR_ORDERED_LIST_OP:
651     case EFI_IFR_ONE_OF_OP:
652     case EFI_IFR_NUMERIC_OP:
653     case EFI_IFR_TIME_OP:
654     case EFI_IFR_DATE_OP:
655     case EFI_IFR_CHECKBOX_OP:
656     case EFI_IFR_PASSWORD_OP:
657     case EFI_IFR_STRING_OP:
658       //
659       // User could change the value of these items
660       //
661       MenuOption->IsQuestion = TRUE;
662       break;
663     case EFI_IFR_TEXT_OP:
664       if (FeaturePcdGet (PcdBrowserGrayOutTextStatement)) {
665         //
666         // Initializing GrayOut option as TRUE for Text setup options
667         // so that those options will be Gray in colour and un selectable.
668         //
669         MenuOption->GrayOut = TRUE;
670       }
671       break;
672     default:
673       MenuOption->IsQuestion = FALSE;
674       break;
675     }
676 
677     if ((Statement->Attribute & HII_DISPLAY_READONLY) != 0) {
678       MenuOption->ReadOnly = TRUE;
679       if (FeaturePcdGet (PcdBrowerGrayOutReadOnlyMenu)) {
680         MenuOption->GrayOut = TRUE;
681       }
682     }
683 
684     if (Index == 0 &&
685       (Statement->OpCode->OpCode != EFI_IFR_DATE_OP) &&
686       (Statement->OpCode->OpCode != EFI_IFR_TIME_OP)) {
687       Width  = GetWidth (MenuOption, NULL);
688       for (; GetLineByWidth (String, Width, &GlyphWidth,&ArrayEntry, &OutputString) != 0x0000;) {
689         //
690         // If there is more string to process print on the next row and increment the Skip value
691         //
692         if (StrLen (&String[ArrayEntry]) != 0) {
693           NumberOfLines++;
694         }
695         FreePool (OutputString);
696       }
697     } else {
698       //
699       // Add three MenuOptions for Date/Time
700       // Data format :      [01/02/2004]      [11:22:33]
701       // Line number :        0  0    1         0  0  1
702       //
703       NumberOfLines = 0;
704     }
705 
706     if (Index == 2) {
707       //
708       // Override LineNumber for the MenuOption in Date/Time sequence
709       //
710       MenuOption->Skip = 1;
711     } else {
712       MenuOption->Skip = NumberOfLines;
713     }
714 
715     InsertTailList (&gMenuOption, &MenuOption->Link);
716   }
717 
718   (*MenuItemCount)++;
719 }
720 
721 /**
722   Create the menu list base on the form data info.
723 
724 **/
725 VOID
ConvertStatementToMenu(VOID)726 ConvertStatementToMenu (
727   VOID
728   )
729 {
730   UINT16                        MenuItemCount;
731   LIST_ENTRY                    *Link;
732   LIST_ENTRY                    *NestLink;
733   FORM_DISPLAY_ENGINE_STATEMENT *Statement;
734   FORM_DISPLAY_ENGINE_STATEMENT *NestStatement;
735 
736   MenuItemCount = 0;
737   InitializeListHead (&gMenuOption);
738 
739   Link = GetFirstNode (&gFormData->StatementListHead);
740   while (!IsNull (&gFormData->StatementListHead, Link)) {
741     Statement = FORM_DISPLAY_ENGINE_STATEMENT_FROM_LINK (Link);
742     Link = GetNextNode (&gFormData->StatementListHead, Link);
743 
744     //
745     // Skip the opcode not recognized by Display core.
746     //
747     if (Statement->OpCode->OpCode == EFI_IFR_GUID_OP) {
748       continue;
749     }
750 
751     UiAddMenuOption (Statement, &MenuItemCount, FALSE);
752 
753     //
754     // Check the statement nest in this host statement.
755     //
756     NestLink = GetFirstNode (&Statement->NestStatementList);
757     while (!IsNull (&Statement->NestStatementList, NestLink)) {
758       NestStatement = FORM_DISPLAY_ENGINE_STATEMENT_FROM_LINK (NestLink);
759       NestLink = GetNextNode (&Statement->NestStatementList, NestLink);
760 
761       //
762       // Skip the opcode not recognized by Display core.
763       //
764       if (NestStatement->OpCode->OpCode == EFI_IFR_GUID_OP) {
765         continue;
766       }
767 
768       UiAddMenuOption (NestStatement, &MenuItemCount, TRUE);
769     }
770   }
771 }
772 
773 /**
774   Count the storage space of a Unicode string.
775 
776   This function handles the Unicode string with NARROW_CHAR
777   and WIDE_CHAR control characters. NARROW_HCAR and WIDE_CHAR
778   does not count in the resultant output. If a WIDE_CHAR is
779   hit, then 2 Unicode character will consume an output storage
780   space with size of CHAR16 till a NARROW_CHAR is hit.
781 
782   If String is NULL, then ASSERT ().
783 
784   @param String          The input string to be counted.
785 
786   @return Storage space for the input string.
787 
788 **/
789 UINTN
GetStringWidth(IN CHAR16 * String)790 GetStringWidth (
791   IN CHAR16               *String
792   )
793 {
794   UINTN Index;
795   UINTN Count;
796   UINTN IncrementValue;
797 
798   ASSERT (String != NULL);
799   if (String == NULL) {
800     return 0;
801   }
802 
803   Index           = 0;
804   Count           = 0;
805   IncrementValue  = 1;
806 
807   do {
808     //
809     // Advance to the null-terminator or to the first width directive
810     //
811     for (;
812          (String[Index] != NARROW_CHAR) && (String[Index] != WIDE_CHAR) && (String[Index] != 0);
813          Index++, Count = Count + IncrementValue
814         )
815       ;
816 
817     //
818     // We hit the null-terminator, we now have a count
819     //
820     if (String[Index] == 0) {
821       break;
822     }
823     //
824     // We encountered a narrow directive - strip it from the size calculation since it doesn't get printed
825     // and also set the flag that determines what we increment by.(if narrow, increment by 1, if wide increment by 2)
826     //
827     if (String[Index] == NARROW_CHAR) {
828       //
829       // Skip to the next character
830       //
831       Index++;
832       IncrementValue = 1;
833     } else {
834       //
835       // Skip to the next character
836       //
837       Index++;
838       IncrementValue = 2;
839     }
840   } while (String[Index] != 0);
841 
842   //
843   // Increment by one to include the null-terminator in the size
844   //
845   Count++;
846 
847   return Count * sizeof (CHAR16);
848 }
849 
850 /**
851   Base on the input option string to update the skip value for a menu option.
852 
853   @param  MenuOption             The MenuOption to be checked.
854   @param  OptionString           The input option string.
855 
856 **/
857 VOID
UpdateSkipInfoForMenu(IN UI_MENU_OPTION * MenuOption,IN CHAR16 * OptionString)858 UpdateSkipInfoForMenu (
859   IN UI_MENU_OPTION               *MenuOption,
860   IN CHAR16                       *OptionString
861   )
862 {
863   UINTN   Index;
864   UINT16  Width;
865   UINTN   Row;
866   CHAR16  *OutputString;
867   UINT16  GlyphWidth;
868 
869   Width         = (UINT16) gOptionBlockWidth;
870   GlyphWidth    = 1;
871   Row           = 1;
872 
873   for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
874     if (StrLen (&OptionString[Index]) != 0) {
875       Row++;
876     }
877 
878     FreePool (OutputString);
879   }
880 
881   if ((Row > MenuOption->Skip) &&
882       (MenuOption->ThisTag->OpCode->OpCode != EFI_IFR_DATE_OP) &&
883       (MenuOption->ThisTag->OpCode->OpCode != EFI_IFR_TIME_OP)) {
884     MenuOption->Skip = Row;
885   }
886 }
887 
888 /**
889   Update display lines for a Menu Option.
890 
891   @param  MenuOption             The MenuOption to be checked.
892 
893 **/
894 VOID
UpdateOptionSkipLines(IN UI_MENU_OPTION * MenuOption)895 UpdateOptionSkipLines (
896   IN UI_MENU_OPTION               *MenuOption
897   )
898 {
899   CHAR16  *OptionString;
900 
901   OptionString  = NULL;
902 
903   ProcessOptions (MenuOption, FALSE, &OptionString, TRUE);
904   if (OptionString != NULL) {
905     UpdateSkipInfoForMenu (MenuOption, OptionString);
906 
907     FreePool (OptionString);
908   }
909 
910   if ((MenuOption->ThisTag->OpCode->OpCode  == EFI_IFR_TEXT_OP) && (((EFI_IFR_TEXT*)MenuOption->ThisTag->OpCode)->TextTwo != 0)) {
911     OptionString   = GetToken (((EFI_IFR_TEXT*)MenuOption->ThisTag->OpCode)->TextTwo, gFormData->HiiHandle);
912 
913     if (OptionString != NULL) {
914       UpdateSkipInfoForMenu (MenuOption, OptionString);
915 
916       FreePool (OptionString);
917     }
918   }
919 }
920 
921 /**
922   Check whether this Menu Option could be print.
923 
924   Check Prompt string, option string or text two string not NULL.
925 
926   This is an internal function.
927 
928   @param  MenuOption             The MenuOption to be checked.
929 
930   @retval TRUE                   This Menu Option is printable.
931   @retval FALSE                  This Menu Option could not be printable.
932 
933 **/
934 BOOLEAN
PrintableMenu(UI_MENU_OPTION * MenuOption)935 PrintableMenu (
936   UI_MENU_OPTION   *MenuOption
937   )
938 {
939   EFI_STATUS    Status;
940   EFI_STRING    OptionString;
941 
942   OptionString = NULL;
943 
944   if (MenuOption->Description[0] != '\0') {
945     return TRUE;
946   }
947 
948   Status = ProcessOptions (MenuOption, FALSE, &OptionString, FALSE);
949   if (EFI_ERROR (Status)) {
950     return FALSE;
951   }
952   if (OptionString != NULL && OptionString[0] != '\0') {
953     FreePool (OptionString);
954     return TRUE;
955   }
956 
957   if ((MenuOption->ThisTag->OpCode->OpCode  == EFI_IFR_TEXT_OP) && (((EFI_IFR_TEXT*)MenuOption->ThisTag->OpCode)->TextTwo != 0)) {
958     OptionString   = GetToken (((EFI_IFR_TEXT*)MenuOption->ThisTag->OpCode)->TextTwo, gFormData->HiiHandle);
959     ASSERT (OptionString != NULL);
960     if (OptionString[0] != '\0'){
961       FreePool (OptionString);
962       return TRUE;
963     }
964   }
965 
966   return FALSE;
967 }
968 
969 /**
970   Check whether this Menu Option could be highlighted.
971 
972   This is an internal function.
973 
974   @param  MenuOption             The MenuOption to be checked.
975 
976   @retval TRUE                   This Menu Option is selectable.
977   @retval FALSE                  This Menu Option could not be selected.
978 
979 **/
980 BOOLEAN
IsSelectable(UI_MENU_OPTION * MenuOption)981 IsSelectable (
982   UI_MENU_OPTION   *MenuOption
983   )
984 {
985   if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) ||
986       MenuOption->GrayOut || MenuOption->ReadOnly || !PrintableMenu (MenuOption)) {
987     return FALSE;
988   } else {
989     return TRUE;
990   }
991 }
992 
993 /**
994   Move to next selectable statement.
995 
996   This is an internal function.
997 
998   @param  GoUp                   The navigation direction. TRUE: up, FALSE: down.
999   @param  CurrentPosition        Current position.
1000   @param  GapToTop               Gap position to top or bottom.
1001   @param  FindInForm             Whether find menu in current form or beyond.
1002 
1003   @return The row distance from current MenuOption to next selectable MenuOption.
1004 
1005   @retval -1       Reach the begin of the menu, still can't find the selectable menu.
1006   @retval Value    Find the selectable menu, maybe the truly selectable, maybe the
1007                    first menu showing beyond current form or last menu showing in
1008                    current form.
1009                    The value is the line number between the new selected menu and the
1010                    current select menu, not include the new selected menu.
1011 
1012 **/
1013 INTN
MoveToNextStatement(IN BOOLEAN GoUp,IN OUT LIST_ENTRY ** CurrentPosition,IN UINTN GapToTop,IN BOOLEAN FindInForm)1014 MoveToNextStatement (
1015   IN     BOOLEAN                   GoUp,
1016   IN OUT LIST_ENTRY                **CurrentPosition,
1017   IN     UINTN                     GapToTop,
1018   IN     BOOLEAN                   FindInForm
1019   )
1020 {
1021   INTN             Distance;
1022   LIST_ENTRY       *Pos;
1023   UI_MENU_OPTION   *NextMenuOption;
1024   UI_MENU_OPTION   *PreMenuOption;
1025 
1026   Distance      = 0;
1027   Pos           = *CurrentPosition;
1028 
1029   if (Pos == &gMenuOption) {
1030     return -1;
1031   }
1032 
1033   PreMenuOption = MENU_OPTION_FROM_LINK (Pos);
1034 
1035   while (TRUE) {
1036     NextMenuOption = MENU_OPTION_FROM_LINK (Pos);
1037     //
1038     // NextMenuOption->Row == 0 means this menu has not calculate
1039     // the NextMenuOption->Skip value yet, just calculate here.
1040     //
1041     if (NextMenuOption->Row == 0) {
1042       UpdateOptionSkipLines (NextMenuOption);
1043     }
1044 
1045     if (IsSelectable (NextMenuOption)) {
1046       break;
1047     }
1048 
1049     //
1050     // In this case, still can't find the selectable menu,
1051     // return the first one beyond the showing form.
1052     //
1053     if ((UINTN) Distance + NextMenuOption->Skip > GapToTop) {
1054       if (FindInForm) {
1055         NextMenuOption = PreMenuOption;
1056       }
1057       break;
1058     }
1059 
1060     Distance += NextMenuOption->Skip;
1061 
1062     //
1063     // Arrive at begin of the menu list.
1064     //
1065     if ((GoUp ? Pos->BackLink : Pos->ForwardLink) == &gMenuOption) {
1066       Distance = -1;
1067       break;
1068     }
1069 
1070     Pos = (GoUp ? Pos->BackLink : Pos->ForwardLink);
1071     PreMenuOption = NextMenuOption;
1072   }
1073 
1074   *CurrentPosition = &NextMenuOption->Link;
1075   return Distance;
1076 }
1077 
1078 
1079 /**
1080   Process option string for date/time opcode.
1081 
1082   @param  MenuOption              Menu option point to date/time.
1083   @param  OptionString            Option string input for process.
1084   @param  AddOptCol               Whether need to update MenuOption->OptCol.
1085 
1086 **/
1087 VOID
ProcessStringForDateTime(UI_MENU_OPTION * MenuOption,CHAR16 * OptionString,BOOLEAN AddOptCol)1088 ProcessStringForDateTime (
1089   UI_MENU_OPTION                  *MenuOption,
1090   CHAR16                          *OptionString,
1091   BOOLEAN                         AddOptCol
1092   )
1093 {
1094   UINTN Index;
1095   UINTN Count;
1096   FORM_DISPLAY_ENGINE_STATEMENT          *Statement;
1097   EFI_IFR_DATE                           *Date;
1098   EFI_IFR_TIME                           *Time;
1099 
1100   ASSERT (MenuOption != NULL && OptionString != NULL);
1101 
1102   Statement = MenuOption->ThisTag;
1103   Date      = NULL;
1104   Time      = NULL;
1105   if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP) {
1106     Date = (EFI_IFR_DATE *) Statement->OpCode;
1107   } else if (Statement->OpCode->OpCode == EFI_IFR_TIME_OP) {
1108     Time = (EFI_IFR_TIME *) Statement->OpCode;
1109   }
1110 
1111   //
1112   // If leading spaces on OptionString - remove the spaces
1113   //
1114   for (Index = 0; OptionString[Index] == L' '; Index++) {
1115     //
1116     // Base on the blockspace to get the option column info.
1117     //
1118     if (AddOptCol) {
1119       MenuOption->OptCol++;
1120     }
1121   }
1122 
1123   for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
1124     OptionString[Count] = OptionString[Index];
1125     Count++;
1126   }
1127   OptionString[Count] = CHAR_NULL;
1128 
1129   //
1130   // Enable to suppress field in the opcode base on the flag.
1131   //
1132   if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP) {
1133     //
1134     // OptionString format is: <**:  **: ****>
1135     //                        |month|day|year|
1136     //                          4     3    5
1137     //
1138     if ((Date->Flags & EFI_QF_DATE_MONTH_SUPPRESS) && (MenuOption->Sequence == 0)) {
1139       //
1140       // At this point, only "<**:" in the optionstring.
1141       // Clean the day's ** field, after clean, the format is "<  :"
1142       //
1143       SetUnicodeMem (&OptionString[1], 2, L' ');
1144     } else if ((Date->Flags & EFI_QF_DATE_DAY_SUPPRESS) && (MenuOption->Sequence == 1)) {
1145       //
1146       // At this point, only "**:" in the optionstring.
1147       // Clean the month's "**" field, after clean, the format is "  :"
1148       //
1149       SetUnicodeMem (&OptionString[0], 2, L' ');
1150     } else if ((Date->Flags & EFI_QF_DATE_YEAR_SUPPRESS) && (MenuOption->Sequence == 2)) {
1151       //
1152       // At this point, only "****>" in the optionstring.
1153       // Clean the year's "****" field, after clean, the format is "  >"
1154       //
1155       SetUnicodeMem (&OptionString[0], 4, L' ');
1156     }
1157   } else if (Statement->OpCode->OpCode == EFI_IFR_TIME_OP) {
1158     //
1159     // OptionString format is: <**:  **:    **>
1160     //                        |hour|minute|second|
1161     //                          4     3      3
1162     //
1163     if ((Time->Flags & QF_TIME_HOUR_SUPPRESS) && (MenuOption->Sequence == 0)) {
1164       //
1165       // At this point, only "<**:" in the optionstring.
1166       // Clean the hour's ** field, after clean, the format is "<  :"
1167       //
1168       SetUnicodeMem (&OptionString[1], 2, L' ');
1169     } else if ((Time->Flags & QF_TIME_MINUTE_SUPPRESS) && (MenuOption->Sequence == 1)) {
1170       //
1171       // At this point, only "**:" in the optionstring.
1172       // Clean the minute's "**" field, after clean, the format is "  :"
1173       //
1174       SetUnicodeMem (&OptionString[0], 2, L' ');
1175     } else if ((Time->Flags & QF_TIME_SECOND_SUPPRESS) && (MenuOption->Sequence == 2)) {
1176       //
1177       // At this point, only "**>" in the optionstring.
1178       // Clean the second's "**" field, after clean, the format is "  >"
1179       //
1180       SetUnicodeMem (&OptionString[0], 2, L' ');
1181     }
1182   }
1183 }
1184 
1185 
1186 /**
1187   Adjust Data and Time position accordingly.
1188   Data format :      [01/02/2004]      [11:22:33]
1189   Line number :        0  0    1         0  0  1
1190 
1191   This is an internal function.
1192 
1193   @param  DirectionUp            the up or down direction. False is down. True is
1194                                  up.
1195   @param  CurrentPosition        Current position. On return: Point to the last
1196                                  Option (Year or Second) if up; Point to the first
1197                                  Option (Month or Hour) if down.
1198 
1199   @return Return line number to pad. It is possible that we stand on a zero-advance
1200   @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1201 
1202 **/
1203 UINTN
AdjustDateAndTimePosition(IN BOOLEAN DirectionUp,IN OUT LIST_ENTRY ** CurrentPosition)1204 AdjustDateAndTimePosition (
1205   IN     BOOLEAN                     DirectionUp,
1206   IN OUT LIST_ENTRY                  **CurrentPosition
1207   )
1208 {
1209   UINTN           Count;
1210   LIST_ENTRY      *NewPosition;
1211   UI_MENU_OPTION  *MenuOption;
1212   UINTN           PadLineNumber;
1213 
1214   PadLineNumber = 0;
1215   NewPosition   = *CurrentPosition;
1216   MenuOption    = MENU_OPTION_FROM_LINK (NewPosition);
1217 
1218   if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_DATE_OP) ||
1219       (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TIME_OP)) {
1220     //
1221     // Calculate the distance from current position to the last Date/Time MenuOption
1222     //
1223     Count = 0;
1224     while (MenuOption->Skip == 0) {
1225       Count++;
1226       NewPosition   = NewPosition->ForwardLink;
1227       MenuOption    = MENU_OPTION_FROM_LINK (NewPosition);
1228       PadLineNumber = 1;
1229     }
1230 
1231     NewPosition = *CurrentPosition;
1232     if (DirectionUp) {
1233       //
1234       // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1235       // to be one that back to the previous set of MenuOptions, we need to advance to the first
1236       // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1237       // checking can be done.
1238       //
1239       while (Count++ < 2) {
1240         NewPosition = NewPosition->BackLink;
1241       }
1242     } else {
1243       //
1244       // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1245       // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1246       // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1247       // checking can be done.
1248       //
1249       while (Count-- > 0) {
1250         NewPosition = NewPosition->ForwardLink;
1251       }
1252     }
1253 
1254     *CurrentPosition = NewPosition;
1255   }
1256 
1257   return PadLineNumber;
1258 }
1259 
1260 /**
1261   Get step info from numeric opcode.
1262 
1263   @param[in] OpCode     The input numeric op code.
1264 
1265   @return step info for this opcode.
1266 **/
1267 UINT64
GetFieldFromNum(IN EFI_IFR_OP_HEADER * OpCode)1268 GetFieldFromNum (
1269   IN  EFI_IFR_OP_HEADER     *OpCode
1270   )
1271 {
1272   EFI_IFR_NUMERIC       *NumericOp;
1273   UINT64                Step;
1274 
1275   NumericOp = (EFI_IFR_NUMERIC *) OpCode;
1276 
1277   switch (NumericOp->Flags & EFI_IFR_NUMERIC_SIZE) {
1278   case EFI_IFR_NUMERIC_SIZE_1:
1279     Step    = NumericOp->data.u8.Step;
1280     break;
1281 
1282   case EFI_IFR_NUMERIC_SIZE_2:
1283     Step    = NumericOp->data.u16.Step;
1284     break;
1285 
1286   case EFI_IFR_NUMERIC_SIZE_4:
1287     Step    = NumericOp->data.u32.Step;
1288     break;
1289 
1290   case EFI_IFR_NUMERIC_SIZE_8:
1291     Step    = NumericOp->data.u64.Step;
1292     break;
1293 
1294   default:
1295     Step = 0;
1296     break;
1297   }
1298 
1299   return Step;
1300 }
1301 
1302 /**
1303   Find the registered HotKey based on KeyData.
1304 
1305   @param[in] KeyData     A pointer to a buffer that describes the keystroke
1306                          information for the hot key.
1307 
1308   @return The registered HotKey context. If no found, NULL will return.
1309 **/
1310 BROWSER_HOT_KEY *
GetHotKeyFromRegisterList(IN EFI_INPUT_KEY * KeyData)1311 GetHotKeyFromRegisterList (
1312   IN EFI_INPUT_KEY *KeyData
1313   )
1314 {
1315   LIST_ENTRY       *Link;
1316   BROWSER_HOT_KEY  *HotKey;
1317 
1318   Link = GetFirstNode (&gFormData->HotKeyListHead);
1319   while (!IsNull (&gFormData->HotKeyListHead, Link)) {
1320     HotKey = BROWSER_HOT_KEY_FROM_LINK (Link);
1321 
1322     if (HotKey->KeyData->ScanCode == KeyData->ScanCode) {
1323       return HotKey;
1324     }
1325 
1326     Link = GetNextNode (&gFormData->HotKeyListHead, Link);
1327   }
1328 
1329   return NULL;
1330 }
1331 
1332 
1333 /**
1334   Determine if the menu is the last menu that can be selected.
1335 
1336   This is an internal function.
1337 
1338   @param  Direction              The scroll direction. False is down. True is up.
1339   @param  CurrentPos             The current focus.
1340 
1341   @return FALSE -- the menu isn't the last menu that can be selected.
1342   @return TRUE  -- the menu is the last menu that can be selected.
1343 
1344 **/
1345 BOOLEAN
ValueIsScroll(IN BOOLEAN Direction,IN LIST_ENTRY * CurrentPos)1346 ValueIsScroll (
1347   IN  BOOLEAN                     Direction,
1348   IN  LIST_ENTRY                  *CurrentPos
1349   )
1350 {
1351   LIST_ENTRY      *Temp;
1352 
1353   Temp = Direction ? CurrentPos->BackLink : CurrentPos->ForwardLink;
1354 
1355   if (Temp == &gMenuOption) {
1356     return TRUE;
1357   }
1358 
1359   return FALSE;
1360 }
1361 
1362 /**
1363   Wait for a given event to fire, or for an optional timeout to expire.
1364 
1365   @param  Event                  The event to wait for
1366 
1367   @retval UI_EVENT_TYPE          The type of the event which is trigged.
1368 
1369 **/
1370 UI_EVENT_TYPE
UiWaitForEvent(IN EFI_EVENT Event)1371 UiWaitForEvent (
1372   IN EFI_EVENT                Event
1373   )
1374 {
1375   EFI_STATUS  Status;
1376   UINTN       Index;
1377   UINTN       EventNum;
1378   UINT64      Timeout;
1379   EFI_EVENT   TimerEvent;
1380   EFI_EVENT   WaitList[3];
1381   UI_EVENT_TYPE  EventType;
1382 
1383   TimerEvent = NULL;
1384   Timeout    = FormExitTimeout(gFormData);
1385 
1386   if (Timeout != 0) {
1387     Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
1388 
1389     //
1390     // Set the timer event
1391     //
1392     gBS->SetTimer (
1393           TimerEvent,
1394           TimerRelative,
1395           Timeout
1396           );
1397   }
1398 
1399   WaitList[0] = Event;
1400   EventNum    = 1;
1401   if (gFormData->FormRefreshEvent != NULL) {
1402     WaitList[EventNum] = gFormData->FormRefreshEvent;
1403     EventNum ++;
1404   }
1405 
1406   if (Timeout != 0) {
1407     WaitList[EventNum] = TimerEvent;
1408     EventNum ++;
1409   }
1410 
1411   Status = gBS->WaitForEvent (EventNum, WaitList, &Index);
1412   ASSERT_EFI_ERROR (Status);
1413 
1414   switch (Index) {
1415   case 0:
1416    EventType = UIEventKey;
1417    break;
1418 
1419   case 1:
1420     if (gFormData->FormRefreshEvent != NULL) {
1421       EventType = UIEventDriver;
1422     } else {
1423       ASSERT (Timeout != 0 && EventNum == 2);
1424       EventType = UIEventTimeOut;
1425     }
1426     break;
1427 
1428   default:
1429     ASSERT (Index == 2 && EventNum == 3);
1430     EventType = UIEventTimeOut;
1431     break;
1432   }
1433 
1434   if (Timeout != 0) {
1435     gBS->CloseEvent (TimerEvent);
1436   }
1437 
1438   return EventType;
1439 }
1440 
1441 /**
1442   Get question id info from the input opcode header.
1443 
1444   @param  OpCode                 The input opcode header pointer.
1445 
1446   @retval                        The question id for this opcode.
1447 
1448 **/
1449 EFI_QUESTION_ID
GetQuestionIdInfo(IN EFI_IFR_OP_HEADER * OpCode)1450 GetQuestionIdInfo (
1451   IN   EFI_IFR_OP_HEADER     *OpCode
1452   )
1453 {
1454   EFI_IFR_QUESTION_HEADER   *QuestionHeader;
1455 
1456   if (OpCode->Length < sizeof (EFI_IFR_OP_HEADER) + sizeof (EFI_IFR_QUESTION_HEADER)) {
1457     return 0;
1458   }
1459 
1460   QuestionHeader = (EFI_IFR_QUESTION_HEADER *)((UINT8 *) OpCode + sizeof(EFI_IFR_OP_HEADER));
1461 
1462   return QuestionHeader->QuestionId;
1463 }
1464 
1465 
1466 /**
1467   Find the top of screen menu base on the current menu.
1468 
1469   @param  CurPos                 Current input menu.
1470   @param  Rows                   Totol screen rows.
1471   @param  SkipValue              SkipValue for this new form.
1472 
1473   @retval TopOfScreen            Top of screen menu for the new form.
1474 
1475 **/
1476 LIST_ENTRY *
FindTopOfScreenMenu(IN LIST_ENTRY * CurPos,IN UINTN Rows,OUT UINTN * SkipValue)1477 FindTopOfScreenMenu (
1478   IN  LIST_ENTRY                      *CurPos,
1479   IN  UINTN                           Rows,
1480   OUT UINTN                           *SkipValue
1481   )
1482 {
1483   LIST_ENTRY        *Link;
1484   LIST_ENTRY        *TopOfScreen;
1485   UI_MENU_OPTION    *PreviousMenuOption;
1486 
1487   Link = CurPos;
1488   PreviousMenuOption = NULL;
1489 
1490   while (Link->BackLink != &gMenuOption) {
1491     Link = Link->BackLink;
1492     PreviousMenuOption = MENU_OPTION_FROM_LINK (Link);
1493     if (PreviousMenuOption->Row == 0) {
1494       UpdateOptionSkipLines (PreviousMenuOption);
1495     }
1496     if (Rows <= PreviousMenuOption->Skip) {
1497       break;
1498     }
1499     Rows = Rows - PreviousMenuOption->Skip;
1500   }
1501 
1502   if (Link->BackLink == &gMenuOption) {
1503     TopOfScreen = gMenuOption.ForwardLink;
1504     if (PreviousMenuOption != NULL && Rows < PreviousMenuOption->Skip) {
1505       *SkipValue = PreviousMenuOption->Skip - Rows;
1506     } else {
1507       *SkipValue = 0;
1508     }
1509   } else {
1510     TopOfScreen = Link;
1511     *SkipValue = PreviousMenuOption->Skip - Rows;
1512   }
1513 
1514   return TopOfScreen;
1515 }
1516 
1517 /**
1518   Get the index info for this opcode.
1519 
1520   @param  OpCode      The input opcode for the statement.
1521 
1522   @retval  The index of this statement.
1523 
1524 **/
1525 UINTN
GetIndexInfoForOpcode(IN EFI_IFR_OP_HEADER * OpCode)1526 GetIndexInfoForOpcode (
1527   IN EFI_IFR_OP_HEADER  *OpCode
1528   )
1529 {
1530   LIST_ENTRY                      *NewPos;
1531   UI_MENU_OPTION                  *MenuOption;
1532   UINTN                           Index;
1533 
1534   NewPos = gMenuOption.ForwardLink;
1535   Index  = 0;
1536 
1537   for (NewPos = gMenuOption.ForwardLink; NewPos != &gMenuOption; NewPos = NewPos->ForwardLink){
1538     MenuOption = MENU_OPTION_FROM_LINK (NewPos);
1539 
1540     if (CompareMem (MenuOption->ThisTag->OpCode, OpCode, OpCode->Length) == 0) {
1541       if (MenuOption->ThisTag->OpCode == OpCode) {
1542         return Index;
1543       }
1544 
1545       Index ++;
1546     }
1547   }
1548 
1549   return Index;
1550 }
1551 
1552 /**
1553   Is this the saved highlight statement.
1554 
1555   @param  HighLightedStatement      The input highlight statement.
1556 
1557   @retval  TRUE   This is the highlight statement.
1558   @retval  FALSE  This is not the highlight statement.
1559 
1560 **/
1561 BOOLEAN
IsSavedHighlightStatement(IN FORM_DISPLAY_ENGINE_STATEMENT * HighLightedStatement)1562 IsSavedHighlightStatement (
1563   IN FORM_DISPLAY_ENGINE_STATEMENT  *HighLightedStatement
1564   )
1565 {
1566   if ((gFormData->HiiHandle == gHighligthMenuInfo.HiiHandle) &&
1567       (gFormData->FormId == gHighligthMenuInfo.FormId)) {
1568     if (gHighligthMenuInfo.HLTQuestionId != 0) {
1569       return (BOOLEAN) (gHighligthMenuInfo.HLTQuestionId == GetQuestionIdInfo (HighLightedStatement->OpCode));
1570     } else {
1571       if (CompareMem (gHighligthMenuInfo.HLTOpCode, HighLightedStatement->OpCode, gHighligthMenuInfo.HLTOpCode->Length) == 0) {
1572         if (gHighligthMenuInfo.HLTIndex == 0 || gHighligthMenuInfo.HLTIndex == GetIndexInfoForOpcode(HighLightedStatement->OpCode)) {
1573           return TRUE;
1574         } else {
1575           return FALSE;
1576         }
1577       }
1578     }
1579   }
1580 
1581   return FALSE;
1582 }
1583 
1584 /**
1585   Is this the highlight menu.
1586 
1587   @param  MenuOption      The input Menu option.
1588 
1589   @retval  TRUE   This is the highlight menu option.
1590   @retval  FALSE  This is not the highlight menu option.
1591 
1592 **/
1593 BOOLEAN
IsHighLightMenuOption(IN UI_MENU_OPTION * MenuOption)1594 IsHighLightMenuOption (
1595   IN UI_MENU_OPTION     *MenuOption
1596   )
1597 {
1598   if (gHighligthMenuInfo.HLTQuestionId != 0) {
1599     if (GetQuestionIdInfo(MenuOption->ThisTag->OpCode) == gHighligthMenuInfo.HLTQuestionId) {
1600       return (BOOLEAN) (MenuOption->Sequence == gHighligthMenuInfo.HLTSequence);
1601     }
1602   } else {
1603     if(CompareMem (gHighligthMenuInfo.HLTOpCode, MenuOption->ThisTag->OpCode, gHighligthMenuInfo.HLTOpCode->Length) == 0) {
1604       if (gHighligthMenuInfo.HLTIndex == 0 || gHighligthMenuInfo.HLTIndex == GetIndexInfoForOpcode(MenuOption->ThisTag->OpCode)) {
1605         return (BOOLEAN) (MenuOption->Sequence == gHighligthMenuInfo.HLTSequence);
1606       } else {
1607         return FALSE;
1608       }
1609     }
1610   }
1611 
1612   return FALSE;
1613 }
1614 
1615 /**
1616   Find the highlight menu.
1617 
1618   If the input is NULL, base on the record highlight info in
1619   gHighligthMenuInfo to find the last highlight menu.
1620 
1621   @param  HighLightedStatement      The input highlight statement.
1622 
1623   @retval  The highlight menu index.
1624 
1625 **/
1626 LIST_ENTRY *
FindHighLightMenuOption(IN FORM_DISPLAY_ENGINE_STATEMENT * HighLightedStatement)1627 FindHighLightMenuOption (
1628  IN FORM_DISPLAY_ENGINE_STATEMENT  *HighLightedStatement
1629  )
1630 {
1631   LIST_ENTRY                      *NewPos;
1632   UI_MENU_OPTION                  *MenuOption;
1633 
1634   NewPos = gMenuOption.ForwardLink;
1635   MenuOption = MENU_OPTION_FROM_LINK (NewPos);
1636 
1637   if (HighLightedStatement != NULL) {
1638     while (MenuOption->ThisTag != HighLightedStatement) {
1639       NewPos     = NewPos->ForwardLink;
1640       if (NewPos == &gMenuOption) {
1641         //
1642         // Not Found it, break
1643         //
1644         break;
1645       }
1646       MenuOption = MENU_OPTION_FROM_LINK (NewPos);
1647     }
1648 
1649     //
1650     // Must find the highlight statement.
1651     //
1652     ASSERT (NewPos != &gMenuOption);
1653 
1654   } else {
1655     while (!IsHighLightMenuOption (MenuOption)) {
1656       NewPos     = NewPos->ForwardLink;
1657       if (NewPos == &gMenuOption) {
1658         //
1659         // Not Found it, break
1660         //
1661         break;
1662       }
1663       MenuOption = MENU_OPTION_FROM_LINK (NewPos);
1664     }
1665 
1666     //
1667     // Highlight statement has disappear (suppressed/disableed)
1668     //
1669     if (NewPos == &gMenuOption) {
1670       NewPos = NULL;
1671     }
1672   }
1673 
1674   return NewPos;
1675 }
1676 
1677 /**
1678   Is this the Top of screen menu.
1679 
1680   @param  MenuOption      The input Menu option.
1681 
1682   @retval  TRUE   This is the Top of screen menu option.
1683   @retval  FALSE  This is not the Top of screen menu option.
1684 
1685 **/
1686 BOOLEAN
IsTopOfScreeMenuOption(IN UI_MENU_OPTION * MenuOption)1687 IsTopOfScreeMenuOption (
1688   IN UI_MENU_OPTION     *MenuOption
1689   )
1690 {
1691   if (gHighligthMenuInfo.TOSQuestionId != 0) {
1692     return (BOOLEAN) (GetQuestionIdInfo(MenuOption->ThisTag->OpCode) == gHighligthMenuInfo.TOSQuestionId);
1693   }
1694 
1695   if(CompareMem (gHighligthMenuInfo.TOSOpCode, MenuOption->ThisTag->OpCode, gHighligthMenuInfo.TOSOpCode->Length) == 0) {
1696     if (gHighligthMenuInfo.TOSIndex == 0 || gHighligthMenuInfo.TOSIndex == GetIndexInfoForOpcode(MenuOption->ThisTag->OpCode)) {
1697       return TRUE;
1698     } else {
1699       return FALSE;
1700     }
1701   }
1702 
1703   return FALSE;
1704 }
1705 
1706 /**
1707   Find the Top of screen menu.
1708 
1709   If the input is NULL, base on the record highlight info in
1710   gHighligthMenuInfo to find the last highlight menu.
1711 
1712   @param  HighLightedStatement      The input highlight statement.
1713 
1714   @retval  The highlight menu index.
1715 
1716 **/
1717 LIST_ENTRY *
FindTopOfScreenMenuOption(VOID)1718 FindTopOfScreenMenuOption (
1719  VOID
1720  )
1721 {
1722   LIST_ENTRY                      *NewPos;
1723   UI_MENU_OPTION                  *MenuOption;
1724 
1725   NewPos = gMenuOption.ForwardLink;
1726   MenuOption = MENU_OPTION_FROM_LINK (NewPos);
1727 
1728   while (!IsTopOfScreeMenuOption(MenuOption)) {
1729     NewPos     = NewPos->ForwardLink;
1730     if (NewPos == &gMenuOption) {
1731       //
1732       // Not Found it, break
1733       //
1734       break;
1735     }
1736     MenuOption = MENU_OPTION_FROM_LINK (NewPos);
1737   }
1738 
1739   //
1740   // Last time top of screen menu has disappeared.
1741   //
1742   if (NewPos == &gMenuOption) {
1743     NewPos = NULL;
1744   }
1745 
1746   return NewPos;
1747 }
1748 
1749 /**
1750   Find the first menu which will be show at the top.
1751 
1752   @param  FormData               The data info for this form.
1753   @param  TopOfScreen            The link_entry pointer to top menu.
1754   @param  HighlightMenu          The menu which will be highlight.
1755   @param  SkipValue              The skip value for the top menu.
1756 
1757 **/
1758 VOID
FindTopMenu(IN FORM_DISPLAY_ENGINE_FORM * FormData,OUT LIST_ENTRY ** TopOfScreen,OUT LIST_ENTRY ** HighlightMenu,OUT UINTN * SkipValue)1759 FindTopMenu (
1760   IN  FORM_DISPLAY_ENGINE_FORM  *FormData,
1761   OUT LIST_ENTRY                **TopOfScreen,
1762   OUT LIST_ENTRY                **HighlightMenu,
1763   OUT UINTN                     *SkipValue
1764   )
1765 {
1766   UINTN                           TopRow;
1767   UINTN                           BottomRow;
1768   UI_MENU_OPTION                  *MenuOption;
1769   UINTN                           TmpValue;
1770 
1771   TopRow    = gStatementDimensions.TopRow    + SCROLL_ARROW_HEIGHT;
1772   BottomRow = gStatementDimensions.BottomRow - SCROLL_ARROW_HEIGHT;
1773 
1774   if (gMisMatch) {
1775     //
1776     // Reenter caused by option mismatch or auto exit caused by refresh form(refresh interval/guid),
1777     // base on the record highlight info to find the highlight menu.
1778     //
1779     ASSERT (gFormData->HiiHandle == gHighligthMenuInfo.HiiHandle &&
1780             gFormData->FormId == gHighligthMenuInfo.FormId);
1781 
1782     *HighlightMenu = FindHighLightMenuOption(NULL);
1783     if (*HighlightMenu != NULL) {
1784       //
1785       // Update skip info for this highlight menu.
1786       //
1787       MenuOption = MENU_OPTION_FROM_LINK (*HighlightMenu);
1788       UpdateOptionSkipLines (MenuOption);
1789 
1790       //
1791       // Found the last time highlight menu.
1792       //
1793       *TopOfScreen = FindTopOfScreenMenuOption();
1794       if (*TopOfScreen != NULL) {
1795         //
1796         // Found the last time selectable top of screen menu.
1797         //
1798         AdjustDateAndTimePosition(TRUE, TopOfScreen);
1799         MenuOption = MENU_OPTION_FROM_LINK (*TopOfScreen);
1800         UpdateOptionSkipLines (MenuOption);
1801 
1802         *SkipValue = gHighligthMenuInfo.SkipValue;
1803       } else {
1804         //
1805         // Not found last time top of screen menu, so base on current highlight menu
1806         // to find the new top of screen menu.
1807         // Make the current highlight menu at the bottom of the form to calculate the
1808         // top of screen menu.
1809         //
1810         if (MenuOption->Skip >= BottomRow - TopRow) {
1811           *TopOfScreen = *HighlightMenu;
1812           TmpValue     = 0;
1813         } else {
1814           *TopOfScreen = FindTopOfScreenMenu(*HighlightMenu, BottomRow - TopRow - MenuOption->Skip, &TmpValue);
1815         }
1816 
1817         *SkipValue   = TmpValue;
1818       }
1819     } else {
1820       //
1821       // Last time highlight menu has disappear, find the first highlightable menu as the defalut one.
1822       //
1823       *HighlightMenu = gMenuOption.ForwardLink;
1824       if (!IsListEmpty (&gMenuOption)) {
1825         MoveToNextStatement (FALSE, HighlightMenu, BottomRow - TopRow, TRUE);
1826       }
1827       *TopOfScreen   = gMenuOption.ForwardLink;
1828       *SkipValue = 0;
1829     }
1830 
1831     gMisMatch = FALSE;
1832   } else if (FormData->HighLightedStatement != NULL) {
1833     if (IsSavedHighlightStatement (FormData->HighLightedStatement)) {
1834       //
1835       // Input highlight menu is same as last time highlight menu.
1836       // Base on last time highlight menu to set the top of screen menu and highlight menu.
1837       //
1838       *HighlightMenu = FindHighLightMenuOption(NULL);
1839       ASSERT (*HighlightMenu != NULL);
1840 
1841       //
1842       // Update skip info for this highlight menu.
1843       //
1844       MenuOption = MENU_OPTION_FROM_LINK (*HighlightMenu);
1845       UpdateOptionSkipLines (MenuOption);
1846 
1847       *TopOfScreen = FindTopOfScreenMenuOption();
1848       if (*TopOfScreen == NULL) {
1849         //
1850         // Not found last time top of screen menu, so base on current highlight menu
1851         // to find the new top of screen menu.
1852         // Make the current highlight menu at the bottom of the form to calculate the
1853         // top of screen menu.
1854         //
1855         if (MenuOption->Skip >= BottomRow - TopRow) {
1856           *TopOfScreen = *HighlightMenu;
1857           TmpValue     = 0;
1858         } else {
1859           *TopOfScreen = FindTopOfScreenMenu(*HighlightMenu, BottomRow - TopRow - MenuOption->Skip, &TmpValue);
1860         }
1861 
1862         *SkipValue   = TmpValue;
1863       } else {
1864         AdjustDateAndTimePosition(TRUE, TopOfScreen);
1865         MenuOption = MENU_OPTION_FROM_LINK (*TopOfScreen);
1866         UpdateOptionSkipLines (MenuOption);
1867 
1868         *SkipValue = gHighligthMenuInfo.SkipValue;
1869       }
1870       AdjustDateAndTimePosition(TRUE, TopOfScreen);
1871     } else {
1872       //
1873       // Input highlight menu is not save as last time highlight menu.
1874       //
1875       *HighlightMenu = FindHighLightMenuOption(FormData->HighLightedStatement);
1876       MenuOption = MENU_OPTION_FROM_LINK (*HighlightMenu);
1877       UpdateOptionSkipLines (MenuOption);
1878 
1879       //
1880       // Make the current highlight menu at the bottom of the form to calculate the
1881       // top of screen menu.
1882       //
1883       if (MenuOption->Skip >= BottomRow - TopRow) {
1884         *TopOfScreen = *HighlightMenu;
1885         TmpValue     = 0;
1886       } else {
1887         *TopOfScreen = FindTopOfScreenMenu(*HighlightMenu, BottomRow - TopRow - MenuOption->Skip, &TmpValue);
1888       }
1889 
1890       *SkipValue   = TmpValue;
1891     }
1892     AdjustDateAndTimePosition(TRUE, TopOfScreen);
1893   } else {
1894     //
1895     // If not has input highlight statement, just return the first one in this form.
1896     //
1897     *TopOfScreen   = gMenuOption.ForwardLink;
1898     *HighlightMenu = gMenuOption.ForwardLink;
1899     if (!IsListEmpty (&gMenuOption)) {
1900       MoveToNextStatement (FALSE, HighlightMenu, BottomRow - TopRow, TRUE);
1901     }
1902     *SkipValue     = 0;
1903   }
1904 }
1905 
1906 /**
1907   Record the highlight menu and top of screen menu info.
1908 
1909   @param  Highlight               The menu opton which is highlight.
1910   @param  TopOfScreen             The menu opton which is at the top of the form.
1911   @param  SkipValue               The skip line info for the top of screen menu.
1912 
1913 **/
1914 VOID
UpdateHighlightMenuInfo(IN LIST_ENTRY * Highlight,IN LIST_ENTRY * TopOfScreen,IN UINTN SkipValue)1915 UpdateHighlightMenuInfo (
1916   IN  LIST_ENTRY                      *Highlight,
1917   IN  LIST_ENTRY                      *TopOfScreen,
1918   IN  UINTN                           SkipValue
1919   )
1920 {
1921   UI_MENU_OPTION                  *MenuOption;
1922   FORM_DISPLAY_ENGINE_STATEMENT   *Statement;
1923 
1924   gHighligthMenuInfo.HiiHandle  = gFormData->HiiHandle;
1925   gHighligthMenuInfo.FormId     = gFormData->FormId;
1926   gHighligthMenuInfo.SkipValue  = (UINT16)SkipValue;
1927 
1928   if (!IsListEmpty (&gMenuOption)) {
1929     MenuOption = MENU_OPTION_FROM_LINK (Highlight);
1930     Statement  = MenuOption->ThisTag;
1931 
1932     gUserInput->SelectedStatement = Statement;
1933 
1934     gHighligthMenuInfo.HLTSequence   = MenuOption->Sequence;
1935     gHighligthMenuInfo.HLTQuestionId = GetQuestionIdInfo(Statement->OpCode);
1936     if (gHighligthMenuInfo.HLTQuestionId == 0) {
1937       //
1938       // if question id == 0, save the opcode buffer..
1939       //
1940       if (gHighligthMenuInfo.HLTOpCode != NULL) {
1941         FreePool (gHighligthMenuInfo.HLTOpCode);
1942       }
1943       gHighligthMenuInfo.HLTOpCode = AllocateCopyPool (Statement->OpCode->Length, Statement->OpCode);
1944       ASSERT (gHighligthMenuInfo.HLTOpCode != NULL);
1945 
1946       gHighligthMenuInfo.HLTIndex = GetIndexInfoForOpcode(Statement->OpCode);
1947     }
1948 
1949     MenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
1950     Statement  = MenuOption->ThisTag;
1951 
1952     gHighligthMenuInfo.TOSQuestionId = GetQuestionIdInfo(Statement->OpCode);
1953     if (gHighligthMenuInfo.TOSQuestionId == 0) {
1954       //
1955       // if question id == 0, save the opcode buffer..
1956       //
1957       if (gHighligthMenuInfo.TOSOpCode != NULL) {
1958         FreePool (gHighligthMenuInfo.TOSOpCode);
1959       }
1960       gHighligthMenuInfo.TOSOpCode = AllocateCopyPool (Statement->OpCode->Length, Statement->OpCode);
1961       ASSERT (gHighligthMenuInfo.TOSOpCode != NULL);
1962 
1963       gHighligthMenuInfo.TOSIndex = GetIndexInfoForOpcode(Statement->OpCode);
1964     }
1965   } else {
1966     gUserInput->SelectedStatement    = NULL;
1967 
1968     gHighligthMenuInfo.HLTSequence   = 0;
1969     gHighligthMenuInfo.HLTQuestionId = 0;
1970     if (gHighligthMenuInfo.HLTOpCode != NULL) {
1971       FreePool (gHighligthMenuInfo.HLTOpCode);
1972     }
1973     gHighligthMenuInfo.HLTOpCode     = NULL;
1974     gHighligthMenuInfo.HLTIndex      = 0;
1975 
1976     gHighligthMenuInfo.TOSQuestionId = 0;
1977     if (gHighligthMenuInfo.TOSOpCode != NULL) {
1978       FreePool (gHighligthMenuInfo.TOSOpCode);
1979     }
1980     gHighligthMenuInfo.TOSOpCode     = NULL;
1981     gHighligthMenuInfo.TOSIndex      = 0;
1982   }
1983 }
1984 
1985 /**
1986   Update attribut for this menu.
1987 
1988   @param  MenuOption               The menu opton which this attribut used to.
1989   @param  Highlight                Whether this menu will be highlight.
1990 
1991 **/
1992 VOID
SetDisplayAttribute(IN UI_MENU_OPTION * MenuOption,IN BOOLEAN Highlight)1993 SetDisplayAttribute (
1994   IN UI_MENU_OPTION                  *MenuOption,
1995   IN BOOLEAN                         Highlight
1996   )
1997 {
1998   FORM_DISPLAY_ENGINE_STATEMENT   *Statement;
1999 
2000   Statement = MenuOption->ThisTag;
2001 
2002   if (Highlight) {
2003     gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());
2004     return;
2005   }
2006 
2007   if (MenuOption->GrayOut) {
2008     gST->ConOut->SetAttribute (gST->ConOut, GetGrayedTextColor ());
2009   } else {
2010     if (Statement->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) {
2011       gST->ConOut->SetAttribute (gST->ConOut, GetSubTitleTextColor ());
2012     } else {
2013       gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
2014     }
2015   }
2016 }
2017 
2018 /**
2019   Print string for this menu option.
2020 
2021   @param  MenuOption               The menu opton which this attribut used to.
2022   @param  Col                      The column that this string will be print at.
2023   @param  Row                      The row that this string will be print at.
2024   @param  String                   The string which need to print.
2025   @param  Width                    The width need to print, if string is less than the
2026                                    width, the block space will be used.
2027   @param  Highlight                Whether this menu will be highlight.
2028 
2029 **/
2030 VOID
DisplayMenuString(IN UI_MENU_OPTION * MenuOption,IN UINTN Col,IN UINTN Row,IN CHAR16 * String,IN UINTN Width,IN BOOLEAN Highlight)2031 DisplayMenuString (
2032   IN UI_MENU_OPTION         *MenuOption,
2033   IN UINTN                  Col,
2034   IN UINTN                  Row,
2035   IN CHAR16                 *String,
2036   IN UINTN                  Width,
2037   IN BOOLEAN                Highlight
2038   )
2039 {
2040   UINTN            Length;
2041 
2042   //
2043   // Print string with normal color.
2044   //
2045   if (!Highlight) {
2046     PrintStringAtWithWidth (Col, Row, String, Width);
2047     return;
2048   }
2049 
2050   //
2051   // Print the highlight menu string.
2052   // First print the highlight string.
2053   //
2054   SetDisplayAttribute(MenuOption, TRUE);
2055   Length = PrintStringAt (Col, Row, String);
2056 
2057   //
2058   // Second, clean the empty after the string.
2059   //
2060   SetDisplayAttribute(MenuOption, FALSE);
2061   PrintStringAtWithWidth (Col + Length, Row, L"", Width - Length);
2062 }
2063 
2064 /**
2065   Check whether this menu can has option string.
2066 
2067   @param  MenuOption               The menu opton which this attribut used to.
2068 
2069   @retval TRUE                     This menu option can have option string.
2070   @retval FALSE                    This menu option can't have option string.
2071 
2072 **/
2073 BOOLEAN
HasOptionString(IN UI_MENU_OPTION * MenuOption)2074 HasOptionString (
2075   IN UI_MENU_OPTION                  *MenuOption
2076   )
2077 {
2078   FORM_DISPLAY_ENGINE_STATEMENT   *Statement;
2079   CHAR16                          *String;
2080   UINTN                           Size;
2081   EFI_IFR_TEXT                    *TestOp;
2082 
2083   Size = 0;
2084   Statement = MenuOption->ThisTag;
2085 
2086   //
2087   // See if the second text parameter is really NULL
2088   //
2089   if (Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) {
2090     TestOp = (EFI_IFR_TEXT *) Statement->OpCode;
2091     if (TestOp->TextTwo != 0) {
2092       String = GetToken (TestOp->TextTwo, gFormData->HiiHandle);
2093       Size   = StrLen (String);
2094       FreePool (String);
2095     }
2096   }
2097 
2098   if ((Statement->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) ||
2099     (Statement->OpCode->OpCode == EFI_IFR_REF_OP) ||
2100     (Statement->OpCode->OpCode == EFI_IFR_PASSWORD_OP) ||
2101     (Statement->OpCode->OpCode == EFI_IFR_ACTION_OP) ||
2102     (Statement->OpCode->OpCode == EFI_IFR_RESET_BUTTON_OP) ||
2103     //
2104     // Allow a wide display if text op-code and no secondary text op-code
2105     //
2106     ((Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) && (Size == 0))
2107     ) {
2108 
2109     return FALSE;
2110   }
2111 
2112   return TRUE;
2113 }
2114 
2115 /**
2116   Double confirm with user about the action.
2117 
2118   @param  Action               The user input action.
2119 
2120   @retval TRUE                 User confirm with the input or not need user confirm.
2121   @retval FALSE                User want ignore this input.
2122 
2123 **/
2124 BOOLEAN
FxConfirmPopup(IN UINT32 Action)2125 FxConfirmPopup (
2126   IN UINT32   Action
2127   )
2128 {
2129   EFI_INPUT_KEY                   Key;
2130   CHAR16                          *CfmStr;
2131   UINTN                           CfmStrLen;
2132   UINT32                          CheckFlags;
2133   BOOLEAN                         RetVal;
2134   UINTN                           CatLen;
2135 
2136   CfmStrLen = 0;
2137   CatLen    = StrLen (gConfirmMsgConnect);
2138 
2139   //
2140   // Below action need extra popup dialog to confirm.
2141   //
2142   CheckFlags = BROWSER_ACTION_DISCARD |
2143                BROWSER_ACTION_DEFAULT |
2144                BROWSER_ACTION_SUBMIT |
2145                BROWSER_ACTION_RESET |
2146                BROWSER_ACTION_EXIT;
2147 
2148   //
2149   // Not need to confirm with user, just return TRUE.
2150   //
2151   if ((Action & CheckFlags) == 0) {
2152     return TRUE;
2153   }
2154 
2155   if ((Action & BROWSER_ACTION_DISCARD) == BROWSER_ACTION_DISCARD) {
2156     CfmStrLen += StrLen (gConfirmDiscardMsg);
2157   }
2158 
2159   if ((Action & BROWSER_ACTION_DEFAULT) == BROWSER_ACTION_DEFAULT) {
2160     if (CfmStrLen != 0) {
2161       CfmStrLen += CatLen;
2162     }
2163 
2164     CfmStrLen += StrLen (gConfirmDefaultMsg);
2165   }
2166 
2167   if ((Action & BROWSER_ACTION_SUBMIT)  == BROWSER_ACTION_SUBMIT) {
2168     if (CfmStrLen != 0) {
2169       CfmStrLen += CatLen;
2170     }
2171 
2172     CfmStrLen += StrLen (gConfirmSubmitMsg);
2173   }
2174 
2175   if ((Action & BROWSER_ACTION_RESET)  == BROWSER_ACTION_RESET) {
2176     if (CfmStrLen != 0) {
2177       CfmStrLen += CatLen;
2178     }
2179 
2180     CfmStrLen += StrLen (gConfirmResetMsg);
2181   }
2182 
2183   if ((Action & BROWSER_ACTION_EXIT)  == BROWSER_ACTION_EXIT) {
2184     if (CfmStrLen != 0) {
2185       CfmStrLen += CatLen;
2186     }
2187 
2188     CfmStrLen += StrLen (gConfirmExitMsg);
2189   }
2190 
2191   //
2192   // Allocate buffer to save the string.
2193   // String + "?" + "\0"
2194   //
2195   CfmStr = AllocateZeroPool ((CfmStrLen + 1 + 1) * sizeof (CHAR16));
2196   ASSERT (CfmStr != NULL);
2197 
2198   if ((Action & BROWSER_ACTION_DISCARD) == BROWSER_ACTION_DISCARD) {
2199     StrCpy (CfmStr, gConfirmDiscardMsg);
2200   }
2201 
2202   if ((Action & BROWSER_ACTION_DEFAULT) == BROWSER_ACTION_DEFAULT) {
2203     if (CfmStr[0] != 0) {
2204       StrCat (CfmStr, gConfirmMsgConnect);
2205       StrCat (CfmStr, gConfirmDefaultMsg2nd);
2206     } else {
2207       StrCpy (CfmStr, gConfirmDefaultMsg);
2208     }
2209   }
2210 
2211   if ((Action & BROWSER_ACTION_SUBMIT)  == BROWSER_ACTION_SUBMIT) {
2212     if (CfmStr[0] != 0) {
2213       StrCat (CfmStr, gConfirmMsgConnect);
2214       StrCat (CfmStr, gConfirmSubmitMsg2nd);
2215     } else {
2216       StrCpy (CfmStr, gConfirmSubmitMsg);
2217     }
2218   }
2219 
2220   if ((Action & BROWSER_ACTION_RESET)  == BROWSER_ACTION_RESET) {
2221     if (CfmStr[0] != 0) {
2222       StrCat (CfmStr, gConfirmMsgConnect);
2223       StrCat (CfmStr, gConfirmResetMsg2nd);
2224     } else {
2225       StrCpy (CfmStr, gConfirmResetMsg);
2226     }
2227   }
2228 
2229   if ((Action & BROWSER_ACTION_EXIT)  == BROWSER_ACTION_EXIT) {
2230     if (CfmStr[0] != 0) {
2231       StrCat (CfmStr, gConfirmMsgConnect);
2232       StrCat (CfmStr, gConfirmExitMsg2nd);
2233     } else {
2234       StrCpy (CfmStr, gConfirmExitMsg);
2235     }
2236   }
2237 
2238   StrCat (CfmStr, gConfirmMsgEnd);
2239 
2240   do {
2241     CreateDialog (&Key, gEmptyString, CfmStr, gConfirmOpt, gEmptyString, NULL);
2242   } while (((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (gConfirmOptYes[0] | UPPER_LOWER_CASE_OFFSET)) &&
2243            ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (gConfirmOptNo[0] | UPPER_LOWER_CASE_OFFSET)));
2244 
2245   if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (gConfirmOptYes[0] | UPPER_LOWER_CASE_OFFSET)) {
2246     RetVal = TRUE;
2247   } else {
2248     RetVal = FALSE;
2249   }
2250 
2251   FreePool (CfmStr);
2252 
2253   return RetVal;
2254 }
2255 
2256 /**
2257   Print string for this menu option.
2258 
2259   @param  MenuOption               The menu opton which this attribut used to.
2260   @param  SkipWidth                The skip width between the left to the start of the prompt.
2261   @param  BeginCol                 The begin column for one menu.
2262   @param  SkipLine                 The skip line for this menu.
2263   @param  BottomRow                The bottom row for this form.
2264   @param  Highlight                Whether this menu will be highlight.
2265   @param  UpdateCol                Whether need to update the column info for Date/Time.
2266 
2267   @retval EFI_SUCESSS              Process the user selection success.
2268 
2269 **/
2270 EFI_STATUS
DisplayOneMenu(IN UI_MENU_OPTION * MenuOption,IN UINTN SkipWidth,IN UINTN BeginCol,IN UINTN SkipLine,IN UINTN BottomRow,IN BOOLEAN Highlight,IN BOOLEAN UpdateCol)2271 DisplayOneMenu (
2272   IN UI_MENU_OPTION                  *MenuOption,
2273   IN UINTN                           SkipWidth,
2274   IN UINTN                           BeginCol,
2275   IN UINTN                           SkipLine,
2276   IN UINTN                           BottomRow,
2277   IN BOOLEAN                         Highlight,
2278   IN BOOLEAN                         UpdateCol
2279   )
2280 {
2281   FORM_DISPLAY_ENGINE_STATEMENT   *Statement;
2282   UINTN                           Index;
2283   UINT16                          Width;
2284   UINT16                          PromptWidth;
2285   CHAR16                          *StringPtr;
2286   CHAR16                          *OptionString;
2287   CHAR16                          *OutputString;
2288   UINT16                          GlyphWidth;
2289   UINTN                           Temp;
2290   UINTN                           Temp2;
2291   UINTN                           Temp3;
2292   EFI_STATUS                      Status;
2293   UINTN                           Row;
2294   UINTN                           Col;
2295   UINTN                           PromptLineNum;
2296   UINTN                           OptionLineNum;
2297   CHAR16                          AdjustValue;
2298   UINTN                           MaxRow;
2299 
2300   Statement = MenuOption->ThisTag;
2301   Temp      = SkipLine;
2302   Temp2     = SkipLine;
2303   Temp3     = SkipLine;
2304   AdjustValue   = 0;
2305   PromptLineNum = 0;
2306   OptionLineNum = 0;
2307   MaxRow        = 0;
2308 
2309   //
2310   // Set default color.
2311   //
2312   SetDisplayAttribute (MenuOption, FALSE);
2313 
2314   //
2315   // 1. Paint the option string.
2316   //
2317   Status = ProcessOptions (MenuOption, FALSE, &OptionString, FALSE);
2318   if (EFI_ERROR (Status)) {
2319     return Status;
2320   }
2321 
2322   if (OptionString != NULL) {
2323     if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP || Statement->OpCode->OpCode == EFI_IFR_TIME_OP) {
2324       //
2325       // Adjust option string for date/time opcode.
2326       //
2327       ProcessStringForDateTime(MenuOption, OptionString, UpdateCol);
2328     }
2329 
2330     Width       = (UINT16) gOptionBlockWidth - 1;
2331     Row         = MenuOption->Row;
2332     GlyphWidth  = 1;
2333     OptionLineNum = 0;
2334 
2335     for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
2336       if (((Temp2 == 0)) && (Row <= BottomRow)) {
2337         if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP || Statement->OpCode->OpCode == EFI_IFR_TIME_OP) {
2338           //
2339           // For date/time question, it has three menu options for this qustion.
2340           // The first/second menu options with the skip value is 0. the last one
2341           // with skip value is 1.
2342           //
2343           if (MenuOption->Skip != 0) {
2344             //
2345             // For date/ time, print the last past (year for date and second for time)
2346             // - 7 means skip [##/##/ for date and [##:##: for time.
2347             //
2348             DisplayMenuString (MenuOption,MenuOption->OptCol, Row, OutputString, Width + 1 - 7, Highlight);
2349           } else {
2350             //
2351             // For date/ time, print the first and second past (year for date and second for time)
2352             // The OutputString has a NARROW_CHAR or WIDE_CHAR at the begin of the string,
2353             // so need to - 1 to remove it, otherwise, it will clean 1 extr char follow it.
2354             DisplayMenuString (MenuOption, MenuOption->OptCol, Row, OutputString, StrLen (OutputString) - 1, Highlight);
2355           }
2356         } else {
2357           DisplayMenuString (MenuOption, MenuOption->OptCol, Row, OutputString, Width + 1, Highlight);
2358         }
2359         OptionLineNum++;
2360       }
2361 
2362       //
2363       // If there is more string to process print on the next row and increment the Skip value
2364       //
2365       if (StrLen (&OptionString[Index]) != 0) {
2366         if (Temp2 == 0) {
2367           Row++;
2368           //
2369           // Since the Number of lines for this menu entry may or may not be reflected accurately
2370           // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
2371           // some testing to ensure we are keeping this in-sync.
2372           //
2373           // If the difference in rows is greater than or equal to the skip value, increase the skip value
2374           //
2375           if ((Row - MenuOption->Row) >= MenuOption->Skip) {
2376             MenuOption->Skip++;
2377           }
2378         }
2379       }
2380 
2381       FreePool (OutputString);
2382       if (Temp2 != 0) {
2383         Temp2--;
2384       }
2385     }
2386 
2387     Highlight = FALSE;
2388 
2389     FreePool (OptionString);
2390   }
2391 
2392   //
2393   // 2. Paint the description.
2394   //
2395   PromptWidth   = GetWidth (MenuOption, &AdjustValue);
2396   Row           = MenuOption->Row;
2397   GlyphWidth    = 1;
2398   PromptLineNum = 0;
2399 
2400   if (MenuOption->Description == NULL || MenuOption->Description[0] == '\0') {
2401     PrintStringAtWithWidth (BeginCol, Row, L"", PromptWidth + AdjustValue + SkipWidth);
2402     PromptLineNum++;
2403   } else {
2404     for (Index = 0; GetLineByWidth (MenuOption->Description, PromptWidth, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
2405       if ((Temp == 0) && (Row <= BottomRow)) {
2406         //
2407         // 1.Clean the start LEFT_SKIPPED_COLUMNS
2408         //
2409         PrintStringAtWithWidth (BeginCol, Row, L"", SkipWidth);
2410 
2411         if (Statement->OpCode->OpCode == EFI_IFR_REF_OP && MenuOption->Col >= 2) {
2412           //
2413           // Print Arrow for Goto button.
2414           //
2415           PrintCharAt (
2416             MenuOption->Col - 2,
2417             Row,
2418             GEOMETRICSHAPE_RIGHT_TRIANGLE
2419             );
2420         }
2421         DisplayMenuString (MenuOption, MenuOption->Col, Row, OutputString, PromptWidth + AdjustValue, Highlight);
2422         PromptLineNum ++;
2423       }
2424       //
2425       // If there is more string to process print on the next row and increment the Skip value
2426       //
2427       if (StrLen (&MenuOption->Description[Index]) != 0) {
2428         if (Temp == 0) {
2429           Row++;
2430         }
2431       }
2432 
2433       FreePool (OutputString);
2434       if (Temp != 0) {
2435         Temp--;
2436       }
2437     }
2438 
2439     Highlight = FALSE;
2440   }
2441 
2442 
2443   //
2444   // 3. If this is a text op with secondary text information
2445   //
2446   if ((Statement->OpCode->OpCode  == EFI_IFR_TEXT_OP) && (((EFI_IFR_TEXT*)Statement->OpCode)->TextTwo != 0)) {
2447     StringPtr   = GetToken (((EFI_IFR_TEXT*)Statement->OpCode)->TextTwo, gFormData->HiiHandle);
2448 
2449     Width       = (UINT16) gOptionBlockWidth - 1;
2450     Row         = MenuOption->Row;
2451     GlyphWidth  = 1;
2452     OptionLineNum = 0;
2453 
2454     for (Index = 0; GetLineByWidth (StringPtr, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
2455       if ((Temp3 == 0) && (Row <= BottomRow)) {
2456         DisplayMenuString (MenuOption, MenuOption->OptCol, Row, OutputString, Width + 1, Highlight);
2457         OptionLineNum++;
2458       }
2459       //
2460       // If there is more string to process print on the next row and increment the Skip value
2461       //
2462       if (StrLen (&StringPtr[Index]) != 0) {
2463         if (Temp3 == 0) {
2464           Row++;
2465           //
2466           // If the rows for text two is greater than or equal to the skip value, increase the skip value
2467           //
2468           if ((Row - MenuOption->Row) >= MenuOption->Skip) {
2469             MenuOption->Skip++;
2470           }
2471         }
2472       }
2473 
2474       FreePool (OutputString);
2475       if (Temp3 != 0) {
2476         Temp3--;
2477       }
2478     }
2479 
2480     FreePool (StringPtr);
2481   }
2482 
2483   //
2484   // 4.Line number for Option string and prompt string are not equal.
2485   //  Clean the column whose line number is less.
2486   //
2487   if (HasOptionString(MenuOption) && (OptionLineNum != PromptLineNum)) {
2488     Col    =  OptionLineNum < PromptLineNum ? MenuOption->OptCol : BeginCol;
2489     Row    = (OptionLineNum < PromptLineNum ? OptionLineNum : PromptLineNum) + MenuOption->Row;
2490     Width  = (UINT16) (OptionLineNum < PromptLineNum ? gOptionBlockWidth : PromptWidth + AdjustValue + SkipWidth);
2491     MaxRow = (OptionLineNum < PromptLineNum ? PromptLineNum : OptionLineNum) + MenuOption->Row - 1;
2492 
2493     while (Row <= MaxRow) {
2494       DisplayMenuString (MenuOption, Col, Row++, L"", Width, FALSE);
2495     }
2496   }
2497 
2498   return EFI_SUCCESS;
2499 }
2500 
2501 /**
2502   Display menu and wait for user to select one menu option, then return it.
2503   If AutoBoot is enabled, then if user doesn't select any option,
2504   after period of time, it will automatically return the first menu option.
2505 
2506   @param  FormData               The current form data info.
2507 
2508   @retval EFI_SUCESSS            Process the user selection success.
2509   @retval EFI_NOT_FOUND          Process option string for orderedlist/Oneof fail.
2510 
2511 **/
2512 EFI_STATUS
UiDisplayMenu(IN FORM_DISPLAY_ENGINE_FORM * FormData)2513 UiDisplayMenu (
2514   IN  FORM_DISPLAY_ENGINE_FORM  *FormData
2515   )
2516 {
2517   UINTN                           SkipValue;
2518   INTN                            Difference;
2519   UINTN                           DistanceValue;
2520   UINTN                           Row;
2521   UINTN                           Col;
2522   UINTN                           Temp;
2523   UINTN                           Temp2;
2524   UINTN                           TopRow;
2525   UINTN                           BottomRow;
2526   UINTN                           Index;
2527   UINT16                          Width;
2528   CHAR16                          *StringPtr;
2529   CHAR16                          *OptionString;
2530   CHAR16                          *OutputString;
2531   CHAR16                          *HelpString;
2532   CHAR16                          *HelpHeaderString;
2533   CHAR16                          *HelpBottomString;
2534   BOOLEAN                         NewLine;
2535   BOOLEAN                         Repaint;
2536   BOOLEAN                         UpArrow;
2537   BOOLEAN                         DownArrow;
2538   EFI_STATUS                      Status;
2539   EFI_INPUT_KEY                   Key;
2540   LIST_ENTRY                      *Link;
2541   LIST_ENTRY                      *NewPos;
2542   LIST_ENTRY                      *TopOfScreen;
2543   LIST_ENTRY                      *SavedListEntry;
2544   UI_MENU_OPTION                  *MenuOption;
2545   UI_MENU_OPTION                  *NextMenuOption;
2546   UI_MENU_OPTION                  *SavedMenuOption;
2547   UI_MENU_OPTION                  *PreviousMenuOption;
2548   UI_CONTROL_FLAG                 ControlFlag;
2549   UI_SCREEN_OPERATION             ScreenOperation;
2550   UINT16                          DefaultId;
2551   FORM_DISPLAY_ENGINE_STATEMENT   *Statement;
2552   BROWSER_HOT_KEY                 *HotKey;
2553   UINTN                           HelpPageIndex;
2554   UINTN                           HelpPageCount;
2555   UINTN                           RowCount;
2556   UINTN                           HelpLine;
2557   UINTN                           HelpHeaderLine;
2558   UINTN                           HelpBottomLine;
2559   BOOLEAN                         MultiHelpPage;
2560   UINT16                          EachLineWidth;
2561   UINT16                          HeaderLineWidth;
2562   UINT16                          BottomLineWidth;
2563   EFI_STRING_ID                   HelpInfo;
2564   UI_EVENT_TYPE                   EventType;
2565   FORM_DISPLAY_ENGINE_STATEMENT   *InitialHighlight;
2566   BOOLEAN                         SkipHighLight;
2567 
2568   EventType           = UIEventNone;
2569   Status              = EFI_SUCCESS;
2570   HelpString          = NULL;
2571   HelpHeaderString    = NULL;
2572   HelpBottomString    = NULL;
2573   OptionString        = NULL;
2574   ScreenOperation     = UiNoOperation;
2575   NewLine             = TRUE;
2576   DefaultId           = 0;
2577   HelpPageCount       = 0;
2578   HelpLine            = 0;
2579   RowCount            = 0;
2580   HelpBottomLine      = 0;
2581   HelpHeaderLine      = 0;
2582   HelpPageIndex       = 0;
2583   MultiHelpPage       = FALSE;
2584   EachLineWidth       = 0;
2585   HeaderLineWidth     = 0;
2586   BottomLineWidth     = 0;
2587   OutputString        = NULL;
2588   UpArrow             = FALSE;
2589   DownArrow           = FALSE;
2590   SkipValue           = 0;
2591   SkipHighLight       = FALSE;
2592 
2593   NextMenuOption      = NULL;
2594   PreviousMenuOption  = NULL;
2595   SavedMenuOption     = NULL;
2596   HotKey              = NULL;
2597   Repaint             = TRUE;
2598   MenuOption          = NULL;
2599   gModalSkipColumn    = (CHAR16) (gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn) / 6;
2600   InitialHighlight    = gFormData->HighLightedStatement;
2601 
2602   ZeroMem (&Key, sizeof (EFI_INPUT_KEY));
2603 
2604   Width     = (UINT16)gOptionBlockWidth - 1;
2605   TopRow    = gStatementDimensions.TopRow    + SCROLL_ARROW_HEIGHT;
2606   BottomRow = gStatementDimensions.BottomRow - SCROLL_ARROW_HEIGHT - 1;
2607 
2608   Row = TopRow;
2609   if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
2610     Col = gStatementDimensions.LeftColumn + LEFT_SKIPPED_COLUMNS + gModalSkipColumn;
2611   } else {
2612     Col = gStatementDimensions.LeftColumn + LEFT_SKIPPED_COLUMNS;
2613   }
2614 
2615   FindTopMenu(FormData, &TopOfScreen, &NewPos, &SkipValue);
2616   if (!IsListEmpty (&gMenuOption)) {
2617     NextMenuOption = MENU_OPTION_FROM_LINK (NewPos);
2618     gUserInput->SelectedStatement = NextMenuOption->ThisTag;
2619   }
2620 
2621   gST->ConOut->EnableCursor (gST->ConOut, FALSE);
2622 
2623   ControlFlag = CfInitialization;
2624   while (TRUE) {
2625     switch (ControlFlag) {
2626     case CfInitialization:
2627       if ((gOldFormEntry.HiiHandle != FormData->HiiHandle) ||
2628           (!CompareGuid (&gOldFormEntry.FormSetGuid, &FormData->FormSetGuid))) {
2629         //
2630         // Clear Statement range if different formset is painted.
2631         //
2632         ClearLines (
2633           gStatementDimensions.LeftColumn,
2634           gStatementDimensions.RightColumn,
2635           TopRow - SCROLL_ARROW_HEIGHT,
2636           BottomRow + SCROLL_ARROW_HEIGHT,
2637           GetFieldTextColor ()
2638           );
2639 
2640       }
2641       ControlFlag = CfRepaint;
2642       break;
2643 
2644     case CfRepaint:
2645       ControlFlag = CfRefreshHighLight;
2646 
2647       if (Repaint) {
2648         //
2649         // Display menu
2650         //
2651         DownArrow       = FALSE;
2652         UpArrow         = FALSE;
2653         Row             = TopRow;
2654 
2655         gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
2656 
2657         //
2658         // 1. Check whether need to print the arrow up.
2659         //
2660         if (!ValueIsScroll (TRUE, TopOfScreen)) {
2661           UpArrow = TRUE;
2662         }
2663 
2664         if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
2665           PrintStringAtWithWidth(gStatementDimensions.LeftColumn + gModalSkipColumn, TopRow - 1, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 * gModalSkipColumn);
2666         } else {
2667           PrintStringAtWithWidth(gStatementDimensions.LeftColumn, TopRow - 1, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn);
2668         }
2669         if (UpArrow) {
2670           gST->ConOut->SetAttribute (gST->ConOut, GetArrowColor ());
2671           PrintCharAt (
2672             gStatementDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
2673             TopRow - SCROLL_ARROW_HEIGHT,
2674             ARROW_UP
2675             );
2676           gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
2677         }
2678 
2679         //
2680         // 2.Paint the menu.
2681         //
2682         for (Link = TopOfScreen; Link != &gMenuOption; Link = Link->ForwardLink) {
2683           MenuOption          = MENU_OPTION_FROM_LINK (Link);
2684           MenuOption->Row     = Row;
2685           MenuOption->Col     = Col;
2686           if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
2687             MenuOption->OptCol  = gStatementDimensions.LeftColumn + LEFT_SKIPPED_COLUMNS + gPromptBlockWidth + gModalSkipColumn;
2688           } else {
2689             MenuOption->OptCol  = gStatementDimensions.LeftColumn + LEFT_SKIPPED_COLUMNS + gPromptBlockWidth;
2690           }
2691 
2692           if (MenuOption->NestInStatement) {
2693             MenuOption->Col += SUBTITLE_INDENT;
2694           }
2695 
2696           //
2697           // Save the highlight menu, will be used in CfRefreshHighLight case.
2698           //
2699           if (Link == NewPos) {
2700             SavedMenuOption = MenuOption;
2701             SkipHighLight   = TRUE;
2702           }
2703 
2704           if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
2705             Status = DisplayOneMenu (MenuOption,
2706                             MenuOption->Col - gStatementDimensions.LeftColumn,
2707                             gStatementDimensions.LeftColumn + gModalSkipColumn,
2708                             Link == TopOfScreen ? SkipValue : 0,
2709                             BottomRow,
2710                             (BOOLEAN) ((Link == NewPos) && IsSelectable(MenuOption)),
2711                             TRUE
2712                             );
2713           } else {
2714             Status = DisplayOneMenu (MenuOption,
2715                             MenuOption->Col - gStatementDimensions.LeftColumn,
2716                             gStatementDimensions.LeftColumn,
2717                             Link == TopOfScreen ? SkipValue : 0,
2718                             BottomRow,
2719                             (BOOLEAN) ((Link == NewPos) && IsSelectable(MenuOption)),
2720                             TRUE
2721                             );
2722           }
2723 
2724           if (EFI_ERROR (Status)) {
2725             if (gMisMatch) {
2726               return EFI_SUCCESS;
2727             } else {
2728               return Status;
2729             }
2730           }
2731           //
2732           // 3. Update the row info which will be used by next menu.
2733           //
2734           if (Link == TopOfScreen) {
2735             Row += MenuOption->Skip - SkipValue;
2736           } else {
2737             Row += MenuOption->Skip;
2738           }
2739 
2740           if (Row > BottomRow) {
2741             if (!ValueIsScroll (FALSE, Link)) {
2742               DownArrow = TRUE;
2743             }
2744 
2745             Row = BottomRow + 1;
2746             break;
2747           }
2748         }
2749 
2750         //
2751         // 3. Menus in this form may not cover all form, clean the remain field.
2752         //
2753         while (Row <= BottomRow) {
2754           if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
2755             PrintStringAtWithWidth(gStatementDimensions.LeftColumn + gModalSkipColumn, Row++, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 * gModalSkipColumn);
2756           } else {
2757             PrintStringAtWithWidth(gStatementDimensions.LeftColumn, Row++, L"", gStatementDimensions.RightColumn - gHelpBlockWidth - gStatementDimensions.LeftColumn);
2758           }
2759         }
2760 
2761         //
2762         // 4. Print the down arrow row.
2763         //
2764         if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
2765           PrintStringAtWithWidth(gStatementDimensions.LeftColumn + gModalSkipColumn, BottomRow + 1, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 *  + gModalSkipColumn);
2766         } else {
2767           PrintStringAtWithWidth(gStatementDimensions.LeftColumn, BottomRow + 1, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn);
2768         }
2769         if (DownArrow) {
2770           gST->ConOut->SetAttribute (gST->ConOut, GetArrowColor ());
2771           PrintCharAt (
2772             gStatementDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
2773             BottomRow + SCROLL_ARROW_HEIGHT,
2774             ARROW_DOWN
2775             );
2776           gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
2777         }
2778 
2779         MenuOption = NULL;
2780       }
2781       break;
2782 
2783     case CfRefreshHighLight:
2784 
2785       //
2786       // MenuOption: Last menu option that need to remove hilight
2787       //             MenuOption is set to NULL in Repaint
2788       // NewPos:     Current menu option that need to hilight
2789       //
2790       ControlFlag = CfUpdateHelpString;
2791 
2792       UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue);
2793 
2794       if (SkipHighLight) {
2795         SkipHighLight = FALSE;
2796         MenuOption    = SavedMenuOption;
2797         RefreshKeyHelp(gFormData, SavedMenuOption->ThisTag, FALSE);
2798         break;
2799       }
2800 
2801       if (IsListEmpty (&gMenuOption)) {
2802         //
2803         // No menu option, just update the hotkey filed.
2804         //
2805         RefreshKeyHelp(gFormData, NULL, FALSE);
2806         break;
2807       }
2808 
2809       if (MenuOption != NULL && TopOfScreen == &MenuOption->Link) {
2810         Temp = SkipValue;
2811       } else {
2812         Temp = 0;
2813       }
2814       if (NewPos == TopOfScreen) {
2815         Temp2 = SkipValue;
2816       } else {
2817         Temp2 = 0;
2818       }
2819 
2820       if (NewPos != NULL && (MenuOption == NULL || NewPos != &MenuOption->Link)) {
2821         if (MenuOption != NULL) {
2822           //
2823           // Remove the old highlight menu.
2824           //
2825           Status = DisplayOneMenu (MenuOption,
2826                           MenuOption->Col - gStatementDimensions.LeftColumn,
2827                           gStatementDimensions.LeftColumn,
2828                           Temp,
2829                           BottomRow,
2830                           FALSE,
2831                           FALSE
2832                           );
2833         }
2834 
2835         //
2836         // This is the current selected statement
2837         //
2838         MenuOption = MENU_OPTION_FROM_LINK (NewPos);
2839         RefreshKeyHelp(gFormData, MenuOption->ThisTag, FALSE);
2840 
2841         if (!IsSelectable (MenuOption)) {
2842           break;
2843         }
2844 
2845         Status = DisplayOneMenu (MenuOption,
2846                         MenuOption->Col - gStatementDimensions.LeftColumn,
2847                         gStatementDimensions.LeftColumn,
2848                         Temp2,
2849                         BottomRow,
2850                         TRUE,
2851                         FALSE
2852                         );
2853       }
2854       break;
2855 
2856     case CfUpdateHelpString:
2857       ControlFlag = CfPrepareToReadKey;
2858       if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
2859         break;
2860       }
2861 
2862       //
2863       // NewLine means only update highlight menu (remove old highlight and highlith
2864       // the new one), not need to full repain the form.
2865       //
2866       if (Repaint || NewLine) {
2867         if (IsListEmpty (&gMenuOption)) {
2868           //
2869           // Don't print anything if no mwnu option.
2870           //
2871           StringPtr = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle);
2872         } else {
2873           //
2874           // Don't print anything if it is a NULL help token
2875           //
2876           ASSERT(MenuOption != NULL);
2877           HelpInfo = ((EFI_IFR_STATEMENT_HEADER *) ((CHAR8 *)MenuOption->ThisTag->OpCode + sizeof (EFI_IFR_OP_HEADER)))->Help;
2878           if (HelpInfo == 0 || !IsSelectable (MenuOption)) {
2879             StringPtr = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle);
2880           } else {
2881             StringPtr = GetToken (HelpInfo, gFormData->HiiHandle);
2882           }
2883         }
2884 
2885         RowCount      = BottomRow - TopRow + 1;
2886         HelpPageIndex = 0;
2887         //
2888         // 1.Calculate how many line the help string need to print.
2889         //
2890         if (HelpString != NULL) {
2891           FreePool (HelpString);
2892           HelpString = NULL;
2893         }
2894         HelpLine = ProcessHelpString (StringPtr, &HelpString, &EachLineWidth, RowCount);
2895         FreePool (StringPtr);
2896 
2897         if (HelpLine > RowCount) {
2898           MultiHelpPage   = TRUE;
2899           StringPtr       = GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_UP), gHiiHandle);
2900           if (HelpHeaderString != NULL) {
2901             FreePool (HelpHeaderString);
2902             HelpHeaderString = NULL;
2903           }
2904           HelpHeaderLine  = ProcessHelpString (StringPtr, &HelpHeaderString, &HeaderLineWidth, 0);
2905           FreePool (StringPtr);
2906           StringPtr       = GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_DOWN), gHiiHandle);
2907           if (HelpBottomString != NULL) {
2908             FreePool (HelpBottomString);
2909             HelpBottomString = NULL;
2910           }
2911           HelpBottomLine  = ProcessHelpString (StringPtr, &HelpBottomString, &BottomLineWidth, 0);
2912           FreePool (StringPtr);
2913           //
2914           // Calculate the help page count.
2915           //
2916           if (HelpLine > 2 * RowCount - 2) {
2917             HelpPageCount = (HelpLine - RowCount + 1) / (RowCount - 2) + 1;
2918             if ((HelpLine - RowCount + 1) % (RowCount - 2) > 1) {
2919               HelpPageCount += 1;
2920             }
2921           } else {
2922             HelpPageCount = 2;
2923           }
2924         } else {
2925           MultiHelpPage = FALSE;
2926         }
2927       }
2928 
2929       //
2930       // Check whether need to show the 'More(U/u)' at the begin.
2931       // Base on current direct info, here shows aligned to the right side of the column.
2932       // If the direction is multi line and aligned to right side may have problem, so
2933       // add ASSERT code here.
2934       //
2935       if (HelpPageIndex > 0) {
2936         gST->ConOut->SetAttribute (gST->ConOut, GetInfoTextColor ());
2937         for (Index = 0; Index < HelpHeaderLine; Index++) {
2938           ASSERT (HelpHeaderLine == 1);
2939           ASSERT (GetStringWidth (HelpHeaderString) / 2 < (UINTN) (gHelpBlockWidth - 1));
2940           PrintStringAtWithWidth (
2941             gStatementDimensions.RightColumn - gHelpBlockWidth,
2942             Index + TopRow,
2943             gEmptyString,
2944             gHelpBlockWidth
2945             );
2946           PrintStringAt (
2947             gStatementDimensions.RightColumn - GetStringWidth (HelpHeaderString) / 2 - 1,
2948             Index + TopRow,
2949             &HelpHeaderString[Index * HeaderLineWidth]
2950             );
2951         }
2952       }
2953 
2954       gST->ConOut->SetAttribute (gST->ConOut, GetHelpTextColor ());
2955       //
2956       // Print the help string info.
2957       //
2958       if (!MultiHelpPage) {
2959         for (Index = 0; Index < HelpLine; Index++) {
2960           PrintStringAtWithWidth (
2961             gStatementDimensions.RightColumn - gHelpBlockWidth,
2962             Index + TopRow,
2963             &HelpString[Index * EachLineWidth],
2964             gHelpBlockWidth
2965             );
2966         }
2967         for (; Index < RowCount; Index ++) {
2968           PrintStringAtWithWidth (
2969             gStatementDimensions.RightColumn - gHelpBlockWidth,
2970             Index + TopRow,
2971             gEmptyString,
2972             gHelpBlockWidth
2973             );
2974         }
2975         gST->ConOut->SetCursorPosition(gST->ConOut, gStatementDimensions.RightColumn-1, BottomRow);
2976       } else  {
2977         if (HelpPageIndex == 0) {
2978           for (Index = 0; Index < RowCount - HelpBottomLine; Index++) {
2979             PrintStringAtWithWidth (
2980               gStatementDimensions.RightColumn - gHelpBlockWidth,
2981               Index + TopRow,
2982               &HelpString[Index * EachLineWidth],
2983               gHelpBlockWidth
2984               );
2985           }
2986         } else {
2987           for (Index = 0; (Index < RowCount - HelpBottomLine - HelpHeaderLine) &&
2988               (Index + HelpPageIndex * (RowCount - 2) + 1 < HelpLine); Index++) {
2989             PrintStringAtWithWidth (
2990               gStatementDimensions.RightColumn - gHelpBlockWidth,
2991               Index + TopRow + HelpHeaderLine,
2992               &HelpString[(Index + HelpPageIndex * (RowCount - 2) + 1)* EachLineWidth],
2993               gHelpBlockWidth
2994               );
2995           }
2996           if (HelpPageIndex == HelpPageCount - 1) {
2997             for (; Index < RowCount - HelpHeaderLine; Index ++) {
2998               PrintStringAtWithWidth (
2999                 gStatementDimensions.RightColumn - gHelpBlockWidth,
3000                 Index + TopRow + HelpHeaderLine,
3001                 gEmptyString,
3002                 gHelpBlockWidth
3003                 );
3004             }
3005             gST->ConOut->SetCursorPosition(gST->ConOut, gStatementDimensions.RightColumn-1, BottomRow);
3006           }
3007         }
3008       }
3009 
3010       //
3011       // Check whether need to print the 'More(D/d)' at the bottom.
3012       // Base on current direct info, here shows aligned to the right side of the column.
3013       // If the direction is multi line and aligned to right side may have problem, so
3014       // add ASSERT code here.
3015       //
3016       if (HelpPageIndex < HelpPageCount - 1 && MultiHelpPage) {
3017         gST->ConOut->SetAttribute (gST->ConOut, GetInfoTextColor ());
3018         for (Index = 0; Index < HelpBottomLine; Index++) {
3019           ASSERT (HelpBottomLine == 1);
3020           ASSERT (GetStringWidth (HelpBottomString) / 2 < (UINTN) (gHelpBlockWidth - 1));
3021           PrintStringAtWithWidth (
3022             gStatementDimensions.RightColumn - gHelpBlockWidth,
3023             BottomRow + Index - HelpBottomLine + 1,
3024             gEmptyString,
3025             gHelpBlockWidth
3026             );
3027           PrintStringAt (
3028             gStatementDimensions.RightColumn - GetStringWidth (HelpBottomString) / 2 - 1,
3029             BottomRow + Index - HelpBottomLine + 1,
3030             &HelpBottomString[Index * BottomLineWidth]
3031             );
3032         }
3033       }
3034       //
3035       // Reset this flag every time we finish using it.
3036       //
3037       Repaint = FALSE;
3038       NewLine = FALSE;
3039       break;
3040 
3041     case CfPrepareToReadKey:
3042       ControlFlag = CfReadKey;
3043       ScreenOperation = UiNoOperation;
3044       break;
3045 
3046     case CfReadKey:
3047       ControlFlag = CfScreenOperation;
3048 
3049       //
3050       // Wait for user's selection
3051       //
3052       while (TRUE) {
3053         Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
3054         if (!EFI_ERROR (Status)) {
3055           EventType = UIEventKey;
3056           break;
3057         }
3058 
3059         //
3060         // If we encounter error, continue to read another key in.
3061         //
3062         if (Status != EFI_NOT_READY) {
3063           continue;
3064         }
3065 
3066         EventType = UiWaitForEvent(gST->ConIn->WaitForKey);
3067         if (EventType == UIEventKey) {
3068           gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
3069         }
3070         break;
3071       }
3072 
3073       if (EventType == UIEventDriver) {
3074         gMisMatch = TRUE;
3075         gUserInput->Action = BROWSER_ACTION_NONE;
3076         ControlFlag = CfExit;
3077         break;
3078       }
3079 
3080       if (EventType == UIEventTimeOut) {
3081         gUserInput->Action = BROWSER_ACTION_FORM_EXIT;
3082         ControlFlag = CfExit;
3083         break;
3084       }
3085 
3086       switch (Key.UnicodeChar) {
3087       case CHAR_CARRIAGE_RETURN:
3088         if(MenuOption == NULL || MenuOption->GrayOut || MenuOption->ReadOnly) {
3089           ControlFlag = CfReadKey;
3090           break;
3091         }
3092 
3093         ScreenOperation = UiSelect;
3094         gDirection      = 0;
3095         break;
3096 
3097       //
3098       // We will push the adjustment of these numeric values directly to the input handler
3099       //  NOTE: we won't handle manual input numeric
3100       //
3101       case '+':
3102       case '-':
3103         //
3104         // If the screen has no menu items, and the user didn't select UiReset
3105         // ignore the selection and go back to reading keys.
3106         //
3107         ASSERT(MenuOption != NULL);
3108         if(IsListEmpty (&gMenuOption) || MenuOption->GrayOut || MenuOption->ReadOnly) {
3109           ControlFlag = CfReadKey;
3110           break;
3111         }
3112 
3113         Statement = MenuOption->ThisTag;
3114         if ((Statement->OpCode->OpCode == EFI_IFR_DATE_OP)
3115           || (Statement->OpCode->OpCode == EFI_IFR_TIME_OP)
3116           || ((Statement->OpCode->OpCode == EFI_IFR_NUMERIC_OP) && (GetFieldFromNum(Statement->OpCode) != 0))
3117         ){
3118           if (Key.UnicodeChar == '+') {
3119             gDirection = SCAN_RIGHT;
3120           } else {
3121             gDirection = SCAN_LEFT;
3122           }
3123 
3124           Status = ProcessOptions (MenuOption, TRUE, &OptionString, TRUE);
3125           if (OptionString != NULL) {
3126             FreePool (OptionString);
3127           }
3128           if (EFI_ERROR (Status)) {
3129             //
3130             // Repaint to clear possible error prompt pop-up
3131             //
3132             Repaint = TRUE;
3133             NewLine = TRUE;
3134           } else {
3135             ControlFlag = CfExit;
3136           }
3137         }
3138         break;
3139 
3140       case '^':
3141         ScreenOperation = UiUp;
3142         break;
3143 
3144       case 'V':
3145       case 'v':
3146         ScreenOperation = UiDown;
3147         break;
3148 
3149       case ' ':
3150         if(IsListEmpty (&gMenuOption)) {
3151           ControlFlag = CfReadKey;
3152           break;
3153         }
3154 
3155         ASSERT(MenuOption != NULL);
3156         if (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_CHECKBOX_OP && !MenuOption->GrayOut && !MenuOption->ReadOnly) {
3157           ScreenOperation = UiSelect;
3158         }
3159         break;
3160 
3161       case 'D':
3162       case 'd':
3163         if (!MultiHelpPage) {
3164           ControlFlag = CfReadKey;
3165           break;
3166         }
3167         ControlFlag    = CfUpdateHelpString;
3168         HelpPageIndex  = HelpPageIndex < HelpPageCount - 1 ? HelpPageIndex + 1 : HelpPageCount - 1;
3169         break;
3170 
3171       case 'U':
3172       case 'u':
3173         if (!MultiHelpPage) {
3174           ControlFlag = CfReadKey;
3175           break;
3176         }
3177         ControlFlag    = CfUpdateHelpString;
3178         HelpPageIndex  = HelpPageIndex > 0 ? HelpPageIndex - 1 : 0;
3179         break;
3180 
3181       case CHAR_NULL:
3182         for (Index = 0; Index < mScanCodeNumber; Index++) {
3183           if (Key.ScanCode == gScanCodeToOperation[Index].ScanCode) {
3184             ScreenOperation = gScanCodeToOperation[Index].ScreenOperation;
3185             break;
3186           }
3187         }
3188 
3189         if (((FormData->Attribute & HII_DISPLAY_MODAL) != 0) && (Key.ScanCode == SCAN_ESC || Index == mScanCodeNumber)) {
3190           //
3191           // ModalForm has no ESC key and Hot Key.
3192           //
3193           ControlFlag = CfReadKey;
3194         } else if (Index == mScanCodeNumber) {
3195           //
3196           // Check whether Key matches the registered hot key.
3197           //
3198           HotKey = NULL;
3199           HotKey = GetHotKeyFromRegisterList (&Key);
3200           if (HotKey != NULL) {
3201             ScreenOperation = UiHotKey;
3202           }
3203         }
3204         break;
3205       }
3206       break;
3207 
3208     case CfScreenOperation:
3209       if ((ScreenOperation != UiReset) && (ScreenOperation != UiHotKey)) {
3210         //
3211         // If the screen has no menu items, and the user didn't select UiReset or UiHotKey
3212         // ignore the selection and go back to reading keys.
3213         //
3214         if (IsListEmpty (&gMenuOption)) {
3215           ControlFlag = CfReadKey;
3216           break;
3217         }
3218       }
3219 
3220       for (Index = 0;
3221            Index < sizeof (gScreenOperationToControlFlag) / sizeof (gScreenOperationToControlFlag[0]);
3222            Index++
3223           ) {
3224         if (ScreenOperation == gScreenOperationToControlFlag[Index].ScreenOperation) {
3225           ControlFlag = gScreenOperationToControlFlag[Index].ControlFlag;
3226           break;
3227         }
3228       }
3229       break;
3230 
3231     case CfUiSelect:
3232       ControlFlag = CfRepaint;
3233 
3234       ASSERT(MenuOption != NULL);
3235       Statement = MenuOption->ThisTag;
3236       if (Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) {
3237         break;
3238       }
3239 
3240       switch (Statement->OpCode->OpCode) {
3241       case EFI_IFR_REF_OP:
3242       case EFI_IFR_ACTION_OP:
3243       case EFI_IFR_RESET_BUTTON_OP:
3244         ControlFlag = CfExit;
3245         break;
3246 
3247       default:
3248         //
3249         // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
3250         //
3251         RefreshKeyHelp (gFormData, Statement, TRUE);
3252         Status = ProcessOptions (MenuOption, TRUE, &OptionString, TRUE);
3253 
3254         if (OptionString != NULL) {
3255           FreePool (OptionString);
3256         }
3257 
3258         if (EFI_ERROR (Status)) {
3259           Repaint = TRUE;
3260           NewLine = TRUE;
3261           RefreshKeyHelp (gFormData, Statement, FALSE);
3262           break;
3263         } else {
3264           ControlFlag = CfExit;
3265           break;
3266         }
3267       }
3268       break;
3269 
3270     case CfUiReset:
3271       //
3272       // We come here when someone press ESC
3273       // If the policy is not exit front page when user press ESC, process here.
3274       //
3275       if (!FormExitPolicy()) {
3276         Repaint     = TRUE;
3277         NewLine     = TRUE;
3278         ControlFlag = CfRepaint;
3279         break;
3280       }
3281 
3282       gUserInput->Action = BROWSER_ACTION_FORM_EXIT;
3283       ControlFlag = CfExit;
3284       break;
3285 
3286     case CfUiHotKey:
3287       ControlFlag = CfRepaint;
3288 
3289       ASSERT (HotKey != NULL);
3290 
3291       if (FxConfirmPopup(HotKey->Action)) {
3292         gUserInput->Action = HotKey->Action;
3293         if ((HotKey->Action & BROWSER_ACTION_DEFAULT) == BROWSER_ACTION_DEFAULT) {
3294           gUserInput->DefaultId = HotKey->DefaultId;
3295         }
3296         ControlFlag = CfExit;
3297       } else {
3298         Repaint     = TRUE;
3299         NewLine     = TRUE;
3300         ControlFlag = CfRepaint;
3301       }
3302 
3303       break;
3304 
3305     case CfUiLeft:
3306       ControlFlag = CfRepaint;
3307       ASSERT(MenuOption != NULL);
3308       if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TIME_OP)) {
3309         if (MenuOption->Sequence != 0) {
3310           //
3311           // In the middle or tail of the Date/Time op-code set, go left.
3312           //
3313           ASSERT(NewPos != NULL);
3314           NewPos = NewPos->BackLink;
3315         }
3316       }
3317       break;
3318 
3319     case CfUiRight:
3320       ControlFlag = CfRepaint;
3321       ASSERT(MenuOption != NULL);
3322       if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TIME_OP)) {
3323         if (MenuOption->Sequence != 2) {
3324           //
3325           // In the middle or tail of the Date/Time op-code set, go left.
3326           //
3327           ASSERT(NewPos != NULL);
3328           NewPos = NewPos->ForwardLink;
3329         }
3330       }
3331       break;
3332 
3333     case CfUiUp:
3334       ControlFlag = CfRepaint;
3335       NewLine     = TRUE;
3336 
3337       SavedListEntry = NewPos;
3338       ASSERT(NewPos != NULL);
3339 
3340       MenuOption = MENU_OPTION_FROM_LINK (NewPos);
3341       ASSERT (MenuOption != NULL);
3342 
3343       //
3344       // Adjust Date/Time position before we advance forward.
3345       //
3346       AdjustDateAndTimePosition (TRUE, &NewPos);
3347 
3348       NewPos     = NewPos->BackLink;
3349       //
3350       // Find next selectable menu or the first menu beyond current form.
3351       //
3352       Difference = MoveToNextStatement (TRUE, &NewPos, MenuOption->Row - TopRow, FALSE);
3353       if (Difference < 0) {
3354         //
3355         // We hit the begining MenuOption that can be focused
3356         // so we simply scroll to the top.
3357         //
3358         Repaint     = TRUE;
3359         if (TopOfScreen != gMenuOption.ForwardLink || SkipValue != 0) {
3360           TopOfScreen = gMenuOption.ForwardLink;
3361           NewPos      = SavedListEntry;
3362           SkipValue = 0;
3363         } else {
3364           //
3365           // Scroll up to the last page when we have arrived at top page.
3366           //
3367           TopOfScreen = FindTopOfScreenMenu (gMenuOption.BackLink, BottomRow - TopRow, &SkipValue);
3368           NewPos = gMenuOption.BackLink;
3369           MoveToNextStatement (TRUE, &NewPos, BottomRow - TopRow, TRUE);
3370         }
3371       } else {
3372         NextMenuOption = MENU_OPTION_FROM_LINK (NewPos);
3373 
3374         if (MenuOption->Row < TopRow + Difference + NextMenuOption->Skip) {
3375           //
3376           // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
3377           //
3378           TopOfScreen = NewPos;
3379           Repaint     = TRUE;
3380           SkipValue   = 0;
3381         }
3382 
3383         //
3384         // Check whether new highlight menu is selectable, if not, keep highlight on the old one.
3385         //
3386         // BottomRow - TopRow + 1 means the total rows current forms supported.
3387         // Difference + NextMenuOption->Skip + 1 means the distance between last highlight menu
3388         // and new top menu. New top menu will all shows in next form, but last highlight menu
3389         // may only shows 1 line. + 1 at right part means at least need to keep 1 line for the
3390         // last highlight menu.
3391         //
3392         if (!IsSelectable(NextMenuOption) && IsSelectable(MenuOption) &&
3393             (BottomRow - TopRow + 1 >= Difference + NextMenuOption->Skip + 1)) {
3394           NewPos = SavedListEntry;
3395         }
3396       }
3397 
3398       UpdateStatusBar (INPUT_ERROR, FALSE);
3399 
3400       //
3401       // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3402       //
3403       AdjustDateAndTimePosition (TRUE, &TopOfScreen);
3404       AdjustDateAndTimePosition (TRUE, &NewPos);
3405 
3406       UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue);
3407       break;
3408 
3409     case CfUiPageUp:
3410       //
3411       // SkipValue means lines is skipped when show the top menu option.
3412       //
3413       ControlFlag = CfRepaint;
3414       NewLine     = TRUE;
3415       Repaint     = TRUE;
3416 
3417       Link      = TopOfScreen;
3418       //
3419       // First minus the menu of the top screen, it's value is SkipValue.
3420       //
3421       if (SkipValue >= BottomRow - TopRow + 1) {
3422         //
3423         // SkipValue > (BottomRow - TopRow + 1) means current menu has more than one
3424         // form of options to be show, so just update the SkipValue to show the next
3425         // parts of options.
3426         //
3427         SkipValue -= BottomRow - TopRow + 1;
3428         NewPos     = TopOfScreen;
3429         break;
3430       } else {
3431         Index     = (BottomRow + 1) - SkipValue - TopRow;
3432       }
3433 
3434       TopOfScreen = FindTopOfScreenMenu(TopOfScreen, Index, &SkipValue);
3435       NewPos = TopOfScreen;
3436       MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow, FALSE);
3437 
3438       UpdateStatusBar (INPUT_ERROR, FALSE);
3439 
3440       //
3441       // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3442       // Don't do this when we are already in the first page.
3443       //
3444       AdjustDateAndTimePosition (TRUE, &TopOfScreen);
3445       AdjustDateAndTimePosition (TRUE, &NewPos);
3446 
3447       UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue);
3448       break;
3449 
3450     case CfUiPageDown:
3451       //
3452       // SkipValue means lines is skipped when show the top menu option.
3453       //
3454       ControlFlag = CfRepaint;
3455       NewLine     = TRUE;
3456       Repaint     = TRUE;
3457 
3458       Link    = TopOfScreen;
3459       NextMenuOption = MENU_OPTION_FROM_LINK (Link);
3460       Index = TopRow + NextMenuOption->Skip - SkipValue;
3461       //
3462       // Count to the menu option which will show at the top of the next form.
3463       //
3464       while ((Index <= BottomRow + 1) && (Link->ForwardLink != &gMenuOption)) {
3465         Link           = Link->ForwardLink;
3466         NextMenuOption = MENU_OPTION_FROM_LINK (Link);
3467         Index = Index + NextMenuOption->Skip;
3468       }
3469 
3470       if ((Link->ForwardLink == &gMenuOption) && (Index <= BottomRow + 1)) {
3471         //
3472         // Highlight on the last menu which can be highlight.
3473         //
3474         Repaint = FALSE;
3475         MoveToNextStatement (TRUE, &Link, Index - TopRow, TRUE);
3476       } else {
3477         //
3478         // Calculate the skip line for top of screen menu.
3479         //
3480         if (Link == TopOfScreen) {
3481           //
3482           // The top of screen menu option occupies the entire form.
3483           //
3484           SkipValue += BottomRow - TopRow + 1;
3485         } else {
3486           SkipValue = NextMenuOption->Skip - (Index - (BottomRow + 1));
3487         }
3488         TopOfScreen = Link;
3489         MenuOption = NULL;
3490         //
3491         // Move to the Next selectable menu.
3492         //
3493         MoveToNextStatement (FALSE, &Link, BottomRow - TopRow, TRUE);
3494       }
3495 
3496       //
3497       // Save the menu as the next highlight menu.
3498       //
3499       NewPos  = Link;
3500 
3501       UpdateStatusBar (INPUT_ERROR, FALSE);
3502 
3503       //
3504       // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3505       // Don't do this when we are already in the last page.
3506       //
3507       AdjustDateAndTimePosition (TRUE, &TopOfScreen);
3508       AdjustDateAndTimePosition (TRUE, &NewPos);
3509 
3510       UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue);
3511       break;
3512 
3513     case CfUiDown:
3514       //
3515       // SkipValue means lines is skipped when show the top menu option.
3516       // NewPos  points to the menu which is highlighted now.
3517       //
3518       ControlFlag = CfRepaint;
3519       NewLine     = TRUE;
3520 
3521       if (NewPos == TopOfScreen) {
3522         Temp2 = SkipValue;
3523       } else {
3524         Temp2 = 0;
3525       }
3526 
3527       SavedListEntry = NewPos;
3528       //
3529       // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
3530       // to be one that progresses to the next set of op-codes, we need to advance to the last
3531       // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
3532       // checking can be done.  The only other logic we need to introduce is that if a Date/Time
3533       // op-code is the last entry in the menu, we need to rewind back to the first op-code of
3534       // the Date/Time op-code.
3535       //
3536       AdjustDateAndTimePosition (FALSE, &NewPos);
3537 
3538       MenuOption = MENU_OPTION_FROM_LINK (NewPos);
3539       NewPos     = NewPos->ForwardLink;
3540       //
3541       // Find the next selectable menu.
3542       //
3543       if (MenuOption->Row + MenuOption->Skip - Temp2 > BottomRow + 1) {
3544         if (gMenuOption.ForwardLink == NewPos || &gMenuOption == NewPos) {
3545           Difference = -1;
3546         } else {
3547           Difference = 0;
3548         }
3549       } else {
3550         Difference = MoveToNextStatement (FALSE, &NewPos, BottomRow + 1 - (MenuOption->Row + MenuOption->Skip - Temp2), FALSE);
3551       }
3552       if (Difference < 0) {
3553         //
3554         // Scroll to the first page.
3555         //
3556         if (TopOfScreen != gMenuOption.ForwardLink || SkipValue != 0) {
3557           TopOfScreen = gMenuOption.ForwardLink;
3558           Repaint     = TRUE;
3559           MenuOption  = NULL;
3560         } else {
3561           MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
3562         }
3563         NewPos        = gMenuOption.ForwardLink;
3564         MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow, TRUE);
3565 
3566         SkipValue = 0;
3567         //
3568         // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3569         //
3570         AdjustDateAndTimePosition (TRUE, &TopOfScreen);
3571         AdjustDateAndTimePosition (TRUE, &NewPos);
3572 
3573         UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue);
3574         break;
3575       }
3576 
3577       //
3578       // Get next selected menu info.
3579       //
3580       AdjustDateAndTimePosition (FALSE, &NewPos);
3581       NextMenuOption  = MENU_OPTION_FROM_LINK (NewPos);
3582       if (NextMenuOption->Row == 0) {
3583         UpdateOptionSkipLines (NextMenuOption);
3584       }
3585 
3586       //
3587       // Calculate new highlight menu end row.
3588       //
3589       Temp = (MenuOption->Row + MenuOption->Skip - Temp2) + Difference + NextMenuOption->Skip - 1;
3590       if (Temp > BottomRow) {
3591         //
3592         // Get the top screen menu info.
3593         //
3594         AdjustDateAndTimePosition (FALSE, &TopOfScreen);
3595         SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
3596 
3597         //
3598         // Current Top screen menu occupy (SavedMenuOption->Skip - SkipValue) rows.
3599         // Full shows the new selected menu need to skip (Temp - BottomRow - 1) rows.
3600         //
3601         if ((Temp - BottomRow) >= (SavedMenuOption->Skip - SkipValue)) {
3602           //
3603           // Skip the top op-code
3604           //
3605           TopOfScreen   = TopOfScreen->ForwardLink;
3606           DistanceValue = (Temp - BottomRow) - (SavedMenuOption->Skip - SkipValue);
3607 
3608           SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
3609 
3610           //
3611           // If we have a remainder, skip that many more op-codes until we drain the remainder
3612           // Special case is the selected highlight menu has more than one form of menus.
3613           //
3614           while (DistanceValue >= SavedMenuOption->Skip && TopOfScreen != NewPos) {
3615             //
3616             // Since the Difference is greater than or equal to this op-code's skip value, skip it
3617             //
3618             DistanceValue   = DistanceValue - (INTN) SavedMenuOption->Skip;
3619             TopOfScreen     = TopOfScreen->ForwardLink;
3620             SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
3621           }
3622           //
3623           // Since we will act on this op-code in the next routine, and increment the
3624           // SkipValue, set the skips to one less than what is required.
3625           //
3626           if (TopOfScreen != NewPos) {
3627             SkipValue = DistanceValue;
3628           } else {
3629             SkipValue = 0;
3630           }
3631         } else {
3632           //
3633           // Since we will act on this op-code in the next routine, and increment the
3634           // SkipValue, set the skips to one less than what is required.
3635           //
3636           SkipValue += Temp - BottomRow;
3637         }
3638         Repaint       = TRUE;
3639       } else if (!IsSelectable (NextMenuOption)) {
3640         //
3641         // Continue to go down until scroll to next page or the selectable option is found.
3642         //
3643         ScreenOperation = UiDown;
3644         ControlFlag     = CfScreenOperation;
3645         break;
3646       }
3647 
3648       MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
3649 
3650       //
3651       // Check whether new highlight menu is selectable, if not, keep highlight on the old one.
3652       //
3653       // BottomRow - TopRow + 1 means the total rows current forms supported.
3654       // Difference + NextMenuOption->Skip + 1 means the distance between last highlight menu
3655       // and new top menu. New top menu will all shows in next form, but last highlight menu
3656       // may only shows 1 line. + 1 at right part means at least need to keep 1 line for the
3657       // last highlight menu.
3658       //
3659       if (!IsSelectable (NextMenuOption) && IsSelectable (MenuOption) &&
3660          (BottomRow - TopRow + 1 >= Difference + NextMenuOption->Skip + 1)) {
3661         NewPos = SavedListEntry;
3662       }
3663 
3664       UpdateStatusBar (INPUT_ERROR, FALSE);
3665 
3666       //
3667       // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3668       //
3669       AdjustDateAndTimePosition (TRUE, &TopOfScreen);
3670       AdjustDateAndTimePosition (TRUE, &NewPos);
3671 
3672       UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue);
3673       break;
3674 
3675     case CfUiNoOperation:
3676       ControlFlag = CfRepaint;
3677       break;
3678 
3679     case CfExit:
3680       gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
3681       if (HelpString != NULL) {
3682         FreePool (HelpString);
3683       }
3684       if (HelpHeaderString != NULL) {
3685         FreePool (HelpHeaderString);
3686       }
3687       if (HelpBottomString != NULL) {
3688         FreePool (HelpBottomString);
3689       }
3690       return EFI_SUCCESS;
3691 
3692     default:
3693       break;
3694     }
3695   }
3696 }
3697 
3698 /**
3699 
3700   Base on the browser status info to show an pop up message.
3701 
3702 **/
3703 VOID
BrowserStatusProcess(VOID)3704 BrowserStatusProcess (
3705   VOID
3706   )
3707 {
3708   CHAR16             *ErrorInfo;
3709   EFI_INPUT_KEY      Key;
3710   EFI_EVENT          WaitList[2];
3711   EFI_EVENT          RefreshIntervalEvent;
3712   EFI_EVENT          TimeOutEvent;
3713   UINT8              TimeOut;
3714   EFI_STATUS         Status;
3715   UINTN              Index;
3716   WARNING_IF_CONTEXT EventContext;
3717   EFI_IFR_OP_HEADER  *OpCodeBuf;
3718   EFI_STRING_ID      StringToken;
3719   CHAR16             DiscardChange;
3720   CHAR16             JumpToFormSet;
3721   CHAR16             *PrintString;
3722 
3723   if (gFormData->BrowserStatus == BROWSER_SUCCESS) {
3724     return;
3725   }
3726 
3727   StringToken          = 0;
3728   TimeOutEvent         = NULL;
3729   RefreshIntervalEvent = NULL;
3730   OpCodeBuf            = NULL;
3731   if (gFormData->HighLightedStatement != NULL) {
3732     OpCodeBuf = gFormData->HighLightedStatement->OpCode;
3733   }
3734 
3735   if (gFormData->BrowserStatus == (BROWSER_WARNING_IF)) {
3736     ASSERT (OpCodeBuf != NULL && OpCodeBuf->OpCode == EFI_IFR_WARNING_IF_OP);
3737 
3738     TimeOut     = ((EFI_IFR_WARNING_IF *) OpCodeBuf)->TimeOut;
3739     StringToken = ((EFI_IFR_WARNING_IF *) OpCodeBuf)->Warning;
3740   } else {
3741     TimeOut = 0;
3742     if ((gFormData->BrowserStatus == (BROWSER_NO_SUBMIT_IF)) &&
3743         (OpCodeBuf != NULL && OpCodeBuf->OpCode == EFI_IFR_NO_SUBMIT_IF_OP)) {
3744       StringToken = ((EFI_IFR_NO_SUBMIT_IF *) OpCodeBuf)->Error;
3745     } else if ((gFormData->BrowserStatus == (BROWSER_INCONSISTENT_IF)) &&
3746                (OpCodeBuf != NULL && OpCodeBuf->OpCode == EFI_IFR_INCONSISTENT_IF_OP)) {
3747       StringToken = ((EFI_IFR_INCONSISTENT_IF *) OpCodeBuf)->Error;
3748     }
3749   }
3750 
3751   if (StringToken != 0) {
3752     ErrorInfo = GetToken (StringToken, gFormData->HiiHandle);
3753   } else if (gFormData->ErrorString != NULL) {
3754     //
3755     // Only used to compatible with old setup browser.
3756     // Not use this field in new browser core.
3757     //
3758     ErrorInfo = gFormData->ErrorString;
3759   } else {
3760     switch (gFormData->BrowserStatus) {
3761     case BROWSER_SUBMIT_FAIL:
3762       ErrorInfo = gSaveFailed;
3763       break;
3764 
3765     case BROWSER_FORM_NOT_FOUND:
3766       ErrorInfo = gFormNotFound;
3767       break;
3768 
3769     case BROWSER_FORM_SUPPRESS:
3770       ErrorInfo = gFormSuppress;
3771       break;
3772 
3773     case BROWSER_PROTOCOL_NOT_FOUND:
3774       ErrorInfo = gProtocolNotFound;
3775       break;
3776 
3777     case BROWSER_SUBMIT_FAIL_NO_SUBMIT_IF:
3778       ErrorInfo = gNoSubmitIfFailed;
3779       break;
3780 
3781     default:
3782       ErrorInfo = gBrwoserError;
3783       break;
3784     }
3785   }
3786 
3787   switch (gFormData->BrowserStatus) {
3788   case BROWSER_SUBMIT_FAIL:
3789   case BROWSER_SUBMIT_FAIL_NO_SUBMIT_IF:
3790     ASSERT (gUserInput != NULL);
3791     if (gFormData->BrowserStatus == (BROWSER_SUBMIT_FAIL)) {
3792       PrintString = gSaveProcess;
3793       JumpToFormSet = gJumpToFormSet[0];
3794     } else {
3795       PrintString = gSaveNoSubmitProcess;
3796       JumpToFormSet = gCheckError[0];
3797     }
3798     DiscardChange = gDiscardChange[0];
3799 
3800     do {
3801       CreateDialog (&Key, gEmptyString, ErrorInfo, PrintString, gEmptyString, NULL);
3802     } while (((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (DiscardChange | UPPER_LOWER_CASE_OFFSET)) &&
3803              ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (JumpToFormSet | UPPER_LOWER_CASE_OFFSET)));
3804 
3805     if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (DiscardChange | UPPER_LOWER_CASE_OFFSET)) {
3806       gUserInput->Action = BROWSER_ACTION_DISCARD;
3807     } else {
3808       gUserInput->Action = BROWSER_ACTION_GOTO;
3809     }
3810     break;
3811 
3812   default:
3813     if (TimeOut == 0) {
3814       do {
3815         CreateDialog (&Key, gEmptyString, ErrorInfo, gPressEnter, gEmptyString, NULL);
3816       } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
3817     } else {
3818       Status = gBS->CreateEvent (EVT_NOTIFY_WAIT, TPL_CALLBACK, EmptyEventProcess, NULL, &TimeOutEvent);
3819       ASSERT_EFI_ERROR (Status);
3820 
3821       EventContext.SyncEvent = TimeOutEvent;
3822       EventContext.TimeOut   = &TimeOut;
3823       EventContext.ErrorInfo = ErrorInfo;
3824 
3825       Status = gBS->CreateEvent (EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, RefreshTimeOutProcess, &EventContext, &RefreshIntervalEvent);
3826       ASSERT_EFI_ERROR (Status);
3827 
3828       //
3829       // Show the dialog first to avoid long time not reaction.
3830       //
3831       gBS->SignalEvent (RefreshIntervalEvent);
3832 
3833       Status = gBS->SetTimer (RefreshIntervalEvent, TimerPeriodic, ONE_SECOND);
3834       ASSERT_EFI_ERROR (Status);
3835 
3836       while (TRUE) {
3837         Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
3838         if (!EFI_ERROR (Status) && Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
3839           break;
3840         }
3841 
3842         if (Status != EFI_NOT_READY) {
3843           continue;
3844         }
3845 
3846         WaitList[0] = TimeOutEvent;
3847         WaitList[1] = gST->ConIn->WaitForKey;
3848 
3849         Status = gBS->WaitForEvent (2, WaitList, &Index);
3850         ASSERT_EFI_ERROR (Status);
3851 
3852         if (Index == 0) {
3853           //
3854           // Timeout occur, close the hoot time out event.
3855           //
3856           break;
3857         }
3858       }
3859 
3860       gBS->CloseEvent (TimeOutEvent);
3861       gBS->CloseEvent (RefreshIntervalEvent);
3862     }
3863     break;
3864   }
3865 
3866   if (StringToken != 0) {
3867     FreePool (ErrorInfo);
3868   }
3869 }
3870 
3871 /**
3872   Display one form, and return user input.
3873 
3874   @param FormData                Form Data to be shown.
3875   @param UserInputData           User input data.
3876 
3877   @retval EFI_SUCCESS            1.Form Data is shown, and user input is got.
3878                                  2.Error info has show and return.
3879   @retval EFI_INVALID_PARAMETER  The input screen dimension is not valid
3880   @retval EFI_NOT_FOUND          New form data has some error.
3881 **/
3882 EFI_STATUS
3883 EFIAPI
FormDisplay(IN FORM_DISPLAY_ENGINE_FORM * FormData,OUT USER_INPUT * UserInputData)3884 FormDisplay (
3885   IN  FORM_DISPLAY_ENGINE_FORM  *FormData,
3886   OUT USER_INPUT                *UserInputData
3887   )
3888 {
3889   EFI_STATUS  Status;
3890 
3891   ASSERT (FormData != NULL);
3892   if (FormData == NULL) {
3893     return EFI_INVALID_PARAMETER;
3894   }
3895 
3896   gUserInput = UserInputData;
3897   gFormData  = FormData;
3898 
3899   //
3900   // Process the status info first.
3901   //
3902   BrowserStatusProcess();
3903   if (gFormData->BrowserStatus != BROWSER_SUCCESS) {
3904     //
3905     // gFormData->BrowserStatus != BROWSER_SUCCESS, means only need to print the error info, return here.
3906     //
3907     return EFI_SUCCESS;
3908   }
3909 
3910   Status = DisplayPageFrame (FormData, &gStatementDimensions);
3911   if (EFI_ERROR (Status)) {
3912     return Status;
3913   }
3914 
3915   //
3916   // Global Widths should be initialized before any MenuOption creation
3917   // or the GetWidth() used in UiAddMenuOption() will return incorrect value.
3918   //
3919   //
3920   //  Left                                              right
3921   //   |<-.->|<-.........->|<- .........->|<-...........->|
3922   //     Skip    Prompt         Option         Help
3923   //
3924   gOptionBlockWidth = (CHAR16) ((gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn) / 3) + 1;
3925   gHelpBlockWidth   = (CHAR16) (gOptionBlockWidth - 1 - LEFT_SKIPPED_COLUMNS);
3926   gPromptBlockWidth = (CHAR16) (gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 * (gOptionBlockWidth - 1) - 1);
3927 
3928   ConvertStatementToMenu();
3929 
3930   //
3931   // Check whether layout is changed.
3932   //
3933   if (mIsFirstForm
3934       || (gOldFormEntry.HiiHandle != FormData->HiiHandle)
3935       || (!CompareGuid (&gOldFormEntry.FormSetGuid, &FormData->FormSetGuid))
3936       || (gOldFormEntry.FormId != FormData->FormId)) {
3937     mStatementLayoutIsChanged = TRUE;
3938   } else {
3939     mStatementLayoutIsChanged = FALSE;
3940   }
3941 
3942   Status = UiDisplayMenu(FormData);
3943 
3944   //
3945   // Backup last form info.
3946   //
3947   mIsFirstForm            = FALSE;
3948   gOldFormEntry.HiiHandle = FormData->HiiHandle;
3949   CopyGuid (&gOldFormEntry.FormSetGuid, &FormData->FormSetGuid);
3950   gOldFormEntry.FormId    = FormData->FormId;
3951 
3952   return Status;
3953 }
3954 
3955 /**
3956   Clear Screen to the initial state.
3957 **/
3958 VOID
3959 EFIAPI
DriverClearDisplayPage(VOID)3960 DriverClearDisplayPage (
3961   VOID
3962   )
3963 {
3964   ClearDisplayPage ();
3965   mIsFirstForm = TRUE;
3966 }
3967 
3968 /**
3969   Set Buffer to Value for Size bytes.
3970 
3971   @param  Buffer                 Memory to set.
3972   @param  Size                   Number of bytes to set
3973   @param  Value                  Value of the set operation.
3974 
3975 **/
3976 VOID
SetUnicodeMem(IN VOID * Buffer,IN UINTN Size,IN CHAR16 Value)3977 SetUnicodeMem (
3978   IN VOID   *Buffer,
3979   IN UINTN  Size,
3980   IN CHAR16 Value
3981   )
3982 {
3983   CHAR16  *Ptr;
3984 
3985   Ptr = Buffer;
3986   while ((Size--)  != 0) {
3987     *(Ptr++) = Value;
3988   }
3989 }
3990 
3991 /**
3992   Initialize Setup Browser driver.
3993 
3994   @param ImageHandle     The image handle.
3995   @param SystemTable     The system table.
3996 
3997   @retval EFI_SUCCESS    The Setup Browser module is initialized correctly..
3998   @return Other value if failed to initialize the Setup Browser module.
3999 
4000 **/
4001 EFI_STATUS
4002 EFIAPI
InitializeDisplayEngine(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)4003 InitializeDisplayEngine (
4004   IN EFI_HANDLE           ImageHandle,
4005   IN EFI_SYSTEM_TABLE     *SystemTable
4006   )
4007 {
4008   EFI_STATUS                          Status;
4009   EFI_INPUT_KEY                       HotKey;
4010   EFI_STRING                          NewString;
4011   EDKII_FORM_BROWSER_EXTENSION2_PROTOCOL *FormBrowserEx2;
4012 
4013   //
4014   // Publish our HII data
4015   //
4016   gHiiHandle = HiiAddPackages (
4017                  &gDisplayEngineGuid,
4018                  ImageHandle,
4019                  DisplayEngineStrings,
4020                  NULL
4021                  );
4022   ASSERT (gHiiHandle != NULL);
4023 
4024   //
4025   // Install Form Display protocol
4026   //
4027   Status = gBS->InstallProtocolInterface (
4028                   &mPrivateData.Handle,
4029                   &gEdkiiFormDisplayEngineProtocolGuid,
4030                   EFI_NATIVE_INTERFACE,
4031                   &mPrivateData.FromDisplayProt
4032                   );
4033   ASSERT_EFI_ERROR (Status);
4034 
4035   InitializeDisplayStrings();
4036 
4037   ZeroMem (&gHighligthMenuInfo, sizeof (gHighligthMenuInfo));
4038   ZeroMem (&gOldFormEntry, sizeof (gOldFormEntry));
4039 
4040   //
4041   // Use BrowserEx2 protocol to register HotKey.
4042   //
4043   Status = gBS->LocateProtocol (&gEdkiiFormBrowserEx2ProtocolGuid, NULL, (VOID **) &FormBrowserEx2);
4044   if (!EFI_ERROR (Status)) {
4045     //
4046     // Register the default HotKey F9 and F10 again.
4047     //
4048     HotKey.UnicodeChar = CHAR_NULL;
4049     HotKey.ScanCode   = SCAN_F10;
4050     NewString         = HiiGetString (gHiiHandle, STRING_TOKEN (FUNCTION_TEN_STRING), NULL);
4051     ASSERT (NewString != NULL);
4052     FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_SUBMIT, 0, NewString);
4053 
4054     HotKey.ScanCode   = SCAN_F9;
4055     NewString         = HiiGetString (gHiiHandle, STRING_TOKEN (FUNCTION_NINE_STRING), NULL);
4056     ASSERT (NewString != NULL);
4057     FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_DEFAULT, EFI_HII_DEFAULT_CLASS_STANDARD, NewString);
4058   }
4059 
4060   return EFI_SUCCESS;
4061 }
4062 
4063 /**
4064   This is the default unload handle for display core drivers.
4065 
4066   @param[in]  ImageHandle       The drivers' driver image.
4067 
4068   @retval EFI_SUCCESS           The image is unloaded.
4069   @retval Others                Failed to unload the image.
4070 
4071 **/
4072 EFI_STATUS
4073 EFIAPI
UnloadDisplayEngine(IN EFI_HANDLE ImageHandle)4074 UnloadDisplayEngine (
4075   IN EFI_HANDLE             ImageHandle
4076   )
4077 {
4078   HiiRemovePackages(gHiiHandle);
4079 
4080   FreeDisplayStrings ();
4081 
4082   if (gHighligthMenuInfo.HLTOpCode != NULL) {
4083     FreePool (gHighligthMenuInfo.HLTOpCode);
4084   }
4085 
4086   if (gHighligthMenuInfo.TOSOpCode != NULL) {
4087     FreePool (gHighligthMenuInfo.TOSOpCode);
4088   }
4089 
4090   return EFI_SUCCESS;
4091 }
4092