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