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