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