Port DriverSample.inf, HiiDatabase.inf and SetupBrowser.inf
[mirror_edk2.git] / IntelFrameworkModulePkg / Universal / SetupBrowserDxe / InputHandler.c
1 /*++
2
3 Copyright (c) 2006 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12 Module Name:
13
14 InputHandler.C
15
16 Abstract:
17
18 Implementation for handling user input from the User Interface
19
20 Revision History
21
22 --*/
23
24 //
25 // Include common header file for this module.
26 //
27 #include "CommonHeader.h"
28
29 #include "Setup.h"
30 #include "Ui.h"
31 #include "Colors.h"
32
33 #define EFI_MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b))
34
35 EFI_STATUS
36 ReadString(
37 IN UI_MENU_OPTION *MenuOption,
38 OUT CHAR16 *StringPtr
39 )
40 {
41 EFI_STATUS Status;
42 EFI_INPUT_KEY Key;
43 CHAR16 NullCharacter;
44 UINTN ScreenSize;
45 EFI_TAG *Tag;
46 CHAR16 Space[2];
47 CHAR16 KeyPad[2];
48 BOOLEAN SelectionComplete;
49 CHAR16 *TempString;
50 CHAR16 *BufferedString;
51 UINTN Index;
52 UINTN Count;
53 UINTN Start;
54 UINTN Top;
55 CHAR16 *PromptForDataString;
56 UINTN DimensionsWidth;
57 UINTN DimensionsHeight;
58 BOOLEAN CursorVisible;
59
60 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
61 DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
62
63 PromptForDataString = GetToken (STRING_TOKEN (PROMPT_FOR_DATA), gHiiHandle);
64
65 NullCharacter = CHAR_NULL;
66 ScreenSize = GetStringWidth (PromptForDataString) / 2;
67 Tag = MenuOption->ThisTag;
68 Space[0] = L' ';
69 Space[1] = CHAR_NULL;
70 SelectionComplete = FALSE;
71
72 TempString = AllocateZeroPool (MenuOption->ThisTag->Maximum * 2);
73 ASSERT (TempString);
74
75 if (ScreenSize < (Tag->Maximum / (UINTN) 2)) {
76 ScreenSize = Tag->Maximum / 2;
77 }
78
79 if ((ScreenSize + 2) > DimensionsWidth) {
80 ScreenSize = DimensionsWidth - 2;
81 }
82
83 BufferedString = AllocateZeroPool (ScreenSize * 2);
84 ASSERT (BufferedString);
85
86 Start = (DimensionsWidth - ScreenSize - 2) / 2 + gScreenDimensions.LeftColumn + 1;
87 Top = ((DimensionsHeight - 6) / 2) + gScreenDimensions.TopRow - 1;
88
89 //
90 // Display prompt for string
91 //
92 CreatePopUp (ScreenSize, 4, &NullCharacter, PromptForDataString, Space, &NullCharacter);
93
94 FreePool (PromptForDataString);
95
96 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
97
98 CursorVisible = gST->ConOut->Mode->CursorVisible;
99 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
100
101 do {
102 Status = WaitForKeyStroke (&Key);
103
104 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
105 switch (Key.UnicodeChar) {
106 case CHAR_NULL:
107 switch (Key.ScanCode) {
108 case SCAN_LEFT:
109 break;
110
111 case SCAN_RIGHT:
112 break;
113
114 case SCAN_ESC:
115 FreePool (TempString);
116 FreePool (BufferedString);
117 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
118 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
119 return EFI_DEVICE_ERROR;
120
121 default:
122 break;
123 }
124
125 break;
126
127 case CHAR_CARRIAGE_RETURN:
128 if (GetStringWidth (StringPtr) >= MenuOption->ThisTag->Minimum) {
129 SelectionComplete = TRUE;
130 FreePool (TempString);
131 FreePool (BufferedString);
132 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
133 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
134 return EFI_SUCCESS;
135 } else {
136 ScreenSize = GetStringWidth (gMiniString) / 2;
137 CreatePopUp (ScreenSize, 4, &NullCharacter, gMiniString, gPressEnter, &NullCharacter);
138 //
139 // Simply create a popup to tell the user that they had typed in too few characters.
140 // To save code space, we can then treat this as an error and return back to the menu.
141 //
142 do {
143 Status = WaitForKeyStroke (&Key);
144 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
145 FreePool (TempString);
146 FreePool (BufferedString);
147 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
148 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
149 return EFI_DEVICE_ERROR;
150 }
151
152 break;
153
154 case CHAR_BACKSPACE:
155 if (StringPtr[0] != CHAR_NULL) {
156 for (Index = 0; StringPtr[Index] != CHAR_NULL; Index++) {
157 TempString[Index] = StringPtr[Index];
158 }
159 //
160 // Effectively truncate string by 1 character
161 //
162 TempString[Index - 1] = CHAR_NULL;
163 StrCpy (StringPtr, TempString);
164 }
165
166 default:
167 //
168 // If it is the beginning of the string, don't worry about checking maximum limits
169 //
170 if ((StringPtr[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
171 StrnCpy (StringPtr, &Key.UnicodeChar, 1);
172 StrnCpy (TempString, &Key.UnicodeChar, 1);
173 } else if ((GetStringWidth (StringPtr) < MenuOption->ThisTag->Maximum) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
174 KeyPad[0] = Key.UnicodeChar;
175 KeyPad[1] = CHAR_NULL;
176 StrCat (StringPtr, KeyPad);
177 StrCat (TempString, KeyPad);
178 }
179 //
180 // If the width of the input string is now larger than the screen, we nee to
181 // adjust the index to start printing portions of the string
182 //
183 SetUnicodeMem (BufferedString, ScreenSize - 1, L' ');
184
185 PrintStringAt (Start + 1, Top + 3, BufferedString);
186
187 if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {
188 Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;
189 } else {
190 Index = 0;
191 }
192
193 for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {
194 BufferedString[Count] = StringPtr[Index];
195 }
196
197 PrintStringAt (Start + 1, Top + 3, BufferedString);
198 break;
199 }
200
201 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
202 gST->ConOut->SetCursorPosition (gST->ConOut, Start + GetStringWidth (StringPtr) / 2, Top + 3);
203 } while (!SelectionComplete);
204 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
205 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
206 return Status;
207 }
208
209 EFI_STATUS
210 ReadPassword (
211 IN UI_MENU_OPTION *MenuOption,
212 IN BOOLEAN PromptForPassword,
213 IN EFI_TAG *Tag,
214 IN EFI_IFR_DATA_ARRAY *PageData,
215 IN BOOLEAN SecondEntry,
216 IN EFI_FILE_FORM_TAGS *FileFormTags,
217 OUT CHAR16 *StringPtr
218 )
219 {
220 EFI_STATUS Status;
221 UINTN ScreenSize;
222 CHAR16 NullCharacter;
223 CHAR16 Space[2];
224 EFI_INPUT_KEY Key;
225 CHAR16 KeyPad[2];
226 UINTN Index;
227 UINTN Start;
228 UINTN Top;
229 CHAR16 *TempString;
230 CHAR16 *TempString2;
231 BOOLEAN Confirmation;
232 BOOLEAN ConfirmationComplete;
233 EFI_HII_CALLBACK_PACKET *Packet;
234 EFI_FORM_CALLBACK_PROTOCOL *FormCallback;
235 EFI_VARIABLE_DEFINITION *VariableDefinition;
236 UINTN DimensionsWidth;
237 UINTN DimensionsHeight;
238 EFI_IFR_DATA_ENTRY *DataEntry;
239 UINTN WidthOfString;
240
241 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
242 DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
243
244 VariableDefinition = NULL;
245 NullCharacter = CHAR_NULL;
246 Space[0] = L' ';
247 Space[1] = CHAR_NULL;
248 Confirmation = FALSE;
249 ConfirmationComplete = FALSE;
250 Status = EFI_SUCCESS;
251 FormCallback = NULL;
252 Packet = NULL;
253
254 //
255 // Remember that dynamic pages in an environment where all pages are not
256 // dynamic require us to call back to the user to give them an opportunity
257 // to register fresh information in the HII database so that we can extract it.
258 //
259 Status = gBS->HandleProtocol (
260 (VOID *) (UINTN) MenuOption->Tags[0].CallbackHandle,
261 &gEfiFormCallbackProtocolGuid,
262 (VOID **) &FormCallback
263 );
264
265 TempString = AllocateZeroPool (MenuOption->ThisTag->Maximum * 2);
266 TempString2 = AllocateZeroPool (MenuOption->ThisTag->Maximum * 2);
267
268 ASSERT (TempString);
269 ASSERT (TempString2);
270
271 if (Tag->Flags & EFI_IFR_FLAG_INTERACTIVE) {
272 //
273 // Password requires a callback to determine if a password exists
274 //
275 DataEntry = (EFI_IFR_DATA_ENTRY *) (PageData + 1);
276 DataEntry->OpCode = EFI_IFR_PASSWORD_OP;
277 DataEntry->Length = 3;
278
279 ExtractRequestedNvMap (FileFormTags, Tag->VariableNumber, &VariableDefinition);
280
281 //
282 // The user is about to be prompted with a password field, Data = 0 (Return Status determines the type of prompt)
283 //
284 DataEntry->Data = (VOID *) (UINTN) (UINT8) (0 + SecondEntry * 2);
285 PageData->NvRamMap = VariableDefinition->NvRamMap;
286
287 if ((FormCallback != NULL) && (FormCallback->Callback != NULL)) {
288 Status = FormCallback->Callback (
289 FormCallback,
290 Tag->Key,
291 PageData,
292 &Packet
293 );
294 }
295 //
296 // If error on return, continue with the reading of a typed in password to verify user knows password
297 // If no error, there is no password set, so prompt for new password
298 // if the previous callback was to verify the user knew password, and user typed it correctly - should return no error
299 //
300 if (!EFI_ERROR (Status)) {
301 PromptForPassword = FALSE;
302
303 //
304 // Simulate this as the second entry into this routine for an interactive behavior
305 //
306 SecondEntry = TRUE;
307 } else if (Status == EFI_NOT_READY) {
308 Error:
309 if (Packet != NULL) {
310 //
311 // Upon error, we will likely receive a string to print out
312 // Display error popup
313 //
314 WidthOfString = GetStringWidth (Packet->String);
315 ScreenSize = EFI_MAX(WidthOfString, GetStringWidth (gPressEnter)) / 2;
316 CreatePopUp (ScreenSize, 4, &NullCharacter, Packet->String, gPressEnter, &NullCharacter);
317 FreePool (Packet);
318
319 do {
320 Status = WaitForKeyStroke (&Key);
321 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
322 }
323
324 Status = EFI_NOT_READY;
325 goto Done;
326 }
327 }
328
329 do {
330 //
331 // Display PopUp Screen
332 //
333 ScreenSize = GetStringWidth (gPromptForNewPassword) / 2;
334 if (GetStringWidth (gConfirmPassword) / 2 > ScreenSize) {
335 ScreenSize = GetStringWidth (gConfirmPassword) / 2;
336 }
337
338 Start = (DimensionsWidth - ScreenSize - 4) / 2 + gScreenDimensions.LeftColumn + 2;
339 Top = ((DimensionsHeight - 6) / 2) + gScreenDimensions.TopRow - 1;
340
341 if (!Confirmation) {
342 if (PromptForPassword) {
343 CreatePopUp (ScreenSize, 4, &NullCharacter, gPromptForPassword, Space, &NullCharacter);
344 } else {
345 CreatePopUp (ScreenSize, 4, &NullCharacter, gPromptForNewPassword, Space, &NullCharacter);
346 }
347 } else {
348 CreatePopUp (ScreenSize, 4, &NullCharacter, gConfirmPassword, Space, &NullCharacter);
349 StringPtr[0] = CHAR_NULL;
350 }
351
352 do {
353 Status = WaitForKeyStroke (&Key);
354
355 switch (Key.UnicodeChar) {
356 case CHAR_NULL:
357 if (Key.ScanCode == SCAN_ESC) {
358 return EFI_NOT_READY;
359 }
360
361 ConfirmationComplete = FALSE;
362 break;
363
364 case CHAR_CARRIAGE_RETURN:
365 if (Tag->Flags & EFI_IFR_FLAG_INTERACTIVE) {
366 //
367 // User just typed a string in
368 //
369 DataEntry = (EFI_IFR_DATA_ENTRY *) (PageData + 1);
370 DataEntry->OpCode = EFI_IFR_PASSWORD_OP;
371
372 //
373 // If the user just typed in a password, Data = 1
374 // If the user just typed in a password to confirm the previous password, Data = 2
375 //
376 if (!Confirmation) {
377 DataEntry->Length = 3;
378 DataEntry->Data = (VOID *) (UINTN) (UINT8) (1 + SecondEntry * 2);
379
380 if ((FormCallback != NULL) && (FormCallback->Callback != NULL)) {
381 Status = FormCallback->Callback (
382 FormCallback,
383 Tag->Key,
384 PageData,
385 &Packet
386 );
387 }
388
389 DataEntry->Length = sizeof (EFI_IFR_DATA_ENTRY);
390 DataEntry->Data = (VOID *) TempString;
391 } else {
392 DataEntry->Length = 3;
393 DataEntry->Data = (VOID *) (UINTN) (UINT8) (2 + SecondEntry * 2);
394
395 if ((FormCallback != NULL) && (FormCallback->Callback != NULL)) {
396 Status = FormCallback->Callback (
397 FormCallback,
398 Tag->Key,
399 PageData,
400 &Packet
401 );
402 }
403
404 DataEntry->Length = sizeof (EFI_IFR_DATA_ENTRY);
405 DataEntry->Data = (VOID *) TempString2;
406 }
407
408 if ((FormCallback != NULL) && (FormCallback->Callback != NULL)) {
409 Status = FormCallback->Callback (
410 FormCallback,
411 Tag->Key,
412 PageData,
413 &Packet
414 );
415 }
416 //
417 // If this was the confirmation round of callbacks
418 // and an error comes back, display an error
419 //
420 if (Confirmation) {
421 if (EFI_ERROR (Status)) {
422 if (Packet->String == NULL) {
423 WidthOfString = GetStringWidth (gConfirmError);
424 ScreenSize = EFI_MAX (WidthOfString, GetStringWidth (gPressEnter)) / 2;
425 CreatePopUp (ScreenSize, 4, &NullCharacter, gConfirmError, gPressEnter, &NullCharacter);
426 } else {
427 WidthOfString = GetStringWidth (Packet->String);
428 ScreenSize = EFI_MAX (WidthOfString, GetStringWidth (gPressEnter)) / 2;
429 CreatePopUp (ScreenSize, 4, &NullCharacter, Packet->String, gPressEnter, &NullCharacter);
430 FreePool (Packet);
431 }
432
433 StringPtr[0] = CHAR_NULL;
434 do {
435 Status = WaitForKeyStroke (&Key);
436
437 if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
438 Status = EFI_NOT_READY;
439 goto Done;
440 }
441 } while (1);
442 } else {
443 Status = EFI_NOT_READY;
444 goto Done;
445 }
446 } else {
447 //
448 // User typed a string in and it wasn't valid somehow from the callback
449 // For instance, callback may have said that some invalid characters were contained in the string
450 //
451 if (Status == EFI_NOT_READY) {
452 goto Error;
453 }
454
455 if (PromptForPassword && EFI_ERROR (Status)) {
456 Status = EFI_DEVICE_ERROR;
457 goto Done;
458 }
459 }
460 }
461
462 if (Confirmation) {
463 //
464 // Compare tempstring and tempstring2, if the same, return with StringPtr success
465 // Otherwise, kick and error box, and return an error
466 //
467 if (StrCmp (TempString, TempString2) == 0) {
468 Status = EFI_SUCCESS;
469 goto Done;
470 } else {
471 WidthOfString = GetStringWidth (gConfirmError);
472 ScreenSize = EFI_MAX (WidthOfString, GetStringWidth (gPressEnter)) / 2;
473 CreatePopUp (ScreenSize, 4, &NullCharacter, gConfirmError, gPressEnter, &NullCharacter);
474 StringPtr[0] = CHAR_NULL;
475 do {
476 Status = WaitForKeyStroke (&Key);
477 if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
478 Status = EFI_DEVICE_ERROR;
479 goto Done;
480 }
481 } while (1);
482 }
483 }
484
485 if (PromptForPassword) {
486 //
487 // I was asked for a password, return it back in StringPtr
488 //
489 Status = EFI_SUCCESS;
490 goto Done;
491 } else {
492 //
493 // If the two passwords were not the same kick an error popup
494 //
495 Confirmation = TRUE;
496 ConfirmationComplete = TRUE;
497 break;
498 }
499
500 case CHAR_BACKSPACE:
501 if (StringPtr[0] != CHAR_NULL) {
502 if (!Confirmation) {
503 for (Index = 0; StringPtr[Index] != CHAR_NULL; Index++) {
504 TempString[Index] = StringPtr[Index];
505 }
506 //
507 // Effectively truncate string by 1 character
508 //
509 TempString[Index - 1] = CHAR_NULL;
510 StrCpy (StringPtr, TempString);
511 } else {
512 for (Index = 0; StringPtr[Index] != CHAR_NULL; Index++) {
513 TempString2[Index] = StringPtr[Index];
514 }
515 //
516 // Effectively truncate string by 1 character
517 //
518 TempString2[Index - 1] = CHAR_NULL;
519 StrCpy (StringPtr, TempString2);
520 }
521
522 ConfirmationComplete = FALSE;
523 } else {
524 ConfirmationComplete = FALSE;
525 }
526
527 //
528 // Must be a character we are interested in!
529 //
530 default:
531 if ((StringPtr[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
532 if (!Confirmation) {
533 StrnCpy (StringPtr, &Key.UnicodeChar, 1);
534 StrnCpy (TempString, &Key.UnicodeChar, 1);
535 } else {
536 StrnCpy (StringPtr, &Key.UnicodeChar, 1);
537 StrnCpy (TempString2, &Key.UnicodeChar, 1);
538 ConfirmationComplete = FALSE;
539 }
540 } else if ((GetStringWidth (StringPtr) / 2 <= (UINTN) (MenuOption->ThisTag->Maximum - 1) / 2) &&
541 (Key.UnicodeChar != CHAR_BACKSPACE)
542 ) {
543 KeyPad[0] = Key.UnicodeChar;
544 KeyPad[1] = CHAR_NULL;
545 if (!Confirmation) {
546 StrCat (StringPtr, KeyPad);
547 StrCat (TempString, KeyPad);
548 } else {
549 StrCat (StringPtr, KeyPad);
550 StrCat (TempString2, KeyPad);
551 }
552 }
553
554 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
555 for (Index = 1; Index < ScreenSize; Index++) {
556 PrintCharAt (Start + Index, Top + 3, L' ');
557 }
558
559 gST->ConOut->SetCursorPosition (
560 gST->ConOut,
561 (DimensionsWidth - GetStringWidth (StringPtr) / 2) / 2 + gScreenDimensions.LeftColumn,
562 Top + 3
563 );
564 for (Index = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++) {
565 PrintChar (L'*');
566 }
567
568 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
569 break;
570 }
571 //
572 // end switch
573 //
574 } while (!ConfirmationComplete);
575
576 } while (1);
577
578 Done:
579 FreePool (TempString);
580 FreePool (TempString2);
581 return Status;
582 }
583
584 VOID
585 EncodePassword (
586 IN CHAR16 *Password,
587 IN UINT8 MaxSize
588 )
589 {
590 UINTN Index;
591 UINTN Loop;
592 CHAR16 *Buffer;
593 CHAR16 *Key;
594
595 Key = (CHAR16 *) L"MAR10648567";
596 Buffer = AllocateZeroPool (MaxSize);
597
598 ASSERT (Buffer);
599
600 for (Index = 0; Key[Index] != 0; Index++) {
601 for (Loop = 0; Loop < (UINT8) (MaxSize / 2); Loop++) {
602 Buffer[Loop] = (CHAR16) (Password[Loop] ^ Key[Index]);
603 }
604 }
605
606 CopyMem (Password, Buffer, MaxSize);
607
608 FreePool (Buffer);
609 return ;
610 }
611
612 EFI_STATUS
613 GetNumericInput (
614 IN UI_MENU_OPTION *MenuOption,
615 IN EFI_FILE_FORM_TAGS *FileFormTagsHead,
616 IN BOOLEAN ManualInput,
617 IN EFI_TAG *Tag,
618 IN UINTN NumericType,
619 OUT UINT16 *Value
620 )
621 /*++
622
623 Routine Description:
624
625 This routine reads a numeric value from the user input.
626
627 Arguments:
628
629 MenuOption - Pointer to the current input menu.
630
631 FileFormTagsHead - Pointer to the root of formset.
632
633 ManualInput - If the input is manual or not.
634
635 Tag - Pointer to all the attributes and values associated with a tag.
636
637 Value - Pointer to the numeric value that is going to be read.
638
639 Returns:
640
641 EFI_SUCCESS - If numerical input is read successfully
642 EFI_DEVICE_ERROR - If operation fails
643
644 --*/
645 {
646 EFI_INPUT_KEY Key;
647 BOOLEAN SelectionComplete;
648 UINTN Column;
649 UINTN Row;
650 CHAR16 FormattedNumber[6];
651 UINTN PreviousNumber[6];
652 INTN Number;
653 UINTN Count;
654 UINT16 BackupValue;
655 STRING_REF PopUp;
656 CHAR16 NullCharacter;
657 CHAR16 *StringPtr;
658 EFI_FILE_FORM_TAGS *FileFormTags;
659 EFI_VARIABLE_DEFINITION *VariableDefinition;
660 UINTN Loop;
661
662 NullCharacter = CHAR_NULL;
663 StringPtr = NULL;
664 Column = MenuOption->OptCol;
665 Row = MenuOption->Row;
666 Number = 0;
667 PreviousNumber[0] = 0;
668 Count = 0;
669 SelectionComplete = FALSE;
670 BackupValue = Tag->Value;
671 FileFormTags = FileFormTagsHead;
672
673 if (ManualInput) {
674 PrintAt (Column, Row, (CHAR16 *) L"[ ]");
675 Column++;
676 if (Tag->Operand != EFI_IFR_TIME_OP) {
677 *Value = BackupValue;
678 }
679 }
680 //
681 // First time we enter this handler, we need to check to see if
682 // we were passed an increment or decrement directive
683 //
684 do {
685 Key.UnicodeChar = CHAR_NULL;
686 if (gDirection != 0) {
687 Key.ScanCode = gDirection;
688 gDirection = 0;
689 goto TheKey2;
690 }
691
692 WaitForKeyStroke (&Key);
693
694 TheKey2:
695 switch (Key.UnicodeChar) {
696 case '+':
697 case '-':
698 if ((Tag->Operand == EFI_IFR_DATE_OP) || (Tag->Operand == EFI_IFR_TIME_OP)) {
699 Key.UnicodeChar = CHAR_NULL;
700 if (Key.UnicodeChar == '+') {
701 Key.ScanCode = SCAN_RIGHT;
702 } else {
703 Key.ScanCode = SCAN_LEFT;
704 }
705
706 goto TheKey2;
707 }
708 break;
709
710 case CHAR_NULL:
711 switch (Key.ScanCode) {
712 case SCAN_LEFT:
713 case SCAN_RIGHT:
714 if ((Tag->Operand == EFI_IFR_DATE_OP) || (Tag->Operand == EFI_IFR_TIME_OP)) {
715 //
716 // By setting this value, we will return back to the caller.
717 // We need to do this since an auto-refresh will destroy the adjustment
718 // based on what the real-time-clock is showing. So we always commit
719 // upon changing the value.
720 //
721 gDirection = SCAN_DOWN;
722 }
723
724 if (!ManualInput) {
725 Tag->Value = *Value;
726 if (Key.ScanCode == SCAN_LEFT) {
727 Number = *Value - Tag->Step;
728 if (Number < Tag->Minimum) {
729 Number = Tag->Minimum;
730 }
731 } else if (Key.ScanCode == SCAN_RIGHT) {
732 Number = *Value + Tag->Step;
733 if (Number > Tag->Maximum) {
734 Number = Tag->Maximum;
735 }
736 }
737
738 Tag->Value = (UINT16) Number;
739 *Value = (UINT16) Number;
740 UnicodeValueToString (
741 FormattedNumber,
742 FALSE,
743 (UINTN) Number,
744 (sizeof (FormattedNumber) / sizeof (FormattedNumber[0]))
745 );
746 Number = (UINT16) GetStringWidth (FormattedNumber);
747
748 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
749 if ((Tag->Operand == EFI_IFR_DATE_OP) || (Tag->Operand == EFI_IFR_TIME_OP)) {
750 for (Loop = 0; Loop < (UINTN) ((Number >= 8) ? 4 : 2); Loop++) {
751 PrintAt (MenuOption->OptCol + Loop, MenuOption->Row, (CHAR16 *) L" ");
752 }
753 } else {
754 for (Loop = 0; Loop < gOptionBlockWidth; Loop++) {
755 PrintAt (MenuOption->OptCol + Loop, MenuOption->Row, (CHAR16 *) L" ");
756 }
757 }
758
759 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT);
760
761 if ((MenuOption->Col + gPromptBlockWidth + 1) == MenuOption->OptCol) {
762 PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);
763 Column = MenuOption->OptCol + 1;
764 }
765 //
766 // If Number looks like "3", convert it to "03/"
767 //
768 if (Number == 4 && (NumericType == DATE_NUMERIC)) {
769 FormattedNumber[3] = FormattedNumber[1];
770 FormattedNumber[2] = DATE_SEPARATOR;
771 FormattedNumber[1] = FormattedNumber[0];
772 FormattedNumber[0] = L'0';
773 Number = 8;
774 }
775 //
776 // If Number looks like "13", convert it to "13/"
777 //
778 if (Number == 6 && (NumericType == DATE_NUMERIC)) {
779 FormattedNumber[3] = FormattedNumber[2];
780 FormattedNumber[2] = DATE_SEPARATOR;
781 Number = 8;
782 }
783
784 if (Number == 4 &&
785 (NumericType == TIME_NUMERIC) &&
786 (MenuOption->Col + gPromptBlockWidth + 8) != MenuOption->OptCol
787 ) {
788 FormattedNumber[3] = FormattedNumber[1];
789 FormattedNumber[2] = TIME_SEPARATOR;
790 FormattedNumber[1] = FormattedNumber[0];
791 FormattedNumber[0] = L'0';
792 Number = 8;
793 }
794
795 if (Number == 4 &&
796 (NumericType == TIME_NUMERIC) &&
797 (MenuOption->Col + gPromptBlockWidth + 8) == MenuOption->OptCol
798 ) {
799 FormattedNumber[3] = FormattedNumber[1];
800 FormattedNumber[2] = RIGHT_NUMERIC_DELIMITER;
801 FormattedNumber[1] = FormattedNumber[0];
802 FormattedNumber[0] = L'0';
803 Number = 8;
804 }
805
806 PrintStringAt (Column, Row, FormattedNumber);
807 if (Number == 10 && (NumericType == DATE_NUMERIC)) {
808 PrintChar (RIGHT_NUMERIC_DELIMITER);
809 }
810
811 if (NumericType == REGULAR_NUMERIC) {
812 PrintChar (RIGHT_NUMERIC_DELIMITER);
813 }
814 }
815 break;
816
817 case SCAN_UP:
818 case SCAN_DOWN:
819 goto EnterCarriageReturn;
820
821 case SCAN_ESC:
822 return EFI_DEVICE_ERROR;
823
824 default:
825 break;
826 }
827
828 break;
829
830 EnterCarriageReturn:
831
832 case CHAR_CARRIAGE_RETURN:
833 //
834 // Check to see if the Value is something reasonable against consistency limitations.
835 // If not, let's kick the error specified.
836 //
837 //
838 // This gives us visibility to the FileFormTags->NvRamMap to check things
839 // ActiveIfr is a global maintained by the menuing code to ensure that we
840 // are pointing to the correct formset's file data.
841 //
842 for (Count = 0; Count < gActiveIfr; Count++) {
843 FileFormTags = FileFormTags->NextFile;
844 }
845
846 ExtractRequestedNvMap (FileFormTags, Tag->VariableNumber, &VariableDefinition);
847
848 CopyMem (&VariableDefinition->NvRamMap[Tag->StorageStart], &Tag->Value, Tag->StorageWidth);
849
850 //
851 // Data associated with a NULL device (in the fake NV storage)
852 //
853 if (Tag->StorageWidth == (UINT16) 0) {
854 CopyMem (&VariableDefinition->FakeNvRamMap[Tag->StorageStart], &Tag->Value, 2);
855 }
856 //
857 // If a late check is required save off the information. This is used when consistency checks
858 // are required, but certain values might be bound by an impossible consistency check such as
859 // if two questions are bound by consistency checks and each only has two possible choices, there
860 // would be no way for a user to switch the values. Thus we require late checking.
861 //
862 if (Tag->Flags & EFI_IFR_FLAG_LATE_CHECK) {
863 CopyMem (&Tag->OldValue, &BackupValue, Tag->StorageWidth);
864 } else {
865 //
866 // In theory, passing the value and the Id are sufficient to determine what needs
867 // to be done. The Id is the key to look for the entry needed in the Inconsistency
868 // database. That will yields operand and ID data - and since the ID's correspond
869 // to the NV storage, we can determine the values for other IDs there.
870 //
871 if (ValueIsNotValid (TRUE, 0, Tag, FileFormTags, &PopUp)) {
872 if (PopUp == 0x0000) {
873 SelectionComplete = TRUE;
874 break;
875 }
876
877 StringPtr = GetToken (PopUp, MenuOption->Handle);
878
879 CreatePopUp (GetStringWidth (StringPtr) / 2, 3, &NullCharacter, StringPtr, &NullCharacter);
880
881 do {
882 WaitForKeyStroke (&Key);
883
884 switch (Key.UnicodeChar) {
885
886 case CHAR_CARRIAGE_RETURN:
887 SelectionComplete = TRUE;
888 FreePool (StringPtr);
889 break;
890
891 default:
892 break;
893 }
894 } while (!SelectionComplete);
895
896 Tag->Value = BackupValue;
897 *Value = BackupValue;
898
899 CopyMem (&VariableDefinition->NvRamMap[Tag->StorageStart], &Tag->Value, Tag->StorageWidth);
900
901 //
902 // Data associated with a NULL device (in the fake NV storage)
903 //
904 if (Tag->StorageWidth == (UINT16) 0) {
905 CopyMem (&VariableDefinition->FakeNvRamMap[Tag->StorageStart], &Tag->Value, 2);
906 }
907
908 return EFI_DEVICE_ERROR;
909 }
910 }
911
912 return EFI_SUCCESS;
913 break;
914
915 case CHAR_BACKSPACE:
916 if (ManualInput) {
917 if (Count == 0) {
918 break;
919 }
920 //
921 // Remove a character
922 //
923 Number = PreviousNumber[Count - 1];
924 *Value = (UINT16) Number;
925 UpdateStatusBar (INPUT_ERROR, Tag->Flags, FALSE);
926 Count--;
927 Column--;
928 PrintAt (Column, Row, (CHAR16 *) L" ");
929 }
930 break;
931
932 default:
933 if (ManualInput) {
934 if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {
935 UpdateStatusBar (INPUT_ERROR, Tag->Flags, TRUE);
936 break;
937 }
938 //
939 // If Count 0-4 is complete, there is no way more is valid
940 //
941 if (Count > 4) {
942 break;
943 }
944 //
945 // Someone typed something valid!
946 //
947 if (Count != 0) {
948 Number = Number * 10 + (Key.UnicodeChar - L'0');
949 } else {
950 Number = Key.UnicodeChar - L'0';
951 }
952
953 if (Number > Tag->Maximum) {
954 UpdateStatusBar (INPUT_ERROR, Tag->Flags, TRUE);
955 Number = PreviousNumber[Count];
956 break;
957 } else {
958 UpdateStatusBar (INPUT_ERROR, Tag->Flags, FALSE);
959 }
960
961 Count++;
962
963 PreviousNumber[Count] = Number;
964 *Value = (UINT16) Number;
965 Tag->Value = (UINT16) Number;
966
967 PrintCharAt (Column, Row, Key.UnicodeChar);
968 Column++;
969 }
970 break;
971 }
972 } while (!SelectionComplete);
973 return EFI_SUCCESS;
974 }
975 //
976 // Notice that this is at least needed for the ordered list manipulation.
977 // Left/Right doesn't make sense for this op-code
978 //
979 EFI_STATUS
980 GetSelectionInputPopUp (
981 IN UI_MENU_OPTION *MenuOption,
982 IN EFI_TAG *Tag,
983 IN UINTN ValueCount,
984 OUT UINT16 *Value,
985 OUT UINT16 *KeyValue
986 )
987 {
988 EFI_INPUT_KEY Key;
989 UINTN Index;
990 UINTN TempIndex;
991 CHAR16 *StringPtr;
992 CHAR16 *TempStringPtr;
993 UINT16 Token;
994 UINTN Index2;
995 UINTN TopOptionIndex;
996 UINTN HighlightPosition;
997 UINTN Start;
998 UINTN End;
999 UINTN Top;
1000 UINTN Bottom;
1001 UINT16 TempValue;
1002 UINTN Count;
1003 UINTN PopUpMenuLines;
1004 UINTN MenuLinesInView;
1005 UINTN PopUpWidth;
1006 CHAR16 Character;
1007 BOOLEAN FirstOptionFoundFlag;
1008 INT32 SavedAttribute;
1009 EFI_TAG TagBackup;
1010 UINT8 *ValueArray;
1011 UINT8 *ValueArrayBackup;
1012 UINT8 ValueBackup;
1013 BOOLEAN Initialized;
1014 BOOLEAN KeyInitialized;
1015 BOOLEAN ShowDownArrow;
1016 BOOLEAN ShowUpArrow;
1017 UINTN DimensionsWidth;
1018
1019 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
1020
1021 TempValue = 0;
1022 TempIndex = 0;
1023 ValueArray = (UINT8 *) Value;
1024 ValueArrayBackup = NULL;
1025 Initialized = FALSE;
1026 KeyInitialized = FALSE;
1027 ShowDownArrow = FALSE;
1028 ShowUpArrow = FALSE;
1029
1030 if (Tag->Operand == EFI_IFR_ORDERED_LIST_OP) {
1031 ValueArrayBackup = AllocateZeroPool (Tag->StorageWidth);
1032 ASSERT (ValueArrayBackup != NULL);
1033 CopyMem (ValueArrayBackup, ValueArray, ValueCount);
1034 TempValue = *(UINT8 *) (ValueArray);
1035 if (ValueArray[0] != 0x00) {
1036 Initialized = TRUE;
1037 }
1038
1039 for (Index = 0; ValueArray[Index] != 0x00; Index++)
1040 ;
1041 ValueCount = Index;
1042 } else {
1043 TempValue = *Value;
1044 }
1045
1046 Count = 0;
1047 PopUpWidth = 0;
1048
1049 FirstOptionFoundFlag = FALSE;
1050
1051 StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);
1052 ASSERT (StringPtr);
1053
1054 //
1055 // Initialization for "One of" pop-up menu
1056 //
1057 //
1058 // Get the number of one of options present and its size
1059 //
1060 for (Index = MenuOption->TagIndex; MenuOption->Tags[Index].Operand != EFI_IFR_END_ONE_OF_OP; Index++) {
1061 if (MenuOption->Tags[Index].Operand == EFI_IFR_ONE_OF_OPTION_OP &&
1062 !MenuOption->Tags[Index].Suppress) {
1063 if (!FirstOptionFoundFlag) {
1064 FirstOptionFoundFlag = TRUE;
1065 }
1066
1067 Count++;
1068 Token = MenuOption->Tags[Index].Text;
1069
1070 //
1071 // If this is an ordered list that is initialized
1072 //
1073 if (Initialized) {
1074 for (ValueBackup = (UINT8) MenuOption->TagIndex;
1075 MenuOption->Tags[ValueBackup].Operand != EFI_IFR_END_OP;
1076 ValueBackup++
1077 ) {
1078 if (MenuOption->Tags[ValueBackup].Value == ((UINT8 *) ValueArrayBackup)[Index - MenuOption->TagIndex - 1]) {
1079 StringPtr = GetToken (MenuOption->Tags[ValueBackup].Text, MenuOption->Handle);
1080 break;
1081 }
1082 }
1083 } else {
1084 StringPtr = GetToken (Token, MenuOption->Handle);
1085 }
1086
1087 if (StrLen (StringPtr) > PopUpWidth) {
1088 PopUpWidth = StrLen (StringPtr);
1089 }
1090
1091 FreePool (StringPtr);
1092 }
1093 }
1094 //
1095 // Perform popup menu initialization.
1096 //
1097 PopUpMenuLines = Count;
1098 PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;
1099
1100 SavedAttribute = gST->ConOut->Mode->Attribute;
1101 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
1102
1103 if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {
1104 PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;
1105 }
1106
1107 Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gScreenDimensions.LeftColumn;
1108 End = Start + PopUpWidth + POPUP_FRAME_WIDTH;
1109 Top = gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT;
1110 Bottom = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT;
1111
1112 MenuLinesInView = Bottom - Top - 1;
1113 if (MenuLinesInView >= PopUpMenuLines) {
1114 Top = Top + (MenuLinesInView - PopUpMenuLines) / 2;
1115 Bottom = Top + PopUpMenuLines + 1;
1116 } else {
1117 TempValue = MenuOption->Tags[MenuOption->TagIndex + 1].Value;
1118 ShowDownArrow = TRUE;
1119 }
1120
1121 TopOptionIndex = 1;
1122 HighlightPosition = 0;
1123 do {
1124 if (Initialized) {
1125 for (Index = MenuOption->TagIndex, Index2 = 0; Index2 < ValueCount; Index++, Index2++) {
1126 //
1127 // Set the value for the item we are looking for
1128 //
1129 Count = ValueArrayBackup[Index2];
1130
1131 //
1132 // If we hit the end of the Array, we are complete
1133 //
1134 if (Count == 0) {
1135 break;
1136 }
1137
1138 if (MenuOption->Tags[Index].Operand == EFI_IFR_ONE_OF_OPTION_OP) {
1139 for (ValueBackup = (UINT8) MenuOption->TagIndex;
1140 MenuOption->Tags[ValueBackup].Operand != EFI_IFR_END_ONE_OF_OP;
1141 ValueBackup++
1142 ) {
1143 //
1144 // We just found what we are looking for
1145 //
1146 if (MenuOption->Tags[ValueBackup].Value == Count) {
1147 //
1148 // As long as the two indexes aren't the same, we have
1149 // two different op-codes we need to swap internally
1150 //
1151 if (Index != ValueBackup) {
1152 //
1153 // Backup destination tag, then copy source to destination, then copy backup to source location
1154 //
1155 CopyMem (&TagBackup, &MenuOption->Tags[Index], sizeof (EFI_TAG));
1156 CopyMem (&MenuOption->Tags[Index], &MenuOption->Tags[ValueBackup], sizeof (EFI_TAG));
1157 CopyMem (&MenuOption->Tags[ValueBackup], &TagBackup, sizeof (EFI_TAG));
1158 } else {
1159 //
1160 // If the indexes are the same, then the op-code is where he belongs
1161 //
1162 }
1163 }
1164 }
1165 } else {
1166 //
1167 // Since this wasn't an option op-code (likely the ordered list op-code) decerement Index2
1168 //
1169 Index2--;
1170 }
1171 }
1172 }
1173 //
1174 // Clear that portion of the screen
1175 //
1176 ClearLines (Start, End, Top, Bottom, POPUP_TEXT | POPUP_BACKGROUND);
1177
1178 //
1179 // Draw "One of" pop-up menu
1180 //
1181 Character = (CHAR16) BOXDRAW_DOWN_RIGHT;
1182 PrintCharAt (Start, Top, Character);
1183 for (Index = Start; Index + 2 < End; Index++) {
1184 if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {
1185 Character = (CHAR16) GEOMETRICSHAPE_UP_TRIANGLE;
1186 } else {
1187 Character = (CHAR16) BOXDRAW_HORIZONTAL;
1188 }
1189
1190 PrintChar (Character);
1191 }
1192
1193 Character = (CHAR16) BOXDRAW_DOWN_LEFT;
1194 PrintChar (Character);
1195 Character = (CHAR16) BOXDRAW_VERTICAL;
1196 for (Index = Top + 1; Index < Bottom; Index++) {
1197 PrintCharAt (Start, Index, Character);
1198 PrintCharAt (End - 1, Index, Character);
1199 }
1200 //
1201 // Display the One of options
1202 //
1203 Index2 = Top + 1;
1204 for (Index = MenuOption->TagIndex + TopOptionIndex;
1205 (MenuOption->Tags[Index].Operand != EFI_IFR_END_ONE_OF_OP) && (Index2 < Bottom);
1206 Index++
1207 ) {
1208 if (MenuOption->Tags[Index].Operand == EFI_IFR_ONE_OF_OPTION_OP) {
1209 Token = MenuOption->Tags[Index].Text;
1210 if (Initialized) {
1211 for (ValueBackup = (UINT8) MenuOption->TagIndex;
1212 MenuOption->Tags[ValueBackup].Operand != EFI_IFR_END_ONE_OF_OP;
1213 ValueBackup++
1214 ) {
1215 if (MenuOption->Tags[ValueBackup].Value == ((UINT8 *) ValueArrayBackup)[Index - MenuOption->TagIndex - 1]) {
1216 StringPtr = GetToken (MenuOption->Tags[ValueBackup].Text, MenuOption->Handle);
1217 break;
1218 }
1219 }
1220 } else {
1221 ValueBackup = (UINT8) Index;
1222 StringPtr = GetToken (Token, MenuOption->Handle);
1223 }
1224 //
1225 // If the string occupies multiple lines, truncate it to fit in one line,
1226 // and append a "..." for indication.
1227 //
1228 if (StrLen (StringPtr) > (PopUpWidth - 1)) {
1229 TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));
1230 ASSERT (TempStringPtr != NULL);
1231 CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));
1232 FreePool (StringPtr);
1233 StringPtr = TempStringPtr;
1234 StrCat (StringPtr, (CHAR16 *) L"...");
1235 }
1236 //
1237 // Code to display the text should go here. Follwed by the [*]
1238 //
1239 if (MenuOption->Tags[ValueBackup].Suppress == TRUE) {
1240 //
1241 // Don't show the one, so decrease the Index2 for balance
1242 //
1243 Index2--;
1244 } else if (MenuOption->Tags[ValueBackup].GrayOut == TRUE) {
1245 //
1246 // Gray Out the one
1247 //
1248 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | POPUP_BACKGROUND);
1249 PrintStringAt (Start + 2, Index2, StringPtr);
1250 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
1251 } else if (MenuOption->Tags[ValueBackup].Value == TempValue) {
1252 //
1253 // Highlight the selected one
1254 //
1255 gST->ConOut->SetAttribute (gST->ConOut, PICKLIST_HIGHLIGHT_TEXT | PICKLIST_HIGHLIGHT_BACKGROUND);
1256 PrintStringAt (Start + 2, Index2, StringPtr);
1257 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
1258 HighlightPosition = Index2;
1259 } else {
1260 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
1261 PrintStringAt (Start + 2, Index2, StringPtr);
1262 }
1263
1264 FreePool (StringPtr);
1265 Index2 = Index2 + 1;
1266 }
1267 }
1268
1269 Character = (CHAR16) BOXDRAW_UP_RIGHT;
1270 PrintCharAt (Start, Bottom, Character);
1271 for (Index = Start; Index + 2 < End; Index++) {
1272 if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {
1273 Character = (CHAR16) GEOMETRICSHAPE_DOWN_TRIANGLE;
1274 } else {
1275 Character = (CHAR16) BOXDRAW_HORIZONTAL;
1276 }
1277
1278 PrintChar (Character);
1279 }
1280
1281 Character = (CHAR16) BOXDRAW_UP_LEFT;
1282 PrintChar (Character);
1283 //
1284 // Get User selection and change TempValue if necessary
1285 //
1286 //
1287 // Stop: One of pop-up menu
1288 //
1289 Key.UnicodeChar = CHAR_NULL;
1290 if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {
1291 Key.ScanCode = gDirection;
1292 gDirection = 0;
1293 goto TheKey;
1294 }
1295
1296 if (!KeyInitialized) {
1297 if (MenuOption->ThisTag->Operand == EFI_IFR_ONE_OF_OP) {
1298 *KeyValue = MenuOption->Tags[MenuOption->TagIndex + 1].Key;
1299 } else {
1300 *KeyValue = MenuOption->ThisTag->Key;
1301 }
1302
1303 KeyInitialized = TRUE;
1304 }
1305
1306 WaitForKeyStroke (&Key);
1307
1308 TheKey:
1309 switch (Key.UnicodeChar) {
1310 case '+':
1311 case '-':
1312 //
1313 // If an ordered list op-code, we will allow for a popup of +/- keys
1314 // to create an ordered list of items
1315 //
1316 if (Tag->Operand == EFI_IFR_ORDERED_LIST_OP) {
1317 if (Key.UnicodeChar == '+') {
1318 if ((TopOptionIndex > 1) && (HighlightPosition == (Top + 1))) {
1319 //
1320 // Highlight reaches the top of the popup window, scroll one menu item.
1321 //
1322 TopOptionIndex--;
1323 ShowDownArrow = TRUE;
1324 }
1325
1326 if (TopOptionIndex == 1) {
1327 ShowUpArrow = FALSE;
1328 }
1329 } else {
1330 if (((TopOptionIndex + MenuLinesInView) <= PopUpMenuLines) && (HighlightPosition == (Bottom - 1))) {
1331 //
1332 // Highlight reaches the bottom of the popup window, scroll one menu item.
1333 //
1334 TopOptionIndex++;
1335 ShowUpArrow = TRUE;
1336 }
1337
1338 if ((TopOptionIndex + MenuLinesInView) == (PopUpMenuLines + 1)) {
1339 ShowDownArrow = FALSE;
1340 }
1341 }
1342
1343 for (Index = MenuOption->TagIndex + TopOptionIndex;
1344 MenuOption->Tags[Index].Operand != EFI_IFR_END_ONE_OF_OP;
1345 Index++
1346 ) {
1347 if (MenuOption->Tags[Index].Operand == EFI_IFR_ORDERED_LIST_OP) {
1348 continue;
1349 }
1350
1351 if (Key.UnicodeChar == '+') {
1352 TempIndex = Index - 1;
1353 } else {
1354 TempIndex = Index + 1;
1355 }
1356 //
1357 // Is this the current tag we are on?
1358 //
1359 if (MenuOption->Tags[Index].Value == TempValue) {
1360 //
1361 // Is this prior tag a valid choice? If not, bail out
1362 //
1363 if (MenuOption->Tags[TempIndex].Operand == EFI_IFR_ONE_OF_OPTION_OP) {
1364 //
1365 // Copy the destination tag to the local variable
1366 //
1367 CopyMem (&TagBackup, &MenuOption->Tags[TempIndex], sizeof (EFI_TAG));
1368 //
1369 // Copy the current tag to the tag location before us
1370 //
1371 CopyMem (&MenuOption->Tags[TempIndex], &MenuOption->Tags[Index], sizeof (EFI_TAG));
1372 //
1373 // Copy the backed up tag to the current location
1374 //
1375 CopyMem (&MenuOption->Tags[Index], &TagBackup, sizeof (EFI_TAG));
1376
1377 //
1378 // Adjust the array of values
1379 //
1380 for (Index = 0; Index < ValueCount; Index++) {
1381 if (ValueArrayBackup[Index] == (UINT8) TempValue) {
1382 if (Key.UnicodeChar == '+') {
1383 if (Index == 0) {
1384 //
1385 // It is the top of the array already
1386 //
1387 break;
1388 }
1389
1390 TempIndex = Index - 1;
1391 } else {
1392 if ((Index + 1) == ValueCount) {
1393 //
1394 // It is the bottom of the array already
1395 //
1396 break;
1397 }
1398
1399 TempIndex = Index + 1;
1400 }
1401
1402 ValueBackup = ValueArrayBackup[TempIndex];
1403 ValueArrayBackup[TempIndex] = ValueArrayBackup[Index];
1404 ValueArrayBackup[Index] = ValueBackup;
1405 Initialized = TRUE;
1406 break;
1407 }
1408 }
1409 break;
1410 } else {
1411 break;
1412 }
1413 }
1414 }
1415 }
1416 break;
1417
1418 case CHAR_NULL:
1419 switch (Key.ScanCode) {
1420 case SCAN_UP:
1421 case SCAN_DOWN:
1422 if (Key.ScanCode == SCAN_UP) {
1423 if ((TopOptionIndex > 1) && (HighlightPosition == (Top + 1))) {
1424 //
1425 // Highlight reaches the top of the popup window, scroll one menu item.
1426 //
1427 TopOptionIndex--;
1428 ShowDownArrow = TRUE;
1429 }
1430
1431 if (TopOptionIndex == 1) {
1432 ShowUpArrow = FALSE;
1433 }
1434 } else {
1435 if (((TopOptionIndex + MenuLinesInView) <= PopUpMenuLines) && (HighlightPosition == (Bottom - 1))) {
1436 //
1437 // Highlight reaches the bottom of the popup window, scroll one menu item.
1438 //
1439 TopOptionIndex++;
1440 ShowUpArrow = TRUE;
1441 }
1442
1443 if ((TopOptionIndex + MenuLinesInView) == (PopUpMenuLines + 1)) {
1444 ShowDownArrow = FALSE;
1445 }
1446 }
1447
1448 for (Index = MenuOption->TagIndex + TopOptionIndex;
1449 MenuOption->Tags[Index].Operand != EFI_IFR_END_ONE_OF_OP;
1450 Index++
1451 ) {
1452 if (MenuOption->Tags[Index].Operand == EFI_IFR_ONE_OF_OPTION_OP) {
1453 if (Initialized) {
1454 for (Index = 0; (ValueArrayBackup[Index] != TempValue) && (Index < ValueCount); Index++)
1455 ;
1456
1457 //
1458 // Did we hit the end of the array? Either get the first TempValue or the next one
1459 //
1460 if (Key.ScanCode == SCAN_UP) {
1461 if (Index == 0) {
1462 TempValue = ValueArrayBackup[0];
1463 } else {
1464 TempValue = ValueArrayBackup[Index - 1];
1465 }
1466 } else {
1467 if ((Index + 1) == ValueCount) {
1468 TempValue = ValueArrayBackup[Index];
1469 } else {
1470 TempValue = ValueArrayBackup[Index + 1];
1471 }
1472 }
1473 break;
1474 } else {
1475 if (Key.ScanCode == SCAN_UP) {
1476 TempIndex = Index - 1;
1477
1478 //
1479 // Keep going until meets meaningful tag.
1480 //
1481 while ((MenuOption->Tags[TempIndex].Operand != EFI_IFR_ONE_OF_OPTION_OP &&
1482 MenuOption->Tags[TempIndex].Operand != EFI_IFR_ONE_OF_OP &&
1483 MenuOption->Tags[TempIndex].Operand != EFI_IFR_END_ONE_OF_OP)
1484 ||
1485 (MenuOption->Tags[TempIndex].Operand == EFI_IFR_ONE_OF_OPTION_OP &&
1486 (MenuOption->Tags[TempIndex].Suppress || MenuOption->Tags[TempIndex].GrayOut))) {
1487 TempIndex--;
1488 }
1489 } else {
1490 TempIndex = Index + 1;
1491
1492 //
1493 // Keep going until meets meaningful tag.
1494 //
1495 while ((MenuOption->Tags[TempIndex].Operand != EFI_IFR_ONE_OF_OPTION_OP &&
1496 MenuOption->Tags[TempIndex].Operand != EFI_IFR_ONE_OF_OP &&
1497 MenuOption->Tags[TempIndex].Operand != EFI_IFR_END_ONE_OF_OP)
1498 ||
1499 (MenuOption->Tags[TempIndex].Operand == EFI_IFR_ONE_OF_OPTION_OP &&
1500 (MenuOption->Tags[TempIndex].Suppress || MenuOption->Tags[TempIndex].GrayOut))) {
1501 TempIndex++;
1502 }
1503 }
1504 //
1505 // The option value is the same as what is stored in NV store. This is where we take action
1506 //
1507 if (MenuOption->Tags[Index].Value == TempValue) {
1508 //
1509 // Only if the previous op-code is an option can we select it, otherwise we are at the left-most option
1510 //
1511 if (MenuOption->Tags[TempIndex].Operand == EFI_IFR_ONE_OF_OPTION_OP) {
1512 TempValue = MenuOption->Tags[TempIndex].Value;
1513 *KeyValue = MenuOption->Tags[TempIndex].Key;
1514 } else {
1515 TempValue = MenuOption->Tags[Index].Value;
1516 *KeyValue = MenuOption->Tags[Index].Key;
1517 }
1518 break;
1519 }
1520 }
1521 }
1522 }
1523 break;
1524
1525 case SCAN_ESC:
1526 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1527 if (ValueArrayBackup != NULL) {
1528 FreePool (ValueArrayBackup);
1529 }
1530
1531 return EFI_DEVICE_ERROR;
1532
1533 default:
1534 break;
1535 }
1536
1537 break;
1538
1539 case CHAR_CARRIAGE_RETURN:
1540 //
1541 // return the current selection
1542 //
1543 if (Tag->Operand == EFI_IFR_ORDERED_LIST_OP) {
1544 CopyMem (ValueArray, ValueArrayBackup, ValueCount);
1545 FreePool (ValueArrayBackup);
1546 } else {
1547 *Value = TempValue;
1548 }
1549
1550 goto Done;
1551
1552 default:
1553 break;
1554 }
1555 } while (1);
1556
1557 Done:
1558 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1559 return EFI_SUCCESS;
1560 }
1561
1562 EFI_STATUS
1563 WaitForKeyStroke (
1564 OUT EFI_INPUT_KEY *Key
1565 )
1566 {
1567 EFI_STATUS Status;
1568
1569 do {
1570 UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0);
1571 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, Key);
1572 } while (EFI_ERROR(Status));
1573
1574 return Status;
1575 }