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