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