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