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