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