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