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