]> git.proxmox.com Git - mirror_edk2.git/blob - EdkModulePkg/Universal/UserInterface/SetupBrowser/Dxe/Ui.c
854d023f8e4a29b2a2e946a6219b2cea88fbd852
[mirror_edk2.git] / EdkModulePkg / Universal / UserInterface / SetupBrowser / Dxe / Ui.c
1 /*++
2
3 Copyright (c) 2006, 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 UINTN Loop;
1299 UINT16 Width;
1300 UINTN Row;
1301 UINTN OriginalRow;
1302 CHAR16 *OutputString;
1303 CHAR16 *OptionString;
1304
1305 Row = 0;
1306 OptionString = *OptionalString;
1307 OutputString = NULL;
1308
1309 ProcessOptions (MenuOption, FALSE, FileFormTagsHead, PageData, &OptionString);
1310
1311 if (OptionString != NULL) {
1312 //
1313 // If leading spaces on OptionString - remove the spaces
1314 //
1315 for (Index = 0; OptionString[Index] == L' '; Index++)
1316 ;
1317
1318 for (Loop = 0; OptionString[Index] != CHAR_NULL; Index++) {
1319 OptionString[Loop] = OptionString[Index];
1320 Loop++;
1321 }
1322
1323 OptionString[Loop] = CHAR_NULL;
1324
1325 Width = (UINT16) gOptionBlockWidth;
1326
1327 OriginalRow = Row;
1328
1329 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
1330 //
1331 // If there is more string to process print on the next row and increment the Skip value
1332 //
1333 if (StrLen (&OptionString[Index])) {
1334 if (SkipValue == 0) {
1335 Row++;
1336 //
1337 // Since the Number of lines for this menu entry may or may not be reflected accurately
1338 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1339 // some testing to ensure we are keeping this in-sync.
1340 //
1341 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1342 //
1343 if ((Row - OriginalRow) >= MenuOption->Skip) {
1344 MenuOption->Skip++;
1345 }
1346 }
1347 }
1348
1349 gBS->FreePool (OutputString);
1350 if (SkipValue != 0) {
1351 SkipValue--;
1352 }
1353 }
1354
1355 Row = OriginalRow;
1356 }
1357
1358 *OptionalString = OptionString;
1359 }
1360 //
1361 // Search table for UiDisplayMenu()
1362 //
1363 SCAN_CODE_TO_SCREEN_OPERATION gScanCodeToOperation[] = {
1364 { SCAN_UP, UiUp },
1365 { SCAN_DOWN, UiDown },
1366 { SCAN_PAGE_UP, UiPageUp },
1367 { SCAN_PAGE_DOWN, UiPageDown},
1368 { SCAN_ESC, UiReset},
1369 { SCAN_F2, UiPrevious},
1370 { SCAN_LEFT, UiLeft },
1371 { SCAN_RIGHT, UiRight },
1372 { SCAN_F9, UiDefault},
1373 { SCAN_F10, UiSave }
1374 };
1375
1376 SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag[] = {
1377 { UiNoOperation, CfUiNoOperation },
1378 { UiDefault, CfUiDefault },
1379 { UiSelect, CfUiSelect },
1380 { UiUp, CfUiUp},
1381 { UiDown, CfUiDown },
1382 { UiLeft, CfUiLeft },
1383 { UiRight, CfUiRight },
1384 { UiReset, CfUiReset },
1385 { UiSave, CfUiSave },
1386 { UiPrevious, CfUiPrevious },
1387 { UiPageUp, CfUiPageUp },
1388 { UiPageDown, CfUiPageDown }
1389 };
1390
1391 UI_MENU_OPTION *
1392 UiDisplayMenu (
1393 IN BOOLEAN SubMenu,
1394 IN EFI_FILE_FORM_TAGS *FileFormTagsHead,
1395 OUT EFI_IFR_DATA_ARRAY *PageData
1396 )
1397 /*++
1398
1399 Routine Description:
1400 Display menu and wait for user to select one menu option, then return it.
1401 If AutoBoot is enabled, then if user doesn't select any option,
1402 after period of time, it will automatically return the first menu option.
1403
1404 Arguments:
1405 SubMenu - Indicate is sub menu.
1406 FileFormTagsHead - A pointer to the EFI_FILE_FORM_TAGS structure.
1407 PageData - A pointer to the EFI_IFR_DATA_ARRAY.
1408
1409 Returns:
1410 Return the pointer of the menu which selected,
1411 otherwise return NULL.
1412
1413 --*/
1414 {
1415 INTN SkipValue;
1416 INTN Difference;
1417 INTN OldSkipValue;
1418 UINTN Row;
1419 UINTN Col;
1420 UINTN Temp;
1421 UINTN Temp2;
1422 UINTN TopRow;
1423 UINTN BottomRow;
1424 UINTN OriginalRow;
1425 UINTN Index;
1426 UINTN DataAndTimeLineNumberPad;
1427 UINT32 Count;
1428 INT16 OriginalTimeOut;
1429 UINT8 *Location;
1430 UINT16 Width;
1431 CHAR16 *StringPtr;
1432 CHAR16 *OptionString;
1433 CHAR16 *OutputString;
1434 CHAR16 *FormattedString;
1435 CHAR16 YesResponse;
1436 CHAR16 NoResponse;
1437 BOOLEAN NewLine;
1438 BOOLEAN Repaint;
1439 BOOLEAN SavedValue;
1440 EFI_STATUS Status;
1441 UI_MENU_LIST *UiMenuList;
1442 EFI_INPUT_KEY Key;
1443 LIST_ENTRY *Link;
1444 LIST_ENTRY *NewPos;
1445 LIST_ENTRY *TopOfScreen;
1446 LIST_ENTRY *SavedListEntry;
1447 UI_MENU_OPTION *Selection;
1448 UI_MENU_OPTION *MenuOption;
1449 UI_MENU_OPTION *NextMenuOption;
1450 UI_MENU_OPTION *SavedMenuOption;
1451 UI_MENU_OPTION *PreviousMenuOption;
1452 EFI_IFR_BINARY *IfrBinary;
1453 UI_CONTROL_FLAG ControlFlag;
1454 EFI_SCREEN_DESCRIPTOR LocalScreen;
1455 EFI_FILE_FORM_TAGS *FileFormTags;
1456 MENU_REFRESH_ENTRY *MenuRefreshEntry;
1457 MENU_REFRESH_ENTRY *OldMenuRefreshEntry;
1458 UI_SCREEN_OPERATION ScreenOperation;
1459 EFI_VARIABLE_DEFINITION *VariableDefinition;
1460 EFI_FORM_CALLBACK_PROTOCOL *FormCallback;
1461 EFI_HII_VARIABLE_PACK_LIST *NvMapListHead;
1462 EFI_HII_VARIABLE_PACK_LIST *NvMapListNode;
1463 VOID *NvMap;
1464 UINTN NvMapSize;
1465
1466 CopyMem (&LocalScreen, &gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR));
1467
1468 VariableDefinition = NULL;
1469 Status = EFI_SUCCESS;
1470 FormattedString = NULL;
1471 OptionString = NULL;
1472 ScreenOperation = UiNoOperation;
1473 NewLine = TRUE;
1474 FormCallback = NULL;
1475 FileFormTags = NULL;
1476 OutputString = NULL;
1477 gUpArrow = FALSE;
1478 gDownArrow = FALSE;
1479 SkipValue = 0;
1480 OldSkipValue = 0;
1481 MenuRefreshEntry = gMenuRefreshHead;
1482 OldMenuRefreshEntry = gMenuRefreshHead;
1483 NextMenuOption = NULL;
1484 PreviousMenuOption = NULL;
1485 SavedMenuOption = NULL;
1486 IfrBinary = NULL;
1487 NvMap = NULL;
1488 NvMapSize = 0;
1489
1490 ZeroMem (&Key, sizeof (EFI_INPUT_KEY));
1491
1492 if (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) {
1493 TopRow = LocalScreen.TopRow + FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1494 Row = LocalScreen.TopRow + FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1495 } else {
1496 TopRow = LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1497 Row = LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1498 }
1499
1500 if (SubMenu) {
1501 Col = LocalScreen.LeftColumn;
1502 } else {
1503 Col = LocalScreen.LeftColumn + LEFT_SKIPPED_COLUMNS;
1504 }
1505
1506 BottomRow = LocalScreen.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT - SCROLL_ARROW_HEIGHT - 1;
1507
1508 TopOfScreen = Menu.ForwardLink;
1509 Repaint = TRUE;
1510 MenuOption = NULL;
1511
1512 //
1513 // Get user's selection
1514 //
1515 Selection = NULL;
1516 NewPos = Menu.ForwardLink;
1517 gST->ConOut->EnableCursor (gST->ConOut, FALSE);
1518
1519 UpdateStatusBar (REFRESH_STATUS_BAR, (UINT8) 0, TRUE);
1520
1521 ControlFlag = CfInitialization;
1522
1523 while (TRUE) {
1524 switch (ControlFlag) {
1525 case CfInitialization:
1526 ControlFlag = CfCheckSelection;
1527 if (gExitRequired) {
1528 ScreenOperation = UiReset;
1529 ControlFlag = CfScreenOperation;
1530 } else if (gSaveRequired) {
1531 ScreenOperation = UiSave;
1532 ControlFlag = CfScreenOperation;
1533 } else if (IsListEmpty (&Menu)) {
1534 ControlFlag = CfReadKey;
1535 }
1536 break;
1537
1538 case CfCheckSelection:
1539 if (Selection != NULL) {
1540 ControlFlag = CfExit;
1541 } else {
1542 ControlFlag = CfRepaint;
1543 }
1544
1545 FileFormTags = FileFormTagsHead;
1546 break;
1547
1548 case CfRepaint:
1549 ControlFlag = CfRefreshHighLight;
1550
1551 if (Repaint) {
1552 //
1553 // Display menu
1554 //
1555 SavedMenuOption = MenuOption;
1556 gDownArrow = FALSE;
1557 gUpArrow = FALSE;
1558 Row = TopRow;
1559
1560 Temp = SkipValue;
1561 Temp2 = SkipValue;
1562
1563 ClearLines (
1564 LocalScreen.LeftColumn,
1565 LocalScreen.RightColumn,
1566 TopRow - SCROLL_ARROW_HEIGHT,
1567 BottomRow + SCROLL_ARROW_HEIGHT,
1568 FIELD_TEXT | FIELD_BACKGROUND
1569 );
1570
1571 while (gMenuRefreshHead != NULL) {
1572 OldMenuRefreshEntry = gMenuRefreshHead->Next;
1573
1574 gBS->FreePool (gMenuRefreshHead);
1575
1576 gMenuRefreshHead = OldMenuRefreshEntry;
1577 }
1578
1579 for (Link = TopOfScreen; Link != &Menu; Link = Link->ForwardLink) {
1580 MenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
1581 MenuOption->Row = Row;
1582 OriginalRow = Row;
1583 MenuOption->Col = Col;
1584 MenuOption->OptCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn;
1585
1586 if (SubMenu) {
1587 if (MenuOption->ThisTag->GrayOut) {
1588 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
1589 } else {
1590 if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP) {
1591 gST->ConOut->SetAttribute (gST->ConOut, SUBTITLE_TEXT | FIELD_BACKGROUND);
1592 }
1593 }
1594
1595 Width = GetWidth (MenuOption->ThisTag, MenuOption->Handle);
1596
1597 OriginalRow = Row;
1598
1599 for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
1600 if ((Temp == 0) && (Row <= BottomRow)) {
1601 PrintStringAt (Col, Row, OutputString);
1602 }
1603 //
1604 // If there is more string to process print on the next row and increment the Skip value
1605 //
1606 if (StrLen (&MenuOption->Description[Index])) {
1607 if (Temp == 0) {
1608 Row++;
1609 }
1610 }
1611
1612 gBS->FreePool (OutputString);
1613 if (Temp != 0) {
1614 Temp--;
1615 }
1616 }
1617
1618 Temp = 0;
1619
1620 Row = OriginalRow;
1621
1622 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
1623 ProcessOptions (MenuOption, FALSE, FileFormTagsHead, PageData, &OptionString);
1624
1625 if (OptionString != NULL) {
1626 //
1627 // If leading spaces on OptionString - remove the spaces
1628 //
1629 for (Index = 0; OptionString[Index] == L' '; Index++) {
1630 MenuOption->OptCol++;
1631 }
1632
1633 for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
1634 OptionString[Count] = OptionString[Index];
1635 Count++;
1636 }
1637
1638 OptionString[Count] = CHAR_NULL;
1639
1640 //
1641 // If this is a date or time op-code and is used to reflect an RTC, register the op-code
1642 //
1643 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP ||
1644 MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP) &&
1645 (MenuOption->ThisTag->StorageStart >= FileFormTags->FormTags.Tags[0].NvDataSize)) {
1646
1647 if (gMenuRefreshHead == NULL) {
1648 MenuRefreshEntry = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
1649 ASSERT (MenuRefreshEntry != NULL);
1650 MenuRefreshEntry->MenuOption = MenuOption;
1651 MenuRefreshEntry->FileFormTagsHead = FileFormTagsHead;
1652 MenuRefreshEntry->CurrentColumn = MenuOption->OptCol;
1653 MenuRefreshEntry->CurrentRow = MenuOption->Row;
1654 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND;
1655 gMenuRefreshHead = MenuRefreshEntry;
1656 } else {
1657 //
1658 // Advance to the last entry
1659 //
1660 for (MenuRefreshEntry = gMenuRefreshHead;
1661 MenuRefreshEntry->Next != NULL;
1662 MenuRefreshEntry = MenuRefreshEntry->Next
1663 )
1664 ;
1665 MenuRefreshEntry->Next = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
1666 ASSERT (MenuRefreshEntry->Next != NULL);
1667 MenuRefreshEntry = MenuRefreshEntry->Next;
1668 MenuRefreshEntry->MenuOption = MenuOption;
1669 MenuRefreshEntry->FileFormTagsHead = FileFormTagsHead;
1670 MenuRefreshEntry->CurrentColumn = MenuOption->OptCol;
1671 MenuRefreshEntry->CurrentRow = MenuOption->Row;
1672 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND;
1673 }
1674 }
1675
1676 Width = (UINT16) gOptionBlockWidth;
1677
1678 OriginalRow = Row;
1679
1680 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
1681 if ((Temp2 == 0) && (Row <= BottomRow)) {
1682 PrintStringAt (MenuOption->OptCol, Row, OutputString);
1683 }
1684 //
1685 // If there is more string to process print on the next row and increment the Skip value
1686 //
1687 if (StrLen (&OptionString[Index])) {
1688 if (Temp2 == 0) {
1689 Row++;
1690 //
1691 // Since the Number of lines for this menu entry may or may not be reflected accurately
1692 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1693 // some testing to ensure we are keeping this in-sync.
1694 //
1695 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1696 //
1697 if ((Row - OriginalRow) >= MenuOption->Skip) {
1698 MenuOption->Skip++;
1699 }
1700 }
1701 }
1702
1703 gBS->FreePool (OutputString);
1704 if (Temp2 != 0) {
1705 Temp2--;
1706 }
1707 }
1708
1709 Temp2 = 0;
1710 Row = OriginalRow;
1711 }
1712 //
1713 // If this is a text op with secondary text information
1714 //
1715 if ((MenuOption->ThisTag->Operand == EFI_IFR_TEXT_OP) && (MenuOption->ThisTag->TextTwo != 0)) {
1716 StringPtr = GetToken (MenuOption->ThisTag->TextTwo, MenuOption->Handle);
1717
1718 Width = (UINT16) gOptionBlockWidth;
1719
1720 OriginalRow = Row;
1721
1722 for (Index = 0; GetLineByWidth (StringPtr, Width, &Index, &OutputString) != 0x0000;) {
1723 if ((Temp == 0) && (Row <= BottomRow)) {
1724 PrintStringAt (MenuOption->OptCol, Row, OutputString);
1725 }
1726 //
1727 // If there is more string to process print on the next row and increment the Skip value
1728 //
1729 if (StrLen (&StringPtr[Index])) {
1730 if (Temp2 == 0) {
1731 Row++;
1732 //
1733 // Since the Number of lines for this menu entry may or may not be reflected accurately
1734 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1735 // some testing to ensure we are keeping this in-sync.
1736 //
1737 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1738 //
1739 if ((Row - OriginalRow) >= MenuOption->Skip) {
1740 MenuOption->Skip++;
1741 }
1742 }
1743 }
1744
1745 gBS->FreePool (OutputString);
1746 if (Temp2 != 0) {
1747 Temp2--;
1748 }
1749 }
1750
1751 Row = OriginalRow;
1752 gBS->FreePool (StringPtr);
1753 }
1754 } else {
1755 //
1756 // For now, assume left-justified 72 width max setup entries
1757 //
1758 PrintStringAt (Col, Row, MenuOption->Description);
1759 }
1760 //
1761 // Tracker 6210 - need to handle the bottom of the display
1762 //
1763 if (MenuOption->Skip > 1) {
1764 Row += MenuOption->Skip - SkipValue;
1765 SkipValue = 0;
1766 } else {
1767 Row += MenuOption->Skip;
1768 }
1769
1770 if (Row > BottomRow) {
1771 if (!ValueIsScroll (FALSE, Link)) {
1772 gDownArrow = TRUE;
1773 }
1774
1775 Row = BottomRow + 1;
1776 break;
1777 }
1778 }
1779
1780 if (!ValueIsScroll (TRUE, TopOfScreen)) {
1781 gUpArrow = TRUE;
1782 }
1783
1784 if (gUpArrow) {
1785 gST->ConOut->SetAttribute (gST->ConOut, ARROW_TEXT | ARROW_BACKGROUND);
1786 PrintAt (
1787 LocalScreen.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
1788 TopRow - SCROLL_ARROW_HEIGHT,
1789 (CHAR16 *) L"%c",
1790 ARROW_UP
1791 );
1792 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
1793 }
1794
1795 if (gDownArrow) {
1796 gST->ConOut->SetAttribute (gST->ConOut, ARROW_TEXT | ARROW_BACKGROUND);
1797 PrintAt (
1798 LocalScreen.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
1799 BottomRow + SCROLL_ARROW_HEIGHT,
1800 (CHAR16 *) L"%c",
1801 ARROW_DOWN
1802 );
1803 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
1804 }
1805
1806 if (SavedMenuOption != NULL) {
1807 MenuOption = SavedMenuOption;
1808 }
1809 }
1810 break;
1811
1812 case CfRefreshHighLight:
1813 ControlFlag = CfUpdateHelpString;
1814 //
1815 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
1816 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
1817 //
1818 SavedValue = Repaint;
1819 Repaint = FALSE;
1820
1821 if (NewPos != NULL) {
1822 gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
1823 if (SubMenu) {
1824 if (gLastOpr && (gEntryNumber != -1)) {
1825 MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
1826 if (gEntryNumber != MenuOption->EntryNumber) {
1827 ScreenOperation = UiDown;
1828 ControlFlag = CfScreenOperation;
1829 break;
1830 } else {
1831 gLastOpr = FALSE;
1832 }
1833 }
1834
1835 ProcessOptions (MenuOption, FALSE, FileFormTagsHead, PageData, &OptionString);
1836 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
1837 if (OptionString != NULL) {
1838 //
1839 // If leading spaces on OptionString - remove the spaces
1840 //
1841 for (Index = 0; OptionString[Index] == L' '; Index++)
1842 ;
1843
1844 for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
1845 OptionString[Count] = OptionString[Index];
1846 Count++;
1847 }
1848
1849 OptionString[Count] = CHAR_NULL;
1850
1851 Width = (UINT16) gOptionBlockWidth;
1852
1853 OriginalRow = MenuOption->Row;
1854
1855 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
1856 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
1857 PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString);
1858 }
1859 //
1860 // If there is more string to process print on the next row and increment the Skip value
1861 //
1862 if (StrLen (&OptionString[Index])) {
1863 MenuOption->Row++;
1864 }
1865
1866 gBS->FreePool (OutputString);
1867 }
1868
1869 MenuOption->Row = OriginalRow;
1870 } else {
1871 if (NewLine) {
1872 if (MenuOption->ThisTag->GrayOut) {
1873 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
1874 } else {
1875 if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP) {
1876 gST->ConOut->SetAttribute (gST->ConOut, SUBTITLE_TEXT | FIELD_BACKGROUND);
1877 }
1878 }
1879
1880 OriginalRow = MenuOption->Row;
1881 Width = GetWidth (MenuOption->ThisTag, MenuOption->Handle);
1882
1883 for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
1884 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
1885 PrintStringAt (Col, MenuOption->Row, OutputString);
1886 }
1887 //
1888 // If there is more string to process print on the next row and increment the Skip value
1889 //
1890 if (StrLen (&MenuOption->Description[Index])) {
1891 MenuOption->Row++;
1892 }
1893
1894 gBS->FreePool (OutputString);
1895 }
1896
1897 MenuOption->Row = OriginalRow;
1898 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
1899 }
1900 }
1901 } else {
1902 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
1903 gST->ConOut->OutputString (gST->ConOut, MenuOption->Description);
1904 }
1905
1906 MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
1907
1908 if ((gPriorMenuEntry != 0) && (MenuOption->EntryNumber != gPriorMenuEntry) && (NewPos->ForwardLink != &Menu)) {
1909 ScreenOperation = UiDown;
1910 ControlFlag = CfScreenOperation;
1911 break;
1912 } else {
1913 gPriorMenuEntry = 0;
1914 }
1915 //
1916 // This is only possible if we entered this page and the first menu option is
1917 // a "non-menu" item. In that case, force it UiDown
1918 //
1919 if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || MenuOption->ThisTag->GrayOut) {
1920 //
1921 // If we previously hit an UP command and we are still sitting on a text operation
1922 // we must continue going up
1923 //
1924 if (ScreenOperation == UiUp) {
1925 ControlFlag = CfScreenOperation;
1926 break;
1927 } else {
1928 ScreenOperation = UiDown;
1929 ControlFlag = CfScreenOperation;
1930 break;
1931 }
1932 }
1933 //
1934 // Set reverse attribute
1935 //
1936 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT);
1937 gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
1938
1939 //
1940 // Assuming that we have a refresh linked-list created, lets annotate the
1941 // appropriate entry that we are highlighting with its new attribute. Just prior to this
1942 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
1943 //
1944 if (gMenuRefreshHead != NULL) {
1945 for (MenuRefreshEntry = gMenuRefreshHead; MenuRefreshEntry != NULL; MenuRefreshEntry = MenuRefreshEntry->Next) {
1946 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND;
1947 if (MenuRefreshEntry->MenuOption == MenuOption) {
1948 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT;
1949 }
1950 }
1951 }
1952
1953 if (SubMenu) {
1954 ProcessOptions (MenuOption, FALSE, FileFormTagsHead, PageData, &OptionString);
1955 if (OptionString != NULL) {
1956 //
1957 // If leading spaces on OptionString - remove the spaces
1958 //
1959 for (Index = 0; OptionString[Index] == L' '; Index++)
1960 ;
1961
1962 for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
1963 OptionString[Count] = OptionString[Index];
1964 Count++;
1965 }
1966
1967 OptionString[Count] = CHAR_NULL;
1968
1969 Width = (UINT16) gOptionBlockWidth;
1970
1971 OriginalRow = MenuOption->Row;
1972
1973 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
1974 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
1975 PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString);
1976 }
1977 //
1978 // If there is more string to process print on the next row and increment the Skip value
1979 //
1980 if (StrLen (&OptionString[Index])) {
1981 MenuOption->Row++;
1982 }
1983
1984 gBS->FreePool (OutputString);
1985 }
1986
1987 MenuOption->Row = OriginalRow;
1988 } else {
1989 if (NewLine) {
1990 OriginalRow = MenuOption->Row;
1991
1992 Width = GetWidth (MenuOption->ThisTag, MenuOption->Handle);
1993
1994 for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
1995 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
1996 PrintStringAt (Col, MenuOption->Row, OutputString);
1997 }
1998 //
1999 // If there is more string to process print on the next row and increment the Skip value
2000 //
2001 if (StrLen (&MenuOption->Description[Index])) {
2002 MenuOption->Row++;
2003 }
2004
2005 gBS->FreePool (OutputString);
2006 }
2007
2008 MenuOption->Row = OriginalRow;
2009
2010 }
2011 }
2012
2013 if (((NewPos->ForwardLink != &Menu) && (ScreenOperation == UiDown)) ||
2014 ((NewPos->BackLink != &Menu) && (ScreenOperation == UiUp)) ||
2015 (ScreenOperation == UiNoOperation)
2016 ) {
2017 UpdateKeyHelp (MenuOption, FALSE);
2018 }
2019 } else {
2020 gST->ConOut->OutputString (gST->ConOut, MenuOption->Description);
2021 }
2022 //
2023 // Clear reverse attribute
2024 //
2025 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
2026 }
2027 //
2028 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2029 // if we didn't break halfway when process CfRefreshHighLight.
2030 //
2031 Repaint = SavedValue;
2032 break;
2033
2034 case CfUpdateHelpString:
2035 ControlFlag = CfPrepareToReadKey;
2036
2037 if (SubMenu &&
2038 (Repaint || NewLine ||
2039 (MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) ||
2040 (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) &&
2041 !(gClassOfVfr == EFI_GENERAL_APPLICATION_SUBCLASS)) {
2042 //
2043 // Don't print anything if it is a NULL help token
2044 //
2045 if (MenuOption->ThisTag->Help == 0x00000000) {
2046 StringPtr = (CHAR16 *) L"\0";
2047 } else {
2048 StringPtr = GetToken (MenuOption->ThisTag->Help, MenuOption->Handle);
2049 }
2050
2051 ProcessHelpString (StringPtr, &FormattedString, BottomRow - TopRow);
2052
2053 gST->ConOut->SetAttribute (gST->ConOut, HELP_TEXT | FIELD_BACKGROUND);
2054
2055 for (Index = 0; Index < BottomRow - TopRow; Index++) {
2056 //
2057 // Pad String with spaces to simulate a clearing of the previous line
2058 //
2059 for (; GetStringWidth (&FormattedString[Index * gHelpBlockWidth]) / 2 < gHelpBlockWidth;) {
2060 StrCat (&FormattedString[Index * gHelpBlockWidth], (CHAR16 *) L" ");
2061 }
2062
2063 PrintStringAt (
2064 LocalScreen.RightColumn - gHelpBlockWidth,
2065 Index + TopRow,
2066 &FormattedString[Index * gHelpBlockWidth]
2067 );
2068 }
2069 }
2070 //
2071 // Reset this flag every time we finish using it.
2072 //
2073 Repaint = FALSE;
2074 NewLine = FALSE;
2075 break;
2076
2077 case CfPrepareToReadKey:
2078 ControlFlag = CfReadKey;
2079
2080 for (Index = 0; Index < MenuOption->IfrNumber; Index++) {
2081 FileFormTags = FileFormTags->NextFile;
2082 }
2083
2084 ScreenOperation = UiNoOperation;
2085
2086 Status = gBS->HandleProtocol (
2087 (VOID *) (UINTN) FileFormTags->FormTags.Tags[0].CallbackHandle,
2088 &gEfiFormCallbackProtocolGuid,
2089 (VOID **) &FormCallback
2090 );
2091
2092 break;
2093
2094 case CfReadKey:
2095 ControlFlag = CfScreenOperation;
2096
2097 OriginalTimeOut = FrontPageTimeOutValue;
2098 do {
2099 if (FrontPageTimeOutValue >= 0 && (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) && FrontPageTimeOutValue != (INT16) -1) {
2100 //
2101 // Remember that if set to 0, must immediately boot an option
2102 //
2103 if (FrontPageTimeOutValue == 0) {
2104 FrontPageTimeOutValue = 0xFFFF;
2105 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
2106 if (EFI_ERROR (Status)) {
2107 Status = EFI_TIMEOUT;
2108 }
2109 break;
2110 }
2111
2112 Status = UiWaitForSingleEvent (gST->ConIn->WaitForKey, ONE_SECOND);
2113 if (Status == EFI_TIMEOUT) {
2114 EFI_IFR_DATA_ENTRY *DataEntry;
2115
2116 DataEntry = (EFI_IFR_DATA_ENTRY *) (PageData + 1);
2117
2118 PageData->EntryCount = 1;
2119 Count = (UINT32) ((OriginalTimeOut - FrontPageTimeOutValue) * 100 / OriginalTimeOut);
2120 CopyMem (&DataEntry->Data, &Count, sizeof (UINT32));
2121
2122 if ((FormCallback != NULL) && (FormCallback->Callback != NULL)) {
2123 FormCallback->Callback (
2124 FormCallback,
2125 0xFFFF,
2126 (EFI_IFR_DATA_ARRAY *) PageData,
2127 NULL
2128 );
2129 }
2130 //
2131 // Count down 1 second
2132 //
2133 FrontPageTimeOutValue--;
2134
2135 } else {
2136 ASSERT (!EFI_ERROR (Status));
2137 PageData->EntryCount = 0;
2138 if ((FormCallback != NULL) && (FormCallback->Callback != NULL)) {
2139 FormCallback->Callback (
2140 FormCallback,
2141 0xFFFE,
2142 (EFI_IFR_DATA_ARRAY *) PageData,
2143 NULL
2144 );
2145 }
2146
2147 FrontPageTimeOutValue = 0xFFFF;
2148 }
2149 } else {
2150 //
2151 // Wait for user's selection, no auto boot
2152 //
2153 Status = UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0);
2154 }
2155 } while (Status == EFI_TIMEOUT);
2156
2157 if (gFirstIn) {
2158 gFirstIn = FALSE;
2159 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
2160 DisableQuietBoot ();
2161 }
2162
2163 if (Status == EFI_TIMEOUT) {
2164 Key.UnicodeChar = CHAR_CARRIAGE_RETURN;
2165 } else {
2166 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
2167 //
2168 // if we encounter error, continue to read another key in.
2169 //
2170 if (EFI_ERROR (Status)) {
2171 ControlFlag = CfReadKey;
2172 continue;
2173 }
2174 }
2175
2176 switch (Key.UnicodeChar) {
2177 case CHAR_CARRIAGE_RETURN:
2178 Selection = MenuOption;
2179 ScreenOperation = UiSelect;
2180 gDirection = 0;
2181 break;
2182
2183 //
2184 // We will push the adjustment of these numeric values directly to the input handler
2185 //
2186 case '+':
2187 case '-':
2188 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
2189
2190 if (Key.UnicodeChar == '+') {
2191 gDirection = SCAN_RIGHT;
2192 } else {
2193 gDirection = SCAN_LEFT;
2194 }
2195
2196 Status = ProcessOptions (MenuOption, TRUE, FileFormTagsHead, NULL, &OptionString);
2197 }
2198 break;
2199
2200 case '^':
2201 ScreenOperation = UiUp;
2202 break;
2203
2204 case 'V':
2205 case 'v':
2206 ScreenOperation = UiDown;
2207 break;
2208
2209 case ' ':
2210 if (gClassOfVfr != EFI_FRONT_PAGE_SUBCLASS) {
2211 if (SubMenu) {
2212 if (MenuOption->ThisTag->Operand == EFI_IFR_CHECKBOX_OP && !(MenuOption->ThisTag->GrayOut)) {
2213 gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
2214 gST->ConOut->OutputString (gST->ConOut, MenuOption->Description);
2215 Selection = MenuOption;
2216 ScreenOperation = UiSelect;
2217 }
2218 }
2219 }
2220 break;
2221
2222 case CHAR_NULL:
2223 if (((Key.ScanCode == SCAN_F1) && ((gFunctionKeySetting & FUNCTION_ONE) != FUNCTION_ONE)) ||
2224 ((Key.ScanCode == SCAN_F2) && ((gFunctionKeySetting & FUNCTION_TWO) != FUNCTION_TWO)) ||
2225 ((Key.ScanCode == SCAN_F9) && ((gFunctionKeySetting & FUNCTION_NINE) != FUNCTION_NINE)) ||
2226 ((Key.ScanCode == SCAN_F10) && ((gFunctionKeySetting & FUNCTION_TEN) != FUNCTION_TEN))
2227 ) {
2228 //
2229 // If the function key has been disabled, just ignore the key.
2230 //
2231 } else {
2232 for (Index = 0; Index < sizeof (gScanCodeToOperation) / sizeof (gScanCodeToOperation[0]); Index++) {
2233 if (Key.ScanCode == gScanCodeToOperation[Index].ScanCode) {
2234 if ((Key.ScanCode == SCAN_F9) || (Key.ScanCode == SCAN_F10)) {
2235 if (SubMenu) {
2236 ScreenOperation = gScanCodeToOperation[Index].ScreenOperation;
2237 }
2238 } else {
2239 ScreenOperation = gScanCodeToOperation[Index].ScreenOperation;
2240 }
2241 }
2242 }
2243 }
2244 break;
2245 }
2246 break;
2247
2248 case CfScreenOperation:
2249 IfrBinary = gBinaryDataHead;
2250
2251 //
2252 // Advance to the Ifr we are using
2253 //
2254 for (Index = 0; Index < gActiveIfr; Index++) {
2255 IfrBinary = IfrBinary->Next;
2256 }
2257
2258 if (ScreenOperation != UiPrevious && ScreenOperation != UiReset) {
2259 //
2260 // If the screen has no menu items, and the user didn't select UiPrevious, or UiReset
2261 // ignore the selection and go back to reading keys.
2262 //
2263 if (IsListEmpty (&Menu)) {
2264 ControlFlag = CfReadKey;
2265 break;
2266 }
2267 //
2268 // if there is nothing logical to place a cursor on, just move on to wait for a key.
2269 //
2270 for (Link = Menu.ForwardLink; Link != &Menu; Link = Link->ForwardLink) {
2271 NextMenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2272 if (!(NextMenuOption->ThisTag->GrayOut) && (NextMenuOption->ThisTag->Operand != EFI_IFR_SUBTITLE_OP)) {
2273 break;
2274 }
2275 }
2276
2277 if (Link == &Menu) {
2278 ControlFlag = CfPrepareToReadKey;
2279 break;
2280 }
2281 }
2282
2283 for (Index = 0;
2284 Index < sizeof (gScreenOperationToControlFlag) / sizeof (gScreenOperationToControlFlag[0]);
2285 Index++
2286 ) {
2287 if (ScreenOperation == gScreenOperationToControlFlag[Index].ScreenOperation) {
2288 ControlFlag = gScreenOperationToControlFlag[Index].ControlFlag;
2289 }
2290 }
2291
2292 break;
2293
2294 case CfUiPrevious:
2295 ControlFlag = CfCheckSelection;
2296 //
2297 // Check for tags that might have LATE_CHECK enabled. If they do, we can't switch pages or save NV data.
2298 //
2299 if (MenuOption != NULL) {
2300 if (!SelectionsAreValid (MenuOption, FileFormTagsHead)) {
2301 Selection = NULL;
2302 Repaint = TRUE;
2303 break;
2304 }
2305 }
2306
2307 if (IsListEmpty (&gMenuList)) {
2308 Selection = NULL;
2309 if (IsListEmpty (&Menu)) {
2310 ControlFlag = CfReadKey;
2311 }
2312 break;
2313 }
2314
2315 gLastOpr = TRUE;
2316
2317 while (gMenuRefreshHead != NULL) {
2318 OldMenuRefreshEntry = gMenuRefreshHead->Next;
2319
2320 gBS->FreePool (gMenuRefreshHead);
2321
2322 gMenuRefreshHead = OldMenuRefreshEntry;
2323 }
2324 //
2325 // Remove the Cached page entry, free and init the menus, flag Selection as jumping to previous page and a valid Tag
2326 //
2327 if (SubMenu) {
2328 UiRemoveMenuListEntry (MenuOption, &Selection);
2329 Selection->Previous = TRUE;
2330 UiFreeMenu ();
2331 UiInitMenu ();
2332 }
2333
2334 gActiveIfr = Selection->IfrNumber;
2335 return Selection;
2336
2337 case CfUiSelect:
2338 ControlFlag = CfCheckSelection;
2339
2340 ExtractRequestedNvMap (FileFormTags, MenuOption->ThisTag->VariableNumber, &VariableDefinition);
2341
2342 if (SubMenu) {
2343 if ((MenuOption->ThisTag->Operand == EFI_IFR_TEXT_OP &&
2344 !(MenuOption->ThisTag->Flags & EFI_IFR_FLAG_INTERACTIVE)) ||
2345 (MenuOption->ThisTag->GrayOut) ||
2346 (MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) ||
2347 (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
2348 Selection = NULL;
2349 break;
2350 }
2351
2352 NewLine = TRUE;
2353 UpdateKeyHelp (MenuOption, TRUE);
2354 Status = ProcessOptions (MenuOption, TRUE, FileFormTagsHead, PageData, &OptionString);
2355
2356 if (EFI_ERROR (Status)) {
2357 Selection = NULL;
2358 Repaint = TRUE;
2359 break;
2360 }
2361
2362 if (OptionString != NULL) {
2363 PrintStringAt (LocalScreen.LeftColumn + gPromptBlockWidth + 1, MenuOption->Row, OptionString);
2364 }
2365
2366 if (MenuOption->ThisTag->Flags & EFI_IFR_FLAG_INTERACTIVE) {
2367 Selection = MenuOption;
2368 }
2369
2370 if (Selection == NULL) {
2371 break;
2372 }
2373
2374 Location = (UINT8 *) &PageData->EntryCount;
2375
2376 //
2377 // If not a goto, dump single piece of data, otherwise dump everything
2378 //
2379 if (Selection->ThisTag->Operand == EFI_IFR_REF_OP) {
2380 //
2381 // Check for tags that might have LATE_CHECK enabled. If they do, we can't switch pages or save NV data.
2382 //
2383 if (!SelectionsAreValid (MenuOption, FileFormTagsHead)) {
2384 Selection = NULL;
2385 Repaint = TRUE;
2386 break;
2387 }
2388
2389 UiAddMenuListEntry (Selection);
2390 gPriorMenuEntry = 0;
2391
2392 //
2393 // Now that we added a menu entry specific to a goto, we can always go back when someone hits the UiPrevious
2394 //
2395 UiMenuList = CR (gMenuList.ForwardLink, UI_MENU_LIST, MenuLink, UI_MENU_LIST_SIGNATURE);
2396 UiMenuList->FormerEntryNumber = MenuOption->EntryNumber;
2397
2398 gLastOpr = FALSE;
2399
2400 //
2401 // Rewind to the beginning of the menu
2402 //
2403 for (; NewPos->BackLink != &Menu; NewPos = NewPos->BackLink)
2404 ;
2405
2406 //
2407 // Get Total Count of Menu entries
2408 //
2409 for (Count = 1; NewPos->ForwardLink != &Menu; NewPos = NewPos->ForwardLink) {
2410 Count++;
2411 }
2412 //
2413 // Rewind to the beginning of the menu
2414 //
2415 for (; NewPos->BackLink != &Menu; NewPos = NewPos->BackLink)
2416 ;
2417
2418 //
2419 // Copy the number of entries being described to the PageData location
2420 //
2421 CopyMem (&Location[0], &Count, sizeof (UINT32));
2422
2423 for (Index = 4; NewPos->ForwardLink != &Menu; Index = Index + MenuOption->ThisTag->StorageWidth + 2) {
2424
2425 MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2426 Location[Index] = MenuOption->ThisTag->Operand;
2427 Location[Index + 1] = (UINT8) (MenuOption->ThisTag->StorageWidth + 4);
2428 CopyMem (
2429 &Location[Index + 4],
2430 &VariableDefinition->NvRamMap[MenuOption->ThisTag->StorageStart],
2431 MenuOption->ThisTag->StorageWidth
2432 );
2433 NewPos = NewPos->ForwardLink;
2434 }
2435 } else {
2436
2437 gPriorMenuEntry = MenuOption->EntryNumber;
2438
2439 Count = 1;
2440
2441 //
2442 // Copy the number of entries being described to the PageData location
2443 //
2444 CopyMem (&Location[0], &Count, sizeof (UINT32));
2445
2446 //
2447 // Start at PageData[4] since the EntryCount is a UINT32
2448 //
2449 Index = 4;
2450
2451 //
2452 // Copy data to destination
2453 //
2454 Location[Index] = MenuOption->ThisTag->Operand;
2455 Location[Index + 1] = (UINT8) (MenuOption->ThisTag->StorageWidth + 4);
2456 CopyMem (
2457 &Location[Index + 4],
2458 &VariableDefinition->NvRamMap[MenuOption->ThisTag->StorageStart],
2459 MenuOption->ThisTag->StorageWidth
2460 );
2461 }
2462 }
2463 break;
2464
2465 case CfUiReset:
2466 ControlFlag = CfCheckSelection;
2467 gLastOpr = FALSE;
2468 if (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) {
2469 break;
2470 }
2471 //
2472 // If NV flag is up, prompt user
2473 //
2474 if (gNvUpdateRequired) {
2475 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
2476
2477 YesResponse = gYesResponse[0];
2478 NoResponse = gNoResponse[0];
2479
2480 do {
2481 CreateDialog (3, TRUE, 0, NULL, &Key, gEmptyString, gAreYouSure, gEmptyString);
2482 } while
2483 (
2484 (Key.ScanCode != SCAN_ESC) &&
2485 ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (NoResponse | UPPER_LOWER_CASE_OFFSET)) &&
2486 ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (YesResponse | UPPER_LOWER_CASE_OFFSET))
2487 );
2488
2489 //
2490 // If the user hits the YesResponse key
2491 //
2492 if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (YesResponse | UPPER_LOWER_CASE_OFFSET)) {
2493 } else {
2494 Repaint = TRUE;
2495 NewLine = TRUE;
2496 break;
2497 }
2498 }
2499 //
2500 // Check for tags that might have LATE_CHECK enabled. If they do, we can't switch pages or save NV data.
2501 //
2502 if (MenuOption != NULL) {
2503 if (!SelectionsAreValid (MenuOption, FileFormTagsHead)) {
2504 Selection = NULL;
2505 Repaint = TRUE;
2506 NewLine = TRUE;
2507 break;
2508 }
2509 }
2510
2511 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
2512 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
2513
2514 if (SubMenu) {
2515 UiFreeMenuList ();
2516 gST->ConOut->ClearScreen (gST->ConOut);
2517 return NULL;
2518 }
2519
2520 UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->Flags, FALSE);
2521 UpdateStatusBar (NV_UPDATE_REQUIRED, MenuOption->ThisTag->Flags, FALSE);
2522
2523 if (IfrBinary->UnRegisterOnExit) {
2524 Hii->RemovePack (Hii, MenuOption->Handle);
2525 }
2526
2527 UiFreeMenu ();
2528
2529 //
2530 // Clean up the allocated data buffers
2531 //
2532 FreeData (FileFormTagsHead, FormattedString, OptionString);
2533
2534 gST->ConOut->ClearScreen (gST->ConOut);
2535 return NULL;
2536
2537 case CfUiLeft:
2538 ControlFlag = CfCheckSelection;
2539 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
2540 if (MenuOption->Skip == 1) {
2541 //
2542 // In the tail of the Date/Time op-code set, go left.
2543 //
2544 NewPos = NewPos->BackLink;
2545 } else {
2546 //
2547 // In the middle of the Data/Time op-code set, go left.
2548 //
2549 NextMenuOption = CR (NewPos->ForwardLink, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2550 if (NextMenuOption->Skip == 1) {
2551 NewPos = NewPos->BackLink;
2552 }
2553 }
2554 }
2555 break;
2556
2557 case CfUiRight:
2558 ControlFlag = CfCheckSelection;
2559 if ((MenuOption->Skip == 0) &&
2560 ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP))
2561 ) {
2562 //
2563 // We are in the head or middle of the Date/Time op-code set, advance right.
2564 //
2565 NewPos = NewPos->ForwardLink;
2566 }
2567 break;
2568
2569 case CfUiUp:
2570 ControlFlag = CfCheckSelection;
2571
2572 if (NewPos->BackLink != &Menu) {
2573 NewLine = TRUE;
2574 //
2575 // Adjust Date/Time position before we advance forward.
2576 //
2577 AdjustDateAndTimePosition (TRUE, &NewPos);
2578
2579 //
2580 // Caution that we have already rewind to the top, don't go backward in this situation.
2581 //
2582 if (NewPos->BackLink != &Menu) {
2583 NewPos = NewPos->BackLink;
2584 }
2585
2586 PreviousMenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2587
2588 //
2589 // Since the behavior of hitting the up arrow on a Date/Time op-code is intended
2590 // to be one that back to the previous set of op-codes, we need to advance to the sencond
2591 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2592 // checking can be done.
2593 //
2594 DataAndTimeLineNumberPad = AdjustDateAndTimePosition (TRUE, &NewPos);
2595
2596 if (SubMenu) {
2597 //
2598 // If the previous MenuOption contains a display-only op-code, skip to the next one
2599 //
2600 if (PreviousMenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || PreviousMenuOption->ThisTag->GrayOut) {
2601 //
2602 // This is ok as long as not at the end of the list
2603 //
2604 if (NewPos->BackLink == &Menu) {
2605 //
2606 // If we are at the start of the list, then this list must start with a display only
2607 // piece of data, so do not allow the backward motion
2608 //
2609 ScreenOperation = UiDown;
2610
2611 if (PreviousMenuOption->Row <= TopRow) {
2612 if (TopOfScreen->BackLink != &Menu) {
2613 TopOfScreen = TopOfScreen->BackLink;
2614 Repaint = TRUE;
2615 }
2616 }
2617
2618 UpdateStatusBar (INPUT_ERROR, PreviousMenuOption->ThisTag->Flags, FALSE);
2619 break;
2620 }
2621 }
2622 }
2623 //
2624 // Check the previous menu entry to see if it was a zero-length advance. If it was,
2625 // don't worry about a redraw.
2626 //
2627 if ((MenuOption->Row - PreviousMenuOption->Skip - DataAndTimeLineNumberPad < TopRow) ||
2628 (PreviousMenuOption->Skip > MenuOption->Row)
2629 ) {
2630 do {
2631 if (TopOfScreen->BackLink == &Menu) {
2632 break;
2633 }
2634
2635 Repaint = TRUE;
2636
2637 //
2638 // Is the current top of screen a zero-advance op-code?
2639 // If so, keep moving forward till we hit a >0 advance op-code
2640 //
2641 SavedMenuOption = CR (TopOfScreen->BackLink, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2642 TopOfScreen = TopOfScreen->BackLink;
2643 } while (SavedMenuOption->Skip == 0);
2644 //
2645 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2646 //
2647 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2648 }
2649
2650 UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->Flags, FALSE);
2651 } else {
2652 if (SubMenu) {
2653 SavedMenuOption = MenuOption;
2654 MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2655 if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || MenuOption->ThisTag->GrayOut) {
2656 //
2657 // If we are at the end of the list and sitting on a text op, we need to more forward
2658 //
2659 ScreenOperation = UiDown;
2660 ControlFlag = CfScreenOperation;
2661 break;
2662 }
2663
2664 MenuOption = SavedMenuOption;
2665 }
2666 }
2667 break;
2668
2669 case CfUiPageUp:
2670 ControlFlag = CfCheckSelection;
2671
2672 SavedListEntry = NewPos;
2673 Link = TopOfScreen;
2674 for (Index = BottomRow; Index >= TopRow + 1; Index -= MenuOption->Skip) {
2675 if (Link->BackLink == &Menu) {
2676 TopOfScreen = Link;
2677 Link = SavedListEntry;
2678 MenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2679 break;
2680 }
2681
2682 NewLine = TRUE;
2683 Repaint = TRUE;
2684 Link = Link->BackLink;
2685 MenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2686 TopOfScreen = Link;
2687 SavedListEntry = Link;
2688 }
2689
2690 NewPos = Link;
2691
2692 //
2693 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2694 // Don't do this when we are already in the first page.
2695 //
2696 if (Repaint) {
2697 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2698 AdjustDateAndTimePosition (TRUE, &NewPos);
2699 MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2700 }
2701 break;
2702
2703 case CfUiPageDown:
2704 ControlFlag = CfCheckSelection;
2705
2706 SavedListEntry = NewPos;
2707 Link = TopOfScreen;
2708 NewPos = TopOfScreen;
2709 for (Index = TopRow; Index <= BottomRow - 1; Index += MenuOption->Skip) {
2710 if (NewPos->ForwardLink == &Menu) {
2711 NewPos = SavedListEntry;
2712 MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2713 Link = TopOfScreen;
2714 NewLine = FALSE;
2715 Repaint = FALSE;
2716 break;
2717 }
2718
2719 NewLine = TRUE;
2720 Repaint = TRUE;
2721 MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2722 NewPos = NewPos->ForwardLink;
2723 Link = NewPos;
2724 }
2725
2726 TopOfScreen = Link;
2727
2728 //
2729 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2730 // Don't do this when we are already in the last page.
2731 //
2732 if (Repaint) {
2733 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2734 AdjustDateAndTimePosition (TRUE, &NewPos);
2735 MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2736 }
2737 break;
2738
2739 case CfUiDown:
2740 ControlFlag = CfCheckSelection;
2741 //
2742 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
2743 // to be one that progresses to the next set of op-codes, we need to advance to the last
2744 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2745 // checking can be done. The only other logic we need to introduce is that if a Date/Time
2746 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
2747 // the Date/Time op-code.
2748 //
2749 DataAndTimeLineNumberPad = AdjustDateAndTimePosition (FALSE, &NewPos);
2750
2751 if (NewPos->ForwardLink != &Menu) {
2752 NewLine = TRUE;
2753 NewPos = NewPos->ForwardLink;
2754 NextMenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2755
2756 if (SubMenu) {
2757 //
2758 // If the next MenuOption contains a display-only op-code, skip to the next one
2759 // Also if the next MenuOption is date or time,
2760 //
2761 if (NextMenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || NextMenuOption->ThisTag->GrayOut) {
2762 //
2763 // This is ok as long as not at the end of the list
2764 //
2765 if (NewPos == &Menu) {
2766 //
2767 // If we are at the end of the list, then this list must end with a display only
2768 // piece of data, so do not allow the forward motion
2769 //
2770 UpdateStatusBar (INPUT_ERROR, NextMenuOption->ThisTag->Flags, FALSE);
2771 NewPos = NewPos->BackLink;
2772 ScreenOperation = UiUp;
2773 break;
2774 }
2775 }
2776 }
2777 //
2778 // An option might be multi-line, so we need to reflect that data in the overall skip value
2779 //
2780 UpdateOptionSkipLines (PageData, NextMenuOption, FileFormTagsHead, &OptionString, SkipValue);
2781
2782 if (NextMenuOption->Skip > 1) {
2783 Temp = MenuOption->Row + MenuOption->Skip + NextMenuOption->Skip - 1;
2784 } else {
2785 Temp = MenuOption->Row + MenuOption->Skip + DataAndTimeLineNumberPad;
2786 }
2787 //
2788 // If we are going to scroll
2789 //
2790 if (Temp > BottomRow) {
2791 do {
2792 //
2793 // Is the current top of screen a zero-advance op-code?
2794 // If so, keep moving forward till we hit a >0 advance op-code
2795 //
2796 SavedMenuOption = CR (TopOfScreen, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2797
2798 //
2799 // If bottom op-code is more than one line or top op-code is more than one line
2800 //
2801 if ((NextMenuOption->Skip > 1) || (MenuOption->Skip > 1)) {
2802 //
2803 // Is the bottom op-code greater than or equal in size to the top op-code?
2804 //
2805 if ((Temp - BottomRow) >= (SavedMenuOption->Skip - OldSkipValue)) {
2806 //
2807 // Skip the top op-code
2808 //
2809 TopOfScreen = TopOfScreen->ForwardLink;
2810 Difference = (Temp - BottomRow) - (SavedMenuOption->Skip - OldSkipValue);
2811
2812 OldSkipValue = Difference;
2813
2814 SavedMenuOption = CR (TopOfScreen, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2815
2816 //
2817 // If we have a remainder, skip that many more op-codes until we drain the remainder
2818 //
2819 for (;
2820 Difference >= (INTN) SavedMenuOption->Skip;
2821 Difference = Difference - (INTN) SavedMenuOption->Skip
2822 ) {
2823 //
2824 // Since the Difference is greater than or equal to this op-code's skip value, skip it
2825 //
2826 TopOfScreen = TopOfScreen->ForwardLink;
2827 SavedMenuOption = CR (TopOfScreen, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2828 if (Difference < (INTN) SavedMenuOption->Skip) {
2829 Difference = SavedMenuOption->Skip - Difference - 1;
2830 break;
2831 } else {
2832 if (Difference == (INTN) SavedMenuOption->Skip) {
2833 TopOfScreen = TopOfScreen->ForwardLink;
2834 SavedMenuOption = CR (TopOfScreen, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2835 Difference = SavedMenuOption->Skip - Difference;
2836 break;
2837 }
2838 }
2839 }
2840 //
2841 // Since we will act on this op-code in the next routine, and increment the
2842 // SkipValue, set the skips to one less than what is required.
2843 //
2844 SkipValue = Difference - 1;
2845
2846 } else {
2847 //
2848 // Since we will act on this op-code in the next routine, and increment the
2849 // SkipValue, set the skips to one less than what is required.
2850 //
2851 SkipValue = OldSkipValue + (Temp - BottomRow) - 1;
2852 }
2853 } else {
2854 if ((OldSkipValue + 1) == (INTN) SavedMenuOption->Skip) {
2855 TopOfScreen = TopOfScreen->ForwardLink;
2856 break;
2857 } else {
2858 SkipValue = OldSkipValue;
2859 }
2860 }
2861 //
2862 // If the op-code at the top of the screen is more than one line, let's not skip it yet
2863 // Let's set a skip flag to smoothly scroll the top of the screen.
2864 //
2865 if (SavedMenuOption->Skip > 1) {
2866 if (SavedMenuOption == NextMenuOption) {
2867 SkipValue = 0;
2868 } else {
2869 SkipValue++;
2870 }
2871 } else {
2872 SkipValue = 0;
2873 TopOfScreen = TopOfScreen->ForwardLink;
2874 }
2875 } while (SavedMenuOption->Skip == 0);
2876
2877 Repaint = TRUE;
2878 OldSkipValue = SkipValue;
2879 }
2880
2881 UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->Flags, FALSE);
2882
2883 } else {
2884 if (SubMenu) {
2885 SavedMenuOption = MenuOption;
2886 MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
2887 if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || MenuOption->ThisTag->GrayOut) {
2888 //
2889 // If we are at the end of the list and sitting on a text op, we need to more forward
2890 //
2891 ScreenOperation = UiUp;
2892 ControlFlag = CfScreenOperation;
2893 break;
2894 }
2895
2896 MenuOption = SavedMenuOption;
2897 //
2898 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
2899 //
2900 AdjustDateAndTimePosition (TRUE, &NewPos);
2901 }
2902 }
2903 break;
2904
2905 case CfUiSave:
2906 ControlFlag = CfCheckSelection;
2907 //
2908 // Check for tags that might have LATE_CHECK enabled. If they do, we can't switch pages or save NV data.
2909 //
2910 if (MenuOption != NULL) {
2911 if (!SelectionsAreValid (MenuOption, FileFormTagsHead)) {
2912 Selection = NULL;
2913 Repaint = TRUE;
2914 break;
2915 }
2916 }
2917 //
2918 // If callbacks are active, and the callback has a Write method, try to use it
2919 //
2920 if (FileFormTags->VariableDefinitions->VariableName == NULL) {
2921 if ((FormCallback != NULL) && (FormCallback->NvWrite != NULL)) {
2922 Status = FormCallback->NvWrite (
2923 FormCallback,
2924 (CHAR16 *) L"Setup",
2925 &FileFormTags->FormTags.Tags[0].GuidValue,
2926 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
2927 VariableDefinition->VariableSize,
2928 (VOID *) VariableDefinition->NvRamMap,
2929 &gResetRequired
2930 );
2931
2932 } else {
2933 Status = gRT->SetVariable (
2934 (CHAR16 *) L"Setup",
2935 &FileFormTags->FormTags.Tags[0].GuidValue,
2936 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
2937 VariableDefinition->VariableSize,
2938 (VOID *) VariableDefinition->NvRamMap
2939 );
2940 }
2941 } else {
2942 VariableDefinition = FileFormTags->VariableDefinitions;
2943
2944 for (; VariableDefinition != NULL; VariableDefinition = VariableDefinition->Next) {
2945 if ((FormCallback != NULL) && (FormCallback->NvWrite != NULL)) {
2946 Status = FormCallback->NvWrite (
2947 FormCallback,
2948 VariableDefinition->VariableName,
2949 &VariableDefinition->Guid,
2950 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
2951 VariableDefinition->VariableSize,
2952 (VOID *) VariableDefinition->NvRamMap,
2953 &gResetRequired
2954 );
2955
2956 } else {
2957 Status = gRT->SetVariable (
2958 VariableDefinition->VariableName,
2959 &VariableDefinition->Guid,
2960 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
2961 VariableDefinition->VariableSize,
2962 (VOID *) VariableDefinition->NvRamMap
2963 );
2964 }
2965 }
2966 }
2967
2968 UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->Flags, FALSE);
2969 UpdateStatusBar (NV_UPDATE_REQUIRED, MenuOption->ThisTag->Flags, FALSE);
2970 break;
2971
2972 case CfUiDefault:
2973 ControlFlag = CfCheckSelection;
2974
2975 NvMapListHead = NULL;
2976
2977 Status = Hii->GetDefaultImage (Hii, MenuOption->Handle, EFI_IFR_FLAG_DEFAULT, &NvMapListHead);
2978
2979 if (!EFI_ERROR (Status)) {
2980 ASSERT_EFI_ERROR (NULL != NvMapListHead);
2981
2982 NvMapListNode = NvMapListHead;
2983
2984 while (NULL != NvMapListNode) {
2985 if (FileFormTags->VariableDefinitions->VariableId == NvMapListNode->VariablePack->VariableId) {
2986 NvMap = (VOID *) ((CHAR8 *) NvMapListNode->VariablePack + sizeof (EFI_HII_VARIABLE_PACK) + NvMapListNode->VariablePack->VariableNameLength);
2987 NvMapSize = NvMapListNode->VariablePack->Header.Length - sizeof (EFI_HII_VARIABLE_PACK) - NvMapListNode->VariablePack->VariableNameLength;
2988 break;
2989 }
2990 NvMapListNode = NvMapListNode->NextVariablePack;
2991 }
2992
2993 //
2994 // Free the buffer that was allocated.
2995 //
2996 gBS->FreePool (FileFormTags->VariableDefinitions->NvRamMap);
2997 gBS->FreePool (FileFormTags->VariableDefinitions->FakeNvRamMap);
2998
2999 //
3000 // Allocate, copy the NvRamMap.
3001 //
3002 FileFormTags->VariableDefinitions->VariableFakeSize = (UINT16) (FileFormTags->VariableDefinitions->VariableFakeSize - FileFormTags->VariableDefinitions->VariableSize);
3003 FileFormTags->VariableDefinitions->VariableSize = (UINT16) NvMapSize;
3004 FileFormTags->VariableDefinitions->VariableFakeSize = (UINT16) (FileFormTags->VariableDefinitions->VariableFakeSize + FileFormTags->VariableDefinitions->VariableSize);
3005
3006 FileFormTags->VariableDefinitions->NvRamMap = AllocateZeroPool (FileFormTags->VariableDefinitions->VariableSize);
3007 ASSERT (FileFormTags->VariableDefinitions->NvRamMap != NULL);
3008
3009 FileFormTags->VariableDefinitions->FakeNvRamMap = AllocateZeroPool (NvMapSize + FileFormTags->VariableDefinitions->VariableFakeSize);
3010 ASSERT (FileFormTags->VariableDefinitions->FakeNvRamMap != NULL);
3011
3012 CopyMem (FileFormTags->VariableDefinitions->NvRamMap, NvMap, NvMapSize);
3013 gBS->FreePool (NvMapListHead);
3014 }
3015
3016 UpdateStatusBar (NV_UPDATE_REQUIRED, MenuOption->ThisTag->Flags, TRUE);
3017 Repaint = TRUE;
3018 //
3019 // After the repaint operation, we should refresh the highlight.
3020 //
3021 NewLine = TRUE;
3022 break;
3023
3024 case CfUiNoOperation:
3025 ControlFlag = CfCheckSelection;
3026 break;
3027
3028 case CfExit:
3029 while (gMenuRefreshHead != NULL) {
3030 OldMenuRefreshEntry = gMenuRefreshHead->Next;
3031
3032 gBS->FreePool (gMenuRefreshHead);
3033
3034 gMenuRefreshHead = OldMenuRefreshEntry;
3035 }
3036
3037 gST->ConOut->SetCursorPosition (gST->ConOut, 0, Row + 4);
3038 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
3039 gST->ConOut->OutputString (gST->ConOut, (CHAR16 *) L"\n");
3040
3041 gActiveIfr = MenuOption->IfrNumber;
3042 return Selection;
3043
3044 default:
3045 break;
3046 }
3047 }
3048 }
3049
3050 BOOLEAN
3051 ValueIsScroll (
3052 IN BOOLEAN Direction,
3053 IN LIST_ENTRY *CurrentPos
3054 )
3055 /*++
3056
3057 Routine Description:
3058 Determine if the menu is the last menu that can be selected.
3059
3060 Arguments:
3061 Direction - the scroll direction. False is down. True is up.
3062
3063 Returns:
3064 FALSE -- the menu isn't the last menu that can be selected.
3065 TRUE -- the menu is the last menu that can be selected.
3066 --*/
3067 {
3068 LIST_ENTRY *Temp;
3069 UI_MENU_OPTION *MenuOption;
3070 MenuOption = NULL;
3071
3072 Temp = Direction ? CurrentPos->BackLink : CurrentPos->ForwardLink;
3073
3074 if (Temp == &Menu) {
3075 return TRUE;
3076 }
3077
3078 for (; Temp != &Menu; Temp = Direction ? Temp->BackLink : Temp->ForwardLink) {
3079 MenuOption = CR (Temp, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
3080 if (!(MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || MenuOption->ThisTag->GrayOut)) {
3081 return FALSE;
3082 }
3083 }
3084
3085 return TRUE;
3086 }
3087
3088 UINTN
3089 AdjustDateAndTimePosition (
3090 IN BOOLEAN DirectionUp,
3091 IN LIST_ENTRY **CurrentPosition
3092 )
3093 /*++
3094 Routine Description:
3095 Adjust Data and Time tag position accordingly.
3096 Data format : [01/02/2004] [11:22:33]
3097 Line number : 0 0 1 0 0 1
3098
3099 Arguments:
3100 Direction - the up or down direction. False is down. True is up.
3101 CurrentPos - Current position.
3102
3103 Returns:
3104 Return line number to pad. It is possible that we stand on a zero-advance
3105 data or time opcode, so pad one line when we judge if we are going to scroll outside.
3106 --*/
3107 {
3108 UINTN Count;
3109 LIST_ENTRY *NewPosition;
3110 UI_MENU_OPTION *MenuOption;
3111 UINTN PadLineNumber;
3112
3113 PadLineNumber = 0;
3114 NewPosition = *CurrentPosition;
3115 MenuOption = CR (NewPosition, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
3116
3117 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
3118 //
3119 // Calculate the distance from current position to the last Date/Time op-code.
3120 //
3121 Count = 0;
3122 while (MenuOption->ThisTag->NumberOfLines == 0) {
3123 Count++;
3124 NewPosition = NewPosition->ForwardLink;
3125 MenuOption = CR (NewPosition, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
3126 PadLineNumber = 1;
3127 }
3128
3129 NewPosition = *CurrentPosition;
3130 if (DirectionUp) {
3131 //
3132 // Since the behavior of hitting the up arrow on a Date/Time op-code is intended
3133 // to be one that back to the previous set of op-codes, we need to advance to the first
3134 // Date/Time op-code and leave the remaining logic in CfUiUp intact so the appropriate
3135 // checking can be done.
3136 //
3137 while (Count++ < 2) {
3138 NewPosition = NewPosition->BackLink;
3139 }
3140 } else {
3141 //
3142 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
3143 // to be one that progresses to the next set of op-codes, we need to advance to the last
3144 // Date/Time op-code and leave the remaining logic in CfUiDown intact so the appropriate
3145 // checking can be done.
3146 //
3147 while (Count-- > 0) {
3148 NewPosition = NewPosition->ForwardLink;
3149 }
3150 }
3151
3152 *CurrentPosition = NewPosition;
3153 }
3154
3155 return PadLineNumber;
3156 }