Port DriverSample.inf, HiiDatabase.inf and SetupBrowser.inf
[mirror_edk2.git] / IntelFrameworkModulePkg / Universal / SetupBrowserDxe / Ui.c
1 /**@file
2 Implementation for UI.
3
4 Copyright (c) 2006 - 2007, Intel Corporation
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 //
16 // Include common header file for this module.
17 //
18 #include "CommonHeader.h"
19
20 #include "Setup.h"
21 #include "Ui.h"
22 #include "Colors.h"
23
24 //
25 // Implementation
26 //
27 VOID
28 SetUnicodeMem (
29 IN VOID *Buffer,
30 IN UINTN Size,
31 IN CHAR16 Value
32 )
33 /*++
34
35 Routine Description:
36
37 Set Buffer to Value for Size bytes.
38
39 Arguments:
40
41 Buffer - Memory to set.
42
43 Size - Number of bytes to set
44
45 Value - Value of the set operation.
46
47 Returns:
48
49 None
50
51 --*/
52 {
53 CHAR16 *Ptr;
54
55 Ptr = Buffer;
56 while (Size--) {
57 *(Ptr++) = Value;
58 }
59 }
60
61 VOID
62 UiInitMenu (
63 VOID
64 )
65 /*++
66
67 Routine Description:
68 Initialize Menu option list.
69
70 Arguments:
71
72 Returns:
73
74 --*/
75 {
76 InitializeListHead (&Menu);
77 }
78
79 VOID
80 UiInitMenuList (
81 VOID
82 )
83 /*++
84
85 Routine Description:
86 Initialize Menu option list.
87
88 Arguments:
89
90 Returns:
91
92 --*/
93 {
94 InitializeListHead (&gMenuList);
95 }
96
97 VOID
98 UiRemoveMenuListEntry (
99 IN UI_MENU_OPTION *Selection,
100 OUT UI_MENU_OPTION **PreviousSelection
101 )
102 /*++
103
104 Routine Description:
105 Remove Menu option list.
106
107 Arguments:
108
109 Returns:
110
111 --*/
112 {
113 UI_MENU_LIST *UiMenuList;
114
115 *PreviousSelection = AllocateZeroPool (sizeof (UI_MENU_OPTION));
116 ASSERT (*PreviousSelection != NULL);
117
118 if (!IsListEmpty (&gMenuList)) {
119 UiMenuList = CR (gMenuList.ForwardLink, UI_MENU_LIST, MenuLink, UI_MENU_LIST_SIGNATURE);
120 (*PreviousSelection)->IfrNumber = UiMenuList->Selection.IfrNumber;
121 (*PreviousSelection)->FormId = UiMenuList->Selection.FormId;
122 (*PreviousSelection)->Tags = UiMenuList->Selection.Tags;
123 (*PreviousSelection)->ThisTag = UiMenuList->Selection.ThisTag;
124 (*PreviousSelection)->Handle = UiMenuList->Selection.Handle;
125 gEntryNumber = UiMenuList->FormerEntryNumber;
126 RemoveEntryList (&UiMenuList->MenuLink);
127 FreePool (UiMenuList);
128 }
129 }
130
131 VOID
132 UiFreeMenuList (
133 VOID
134 )
135 /*++
136
137 Routine Description:
138 Free Menu option linked list.
139
140 Arguments:
141
142 Returns:
143
144 --*/
145 {
146 UI_MENU_LIST *UiMenuList;
147
148 while (!IsListEmpty (&gMenuList)) {
149 UiMenuList = CR (gMenuList.ForwardLink, UI_MENU_LIST, MenuLink, UI_MENU_LIST_SIGNATURE);
150 RemoveEntryList (&UiMenuList->MenuLink);
151 FreePool (UiMenuList);
152 }
153 }
154
155 VOID
156 UiAddMenuListEntry (
157 IN UI_MENU_OPTION *Selection
158 )
159 /*++
160
161 Routine Description:
162 Add one menu entry to the linked lst
163
164 Arguments:
165
166 Returns:
167
168 --*/
169 {
170 UI_MENU_LIST *UiMenuList;
171
172 UiMenuList = AllocateZeroPool (sizeof (UI_MENU_LIST));
173 ASSERT (UiMenuList != NULL);
174
175 UiMenuList->Signature = UI_MENU_LIST_SIGNATURE;
176 CopyMem (&UiMenuList->Selection, Selection, sizeof (UI_MENU_OPTION));
177
178 InsertHeadList (&gMenuList, &UiMenuList->MenuLink);
179 }
180
181 VOID
182 UiFreeMenu (
183 VOID
184 )
185 /*++
186
187 Routine Description:
188 Free Menu option linked list.
189
190 Arguments:
191
192 Returns:
193
194 --*/
195 {
196 UI_MENU_OPTION *MenuOption;
197
198 while (!IsListEmpty (&Menu)) {
199 MenuOption = CR (Menu.ForwardLink, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
200 RemoveEntryList (&MenuOption->Link);
201
202 //
203 // We allocated space for this description when we did a GetToken, free it here
204 //
205 FreePool (MenuOption->Description);
206 FreePool (MenuOption);
207 }
208 }
209
210 STATIC
211 VOID
212 UpdateDateAndTime (
213 VOID
214 )
215 /*++
216
217 Routine Description:
218 Refresh screen with current date and/or time based on screen context
219
220 Arguments:
221
222 Returns:
223
224 --*/
225 {
226 CHAR16 *OptionString;
227 MENU_REFRESH_ENTRY *MenuRefreshEntry;
228 UINTN Index;
229 UINTN Loop;
230
231 OptionString = NULL;
232
233 if (gMenuRefreshHead != NULL) {
234
235 MenuRefreshEntry = gMenuRefreshHead;
236
237 do {
238 gST->ConOut->SetAttribute (gST->ConOut, MenuRefreshEntry->CurrentAttribute);
239 ProcessOptions (MenuRefreshEntry->MenuOption, FALSE, MenuRefreshEntry->FileFormTagsHead, NULL, &OptionString);
240
241 if (OptionString != NULL) {
242 //
243 // If leading spaces on OptionString - remove the spaces
244 //
245 for (Index = 0; OptionString[Index] == L' '; Index++)
246 ;
247
248 for (Loop = 0; OptionString[Index] != CHAR_NULL; Index++) {
249 OptionString[Loop] = OptionString[Index];
250 Loop++;
251 }
252
253 OptionString[Loop] = CHAR_NULL;
254
255 PrintStringAt (MenuRefreshEntry->CurrentColumn, MenuRefreshEntry->CurrentRow, OptionString);
256 }
257
258 MenuRefreshEntry = MenuRefreshEntry->Next;
259
260 } while (MenuRefreshEntry != NULL);
261 }
262
263 if (OptionString != NULL) {
264 FreePool (OptionString);
265 }
266 }
267
268 EFI_STATUS
269 UiWaitForSingleEvent (
270 IN EFI_EVENT Event,
271 IN UINT64 Timeout OPTIONAL
272 )
273 /*++
274
275 Routine Description:
276 Wait for a given event to fire, or for an optional timeout to expire.
277
278 Arguments:
279 Event - The event to wait for
280
281 Timeout - An optional timeout value in 100 ns units.
282
283 Returns:
284
285 EFI_SUCCESS - Event fired before Timeout expired.
286 EFI_TIME_OUT - Timout expired before Event fired.
287
288 --*/
289 {
290 EFI_STATUS Status;
291 UINTN Index;
292 EFI_EVENT TimerEvent;
293 EFI_EVENT WaitList[2];
294
295 if (Timeout) {
296 //
297 // Create a timer event
298 //
299 Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
300 if (!EFI_ERROR (Status)) {
301 //
302 // Set the timer event
303 //
304 gBS->SetTimer (
305 TimerEvent,
306 TimerRelative,
307 Timeout
308 );
309
310 //
311 // Wait for the original event or the timer
312 //
313 WaitList[0] = Event;
314 WaitList[1] = TimerEvent;
315 Status = gBS->WaitForEvent (2, WaitList, &Index);
316 gBS->CloseEvent (TimerEvent);
317
318 //
319 // If the timer expired, change the return to timed out
320 //
321 if (!EFI_ERROR (Status) && Index == 1) {
322 Status = EFI_TIMEOUT;
323 }
324 }
325 } else {
326 //
327 // Update screen every second
328 //
329 Timeout = ONE_SECOND;
330
331 do {
332 Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
333
334 //
335 // Set the timer event
336 //
337 gBS->SetTimer (
338 TimerEvent,
339 TimerRelative,
340 Timeout
341 );
342
343 //
344 // Wait for the original event or the timer
345 //
346 WaitList[0] = Event;
347 WaitList[1] = TimerEvent;
348 Status = gBS->WaitForEvent (2, WaitList, &Index);
349
350 //
351 // If the timer expired, update anything that needs a refresh and keep waiting
352 //
353 if (!EFI_ERROR (Status) && Index == 1) {
354 Status = EFI_TIMEOUT;
355 UpdateDateAndTime ();
356 }
357
358 gBS->CloseEvent (TimerEvent);
359 } while (Status == EFI_TIMEOUT);
360 }
361
362 return Status;
363 }
364
365 VOID
366 UiAddMenuOption (
367 IN CHAR16 *String,
368 IN EFI_HII_HANDLE Handle,
369 IN EFI_TAG *Tags,
370 IN VOID *FormBinary,
371 IN UINTN IfrNumber
372 )
373 /*++
374
375 Routine Description:
376 Add one menu option by specified description and context.
377
378 Arguments:
379 String - String description for this option.
380 Context - Context data for entry.
381
382 Returns:
383
384 --*/
385 {
386 UI_MENU_OPTION *MenuOption;
387
388 MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION));
389 ASSERT (MenuOption);
390
391 MenuOption->Signature = UI_MENU_OPTION_SIGNATURE;
392 MenuOption->Description = String;
393 MenuOption->Handle = Handle;
394 MenuOption->FormBinary = FormBinary;
395 MenuOption->IfrNumber = IfrNumber;
396 MenuOption->Skip = 1;
397 MenuOption->Tags = Tags;
398 MenuOption->TagIndex = 0;
399 MenuOption->ThisTag = &(MenuOption->Tags[MenuOption->TagIndex]);
400 MenuOption->EntryNumber = (UINT16) IfrNumber;
401
402 InsertTailList (&Menu, &MenuOption->Link);
403 }
404
405 VOID
406 UiAddSubMenuOption (
407 IN CHAR16 *String,
408 IN EFI_HII_HANDLE Handle,
409 IN EFI_TAG *Tags,
410 IN UINTN TagIndex,
411 IN UINT16 FormId,
412 IN UINT16 MenuItemCount
413 )
414 /*++
415
416 Routine Description:
417 Add one menu option by specified description and context.
418
419 Arguments:
420 String - String description for this option.
421 Context - Context data for entry.
422
423 Returns:
424
425 --*/
426 {
427 UI_MENU_OPTION *MenuOption;
428
429 MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION));
430 ASSERT (MenuOption);
431
432 MenuOption->Signature = UI_MENU_OPTION_SIGNATURE;
433 MenuOption->Description = String;
434 MenuOption->Handle = Handle;
435 MenuOption->Skip = Tags[TagIndex].NumberOfLines;
436 MenuOption->IfrNumber = gActiveIfr;
437 MenuOption->Tags = Tags;
438 MenuOption->TagIndex = TagIndex;
439 MenuOption->ThisTag = &(MenuOption->Tags[MenuOption->TagIndex]);
440 MenuOption->Consistency = Tags[TagIndex].Consistency;
441 MenuOption->FormId = FormId;
442 MenuOption->GrayOut = Tags[TagIndex].GrayOut;
443 MenuOption->EntryNumber = MenuItemCount;
444
445 InsertTailList (&Menu, &MenuOption->Link);
446 }
447
448 EFI_STATUS
449 CreateDialog (
450 IN UINTN NumberOfLines,
451 IN BOOLEAN HotKey,
452 IN UINTN MaximumStringSize,
453 OUT CHAR16 *StringBuffer,
454 OUT EFI_INPUT_KEY *KeyValue,
455 IN CHAR16 *String,
456 ...
457 )
458 /*++
459
460 Routine Description:
461 Routine used to abstract a generic dialog interface and return the selected key or string
462
463 Arguments:
464 NumberOfLines - The number of lines for the dialog box
465 HotKey - Defines whether a single character is parsed (TRUE) and returned in KeyValue
466 or a string is returned in StringBuffer. Two special characters are considered when entering a string, a SCAN_ESC and
467 an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates string input and returns
468 MaximumStringSize - The maximum size in bytes of a typed in string (each character is a CHAR16) and the minimum string returned is two bytes
469 StringBuffer - The passed in pointer to the buffer which will hold the typed in string if HotKey is FALSE
470 KeyValue - The EFI_KEY value returned if HotKey is TRUE..
471 String - Pointer to the first string in the list
472 ... - A series of (quantity == NumberOfLines) text strings which will be used to construct the dialog box
473
474 Returns:
475 EFI_SUCCESS - Displayed dialog and received user interaction
476 EFI_INVALID_PARAMETER - One of the parameters was invalid (e.g. (StringBuffer == NULL) && (HotKey == FALSE))
477 EFI_DEVICE_ERROR - User typed in an ESC character to exit the routine
478
479 --*/
480 {
481 VA_LIST Marker;
482 UINTN Count;
483 EFI_INPUT_KEY Key;
484 UINTN LargestString;
485 CHAR16 *TempString;
486 CHAR16 *BufferedString;
487 CHAR16 *StackString;
488 CHAR16 KeyPad[2];
489 UINTN Start;
490 UINTN Top;
491 UINTN Index;
492 BOOLEAN SelectionComplete;
493 UINTN InputOffset;
494 UINTN CurrentAttribute;
495 UINTN DimensionsWidth;
496 UINTN DimensionsHeight;
497
498 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
499 DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
500
501 SelectionComplete = FALSE;
502 InputOffset = 0;
503 TempString = AllocateZeroPool (MaximumStringSize * 2);
504 BufferedString = AllocateZeroPool (MaximumStringSize * 2);
505 CurrentAttribute = gST->ConOut->Mode->Attribute;
506
507 ASSERT (TempString);
508 ASSERT (BufferedString);
509
510 VA_START (Marker, String);
511
512 //
513 // Zero the outgoing buffer
514 //
515 ZeroMem (StringBuffer, MaximumStringSize);
516
517 if (HotKey) {
518 if (KeyValue == NULL) {
519 return EFI_INVALID_PARAMETER;
520 }
521 } else {
522 if (StringBuffer == NULL) {
523 return EFI_INVALID_PARAMETER;
524 }
525 }
526 //
527 // Disable cursor
528 //
529 gST->ConOut->EnableCursor (gST->ConOut, FALSE);
530
531 LargestString = (GetStringWidth (String) / 2);
532
533 if (LargestString == L' ') {
534 InputOffset = 1;
535 }
536 //
537 // Determine the largest string in the dialog box
538 // Notice we are starting with 1 since String is the first string
539 //
540 for (Count = 1; Count < NumberOfLines; Count++) {
541 StackString = VA_ARG (Marker, CHAR16 *);
542
543 if (StackString[0] == L' ') {
544 InputOffset = Count + 1;
545 }
546
547 if ((GetStringWidth (StackString) / 2) > LargestString) {
548 //
549 // Size of the string visually and subtract the width by one for the null-terminator
550 //
551 LargestString = (GetStringWidth (StackString) / 2);
552 }
553 }
554
555 Start = (DimensionsWidth - LargestString - 2) / 2 + gScreenDimensions.LeftColumn + 1;
556 Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gScreenDimensions.TopRow - 1;
557
558 Count = 0;
559
560 //
561 // Display the Popup
562 //
563 CreateSharedPopUp (LargestString, NumberOfLines, &String);
564
565 //
566 // Take the first key typed and report it back?
567 //
568 if (HotKey) {
569 WaitForKeyStroke (&Key);
570 CopyMem (KeyValue, &Key, sizeof (EFI_INPUT_KEY));
571
572 } else {
573 do {
574 WaitForKeyStroke (&Key);
575
576 switch (Key.UnicodeChar) {
577 case CHAR_NULL:
578 switch (Key.ScanCode) {
579 case SCAN_ESC:
580 FreePool (TempString);
581 FreePool (BufferedString);
582 gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
583 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
584 return EFI_DEVICE_ERROR;
585
586 default:
587 break;
588 }
589
590 break;
591
592 case CHAR_CARRIAGE_RETURN:
593 SelectionComplete = TRUE;
594 FreePool (TempString);
595 FreePool (BufferedString);
596 gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
597 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
598 return EFI_SUCCESS;
599 break;
600
601 case CHAR_BACKSPACE:
602 if (StringBuffer[0] != CHAR_NULL) {
603 for (Index = 0; StringBuffer[Index] != CHAR_NULL; Index++) {
604 TempString[Index] = StringBuffer[Index];
605 }
606 //
607 // Effectively truncate string by 1 character
608 //
609 TempString[Index - 1] = CHAR_NULL;
610 StrCpy (StringBuffer, TempString);
611 }
612
613 default:
614 //
615 // If it is the beginning of the string, don't worry about checking maximum limits
616 //
617 if ((StringBuffer[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
618 StrnCpy (StringBuffer, &Key.UnicodeChar, 1);
619 StrnCpy (TempString, &Key.UnicodeChar, 1);
620 } else if ((GetStringWidth (StringBuffer) < MaximumStringSize) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
621 KeyPad[0] = Key.UnicodeChar;
622 KeyPad[1] = CHAR_NULL;
623 StrCat (StringBuffer, KeyPad);
624 StrCat (TempString, KeyPad);
625 }
626 //
627 // If the width of the input string is now larger than the screen, we nee to
628 // adjust the index to start printing portions of the string
629 //
630 SetUnicodeMem (BufferedString, LargestString, L' ');
631
632 PrintStringAt (Start + 1, Top + InputOffset, BufferedString);
633
634 if ((GetStringWidth (StringBuffer) / 2) > (DimensionsWidth - 2)) {
635 Index = (GetStringWidth (StringBuffer) / 2) - DimensionsWidth + 2;
636 } else {
637 Index = 0;
638 }
639
640 for (Count = 0; Index + 1 < GetStringWidth (StringBuffer) / 2; Index++, Count++) {
641 BufferedString[Count] = StringBuffer[Index];
642 }
643
644 PrintStringAt (Start + 1, Top + InputOffset, BufferedString);
645 break;
646 }
647 } while (!SelectionComplete);
648 }
649
650 gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
651 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
652 return EFI_SUCCESS;
653 }
654
655 VOID
656 CreateSharedPopUp (
657 IN UINTN RequestedWidth,
658 IN UINTN NumberOfLines,
659 IN CHAR16 **ArrayOfStrings
660 )
661 {
662 UINTN Index;
663 UINTN Count;
664 CHAR16 Character;
665 UINTN Start;
666 UINTN End;
667 UINTN Top;
668 UINTN Bottom;
669 CHAR16 *String;
670
671 UINTN DimensionsWidth;
672 UINTN DimensionsHeight;
673
674 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
675 DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
676
677 Count = 0;
678
679 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
680
681 if ((RequestedWidth + 2) > DimensionsWidth) {
682 RequestedWidth = DimensionsWidth - 2;
683 }
684 //
685 // Subtract the PopUp width from total Columns, allow for one space extra on
686 // each end plus a border.
687 //
688 Start = (DimensionsWidth - RequestedWidth - 2) / 2 + gScreenDimensions.LeftColumn + 1;
689 End = Start + RequestedWidth + 1;
690
691 Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gScreenDimensions.TopRow - 1;
692 Bottom = Top + NumberOfLines + 2;
693
694 Character = (CHAR16) BOXDRAW_DOWN_RIGHT;
695 PrintCharAt (Start, Top, Character);
696 Character = (CHAR16) BOXDRAW_HORIZONTAL;
697 for (Index = Start; Index + 2 < End; Index++) {
698 PrintChar (Character);
699 }
700
701 Character = (CHAR16) BOXDRAW_DOWN_LEFT;
702 PrintChar (Character);
703 Character = (CHAR16) BOXDRAW_VERTICAL;
704 for (Index = Top; Index + 2 < Bottom; Index++) {
705 String = ArrayOfStrings[Count];
706 Count++;
707
708 //
709 // This will clear the background of the line - we never know who might have been
710 // here before us. This differs from the next clear in that it used the non-reverse
711 // video for normal printing.
712 //
713 if (GetStringWidth (String) / 2 > 1) {
714 ClearLines (Start, End, Index + 1, Index + 1, POPUP_TEXT | POPUP_BACKGROUND);
715 }
716 //
717 // Passing in a space results in the assumption that this is where typing will occur
718 //
719 if (String[0] == L' ') {
720 ClearLines (Start + 1, End - 1, Index + 1, Index + 1, POPUP_INVERSE_TEXT | POPUP_INVERSE_BACKGROUND);
721 }
722 //
723 // Passing in a NULL results in a blank space
724 //
725 if (String[0] == CHAR_NULL) {
726 ClearLines (Start, End, Index + 1, Index + 1, POPUP_TEXT | POPUP_BACKGROUND);
727 }
728
729 PrintStringAt (
730 ((DimensionsWidth - GetStringWidth (String) / 2) / 2) + gScreenDimensions.LeftColumn + 1,
731 Index + 1,
732 String
733 );
734 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
735 PrintCharAt (Start, Index + 1, Character);
736 PrintCharAt (End - 1, Index + 1, Character);
737 }
738
739 Character = (CHAR16) BOXDRAW_UP_RIGHT;
740 PrintCharAt (Start, Bottom - 1, Character);
741 Character = (CHAR16) BOXDRAW_HORIZONTAL;
742 for (Index = Start; Index + 2 < End; Index++) {
743 PrintChar (Character);
744 }
745
746 Character = (CHAR16) BOXDRAW_UP_LEFT;
747 PrintChar (Character);
748 }
749
750 VOID
751 CreatePopUp (
752 IN UINTN RequestedWidth,
753 IN UINTN NumberOfLines,
754 IN CHAR16 *ArrayOfStrings,
755 ...
756 )
757 {
758 CreateSharedPopUp (RequestedWidth, NumberOfLines, &ArrayOfStrings);
759 }
760
761 VOID
762 UpdateStatusBar (
763 IN UINTN MessageType,
764 IN UINT8 Flags,
765 IN BOOLEAN State
766 )
767 {
768 UINTN Index;
769 STATIC BOOLEAN InputError;
770 CHAR16 *NvUpdateMessage;
771 CHAR16 *InputErrorMessage;
772
773 NvUpdateMessage = GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE), gHiiHandle);
774 InputErrorMessage = GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE), gHiiHandle);
775
776 switch (MessageType) {
777 case INPUT_ERROR:
778 if (State) {
779 gST->ConOut->SetAttribute (gST->ConOut, ERROR_TEXT);
780 PrintStringAt (
781 gScreenDimensions.LeftColumn + gPromptBlockWidth,
782 gScreenDimensions.BottomRow - 1,
783 InputErrorMessage
784 );
785 InputError = TRUE;
786 } else {
787 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT);
788 for (Index = 0; Index < (GetStringWidth (InputErrorMessage) - 2) / 2; Index++) {
789 PrintAt (gScreenDimensions.LeftColumn + gPromptBlockWidth + Index, gScreenDimensions.BottomRow - 1, (CHAR16 *) L" ");
790 }
791
792 InputError = FALSE;
793 }
794 break;
795
796 case NV_UPDATE_REQUIRED:
797 if (gClassOfVfr != EFI_FRONT_PAGE_SUBCLASS) {
798 if (State) {
799 gST->ConOut->SetAttribute (gST->ConOut, INFO_TEXT);
800 PrintStringAt (
801 gScreenDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth,
802 gScreenDimensions.BottomRow - 1,
803 NvUpdateMessage
804 );
805 gResetRequired = (BOOLEAN) (gResetRequired | ((Flags & EFI_IFR_FLAG_RESET_REQUIRED) == EFI_IFR_FLAG_RESET_REQUIRED));
806
807 gNvUpdateRequired = TRUE;
808 } else {
809 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT);
810 for (Index = 0; Index < (GetStringWidth (NvUpdateMessage) - 2) / 2; Index++) {
811 PrintAt (
812 (gScreenDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + Index),
813 gScreenDimensions.BottomRow - 1,
814 (CHAR16 *) L" "
815 );
816 }
817
818 gNvUpdateRequired = FALSE;
819 }
820 }
821 break;
822
823 case REFRESH_STATUS_BAR:
824 if (InputError) {
825 UpdateStatusBar (INPUT_ERROR, Flags, TRUE);
826 }
827
828 if (gNvUpdateRequired) {
829 UpdateStatusBar (NV_UPDATE_REQUIRED, Flags, TRUE);
830 }
831 break;
832
833 default:
834 break;
835 }
836
837 FreePool (InputErrorMessage);
838 FreePool (NvUpdateMessage);
839 return ;
840 }
841
842 VOID
843 FreeData (
844 IN EFI_FILE_FORM_TAGS *FileFormTagsHead,
845 IN CHAR16 *FormattedString,
846 IN CHAR16 *OptionString
847 )
848 /*++
849
850 Routine Description:
851
852 Used to remove the allocated data instances
853
854 Arguments:
855
856 Returns:
857
858 --*/
859 {
860 EFI_FILE_FORM_TAGS *FileForm;
861 EFI_FILE_FORM_TAGS *PreviousFileForm;
862 EFI_FORM_TAGS *FormTags;
863 EFI_FORM_TAGS *PreviousFormTags;
864 EFI_IFR_BINARY *IfrBinary;
865 EFI_IFR_BINARY *PreviousIfrBinary;
866 EFI_INCONSISTENCY_DATA *Inconsistent;
867 EFI_VARIABLE_DEFINITION *VariableDefinition;
868 EFI_VARIABLE_DEFINITION *PreviousVariableDefinition;
869 VOID *Buffer;
870 UINTN Index;
871
872 FileForm = FileFormTagsHead;
873
874 if (FormattedString != NULL) {
875 FreePool (FormattedString);
876 }
877
878 if (OptionString != NULL) {
879 FreePool (OptionString);
880 }
881
882 for (; FileForm != NULL;) {
883 PreviousFileForm = NULL;
884
885 //
886 // Advance FileForm to the last entry
887 //
888 for (; FileForm->NextFile != NULL; FileForm = FileForm->NextFile) {
889 PreviousFileForm = FileForm;
890 }
891
892 FormTags = &FileForm->FormTags;
893
894 for (; FormTags != NULL;) {
895 FormTags = &FileForm->FormTags;
896 PreviousFormTags = NULL;
897
898 //
899 // Advance FormTags to the last entry
900 //
901 for (; FormTags->Next != NULL; FormTags = FormTags->Next) {
902 PreviousFormTags = FormTags;
903 }
904 //
905 // Walk through each of the tags and free the IntList allocation
906 //
907 for (Index = 0; FormTags->Tags[Index].Operand != EFI_IFR_END_FORM_OP; Index++) {
908 //
909 // It is more than likely that the very last page will contain an end formset
910 //
911 if (FormTags->Tags[Index].Operand == EFI_IFR_END_FORM_SET_OP) {
912 break;
913 }
914
915 if (FormTags->Tags[Index].IntList != NULL) {
916 FreePool (FormTags->Tags[Index].IntList);
917 }
918 }
919
920 if (PreviousFormTags != NULL) {
921 FreePool (FormTags->Tags);
922 FormTags = PreviousFormTags;
923 FreePool (FormTags->Next);
924 FormTags->Next = NULL;
925 } else {
926 FreePool (FormTags->Tags);
927 FormTags = NULL;
928 }
929 }
930 //
931 // Last FileForm entry's Inconsistent database
932 //
933 Inconsistent = FileForm->InconsistentTags;
934
935 //
936 // Advance Inconsistent to the last entry
937 //
938 for (; Inconsistent->Next != NULL; Inconsistent = Inconsistent->Next)
939 ;
940
941 for (; Inconsistent != NULL;) {
942 //
943 // Preserve the Previous pointer
944 //
945 Buffer = (VOID *) Inconsistent->Previous;
946
947 //
948 // Free the current entry
949 //
950 FreePool (Inconsistent);
951
952 //
953 // Restore the Previous pointer
954 //
955 Inconsistent = (EFI_INCONSISTENCY_DATA *) Buffer;
956 }
957
958 VariableDefinition = FileForm->VariableDefinitions;
959
960 for (; VariableDefinition != NULL;) {
961 VariableDefinition = FileForm->VariableDefinitions;
962 PreviousVariableDefinition = NULL;
963
964 //
965 // Advance VariableDefinitions to the last entry
966 //
967 for (; VariableDefinition->Next != NULL; VariableDefinition = VariableDefinition->Next) {
968 PreviousVariableDefinition = VariableDefinition;
969 }
970
971 FreePool (VariableDefinition->VariableName);
972
973 if (VariableDefinition->NvRamMap != NULL) {
974 FreePool (VariableDefinition->NvRamMap);
975 }
976
977 if (VariableDefinition->FakeNvRamMap != NULL) {
978 FreePool (VariableDefinition->FakeNvRamMap);
979 }
980
981 if (PreviousVariableDefinition != NULL) {
982 VariableDefinition = PreviousVariableDefinition;
983 FreePool (VariableDefinition->Next);
984 VariableDefinition->Next = NULL;
985 } else {
986 FreePool (VariableDefinition);
987 VariableDefinition = NULL;
988 }
989 }
990
991 if (PreviousFileForm != NULL) {
992 FileForm = PreviousFileForm;
993 FreePool (FileForm->NextFile);
994 FileForm->NextFile = NULL;
995 } else {
996 FreePool (FileForm);
997 FileForm = NULL;
998 }
999 }
1000
1001 IfrBinary = gBinaryDataHead;
1002
1003 for (; IfrBinary != NULL;) {
1004 IfrBinary = gBinaryDataHead;
1005 PreviousIfrBinary = NULL;
1006
1007 //
1008 // Advance IfrBinary to the last entry
1009 //
1010 for (; IfrBinary->Next != NULL; IfrBinary = IfrBinary->Next) {
1011 PreviousIfrBinary = IfrBinary;
1012 }
1013
1014 FreePool (IfrBinary->IfrPackage);
1015
1016 if (PreviousIfrBinary != NULL) {
1017 IfrBinary = PreviousIfrBinary;
1018 FreePool (IfrBinary->Next);
1019 IfrBinary->Next = NULL;
1020 } else {
1021 FreePool (IfrBinary);
1022 IfrBinary = NULL;
1023 }
1024 }
1025
1026 FreePool (gPreviousValue);
1027 gPreviousValue = NULL;
1028
1029 //
1030 // Free Browser Strings
1031 //
1032 FreePool (gPressEnter);
1033 FreePool (gConfirmError);
1034 FreePool (gConfirmPassword);
1035 FreePool (gPromptForNewPassword);
1036 FreePool (gPromptForPassword);
1037 FreePool (gToggleCheckBox);
1038 FreePool (gNumericInput);
1039 FreePool (gMakeSelection);
1040 FreePool (gMoveHighlight);
1041 FreePool (gEscapeString);
1042 FreePool (gEnterCommitString);
1043 FreePool (gEnterString);
1044 FreePool (gFunctionOneString);
1045 FreePool (gFunctionTwoString);
1046 FreePool (gFunctionNineString);
1047 FreePool (gFunctionTenString);
1048 return ;
1049 }
1050
1051 STATIC
1052 BOOLEAN
1053 SelectionsAreValid (
1054 IN UI_MENU_OPTION *MenuOption,
1055 IN EFI_FILE_FORM_TAGS *FileFormTagsHead
1056 )
1057 /*++
1058
1059 Routine Description:
1060 Initiate late consistency checks against the current page.
1061
1062 Arguments:
1063 None
1064
1065 Returns:
1066
1067 --*/
1068 {
1069 LIST_ENTRY *Link;
1070 EFI_TAG *Tag;
1071 EFI_FILE_FORM_TAGS *FileFormTags;
1072 CHAR16 *StringPtr;
1073 CHAR16 NullCharacter;
1074 UINTN Index;
1075 UINT16 *NvRamMap;
1076 STRING_REF PopUp;
1077 EFI_INPUT_KEY Key;
1078 EFI_VARIABLE_DEFINITION *VariableDefinition;
1079
1080 StringPtr = (CHAR16 *) L"\0";
1081 NullCharacter = CHAR_NULL;
1082
1083 FileFormTags = FileFormTagsHead;
1084
1085 for (Index = 0; Index < MenuOption->IfrNumber; Index++) {
1086 FileFormTags = FileFormTags->NextFile;
1087 }
1088
1089 for (Link = Menu.ForwardLink; Link != &Menu; Link = Link->ForwardLink) {
1090 MenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
1091
1092 Tag = MenuOption->ThisTag;
1093
1094 ExtractRequestedNvMap (FileFormTags, Tag->VariableNumber, &VariableDefinition);
1095 NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
1096
1097 //
1098 // If the op-code has a late check, ensure consistency checks are now applied
1099 //
1100 if (Tag->Flags & EFI_IFR_FLAG_LATE_CHECK) {
1101 if (ValueIsNotValid (TRUE, 0, Tag, FileFormTags, &PopUp)) {
1102 if (PopUp != 0x0000) {
1103 StringPtr = GetToken (PopUp, MenuOption->Handle);
1104
1105 CreatePopUp (GetStringWidth (StringPtr) / 2, 3, &NullCharacter, StringPtr, &NullCharacter);
1106
1107 do {
1108 WaitForKeyStroke (&Key);
1109
1110 switch (Key.UnicodeChar) {
1111
1112 case CHAR_CARRIAGE_RETURN:
1113 //
1114 // Since the value can be one byte long or two bytes long, do a CopyMem based on StorageWidth
1115 //
1116 CopyMem (NvRamMap, &Tag->OldValue, Tag->StorageWidth);
1117 FreePool (StringPtr);
1118 break;
1119
1120 default:
1121 break;
1122 }
1123 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
1124 }
1125
1126 return FALSE;
1127 }
1128 }
1129 }
1130
1131 return TRUE;
1132 }
1133
1134 UINT16
1135 GetWidth (
1136 IN EFI_TAG *Tag,
1137 IN EFI_HII_HANDLE Handle
1138 )
1139 /*++
1140
1141 Routine Description:
1142 Get the supported width for a particular op-code
1143
1144 Arguments:
1145 Tag - The Tag structure passed in.
1146 Handle - The handle in the HII database being used
1147
1148 Returns:
1149 Returns the number of CHAR16 characters that is support.
1150
1151
1152 --*/
1153 {
1154 CHAR16 *String;
1155 UINTN Size;
1156
1157 Size = 0x00;
1158
1159 //
1160 // See if the second text parameter is really NULL
1161 //
1162 if ((Tag->Operand == EFI_IFR_TEXT_OP) && (Tag->TextTwo != 0)) {
1163 String = GetToken (Tag->TextTwo, Handle);
1164 Size = StrLen (String);
1165 FreePool (String);
1166 }
1167
1168 if ((Tag->Operand == EFI_IFR_SUBTITLE_OP) ||
1169 (Tag->Operand == EFI_IFR_REF_OP) ||
1170 (Tag->Operand == EFI_IFR_PASSWORD_OP) ||
1171 (Tag->Operand == EFI_IFR_STRING_OP) ||
1172 (Tag->Operand == EFI_IFR_INVENTORY_OP) ||
1173 //
1174 // Allow a wide display if text op-code and no secondary text op-code
1175 //
1176 ((Tag->Operand == EFI_IFR_TEXT_OP) && (Size == 0x0000))
1177 ) {
1178 return (UINT16) (gPromptBlockWidth + gOptionBlockWidth);
1179 } else {
1180 return (UINT16) gPromptBlockWidth;
1181 }
1182 }
1183
1184 UINT16
1185 GetLineByWidth (
1186 IN CHAR16 *InputString,
1187 IN UINT16 LineWidth,
1188 IN OUT UINTN *Index,
1189 OUT CHAR16 **OutputString
1190 )
1191 /*++
1192
1193 Routine Description:
1194 Will copy LineWidth amount of a string in the OutputString buffer and return the
1195 number of CHAR16 characters that were copied into the OutputString buffer.
1196
1197 Arguments:
1198 InputString - String description for this option.
1199 LineWidth - Width of the desired string to extract in CHAR16 characters
1200 Index - Where in InputString to start the copy process
1201 OutputString - Buffer to copy the string into
1202
1203 Returns:
1204 Returns the number of CHAR16 characters that were copied into the OutputString buffer.
1205
1206
1207 --*/
1208 {
1209 static BOOLEAN Finished;
1210 UINT16 Count;
1211 UINT16 Count2;
1212
1213 if (Finished) {
1214 Finished = FALSE;
1215 return (UINT16) 0;
1216 }
1217
1218 Count = LineWidth;
1219 Count2 = 0;
1220
1221 *OutputString = AllocateZeroPool (((UINTN) (LineWidth + 1) * 2));
1222
1223 //
1224 // Ensure we have got a valid buffer
1225 //
1226 if (*OutputString != NULL) {
1227
1228 //
1229 //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.
1230 //To avoid displaying this empty line in screen, just skip the two CHARs here.
1231 //
1232 if ((InputString[*Index] == NARROW_CHAR) && (InputString[*Index + 1] == CHAR_CARRIAGE_RETURN)) {
1233 *Index = *Index + 2;
1234 }
1235
1236 //
1237 // Fast-forward the string and see if there is a carriage-return in the string
1238 //
1239 for (; (InputString[*Index + Count2] != CHAR_CARRIAGE_RETURN) && (Count2 != LineWidth); Count2++)
1240 ;
1241
1242 //
1243 // Copy the desired LineWidth of data to the output buffer.
1244 // Also make sure that we don't copy more than the string.
1245 // Also make sure that if there are linefeeds, we account for them.
1246 //
1247 if ((StrSize (&InputString[*Index]) <= ((UINTN) (LineWidth + 1) * 2)) &&
1248 (StrSize (&InputString[*Index]) <= ((UINTN) (Count2 + 1) * 2))
1249 ) {
1250 //
1251 // Convert to CHAR16 value and show that we are done with this operation
1252 //
1253 LineWidth = (UINT16) ((StrSize (&InputString[*Index]) - 2) / 2);
1254 if (LineWidth != 0) {
1255 Finished = TRUE;
1256 }
1257 } else {
1258 if (Count2 == LineWidth) {
1259 //
1260 // Rewind the string from the maximum size until we see a space to break the line
1261 //
1262 for (; (InputString[*Index + LineWidth] != CHAR_SPACE) && (LineWidth != 0); LineWidth--)
1263 ;
1264 if (LineWidth == 0) {
1265 LineWidth = Count;
1266 }
1267 } else {
1268 LineWidth = Count2;
1269 }
1270 }
1271
1272 CopyMem (*OutputString, &InputString[*Index], LineWidth * 2);
1273
1274 //
1275 // If currently pointing to a space, increment the index to the first non-space character
1276 //
1277 for (;
1278 (InputString[*Index + LineWidth] == CHAR_SPACE) || (InputString[*Index + LineWidth] == CHAR_CARRIAGE_RETURN);
1279 (*Index)++
1280 )
1281 ;
1282 *Index = (UINT16) (*Index + LineWidth);
1283 return LineWidth;
1284 } else {
1285 return (UINT16) 0;
1286 }
1287 }
1288
1289 STATIC
1290 VOID
1291 UpdateOptionSkipLines (
1292 IN EFI_IFR_DATA_ARRAY *PageData,
1293 IN UI_MENU_OPTION *MenuOption,
1294 IN EFI_FILE_FORM_TAGS *FileFormTagsHead,
1295 IN CHAR16 **OptionalString,
1296 IN UINTN SkipValue
1297 )
1298 {
1299 UINTN Index;
1300 UINT16 Width;
1301 UINTN Row;
1302 UINTN OriginalRow;
1303 CHAR16 *OutputString;
1304 CHAR16 *OptionString;
1305
1306 Row = 0;
1307 OptionString = *OptionalString;
1308 OutputString = NULL;
1309
1310 ProcessOptions (MenuOption, FALSE, FileFormTagsHead, PageData, &OptionString);
1311
1312 if (OptionString != NULL) {
1313 Width = (UINT16) gOptionBlockWidth;
1314
1315 OriginalRow = Row;
1316
1317 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
1318 //
1319 // If there is more string to process print on the next row and increment the Skip value
1320 //
1321 if (StrLen (&OptionString[Index])) {
1322 if (SkipValue == 0) {
1323 Row++;
1324 //
1325 // Since the Number of lines for this menu entry may or may not be reflected accurately
1326 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1327 // some testing to ensure we are keeping this in-sync.
1328 //
1329 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1330 //
1331 if ((Row - OriginalRow) >= MenuOption->Skip) {
1332 MenuOption->Skip++;
1333 }
1334 }
1335 }
1336
1337 FreePool (OutputString);
1338 if (SkipValue != 0) {
1339 SkipValue--;
1340 }
1341 }
1342
1343 Row = OriginalRow;
1344 }
1345
1346 *OptionalString = OptionString;
1347 }
1348 //
1349 // Search table for UiDisplayMenu()
1350 //
1351 SCAN_CODE_TO_SCREEN_OPERATION gScanCodeToOperation[] = {
1352 { SCAN_UP, UiUp },
1353 { SCAN_DOWN, UiDown },
1354 { SCAN_PAGE_UP, UiPageUp },
1355 { SCAN_PAGE_DOWN, UiPageDown},
1356 { SCAN_ESC, UiReset},
1357 { SCAN_F2, UiPrevious},
1358 { SCAN_LEFT, UiLeft },
1359 { SCAN_RIGHT, UiRight },
1360 { SCAN_F9, UiDefault},
1361 { SCAN_F10, UiSave }
1362 };
1363
1364 SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag[] = {
1365 { UiNoOperation, CfUiNoOperation },
1366 { UiDefault, CfUiDefault },
1367 { UiSelect, CfUiSelect },
1368 { UiUp, CfUiUp},
1369 { UiDown, CfUiDown },
1370 { UiLeft, CfUiLeft },
1371 { UiRight, CfUiRight },
1372 { UiReset, CfUiReset },
1373 { UiSave, CfUiSave },
1374 { UiPrevious, CfUiPrevious },
1375 { UiPageUp, CfUiPageUp },
1376 { UiPageDown, CfUiPageDown }
1377 };
1378
1379 UI_MENU_OPTION *
1380 UiDisplayMenu (
1381 IN BOOLEAN SubMenu,
1382 IN EFI_FILE_FORM_TAGS *FileFormTagsHead,
1383 OUT EFI_IFR_DATA_ARRAY *PageData
1384 )
1385 /*++
1386
1387 Routine Description:
1388 Display menu and wait for user to select one menu option, then return it.
1389 If AutoBoot is enabled, then if user doesn't select any option,
1390 after period of time, it will automatically return the first menu option.
1391
1392 Arguments:
1393 SubMenu - Indicate is sub menu.
1394 FileFormTagsHead - A pointer to the EFI_FILE_FORM_TAGS structure.
1395 PageData - A pointer to the EFI_IFR_DATA_ARRAY.
1396
1397 Returns:
1398 Return the pointer of the menu which selected,
1399 otherwise return NULL.
1400
1401 --*/
1402 {
1403 INTN SkipValue;
1404 INTN Difference;
1405 INTN OldSkipValue;
1406 UINTN Row;
1407 UINTN Col;
1408 UINTN Temp;
1409 UINTN Temp2;
1410 UINTN TopRow;
1411 UINTN BottomRow;
1412 UINTN OriginalRow;
1413 UINTN Index;
1414 UINTN DataAndTimeLineNumberPad;
1415 UINT32 Count;
1416 INT16 OriginalTimeOut;
1417 UINT8 *Location;
1418 UINT16 Width;
1419 CHAR16 *StringPtr;
1420 CHAR16 *OptionString;
1421 CHAR16 *OutputString;
1422 CHAR16 *FormattedString;
1423 CHAR16 YesResponse;
1424 CHAR16 NoResponse;
1425 BOOLEAN NewLine;
1426 BOOLEAN Repaint;
1427 BOOLEAN SavedValue;
1428 EFI_STATUS Status;
1429 UI_MENU_LIST *UiMenuList;
1430 EFI_INPUT_KEY Key;
1431 LIST_ENTRY *Link;
1432 LIST_ENTRY *NewPos;
1433 LIST_ENTRY *TopOfScreen;
1434 LIST_ENTRY *SavedListEntry;
1435 UI_MENU_OPTION *Selection;
1436 UI_MENU_OPTION *MenuOption;
1437 UI_MENU_OPTION *NextMenuOption;
1438 UI_MENU_OPTION *SavedMenuOption;
1439 UI_MENU_OPTION *PreviousMenuOption;
1440 EFI_IFR_BINARY *IfrBinary;
1441 UI_CONTROL_FLAG ControlFlag;
1442 EFI_SCREEN_DESCRIPTOR LocalScreen;
1443 EFI_FILE_FORM_TAGS *FileFormTags;
1444 MENU_REFRESH_ENTRY *MenuRefreshEntry;
1445 MENU_REFRESH_ENTRY *OldMenuRefreshEntry;
1446 UI_SCREEN_OPERATION ScreenOperation;
1447 EFI_VARIABLE_DEFINITION *VariableDefinition;
1448 EFI_FORM_CALLBACK_PROTOCOL *FormCallback;
1449 EFI_HII_VARIABLE_PACK_LIST *NvMapListHead;
1450 EFI_HII_VARIABLE_PACK_LIST *NvMapListNode;
1451 VOID *NvMap;
1452 UINTN NvMapSize;
1453
1454 CopyMem (&LocalScreen, &gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR));
1455
1456 VariableDefinition = NULL;
1457 Status = EFI_SUCCESS;
1458 FormattedString = NULL;
1459 OptionString = NULL;
1460 ScreenOperation = UiNoOperation;
1461 NewLine = TRUE;
1462 FormCallback = NULL;
1463 FileFormTags = NULL;
1464 OutputString = NULL;
1465 gUpArrow = FALSE;
1466 gDownArrow = FALSE;
1467 SkipValue = 0;
1468 OldSkipValue = 0;
1469 MenuRefreshEntry = gMenuRefreshHead;
1470 OldMenuRefreshEntry = gMenuRefreshHead;
1471 NextMenuOption = NULL;
1472 PreviousMenuOption = NULL;
1473 SavedMenuOption = NULL;
1474 IfrBinary = NULL;
1475 NvMap = NULL;
1476 NvMapSize = 0;
1477
1478 ZeroMem (&Key, sizeof (EFI_INPUT_KEY));
1479
1480 if (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) {
1481 TopRow = LocalScreen.TopRow + FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1482 Row = LocalScreen.TopRow + FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1483 } else {
1484 TopRow = LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1485 Row = LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1486 }
1487
1488 if (SubMenu) {
1489 Col = LocalScreen.LeftColumn;
1490 } else {
1491 Col = LocalScreen.LeftColumn + LEFT_SKIPPED_COLUMNS;
1492 }
1493
1494 BottomRow = LocalScreen.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT - SCROLL_ARROW_HEIGHT - 1;
1495
1496 TopOfScreen = Menu.ForwardLink;
1497 Repaint = TRUE;
1498 MenuOption = NULL;
1499
1500 //
1501 // Get user's selection
1502 //
1503 Selection = NULL;
1504 NewPos = Menu.ForwardLink;
1505 gST->ConOut->EnableCursor (gST->ConOut, FALSE);
1506
1507 UpdateStatusBar (REFRESH_STATUS_BAR, (UINT8) 0, TRUE);
1508
1509 ControlFlag = CfInitialization;
1510
1511 while (TRUE) {
1512 switch (ControlFlag) {
1513 case CfInitialization:
1514 ControlFlag = CfCheckSelection;
1515 if (gExitRequired) {
1516 ScreenOperation = UiReset;
1517 ControlFlag = CfScreenOperation;
1518 } else if (gSaveRequired) {
1519 ScreenOperation = UiSave;
1520 ControlFlag = CfScreenOperation;
1521 } else if (IsListEmpty (&Menu)) {
1522 ControlFlag = CfReadKey;
1523 }
1524 break;
1525
1526 case CfCheckSelection:
1527 if (Selection != NULL) {
1528 ControlFlag = CfExit;
1529 } else {
1530 ControlFlag = CfRepaint;
1531 }
1532
1533 FileFormTags = FileFormTagsHead;
1534 break;
1535
1536 case CfRepaint:
1537 ControlFlag = CfRefreshHighLight;
1538
1539 if (Repaint) {
1540 //
1541 // Display menu
1542 //
1543 SavedMenuOption = MenuOption;
1544 gDownArrow = FALSE;
1545 gUpArrow = FALSE;
1546 Row = TopRow;
1547
1548 Temp = SkipValue;
1549 Temp2 = SkipValue;
1550
1551 ClearLines (
1552 LocalScreen.LeftColumn,
1553 LocalScreen.RightColumn,
1554 TopRow - SCROLL_ARROW_HEIGHT,
1555 BottomRow + SCROLL_ARROW_HEIGHT,
1556 FIELD_TEXT | FIELD_BACKGROUND
1557 );
1558
1559 while (gMenuRefreshHead != NULL) {
1560 OldMenuRefreshEntry = gMenuRefreshHead->Next;
1561
1562 FreePool (gMenuRefreshHead);
1563
1564 gMenuRefreshHead = OldMenuRefreshEntry;
1565 }
1566
1567 for (Link = TopOfScreen; Link != &Menu; Link = Link->ForwardLink) {
1568 MenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
1569 MenuOption->Row = Row;
1570 OriginalRow = Row;
1571 MenuOption->Col = Col;
1572 MenuOption->OptCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn;
1573
1574 if (SubMenu) {
1575 if (MenuOption->ThisTag->GrayOut) {
1576 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
1577 } else {
1578 if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP) {
1579 gST->ConOut->SetAttribute (gST->ConOut, SUBTITLE_TEXT | FIELD_BACKGROUND);
1580 }
1581 }
1582
1583 Width = GetWidth (MenuOption->ThisTag, MenuOption->Handle);
1584
1585 OriginalRow = Row;
1586
1587 for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
1588 if ((Temp == 0) && (Row <= BottomRow)) {
1589 PrintStringAt (Col, Row, OutputString);
1590 }
1591 //
1592 // If there is more string to process print on the next row and increment the Skip value
1593 //
1594 if (StrLen (&MenuOption->Description[Index])) {
1595 if (Temp == 0) {
1596 Row++;
1597 }
1598 }
1599
1600 FreePool (OutputString);
1601 if (Temp != 0) {
1602 Temp--;
1603 }
1604 }
1605
1606 Temp = 0;
1607
1608 Row = OriginalRow;
1609
1610 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
1611 ProcessOptions (MenuOption, FALSE, FileFormTagsHead, PageData, &OptionString);
1612
1613 if (OptionString != NULL) {
1614 if (MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP ||
1615 MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP
1616 ) {
1617 //
1618 // If leading spaces on OptionString - remove the spaces
1619 //
1620 for (Index = 0; OptionString[Index] == L' '; Index++) {
1621 MenuOption->OptCol++;
1622 }
1623
1624 for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
1625 OptionString[Count] = OptionString[Index];
1626 Count++;
1627 }
1628
1629 OptionString[Count] = CHAR_NULL;
1630 }
1631
1632 //
1633 // If this is a date or time op-code and is used to reflect an RTC, register the op-code
1634 //
1635 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP ||
1636 MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP) &&
1637 (MenuOption->ThisTag->StorageStart >= FileFormTags->FormTags.Tags[0].NvDataSize)) {
1638
1639 if (gMenuRefreshHead == NULL) {
1640 MenuRefreshEntry = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
1641 ASSERT (MenuRefreshEntry != NULL);
1642 MenuRefreshEntry->MenuOption = MenuOption;
1643 MenuRefreshEntry->FileFormTagsHead = FileFormTagsHead;
1644 MenuRefreshEntry->CurrentColumn = MenuOption->OptCol;
1645 MenuRefreshEntry->CurrentRow = MenuOption->Row;
1646 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND;
1647 gMenuRefreshHead = MenuRefreshEntry;
1648 } else {
1649 //
1650 // Advance to the last entry
1651 //
1652 for (MenuRefreshEntry = gMenuRefreshHead;
1653 MenuRefreshEntry->Next != NULL;
1654 MenuRefreshEntry = MenuRefreshEntry->Next
1655 )
1656 ;
1657 MenuRefreshEntry->Next = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
1658 ASSERT (MenuRefreshEntry->Next != NULL);
1659 MenuRefreshEntry = MenuRefreshEntry->Next;
1660 MenuRefreshEntry->MenuOption = MenuOption;
1661 MenuRefreshEntry->FileFormTagsHead = FileFormTagsHead;
1662 MenuRefreshEntry->CurrentColumn = MenuOption->OptCol;
1663 MenuRefreshEntry->CurrentRow = MenuOption->Row;
1664 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND;
1665 }
1666 }
1667
1668 Width = (UINT16) gOptionBlockWidth;
1669
1670 OriginalRow = Row;
1671
1672 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
1673 if ((Temp2 == 0) && (Row <= BottomRow)) {
1674 PrintStringAt (MenuOption->OptCol, Row, OutputString);
1675 }
1676 //
1677 // If there is more string to process print on the next row and increment the Skip value
1678 //
1679 if (StrLen (&OptionString[Index])) {
1680 if (Temp2 == 0) {
1681 Row++;
1682 //
1683 // Since the Number of lines for this menu entry may or may not be reflected accurately
1684 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1685 // some testing to ensure we are keeping this in-sync.
1686 //
1687 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1688 //
1689 if ((Row - OriginalRow) >= MenuOption->Skip) {
1690 MenuOption->Skip++;
1691 }
1692 }
1693 }
1694
1695 FreePool (OutputString);
1696 if (Temp2 != 0) {
1697 Temp2--;
1698 }
1699 }
1700
1701 Temp2 = 0;
1702 Row = OriginalRow;
1703 }
1704 //
1705 // If this is a text op with secondary text information
1706 //
1707 if ((MenuOption->ThisTag->Operand == EFI_IFR_TEXT_OP) && (MenuOption->ThisTag->TextTwo != 0)) {
1708 StringPtr = GetToken (MenuOption->ThisTag->TextTwo, MenuOption->Handle);
1709
1710 Width = (UINT16) gOptionBlockWidth;
1711
1712 OriginalRow = Row;
1713
1714 for (Index = 0; GetLineByWidth (StringPtr, Width, &Index, &OutputString) != 0x0000;) {
1715 if ((Temp == 0) && (Row <= BottomRow)) {
1716 PrintStringAt (MenuOption->OptCol, Row, OutputString);
1717 }
1718 //
1719 // If there is more string to process print on the next row and increment the Skip value
1720 //
1721 if (StrLen (&StringPtr[Index])) {
1722 if (Temp2 == 0) {
1723 Row++;
1724 //
1725 // Since the Number of lines for this menu entry may or may not be reflected accurately
1726 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1727 // some testing to ensure we are keeping this in-sync.
1728 //
1729 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1730 //
1731 if ((Row - OriginalRow) >= MenuOption->Skip) {
1732 MenuOption->Skip++;
1733 }
1734 }
1735 }
1736
1737 FreePool (OutputString);
1738 if (Temp2 != 0) {
1739 Temp2--;
1740 }
1741 }
1742
1743 Row = OriginalRow;
1744 FreePool (StringPtr);
1745 }
1746 } else {
1747 //
1748 // For now, assume left-justified 72 width max setup entries
1749 //
1750 PrintStringAt (Col, Row, MenuOption->Description);
1751 }
1752 //
1753 // Tracker 6210 - need to handle the bottom of the display
1754 //
1755 if (MenuOption->Skip > 1) {
1756 Row += MenuOption->Skip - SkipValue;
1757 SkipValue = 0;
1758 } else {
1759 Row += MenuOption->Skip;
1760 }
1761
1762 if (Row > BottomRow) {
1763 if (!ValueIsScroll (FALSE, Link)) {
1764 gDownArrow = TRUE;
1765 }
1766
1767 Row = BottomRow + 1;
1768 break;
1769 }
1770 }
1771
1772 if (!ValueIsScroll (TRUE, TopOfScreen)) {
1773 gUpArrow = TRUE;
1774 }
1775
1776 if (gUpArrow) {
1777 gST->ConOut->SetAttribute (gST->ConOut, ARROW_TEXT | ARROW_BACKGROUND);
1778 PrintAt (
1779 LocalScreen.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
1780 TopRow - SCROLL_ARROW_HEIGHT,
1781 (CHAR16 *) L"%c",
1782 ARROW_UP
1783 );
1784 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
1785 }
1786
1787 if (gDownArrow) {
1788 gST->ConOut->SetAttribute (gST->ConOut, ARROW_TEXT | ARROW_BACKGROUND);
1789 PrintAt (
1790 LocalScreen.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
1791 BottomRow + SCROLL_ARROW_HEIGHT,
1792 (CHAR16 *) L"%c",
1793 ARROW_DOWN
1794 );
1795 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
1796 }
1797
1798 if (SavedMenuOption != NULL) {
1799 MenuOption = SavedMenuOption;
1800 }
1801 }
1802 break;
1803
1804 case CfRefreshHighLight:
1805 ControlFlag = CfUpdateHelpString;
1806 //
1807 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
1808 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
1809 //
1810 SavedValue = Repaint;
1811 Repaint = FALSE;
1812
1813 if (NewPos != NULL) {
1814 gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
1815 if (SubMenu) {
1816 if (gLastOpr && (gEntryNumber != -1)) {
1817 MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
1818 if (gEntryNumber != MenuOption->EntryNumber) {
1819 ScreenOperation = UiDown;
1820 ControlFlag = CfScreenOperation;
1821 break;
1822 } else {
1823 gLastOpr = FALSE;
1824 }
1825 }
1826
1827 ProcessOptions (MenuOption, FALSE, FileFormTagsHead, PageData, &OptionString);
1828 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
1829 if (OptionString != NULL) {
1830 if (MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP ||
1831 MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP
1832 ) {
1833 //
1834 // If leading spaces on OptionString - remove the spaces
1835 //
1836 for (Index = 0; OptionString[Index] == L' '; Index++)
1837 ;
1838
1839 for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
1840 OptionString[Count] = OptionString[Index];
1841 Count++;
1842 }
1843
1844 OptionString[Count] = CHAR_NULL;
1845 }
1846
1847 Width = (UINT16) gOptionBlockWidth;
1848
1849 OriginalRow = MenuOption->Row;
1850
1851 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
1852 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
1853 PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString);
1854 }
1855 //
1856 // If there is more string to process print on the next row and increment the Skip value
1857 //
1858 if (StrLen (&OptionString[Index])) {
1859 MenuOption->Row++;
1860 }
1861
1862 FreePool (OutputString);
1863 }
1864
1865 MenuOption->Row = OriginalRow;
1866 } else {
1867 if (NewLine) {
1868 if (MenuOption->ThisTag->GrayOut) {
1869 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
1870 } else {
1871 if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP) {
1872 gST->ConOut->SetAttribute (gST->ConOut, SUBTITLE_TEXT | FIELD_BACKGROUND);
1873 }
1874 }
1875
1876 OriginalRow = MenuOption->Row;
1877 Width = GetWidth (MenuOption->ThisTag, MenuOption->Handle);
1878
1879 for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
1880 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
1881 PrintStringAt (Col, MenuOption->Row, OutputString);
1882 }
1883 //
1884 // If there is more string to process print on the next row and increment the Skip value
1885 //
1886 if (StrLen (&MenuOption->Description[Index])) {
1887 MenuOption->Row++;
1888 }
1889
1890 FreePool (OutputString);
1891 }
1892
1893 MenuOption->Row = OriginalRow;
1894 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
1895 }
1896 }
1897 } else {
1898 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
1899 gST->ConOut->OutputString (gST->ConOut, MenuOption->Description);
1900 }
1901
1902 MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
1903
1904 if ((gPriorMenuEntry != 0) && (MenuOption->EntryNumber != gPriorMenuEntry) && (NewPos->ForwardLink != &Menu)) {
1905 ScreenOperation = UiDown;
1906 ControlFlag = CfScreenOperation;
1907 break;
1908 } else {
1909 gPriorMenuEntry = 0;
1910 }
1911 //
1912 // This is only possible if we entered this page and the first menu option is
1913 // a "non-menu" item. In that case, force it UiDown
1914 //
1915 if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || MenuOption->ThisTag->GrayOut) {
1916 //
1917 // If we previously hit an UP command and we are still sitting on a text operation
1918 // we must continue going up
1919 //
1920 if (ScreenOperation == UiUp) {
1921 ControlFlag = CfScreenOperation;
1922 break;
1923 } else {
1924 ScreenOperation = UiDown;
1925 ControlFlag = CfScreenOperation;
1926 break;
1927 }
1928 }
1929 //
1930 // Set reverse attribute
1931 //
1932 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT);
1933 gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
1934
1935 //
1936 // Assuming that we have a refresh linked-list created, lets annotate the
1937 // appropriate entry that we are highlighting with its new attribute. Just prior to this
1938 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
1939 //
1940 if (gMenuRefreshHead != NULL) {
1941 for (MenuRefreshEntry = gMenuRefreshHead; MenuRefreshEntry != NULL; MenuRefreshEntry = MenuRefreshEntry->Next) {
1942 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND;
1943 if (MenuRefreshEntry->MenuOption == MenuOption) {
1944 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT;
1945 }
1946 }
1947 }
1948
1949 if (SubMenu) {
1950 ProcessOptions (MenuOption, FALSE, FileFormTagsHead, PageData, &OptionString);
1951 if (OptionString != NULL) {
1952 if (MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP ||
1953 MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP
1954 ) {
1955 //
1956 // If leading spaces on OptionString - remove the spaces
1957 //
1958 for (Index = 0; OptionString[Index] == L' '; Index++)
1959 ;
1960
1961 for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
1962 OptionString[Count] = OptionString[Index];
1963 Count++;
1964 }
1965
1966 OptionString[Count] = CHAR_NULL;
1967 }
1968 Width = (UINT16) gOptionBlockWidth;
1969
1970 OriginalRow = MenuOption->Row;
1971
1972 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
1973 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
1974 PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString);
1975 }
1976 //
1977 // If there is more string to process print on the next row and increment the Skip value
1978 //
1979 if (StrLen (&OptionString[Index])) {
1980 MenuOption->Row++;
1981 }
1982
1983 FreePool (OutputString);
1984 }
1985
1986 MenuOption->Row = OriginalRow;
1987 } else {
1988 if (NewLine) {
1989 OriginalRow = MenuOption->Row;
1990
1991 Width = GetWidth (MenuOption->ThisTag, MenuOption->Handle);
1992
1993 for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
1994 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
1995 PrintStringAt (Col, MenuOption->Row, OutputString);
1996 }
1997 //
1998 // If there is more string to process print on the next row and increment the Skip value
1999 //
2000 if (StrLen (&MenuOption->Description[Index])) {
2001 MenuOption->Row++;
2002 }
2003
2004 FreePool (OutputString);
2005 }
2006
2007 MenuOption->Row = OriginalRow;
2008
2009 }
2010 }
2011
2012 if (((NewPos->ForwardLink != &Menu) && (ScreenOperation == UiDown)) ||
2013 ((NewPos->BackLink != &Menu) && (ScreenOperation == UiUp)) ||
2014 (ScreenOperation == UiNoOperation)
2015 ) {
2016 UpdateKeyHelp (MenuOption, FALSE);
2017 }
2018 } else {
2019 gST->ConOut->OutputString (gST->ConOut, MenuOption->Description);
2020 }
2021 //
2022 // Clear reverse attribute
2023 //
2024 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
2025 }
2026 //
2027 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2028 // if we didn't break halfway when process CfRefreshHighLight.
2029 //
2030 Repaint = SavedValue;
2031 break;
2032
2033 case CfUpdateHelpString:
2034 ControlFlag = CfPrepareToReadKey;
2035
2036 if (SubMenu &&
2037 (Repaint || NewLine ||
2038 (MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) ||
2039 (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) &&
2040 !(gClassOfVfr == EFI_GENERAL_APPLICATION_SUBCLASS)) {
2041 //
2042 // Don't print anything if it is a NULL help token
2043 //
2044 if (MenuOption->ThisTag->Help == 0x00000000) {
2045 StringPtr = (CHAR16 *) L"\0";
2046 } else {
2047 StringPtr = GetToken (MenuOption->ThisTag->Help, MenuOption->Handle);
2048 }
2049
2050 ProcessHelpString (StringPtr, &FormattedString, BottomRow - TopRow);
2051
2052 gST->ConOut->SetAttribute (gST->ConOut, HELP_TEXT | FIELD_BACKGROUND);
2053
2054 for (Index = 0; Index < BottomRow - TopRow; Index++) {
2055 //
2056 // Pad String with spaces to simulate a clearing of the previous line
2057 //
2058 for (; GetStringWidth (&FormattedString[Index * gHelpBlockWidth * 2]) / 2 < gHelpBlockWidth;) {
2059 StrCat (&FormattedString[Index * gHelpBlockWidth * 2], (CHAR16 *) L" ");
2060 }
2061
2062 PrintStringAt (
2063 LocalScreen.RightColumn - gHelpBlockWidth,
2064 Index + TopRow,
2065 &FormattedString[Index * gHelpBlockWidth * 2]
2066 );
2067 }
2068 }
2069 //
2070 // Reset this flag every time we finish using it.
2071 //
2072 Repaint = FALSE;
2073 NewLine = FALSE;
2074 break;
2075
2076 case CfPrepareToReadKey:
2077 ControlFlag = CfReadKey;
2078
2079 for (Index = 0; Index < MenuOption->IfrNumber; Index++) {
2080 FileFormTags = FileFormTags->NextFile;
2081 }
2082
2083 ScreenOperation = UiNoOperation;
2084
2085 Status = gBS->HandleProtocol (
2086 (VOID *) (UINTN) FileFormTags->FormTags.Tags[0].CallbackHandle,
2087 &gEfiFormCallbackProtocolGuid,
2088 (VOID **) &FormCallback
2089 );
2090
2091 break;
2092
2093 case CfReadKey:
2094 ControlFlag = CfScreenOperation;
2095
2096 OriginalTimeOut = FrontPageTimeOutValue;
2097 do {
2098 if (FrontPageTimeOutValue >= 0 && (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) && FrontPageTimeOutValue != (INT16) -1) {
2099 //
2100 // Remember that if set to 0, must immediately boot an option
2101 //
2102 if (FrontPageTimeOutValue == 0) {
2103 FrontPageTimeOutValue = 0xFFFF;
2104 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
2105 if (EFI_ERROR (Status)) {
2106 Status = EFI_TIMEOUT;
2107 }
2108 break;
2109 }
2110
2111 Status = UiWaitForSingleEvent (gST->ConIn->WaitForKey, ONE_SECOND);
2112 if (Status == EFI_TIMEOUT) {
2113 EFI_IFR_DATA_ENTRY *DataEntry;
2114
2115 DataEntry = (EFI_IFR_DATA_ENTRY *) (PageData + 1);
2116
2117 PageData->EntryCount = 1;
2118 Count = (UINT32) ((OriginalTimeOut - FrontPageTimeOutValue) * 100 / OriginalTimeOut);
2119 CopyMem (&DataEntry->Data, &Count, sizeof (UINT32));
2120
2121 if ((FormCallback != NULL) && (FormCallback->Callback != NULL)) {
2122 FormCallback->Callback (
2123 FormCallback,
2124 0xFFFF,
2125 (EFI_IFR_DATA_ARRAY *) PageData,
2126 NULL
2127 );
2128 }
2129 //
2130 // Count down 1 second
2131 //
2132 FrontPageTimeOutValue--;
2133
2134 } else {
2135 ASSERT (!EFI_ERROR (Status));
2136 PageData->EntryCount = 0;
2137 if ((FormCallback != NULL) && (FormCallback->Callback != NULL)) {
2138 FormCallback->Callback (
2139 FormCallback,
2140 0xFFFE,
2141 (EFI_IFR_DATA_ARRAY *) PageData,
2142 NULL
2143 );
2144 }
2145
2146 FrontPageTimeOutValue = 0xFFFF;
2147 }
2148 } else {
2149 //
2150 // Wait for user's selection, no auto boot
2151 //
2152 Status = UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0);
2153 }
2154 } while (Status == EFI_TIMEOUT);
2155
2156 if (gFirstIn) {
2157 gFirstIn = FALSE;
2158 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
2159 DisableQuietBoot ();
2160 }
2161
2162 if (Status == EFI_TIMEOUT) {
2163 Key.UnicodeChar = CHAR_CARRIAGE_RETURN;
2164 } else {
2165 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
2166 //
2167 // if we encounter error, continue to read another key in.
2168 //
2169 if (EFI_ERROR (Status)) {
2170 ControlFlag = CfReadKey;
2171 continue;
2172 }
2173 }
2174
2175 switch (Key.UnicodeChar) {
2176 case CHAR_CARRIAGE_RETURN:
2177 Selection = MenuOption;
2178 ScreenOperation = UiSelect;
2179 gDirection = 0;
2180 break;
2181
2182 //
2183 // We will push the adjustment of these numeric values directly to the input handler
2184 //
2185 case '+':
2186 case '-':
2187 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
2188
2189 if (Key.UnicodeChar == '+') {
2190 gDirection = SCAN_RIGHT;
2191 } else {
2192 gDirection = SCAN_LEFT;
2193 }
2194
2195 Status = ProcessOptions (MenuOption, TRUE, FileFormTagsHead, NULL, &OptionString);
2196 }
2197 break;
2198
2199 case '^':
2200 ScreenOperation = UiUp;
2201 break;
2202
2203 case 'V':
2204 case 'v':
2205 ScreenOperation = UiDown;
2206 break;
2207
2208 case ' ':
2209 if (gClassOfVfr != EFI_FRONT_PAGE_SUBCLASS) {
2210 if (SubMenu) {
2211 if (MenuOption->ThisTag->Operand == EFI_IFR_CHECKBOX_OP && !(MenuOption->ThisTag->GrayOut)) {
2212 gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
2213 gST->ConOut->OutputString (gST->ConOut, MenuOption->Description);
2214 Selection = MenuOption;
2215 ScreenOperation = UiSelect;
2216 }
2217 }
2218 }
2219 break;
2220
2221 case CHAR_NULL:
2222 if (((Key.ScanCode == SCAN_F1) && ((gFunctionKeySetting & FUNCTION_ONE) != FUNCTION_ONE)) ||
2223 ((Key.ScanCode == SCAN_F2) && ((gFunctionKeySetting & FUNCTION_TWO) != FUNCTION_TWO)) ||
2224 ((Key.ScanCode == SCAN_F9) && ((gFunctionKeySetting & FUNCTION_NINE) != FUNCTION_NINE)) ||
2225 ((Key.ScanCode == SCAN_F10) && ((gFunctionKeySetting & FUNCTION_TEN) != FUNCTION_TEN))
2226 ) {
2227 //
2228 // If the function key has been disabled, just ignore the key.
2229 //
2230 } else {
2231 for (Index = 0; Index < sizeof (gScanCodeToOperation) / sizeof (gScanCodeToOperation[0]); Index++) {
2232 if (Key.ScanCode == gScanCodeToOperation[Index].ScanCode) {
2233 if ((Key.ScanCode == SCAN_F9) || (Key.ScanCode == SCAN_F10)) {
2234 if (SubMenu) {
2235 ScreenOperation = gScanCodeToOperation[Index].ScreenOperation;
2236 }
2237 } else {
2238 ScreenOperation = gScanCodeToOperation[Index].ScreenOperation;
2239 }
2240 }
2241 }
2242 }
2243 break;
2244 }
2245 break;
2246
2247 case CfScreenOperation:
2248 IfrBinary = gBinaryDataHead;
2249
2250 //
2251 // Advance to the Ifr we are using
2252 //
2253 for (Index = 0; Index < gActiveIfr; Index++) {
2254 IfrBinary = IfrBinary->Next;
2255 }
2256
2257 if (ScreenOperation != UiPrevious && ScreenOperation != UiReset) {
2258 //
2259 // If the screen has no menu items, and the user didn't select UiPrevious, or UiReset
2260 // ignore the selection and go back to reading keys.
2261 //
2262 if (IsListEmpty (&Menu)) {
2263 ControlFlag = CfReadKey;
2264 break;
2265 }
2266 //
2267 // if there is nothing logical to place a cursor on, just move on to wait for a key.
2268 //
2269 for (Link = Menu.ForwardLink; Link != &Menu; Link = Link->ForwardLink) {
2270 NextMenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2271 if (!(NextMenuOption->ThisTag->GrayOut) && (NextMenuOption->ThisTag->Operand != EFI_IFR_SUBTITLE_OP)) {
2272 break;
2273 }
2274 }
2275
2276 if (Link == &Menu) {
2277 ControlFlag = CfPrepareToReadKey;
2278 break;
2279 }
2280 }
2281
2282 for (Index = 0;
2283 Index < sizeof (gScreenOperationToControlFlag) / sizeof (gScreenOperationToControlFlag[0]);
2284 Index++
2285 ) {
2286 if (ScreenOperation == gScreenOperationToControlFlag[Index].ScreenOperation) {
2287 ControlFlag = gScreenOperationToControlFlag[Index].ControlFlag;
2288 }
2289 }
2290
2291 break;
2292
2293 case CfUiPrevious:
2294 ControlFlag = CfCheckSelection;
2295 //
2296 // Check for tags that might have LATE_CHECK enabled. If they do, we can't switch pages or save NV data.
2297 //
2298 if (MenuOption != NULL) {
2299 if (!SelectionsAreValid (MenuOption, FileFormTagsHead)) {
2300 Selection = NULL;
2301 Repaint = TRUE;
2302 break;
2303 }
2304 }
2305
2306 if (IsListEmpty (&gMenuList)) {
2307 Selection = NULL;
2308 if (IsListEmpty (&Menu)) {
2309 ControlFlag = CfReadKey;
2310 }
2311 break;
2312 }
2313
2314 gLastOpr = TRUE;
2315
2316 while (gMenuRefreshHead != NULL) {
2317 OldMenuRefreshEntry = gMenuRefreshHead->Next;
2318
2319 FreePool (gMenuRefreshHead);
2320
2321 gMenuRefreshHead = OldMenuRefreshEntry;
2322 }
2323 //
2324 // Remove the Cached page entry, free and init the menus, flag Selection as jumping to previous page and a valid Tag
2325 //
2326 if (SubMenu) {
2327 UiRemoveMenuListEntry (MenuOption, &Selection);
2328 Selection->Previous = TRUE;
2329 UiFreeMenu ();
2330 UiInitMenu ();
2331 }
2332
2333 gActiveIfr = Selection->IfrNumber;
2334 return Selection;
2335
2336 case CfUiSelect:
2337 ControlFlag = CfCheckSelection;
2338
2339 ExtractRequestedNvMap (FileFormTags, MenuOption->ThisTag->VariableNumber, &VariableDefinition);
2340
2341 if (SubMenu) {
2342 if ((MenuOption->ThisTag->Operand == EFI_IFR_TEXT_OP &&
2343 !(MenuOption->ThisTag->Flags & EFI_IFR_FLAG_INTERACTIVE)) ||
2344 (MenuOption->ThisTag->GrayOut) ||
2345 (MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) ||
2346 (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
2347 Selection = NULL;
2348 break;
2349 }
2350
2351 NewLine = TRUE;
2352 UpdateKeyHelp (MenuOption, TRUE);
2353 Status = ProcessOptions (MenuOption, TRUE, FileFormTagsHead, PageData, &OptionString);
2354
2355 if (EFI_ERROR (Status)) {
2356 Selection = NULL;
2357 Repaint = TRUE;
2358 break;
2359 }
2360
2361 if (OptionString != NULL) {
2362 PrintStringAt (LocalScreen.LeftColumn + gPromptBlockWidth + 1, MenuOption->Row, OptionString);
2363 }
2364
2365 if (MenuOption->ThisTag->Flags & EFI_IFR_FLAG_INTERACTIVE) {
2366 Selection = MenuOption;
2367 }
2368
2369 if (Selection == NULL) {
2370 break;
2371 }
2372
2373 Location = (UINT8 *) &PageData->EntryCount;
2374
2375 //
2376 // If not a goto, dump single piece of data, otherwise dump everything
2377 //
2378 if (Selection->ThisTag->Operand == EFI_IFR_REF_OP) {
2379 //
2380 // Check for tags that might have LATE_CHECK enabled. If they do, we can't switch pages or save NV data.
2381 //
2382 if (!SelectionsAreValid (MenuOption, FileFormTagsHead)) {
2383 Selection = NULL;
2384 Repaint = TRUE;
2385 break;
2386 }
2387
2388 UiAddMenuListEntry (Selection);
2389 gPriorMenuEntry = 0;
2390
2391 //
2392 // Now that we added a menu entry specific to a goto, we can always go back when someone hits the UiPrevious
2393 //
2394 UiMenuList = CR (gMenuList.ForwardLink, UI_MENU_LIST, MenuLink, UI_MENU_LIST_SIGNATURE);
2395 UiMenuList->FormerEntryNumber = MenuOption->EntryNumber;
2396
2397 gLastOpr = FALSE;
2398
2399 //
2400 // Rewind to the beginning of the menu
2401 //
2402 for (; NewPos->BackLink != &Menu; NewPos = NewPos->BackLink)
2403 ;
2404
2405 //
2406 // Get Total Count of Menu entries
2407 //
2408 for (Count = 1; NewPos->ForwardLink != &Menu; NewPos = NewPos->ForwardLink) {
2409 Count++;
2410 }
2411 //
2412 // Rewind to the beginning of the menu
2413 //
2414 for (; NewPos->BackLink != &Menu; NewPos = NewPos->BackLink)
2415 ;
2416
2417 //
2418 // Copy the number of entries being described to the PageData location
2419 //
2420 CopyMem (&Location[0], &Count, sizeof (UINT32));
2421
2422 for (Index = 4; NewPos->ForwardLink != &Menu; Index = Index + MenuOption->ThisTag->StorageWidth + 2) {
2423
2424 MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2425 Location[Index] = MenuOption->ThisTag->Operand;
2426 Location[Index + 1] = (UINT8) (MenuOption->ThisTag->StorageWidth + 4);
2427 CopyMem (
2428 &Location[Index + 4],
2429 &VariableDefinition->NvRamMap[MenuOption->ThisTag->StorageStart],
2430 MenuOption->ThisTag->StorageWidth
2431 );
2432 NewPos = NewPos->ForwardLink;
2433 }
2434 } else {
2435
2436 gPriorMenuEntry = MenuOption->EntryNumber;
2437
2438 Count = 1;
2439
2440 //
2441 // Copy the number of entries being described to the PageData location
2442 //
2443 CopyMem (&Location[0], &Count, sizeof (UINT32));
2444
2445 //
2446 // Start at PageData[4] since the EntryCount is a UINT32
2447 //
2448 Index = 4;
2449
2450 //
2451 // Copy data to destination
2452 //
2453 Location[Index] = MenuOption->ThisTag->Operand;
2454 Location[Index + 1] = (UINT8) (MenuOption->ThisTag->StorageWidth + 4);
2455 CopyMem (
2456 &Location[Index + 4],
2457 &VariableDefinition->NvRamMap[MenuOption->ThisTag->StorageStart],
2458 MenuOption->ThisTag->StorageWidth
2459 );
2460 }
2461 }
2462 break;
2463
2464 case CfUiReset:
2465 ControlFlag = CfCheckSelection;
2466 gLastOpr = FALSE;
2467 if (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) {
2468 break;
2469 }
2470 //
2471 // If NV flag is up, prompt user
2472 //
2473 if (gNvUpdateRequired) {
2474 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
2475
2476 YesResponse = gYesResponse[0];
2477 NoResponse = gNoResponse[0];
2478
2479 do {
2480 CreateDialog (3, TRUE, 0, NULL, &Key, gEmptyString, gAreYouSure, gEmptyString);
2481 } while
2482 (
2483 (Key.ScanCode != SCAN_ESC) &&
2484 ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (NoResponse | UPPER_LOWER_CASE_OFFSET)) &&
2485 ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (YesResponse | UPPER_LOWER_CASE_OFFSET))
2486 );
2487
2488 //
2489 // If the user hits the YesResponse key
2490 //
2491 if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (YesResponse | UPPER_LOWER_CASE_OFFSET)) {
2492 } else {
2493 Repaint = TRUE;
2494 NewLine = TRUE;
2495 break;
2496 }
2497 }
2498 //
2499 // Check for tags that might have LATE_CHECK enabled. If they do, we can't switch pages or save NV data.
2500 //
2501 if (MenuOption != NULL) {
2502 if (!SelectionsAreValid (MenuOption, FileFormTagsHead)) {
2503 Selection = NULL;
2504 Repaint = TRUE;
2505 NewLine = TRUE;
2506 break;
2507 }
2508 }
2509
2510 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
2511 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
2512
2513 if (SubMenu) {
2514 UiFreeMenuList ();
2515 gST->ConOut->ClearScreen (gST->ConOut);
2516 return NULL;
2517 }
2518
2519 UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->Flags, FALSE);
2520 UpdateStatusBar (NV_UPDATE_REQUIRED, MenuOption->ThisTag->Flags, FALSE);
2521
2522 if (IfrBinary->UnRegisterOnExit) {
2523 Hii->RemovePack (Hii, MenuOption->Handle);
2524 }
2525
2526 UiFreeMenu ();
2527
2528 //
2529 // Clean up the allocated data buffers
2530 //
2531 FreeData (FileFormTagsHead, FormattedString, OptionString);
2532
2533 gST->ConOut->ClearScreen (gST->ConOut);
2534 return NULL;
2535
2536 case CfUiLeft:
2537 ControlFlag = CfCheckSelection;
2538 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
2539 if (MenuOption->Skip == 1) {
2540 //
2541 // In the tail of the Date/Time op-code set, go left.
2542 //
2543 NewPos = NewPos->BackLink;
2544 } else {
2545 //
2546 // In the middle of the Data/Time op-code set, go left.
2547 //
2548 NextMenuOption = CR (NewPos->ForwardLink, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2549 if (NextMenuOption->Skip == 1) {
2550 NewPos = NewPos->BackLink;
2551 }
2552 }
2553 }
2554 break;
2555
2556 case CfUiRight:
2557 ControlFlag = CfCheckSelection;
2558 if ((MenuOption->Skip == 0) &&
2559 ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP))
2560 ) {
2561 //
2562 // We are in the head or middle of the Date/Time op-code set, advance right.
2563 //
2564 NewPos = NewPos->ForwardLink;
2565 }
2566 break;
2567
2568 case CfUiUp:
2569 ControlFlag = CfCheckSelection;
2570
2571 if (NewPos->BackLink != &Menu) {
2572 NewLine = TRUE;
2573 //
2574 // Adjust Date/Time position before we advance forward.
2575 //
2576 AdjustDateAndTimePosition (TRUE, &NewPos);
2577
2578 //
2579 // Caution that we have already rewind to the top, don't go backward in this situation.
2580 //
2581 if (NewPos->BackLink != &Menu) {
2582 NewPos = NewPos->BackLink;
2583 }
2584
2585 PreviousMenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2586
2587 //
2588 // Since the behavior of hitting the up arrow on a Date/Time op-code is intended
2589 // to be one that back to the previous set of op-codes, we need to advance to the sencond
2590 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2591 // checking can be done.
2592 //
2593 DataAndTimeLineNumberPad = AdjustDateAndTimePosition (TRUE, &NewPos);
2594
2595 if (SubMenu) {
2596 //
2597 // If the previous MenuOption contains a display-only op-code, skip to the next one
2598 //
2599 if (PreviousMenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || PreviousMenuOption->ThisTag->GrayOut) {
2600 //
2601 // This is ok as long as not at the end of the list
2602 //
2603 if (NewPos->BackLink == &Menu) {
2604 //
2605 // If we are at the start of the list, then this list must start with a display only
2606 // piece of data, so do not allow the backward motion
2607 //
2608 ScreenOperation = UiDown;
2609
2610 if (PreviousMenuOption->Row <= TopRow) {
2611 if (TopOfScreen->BackLink != &Menu) {
2612 TopOfScreen = TopOfScreen->BackLink;
2613 Repaint = TRUE;
2614 }
2615 }
2616
2617 UpdateStatusBar (INPUT_ERROR, PreviousMenuOption->ThisTag->Flags, FALSE);
2618 break;
2619 }
2620 }
2621 }
2622 //
2623 // Check the previous menu entry to see if it was a zero-length advance. If it was,
2624 // don't worry about a redraw.
2625 //
2626 if ((MenuOption->Row - PreviousMenuOption->Skip - DataAndTimeLineNumberPad < TopRow) ||
2627 (PreviousMenuOption->Skip > MenuOption->Row)
2628 ) {
2629 do {
2630 if (TopOfScreen->BackLink == &Menu) {
2631 break;
2632 }
2633
2634 Repaint = TRUE;
2635
2636 //
2637 // Is the current top of screen a zero-advance op-code?
2638 // If so, keep moving forward till we hit a >0 advance op-code
2639 //
2640 SavedMenuOption = CR (TopOfScreen->BackLink, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2641 TopOfScreen = TopOfScreen->BackLink;
2642 } while (SavedMenuOption->Skip == 0);
2643 //
2644 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2645 //
2646 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2647 }
2648
2649 UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->Flags, FALSE);
2650 } else {
2651 if (SubMenu) {
2652 SavedMenuOption = MenuOption;
2653 MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2654 if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || MenuOption->ThisTag->GrayOut) {
2655 //
2656 // If we are at the end of the list and sitting on a text op, we need to more forward
2657 //
2658 ScreenOperation = UiDown;
2659 ControlFlag = CfScreenOperation;
2660 break;
2661 }
2662
2663 MenuOption = SavedMenuOption;
2664 }
2665 }
2666 break;
2667
2668 case CfUiPageUp:
2669 ControlFlag = CfCheckSelection;
2670
2671 SavedListEntry = NewPos;
2672 Link = TopOfScreen;
2673 for (Index = BottomRow; Index >= TopRow + 1; Index -= MenuOption->Skip) {
2674 if (Link->BackLink == &Menu) {
2675 TopOfScreen = Link;
2676 Link = SavedListEntry;
2677 MenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2678 break;
2679 }
2680
2681 NewLine = TRUE;
2682 Repaint = TRUE;
2683 Link = Link->BackLink;
2684 MenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2685 TopOfScreen = Link;
2686 SavedListEntry = Link;
2687 }
2688
2689 NewPos = Link;
2690
2691 //
2692 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2693 // Don't do this when we are already in the first page.
2694 //
2695 if (Repaint) {
2696 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2697 AdjustDateAndTimePosition (TRUE, &NewPos);
2698 MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2699 }
2700 break;
2701
2702 case CfUiPageDown:
2703 ControlFlag = CfCheckSelection;
2704
2705 SavedListEntry = NewPos;
2706 Link = TopOfScreen;
2707 NewPos = TopOfScreen;
2708 for (Index = TopRow; Index <= BottomRow - 1; Index += MenuOption->Skip) {
2709 if (NewPos->ForwardLink == &Menu) {
2710 NewPos = SavedListEntry;
2711 MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2712 Link = TopOfScreen;
2713 NewLine = FALSE;
2714 Repaint = FALSE;
2715 break;
2716 }
2717
2718 NewLine = TRUE;
2719 Repaint = TRUE;
2720 MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2721 NewPos = NewPos->ForwardLink;
2722 Link = NewPos;
2723 }
2724
2725 TopOfScreen = Link;
2726
2727 //
2728 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2729 // Don't do this when we are already in the last page.
2730 //
2731 if (Repaint) {
2732 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2733 AdjustDateAndTimePosition (TRUE, &NewPos);
2734 MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2735 }
2736 break;
2737
2738 case CfUiDown:
2739 ControlFlag = CfCheckSelection;
2740 //
2741 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
2742 // to be one that progresses to the next set of op-codes, we need to advance to the last
2743 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2744 // checking can be done. The only other logic we need to introduce is that if a Date/Time
2745 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
2746 // the Date/Time op-code.
2747 //
2748 DataAndTimeLineNumberPad = AdjustDateAndTimePosition (FALSE, &NewPos);
2749
2750 if (NewPos->ForwardLink != &Menu) {
2751 NewLine = TRUE;
2752 NewPos = NewPos->ForwardLink;
2753 NextMenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2754
2755 if (SubMenu) {
2756 //
2757 // If the next MenuOption contains a display-only op-code, skip to the next one
2758 // Also if the next MenuOption is date or time,
2759 //
2760 if (NextMenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || NextMenuOption->ThisTag->GrayOut) {
2761 //
2762 // This is ok as long as not at the end of the list
2763 //
2764 if (NewPos == &Menu) {
2765 //
2766 // If we are at the end of the list, then this list must end with a display only
2767 // piece of data, so do not allow the forward motion
2768 //
2769 UpdateStatusBar (INPUT_ERROR, NextMenuOption->ThisTag->Flags, FALSE);
2770 NewPos = NewPos->BackLink;
2771 ScreenOperation = UiUp;
2772 break;
2773 }
2774 }
2775 }
2776 //
2777 // An option might be multi-line, so we need to reflect that data in the overall skip value
2778 //
2779 UpdateOptionSkipLines (PageData, NextMenuOption, FileFormTagsHead, &OptionString, SkipValue);
2780
2781 if (NextMenuOption->Skip > 1) {
2782 Temp = MenuOption->Row + MenuOption->Skip + NextMenuOption->Skip - 1;
2783 } else {
2784 Temp = MenuOption->Row + MenuOption->Skip + DataAndTimeLineNumberPad;
2785 }
2786 //
2787 // If we are going to scroll
2788 //
2789 if (Temp > BottomRow) {
2790 do {
2791 //
2792 // Is the current top of screen a zero-advance op-code?
2793 // If so, keep moving forward till we hit a >0 advance op-code
2794 //
2795 SavedMenuOption = CR (TopOfScreen, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2796
2797 //
2798 // If bottom op-code is more than one line or top op-code is more than one line
2799 //
2800 if ((NextMenuOption->Skip > 1) || (MenuOption->Skip > 1)) {
2801 //
2802 // Is the bottom op-code greater than or equal in size to the top op-code?
2803 //
2804 if ((Temp - BottomRow) >= (SavedMenuOption->Skip - OldSkipValue)) {
2805 //
2806 // Skip the top op-code
2807 //
2808 TopOfScreen = TopOfScreen->ForwardLink;
2809 Difference = (Temp - BottomRow) - (SavedMenuOption->Skip - OldSkipValue);
2810
2811 OldSkipValue = Difference;
2812
2813 SavedMenuOption = CR (TopOfScreen, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2814
2815 //
2816 // If we have a remainder, skip that many more op-codes until we drain the remainder
2817 //
2818 for (;
2819 Difference >= (INTN) SavedMenuOption->Skip;
2820 Difference = Difference - (INTN) SavedMenuOption->Skip
2821 ) {
2822 //
2823 // Since the Difference is greater than or equal to this op-code's skip value, skip it
2824 //
2825 TopOfScreen = TopOfScreen->ForwardLink;
2826 SavedMenuOption = CR (TopOfScreen, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2827 if (Difference < (INTN) SavedMenuOption->Skip) {
2828 Difference = SavedMenuOption->Skip - Difference - 1;
2829 break;
2830 } else {
2831 if (Difference == (INTN) SavedMenuOption->Skip) {
2832 TopOfScreen = TopOfScreen->ForwardLink;
2833 SavedMenuOption = CR (TopOfScreen, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2834 Difference = SavedMenuOption->Skip - Difference;
2835 break;
2836 }
2837 }
2838 }
2839 //
2840 // Since we will act on this op-code in the next routine, and increment the
2841 // SkipValue, set the skips to one less than what is required.
2842 //
2843 SkipValue = Difference - 1;
2844
2845 } else {
2846 //
2847 // Since we will act on this op-code in the next routine, and increment the
2848 // SkipValue, set the skips to one less than what is required.
2849 //
2850 SkipValue = OldSkipValue + (Temp - BottomRow) - 1;
2851 }
2852 } else {
2853 if ((OldSkipValue + 1) == (INTN) SavedMenuOption->Skip) {
2854 TopOfScreen = TopOfScreen->ForwardLink;
2855 break;
2856 } else {
2857 SkipValue = OldSkipValue;
2858 }
2859 }
2860 //
2861 // If the op-code at the top of the screen is more than one line, let's not skip it yet
2862 // Let's set a skip flag to smoothly scroll the top of the screen.
2863 //
2864 if (SavedMenuOption->Skip > 1) {
2865 if (SavedMenuOption == NextMenuOption) {
2866 SkipValue = 0;
2867 } else {
2868 SkipValue++;
2869 }
2870 } else {
2871 SkipValue = 0;
2872 TopOfScreen = TopOfScreen->ForwardLink;
2873 }
2874 } while (SavedMenuOption->Skip == 0);
2875
2876 Repaint = TRUE;
2877 OldSkipValue = SkipValue;
2878 }
2879
2880 UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->Flags, FALSE);
2881
2882 } else {
2883 if (SubMenu) {
2884 SavedMenuOption = MenuOption;
2885 MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2886 if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || MenuOption->ThisTag->GrayOut) {
2887 //
2888 // If we are at the end of the list and sitting on a text op, we need to more forward
2889 //
2890 ScreenOperation = UiUp;
2891 ControlFlag = CfScreenOperation;
2892 break;
2893 }
2894
2895 MenuOption = SavedMenuOption;
2896 //
2897 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
2898 //
2899 AdjustDateAndTimePosition (TRUE, &NewPos);
2900 }
2901 }
2902 break;
2903
2904 case CfUiSave:
2905 ControlFlag = CfCheckSelection;
2906 //
2907 // Check for tags that might have LATE_CHECK enabled. If they do, we can't switch pages or save NV data.
2908 //
2909 if (MenuOption != NULL) {
2910 if (!SelectionsAreValid (MenuOption, FileFormTagsHead)) {
2911 Selection = NULL;
2912 Repaint = TRUE;
2913 break;
2914 }
2915 }
2916 //
2917 // If callbacks are active, and the callback has a Write method, try to use it
2918 //
2919 if (FileFormTags->VariableDefinitions->VariableName == NULL) {
2920 if ((FormCallback != NULL) && (FormCallback->NvWrite != NULL)) {
2921 Status = FormCallback->NvWrite (
2922 FormCallback,
2923 (CHAR16 *) L"Setup",
2924 &FileFormTags->FormTags.Tags[0].GuidValue,
2925 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
2926 VariableDefinition->VariableSize,
2927 (VOID *) VariableDefinition->NvRamMap,
2928 &gResetRequired
2929 );
2930
2931 } else {
2932 Status = gRT->SetVariable (
2933 (CHAR16 *) L"Setup",
2934 &FileFormTags->FormTags.Tags[0].GuidValue,
2935 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
2936 VariableDefinition->VariableSize,
2937 (VOID *) VariableDefinition->NvRamMap
2938 );
2939 }
2940 } else {
2941 VariableDefinition = FileFormTags->VariableDefinitions;
2942
2943 for (; VariableDefinition != NULL; VariableDefinition = VariableDefinition->Next) {
2944 if ((FormCallback != NULL) && (FormCallback->NvWrite != NULL)) {
2945 Status = FormCallback->NvWrite (
2946 FormCallback,
2947 VariableDefinition->VariableName,
2948 &VariableDefinition->Guid,
2949 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
2950 VariableDefinition->VariableSize,
2951 (VOID *) VariableDefinition->NvRamMap,
2952 &gResetRequired
2953 );
2954
2955 } else {
2956 Status = gRT->SetVariable (
2957 VariableDefinition->VariableName,
2958 &VariableDefinition->Guid,
2959 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
2960 VariableDefinition->VariableSize,
2961 (VOID *) VariableDefinition->NvRamMap
2962 );
2963 }
2964 }
2965 }
2966
2967 UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->Flags, FALSE);
2968 UpdateStatusBar (NV_UPDATE_REQUIRED, MenuOption->ThisTag->Flags, FALSE);
2969 break;
2970
2971 case CfUiDefault:
2972 ControlFlag = CfCheckSelection;
2973
2974 NvMapListHead = NULL;
2975
2976 Status = Hii->GetDefaultImage (Hii, MenuOption->Handle, EFI_IFR_FLAG_DEFAULT, &NvMapListHead);
2977
2978 if (!EFI_ERROR (Status)) {
2979 ASSERT_EFI_ERROR (NULL != NvMapListHead);
2980
2981 NvMapListNode = NvMapListHead;
2982
2983 while (NULL != NvMapListNode) {
2984 if (FileFormTags->VariableDefinitions->VariableId == NvMapListNode->VariablePack->VariableId) {
2985 NvMap = (VOID *) ((CHAR8 *) NvMapListNode->VariablePack + sizeof (EFI_HII_VARIABLE_PACK) + NvMapListNode->VariablePack->VariableNameLength);
2986 NvMapSize = NvMapListNode->VariablePack->Header.Length - sizeof (EFI_HII_VARIABLE_PACK) - NvMapListNode->VariablePack->VariableNameLength;
2987 break;
2988 }
2989 NvMapListNode = NvMapListNode->NextVariablePack;
2990 }
2991
2992 //
2993 // Free the buffer that was allocated.
2994 //
2995 FreePool (FileFormTags->VariableDefinitions->NvRamMap);
2996 FreePool (FileFormTags->VariableDefinitions->FakeNvRamMap);
2997
2998 //
2999 // Allocate, copy the NvRamMap.
3000 //
3001 FileFormTags->VariableDefinitions->VariableFakeSize = (UINT16) (FileFormTags->VariableDefinitions->VariableFakeSize - FileFormTags->VariableDefinitions->VariableSize);
3002 FileFormTags->VariableDefinitions->VariableSize = (UINT16) NvMapSize;
3003 FileFormTags->VariableDefinitions->VariableFakeSize = (UINT16) (FileFormTags->VariableDefinitions->VariableFakeSize + FileFormTags->VariableDefinitions->VariableSize);
3004
3005 FileFormTags->VariableDefinitions->NvRamMap = AllocateZeroPool (FileFormTags->VariableDefinitions->VariableSize);
3006 ASSERT (FileFormTags->VariableDefinitions->NvRamMap != NULL);
3007
3008 FileFormTags->VariableDefinitions->FakeNvRamMap = AllocateZeroPool (NvMapSize + FileFormTags->VariableDefinitions->VariableFakeSize);
3009 ASSERT (FileFormTags->VariableDefinitions->FakeNvRamMap != NULL);
3010
3011 CopyMem (FileFormTags->VariableDefinitions->NvRamMap, NvMap, NvMapSize);
3012 FreePool (NvMapListHead);
3013 }
3014
3015 UpdateStatusBar (NV_UPDATE_REQUIRED, MenuOption->ThisTag->Flags, TRUE);
3016 Repaint = TRUE;
3017 //
3018 // After the repaint operation, we should refresh the highlight.
3019 //
3020 NewLine = TRUE;
3021 break;
3022
3023 case CfUiNoOperation:
3024 ControlFlag = CfCheckSelection;
3025 break;
3026
3027 case CfExit:
3028 while (gMenuRefreshHead != NULL) {
3029 OldMenuRefreshEntry = gMenuRefreshHead->Next;
3030
3031 FreePool (gMenuRefreshHead);
3032
3033 gMenuRefreshHead = OldMenuRefreshEntry;
3034 }
3035
3036 gST->ConOut->SetCursorPosition (gST->ConOut, 0, Row + 4);
3037 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
3038 gST->ConOut->OutputString (gST->ConOut, (CHAR16 *) L"\n");
3039
3040 gActiveIfr = MenuOption->IfrNumber;
3041 return Selection;
3042
3043 default:
3044 break;
3045 }
3046 }
3047 }
3048
3049 BOOLEAN
3050 ValueIsScroll (