]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/SetupBrowserDxe/InputHandler.c
When do discard action, enable callback action for questions which have value changed.
[mirror_edk2.git] / MdeModulePkg / Universal / SetupBrowserDxe / InputHandler.c
1 /** @file
2 Implementation for handling user input from the User Interfaces.
3
4 Copyright (c) 2004 - 2012, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "Setup.h"
16
17
18 /**
19 Get string or password input from user.
20
21 @param MenuOption Pointer to the current input menu.
22 @param Prompt The prompt string shown on popup window.
23 @param StringPtr Old user input and destination for use input string.
24
25 @retval EFI_SUCCESS If string input is read successfully
26 @retval EFI_DEVICE_ERROR If operation fails
27
28 **/
29 EFI_STATUS
30 ReadString (
31 IN UI_MENU_OPTION *MenuOption,
32 IN CHAR16 *Prompt,
33 IN OUT CHAR16 *StringPtr
34 )
35 {
36 EFI_STATUS Status;
37 EFI_INPUT_KEY Key;
38 CHAR16 NullCharacter;
39 UINTN ScreenSize;
40 CHAR16 Space[2];
41 CHAR16 KeyPad[2];
42 CHAR16 *TempString;
43 CHAR16 *BufferedString;
44 UINTN Index;
45 UINTN Index2;
46 UINTN Count;
47 UINTN Start;
48 UINTN Top;
49 UINTN DimensionsWidth;
50 UINTN DimensionsHeight;
51 UINTN CurrentCursor;
52 BOOLEAN CursorVisible;
53 UINTN Minimum;
54 UINTN Maximum;
55 FORM_BROWSER_STATEMENT *Question;
56 BOOLEAN IsPassword;
57
58 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
59 DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
60
61 NullCharacter = CHAR_NULL;
62 ScreenSize = GetStringWidth (Prompt) / sizeof (CHAR16);
63 Space[0] = L' ';
64 Space[1] = CHAR_NULL;
65
66 Question = MenuOption->ThisTag;
67 Minimum = (UINTN) Question->Minimum;
68 Maximum = (UINTN) Question->Maximum;
69
70 if (Question->Operand == EFI_IFR_PASSWORD_OP) {
71 IsPassword = TRUE;
72 } else {
73 IsPassword = FALSE;
74 }
75
76 TempString = AllocateZeroPool ((Maximum + 1)* sizeof (CHAR16));
77 ASSERT (TempString);
78
79 if (ScreenSize < (Maximum + 1)) {
80 ScreenSize = Maximum + 1;
81 }
82
83 if ((ScreenSize + 2) > DimensionsWidth) {
84 ScreenSize = DimensionsWidth - 2;
85 }
86
87 BufferedString = AllocateZeroPool (ScreenSize * 2);
88 ASSERT (BufferedString);
89
90 Start = (DimensionsWidth - ScreenSize - 2) / 2 + gScreenDimensions.LeftColumn + 1;
91 Top = ((DimensionsHeight - 6) / 2) + gScreenDimensions.TopRow - 1;
92
93 //
94 // Display prompt for string
95 //
96 CreateMultiStringPopUp (ScreenSize, 4, &NullCharacter, Prompt, Space, &NullCharacter);
97
98 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
99
100 CursorVisible = gST->ConOut->Mode->CursorVisible;
101 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
102
103 CurrentCursor = GetStringWidth (StringPtr) / 2 - 1;
104 if (CurrentCursor != 0) {
105 //
106 // Show the string which has beed saved before.
107 //
108 SetUnicodeMem (BufferedString, ScreenSize - 1, L' ');
109 PrintStringAt (Start + 1, Top + 3, BufferedString);
110
111 if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {
112 Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;
113 } else {
114 Index = 0;
115 }
116
117 if (IsPassword) {
118 gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3);
119 }
120
121 for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {
122 BufferedString[Count] = StringPtr[Index];
123
124 if (IsPassword) {
125 PrintChar (L'*');
126 }
127 }
128
129 if (!IsPassword) {
130 PrintStringAt (Start + 1, Top + 3, BufferedString);
131 }
132
133 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
134 gST->ConOut->SetCursorPosition (gST->ConOut, Start + GetStringWidth (StringPtr) / 2, Top + 3);
135 }
136
137 do {
138 Status = WaitForKeyStroke (&Key);
139 ASSERT_EFI_ERROR (Status);
140
141 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
142 switch (Key.UnicodeChar) {
143 case CHAR_NULL:
144 switch (Key.ScanCode) {
145 case SCAN_LEFT:
146 if (CurrentCursor > 0) {
147 CurrentCursor--;
148 }
149 break;
150
151 case SCAN_RIGHT:
152 if (CurrentCursor < (GetStringWidth (StringPtr) / 2 - 1)) {
153 CurrentCursor++;
154 }
155 break;
156
157 case SCAN_ESC:
158 FreePool (TempString);
159 FreePool (BufferedString);
160 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
161 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
162 return EFI_DEVICE_ERROR;
163
164 default:
165 break;
166 }
167
168 break;
169
170 case CHAR_CARRIAGE_RETURN:
171 if (GetStringWidth (StringPtr) >= ((Minimum + 1) * sizeof (CHAR16))) {
172
173 FreePool (TempString);
174 FreePool (BufferedString);
175 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
176 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
177 return EFI_SUCCESS;
178 } else {
179 //
180 // Simply create a popup to tell the user that they had typed in too few characters.
181 // To save code space, we can then treat this as an error and return back to the menu.
182 //
183 do {
184 CreateDialog (4, TRUE, 0, NULL, &Key, &NullCharacter, gMiniString, gPressEnter, &NullCharacter);
185 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
186
187 FreePool (TempString);
188 FreePool (BufferedString);
189 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
190 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
191 return EFI_DEVICE_ERROR;
192 }
193
194 break;
195
196 case CHAR_BACKSPACE:
197 if (StringPtr[0] != CHAR_NULL && CurrentCursor != 0) {
198 for (Index = 0; Index < CurrentCursor - 1; Index++) {
199 TempString[Index] = StringPtr[Index];
200 }
201 Count = GetStringWidth (StringPtr) / 2 - 1;
202 if (Count >= CurrentCursor) {
203 for (Index = CurrentCursor - 1, Index2 = CurrentCursor; Index2 < Count; Index++, Index2++) {
204 TempString[Index] = StringPtr[Index2];
205 }
206 TempString[Index] = CHAR_NULL;
207 }
208 //
209 // Effectively truncate string by 1 character
210 //
211 StrCpy (StringPtr, TempString);
212 CurrentCursor --;
213 }
214
215 default:
216 //
217 // If it is the beginning of the string, don't worry about checking maximum limits
218 //
219 if ((StringPtr[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
220 StrnCpy (StringPtr, &Key.UnicodeChar, 1);
221 CurrentCursor++;
222 } else if ((GetStringWidth (StringPtr) < ((Maximum + 1) * sizeof (CHAR16))) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
223 KeyPad[0] = Key.UnicodeChar;
224 KeyPad[1] = CHAR_NULL;
225 Count = GetStringWidth (StringPtr) / 2 - 1;
226 if (CurrentCursor < Count) {
227 for (Index = 0; Index < CurrentCursor; Index++) {
228 TempString[Index] = StringPtr[Index];
229 }
230 TempString[Index] = CHAR_NULL;
231 StrCat (TempString, KeyPad);
232 StrCat (TempString, StringPtr + CurrentCursor);
233 StrCpy (StringPtr, TempString);
234 } else {
235 StrCat (StringPtr, KeyPad);
236 }
237 CurrentCursor++;
238 }
239
240 //
241 // If the width of the input string is now larger than the screen, we nee to
242 // adjust the index to start printing portions of the string
243 //
244 SetUnicodeMem (BufferedString, ScreenSize - 1, L' ');
245 PrintStringAt (Start + 1, Top + 3, BufferedString);
246
247 if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {
248 Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;
249 } else {
250 Index = 0;
251 }
252
253 if (IsPassword) {
254 gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3);
255 }
256
257 for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {
258 BufferedString[Count] = StringPtr[Index];
259
260 if (IsPassword) {
261 PrintChar (L'*');
262 }
263 }
264
265 if (!IsPassword) {
266 PrintStringAt (Start + 1, Top + 3, BufferedString);
267 }
268 break;
269 }
270
271 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
272 gST->ConOut->SetCursorPosition (gST->ConOut, Start + CurrentCursor + 1, Top + 3);
273 } while (TRUE);
274
275 }
276
277 /**
278 Adjust the value to the correct one. Rules follow the sample:
279 like: Year change: 2012.02.29 -> 2013.02.29 -> 2013.02.01
280 Month change: 2013.03.29 -> 2013.02.29 -> 2013.02.28
281
282 @param Question Pointer to current question.
283 @param Sequence The sequence of the field in the question.
284 **/
285 VOID
286 AdjustQuestionValue (
287 IN FORM_BROWSER_STATEMENT *Question,
288 IN UINT8 Sequence
289 )
290 {
291 UINT8 Month;
292 UINT16 Year;
293 UINT8 Maximum;
294 UINT8 Minimum;
295
296 if (Question->Operand != EFI_IFR_DATE_OP) {
297 return;
298 }
299
300 Month = Question->HiiValue.Value.date.Month;
301 Year = Question->HiiValue.Value.date.Year;
302 Minimum = 1;
303
304 switch (Month) {
305 case 2:
306 if ((Year % 4) == 0 && ((Year % 100) != 0 || (Year % 400) == 0)) {
307 Maximum = 29;
308 } else {
309 Maximum = 28;
310 }
311 break;
312 case 4:
313 case 6:
314 case 9:
315 case 11:
316 Maximum = 30;
317 break;
318 default:
319 Maximum = 31;
320 break;
321 }
322
323 //
324 // Change the month area.
325 //
326 if (Sequence == 0) {
327 if (Question->HiiValue.Value.date.Day > Maximum) {
328 Question->HiiValue.Value.date.Day = Maximum;
329 }
330 }
331
332 //
333 // Change the Year area.
334 //
335 if (Sequence == 2) {
336 if (Question->HiiValue.Value.date.Day > Maximum) {
337 Question->HiiValue.Value.date.Day = Minimum;
338 }
339 }
340 }
341
342 /**
343 This routine reads a numeric value from the user input.
344
345 @param Selection Pointer to current selection.
346 @param MenuOption Pointer to the current input menu.
347
348 @retval EFI_SUCCESS If numerical input is read successfully
349 @retval EFI_DEVICE_ERROR If operation fails
350
351 **/
352 EFI_STATUS
353 GetNumericInput (
354 IN UI_MENU_SELECTION *Selection,
355 IN UI_MENU_OPTION *MenuOption
356 )
357 {
358 EFI_STATUS Status;
359 UINTN Column;
360 UINTN Row;
361 CHAR16 InputText[MAX_NUMERIC_INPUT_WIDTH];
362 CHAR16 FormattedNumber[MAX_NUMERIC_INPUT_WIDTH - 1];
363 UINT64 PreviousNumber[MAX_NUMERIC_INPUT_WIDTH - 3];
364 UINTN Count;
365 UINTN Loop;
366 BOOLEAN ManualInput;
367 BOOLEAN HexInput;
368 BOOLEAN DateOrTime;
369 UINTN InputWidth;
370 UINT64 EditValue;
371 UINT64 Step;
372 UINT64 Minimum;
373 UINT64 Maximum;
374 UINTN EraseLen;
375 UINT8 Digital;
376 EFI_INPUT_KEY Key;
377 EFI_HII_VALUE *QuestionValue;
378 FORM_BROWSER_FORM *Form;
379 FORM_BROWSER_FORMSET *FormSet;
380 FORM_BROWSER_STATEMENT *Question;
381
382 Column = MenuOption->OptCol;
383 Row = MenuOption->Row;
384 PreviousNumber[0] = 0;
385 Count = 0;
386 InputWidth = 0;
387 Digital = 0;
388
389 FormSet = Selection->FormSet;
390 Form = Selection->Form;
391 Question = MenuOption->ThisTag;
392 QuestionValue = &Question->HiiValue;
393 Step = Question->Step;
394 Minimum = Question->Minimum;
395 Maximum = Question->Maximum;
396
397 //
398 // Only two case, user can enter to this function: Enter and +/- case.
399 // In Enter case, gDirection = 0; in +/- case, gDirection = SCAN_LEFT/SCAN_WRIGHT
400 //
401 ManualInput = (BOOLEAN)(gDirection == 0 ? TRUE : FALSE);
402
403 if ((Question->Operand == EFI_IFR_DATE_OP) || (Question->Operand == EFI_IFR_TIME_OP)) {
404 DateOrTime = TRUE;
405 } else {
406 DateOrTime = FALSE;
407 }
408
409 //
410 // Prepare Value to be edit
411 //
412 EraseLen = 0;
413 EditValue = 0;
414 if (Question->Operand == EFI_IFR_DATE_OP) {
415 Step = 1;
416 Minimum = 1;
417
418 switch (MenuOption->Sequence) {
419 case 0:
420 Maximum = 12;
421 EraseLen = 4;
422 EditValue = QuestionValue->Value.date.Month;
423 break;
424
425 case 1:
426 switch (QuestionValue->Value.date.Month) {
427 case 2:
428 if ((QuestionValue->Value.date.Year % 4) == 0 &&
429 ((QuestionValue->Value.date.Year % 100) != 0 ||
430 (QuestionValue->Value.date.Year % 400) == 0)) {
431 Maximum = 29;
432 } else {
433 Maximum = 28;
434 }
435 break;
436 case 4:
437 case 6:
438 case 9:
439 case 11:
440 Maximum = 30;
441 break;
442 default:
443 Maximum = 31;
444 break;
445 }
446
447 EraseLen = 3;
448 EditValue = QuestionValue->Value.date.Day;
449 break;
450
451 case 2:
452 Maximum = 0xffff;
453 EraseLen = 5;
454 EditValue = QuestionValue->Value.date.Year;
455 break;
456
457 default:
458 break;
459 }
460 } else if (Question->Operand == EFI_IFR_TIME_OP) {
461 Step = 1;
462 Minimum = 0;
463
464 switch (MenuOption->Sequence) {
465 case 0:
466 Maximum = 23;
467 EraseLen = 4;
468 EditValue = QuestionValue->Value.time.Hour;
469 break;
470
471 case 1:
472 Maximum = 59;
473 EraseLen = 3;
474 EditValue = QuestionValue->Value.time.Minute;
475 break;
476
477 case 2:
478 Maximum = 59;
479 EraseLen = 3;
480 EditValue = QuestionValue->Value.time.Second;
481 break;
482
483 default:
484 break;
485 }
486 } else {
487 //
488 // Numeric
489 //
490 EraseLen = gOptionBlockWidth;
491 EditValue = QuestionValue->Value.u64;
492 if (Maximum == 0) {
493 Maximum = (UINT64) -1;
494 }
495 }
496
497 if ((Question->Operand == EFI_IFR_NUMERIC_OP) &&
498 ((Question->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX)) {
499 HexInput = TRUE;
500 } else {
501 HexInput = FALSE;
502 }
503
504 //
505 // Enter from "Enter" input, clear the old word showing.
506 //
507 if (ManualInput) {
508 if (Question->Operand == EFI_IFR_NUMERIC_OP) {
509 if (HexInput) {
510 InputWidth = Question->StorageWidth * 2;
511 } else {
512 switch (Question->StorageWidth) {
513 case 1:
514 InputWidth = 3;
515 break;
516
517 case 2:
518 InputWidth = 5;
519 break;
520
521 case 4:
522 InputWidth = 10;
523 break;
524
525 case 8:
526 InputWidth = 20;
527 break;
528
529 default:
530 InputWidth = 0;
531 break;
532 }
533 }
534
535 InputText[0] = LEFT_NUMERIC_DELIMITER;
536 SetUnicodeMem (InputText + 1, InputWidth, L' ');
537 ASSERT (InputWidth + 2 < MAX_NUMERIC_INPUT_WIDTH);
538 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
539 InputText[InputWidth + 2] = L'\0';
540
541 PrintAt (Column, Row, InputText);
542 Column++;
543 }
544
545 if (Question->Operand == EFI_IFR_DATE_OP) {
546 if (MenuOption->Sequence == 2) {
547 InputWidth = 4;
548 } else {
549 InputWidth = 2;
550 }
551
552 if (MenuOption->Sequence == 0) {
553 InputText[0] = LEFT_NUMERIC_DELIMITER;
554 SetUnicodeMem (InputText + 1, InputWidth, L' ');
555 } else {
556 SetUnicodeMem (InputText, InputWidth, L' ');
557 }
558
559 if (MenuOption->Sequence == 2) {
560 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
561 } else {
562 InputText[InputWidth + 1] = DATE_SEPARATOR;
563 }
564 InputText[InputWidth + 2] = L'\0';
565
566 PrintAt (Column, Row, InputText);
567 if (MenuOption->Sequence == 0) {
568 Column++;
569 }
570 }
571
572 if (Question->Operand == EFI_IFR_TIME_OP) {
573 InputWidth = 2;
574
575 if (MenuOption->Sequence == 0) {
576 InputText[0] = LEFT_NUMERIC_DELIMITER;
577 SetUnicodeMem (InputText + 1, InputWidth, L' ');
578 } else {
579 SetUnicodeMem (InputText, InputWidth, L' ');
580 }
581
582 if (MenuOption->Sequence == 2) {
583 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
584 } else {
585 InputText[InputWidth + 1] = TIME_SEPARATOR;
586 }
587 InputText[InputWidth + 2] = L'\0';
588
589 PrintAt (Column, Row, InputText);
590 if (MenuOption->Sequence == 0) {
591 Column++;
592 }
593 }
594 }
595
596 //
597 // First time we enter this handler, we need to check to see if
598 // we were passed an increment or decrement directive
599 //
600 do {
601 Key.UnicodeChar = CHAR_NULL;
602 if (gDirection != 0) {
603 Key.ScanCode = gDirection;
604 gDirection = 0;
605 goto TheKey2;
606 }
607
608 Status = WaitForKeyStroke (&Key);
609
610 TheKey2:
611 switch (Key.UnicodeChar) {
612
613 case '+':
614 case '-':
615 if (Key.UnicodeChar == '+') {
616 Key.ScanCode = SCAN_RIGHT;
617 } else {
618 Key.ScanCode = SCAN_LEFT;
619 }
620 Key.UnicodeChar = CHAR_NULL;
621 goto TheKey2;
622
623 case CHAR_NULL:
624 switch (Key.ScanCode) {
625 case SCAN_LEFT:
626 case SCAN_RIGHT:
627 if (DateOrTime && !ManualInput) {
628 //
629 // By setting this value, we will return back to the caller.
630 // We need to do this since an auto-refresh will destroy the adjustment
631 // based on what the real-time-clock is showing. So we always commit
632 // upon changing the value.
633 //
634 gDirection = SCAN_DOWN;
635 }
636
637 if ((Step != 0) && !ManualInput) {
638 if (Key.ScanCode == SCAN_LEFT) {
639 if (EditValue >= Minimum + Step) {
640 EditValue = EditValue - Step;
641 } else if (EditValue > Minimum){
642 EditValue = Minimum;
643 } else {
644 EditValue = Maximum;
645 }
646 } else if (Key.ScanCode == SCAN_RIGHT) {
647 if (EditValue + Step <= Maximum) {
648 EditValue = EditValue + Step;
649 } else if (EditValue < Maximum) {
650 EditValue = Maximum;
651 } else {
652 EditValue = Minimum;
653 }
654 }
655
656 ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
657 if (Question->Operand == EFI_IFR_DATE_OP) {
658 if (MenuOption->Sequence == 2) {
659 //
660 // Year
661 //
662 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINT16) EditValue);
663 } else {
664 //
665 // Month/Day
666 //
667 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);
668 }
669
670 if (MenuOption->Sequence == 0) {
671 ASSERT (EraseLen >= 2);
672 FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;
673 } else if (MenuOption->Sequence == 1) {
674 ASSERT (EraseLen >= 1);
675 FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;
676 }
677 } else if (Question->Operand == EFI_IFR_TIME_OP) {
678 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);
679
680 if (MenuOption->Sequence == 0) {
681 ASSERT (EraseLen >= 2);
682 FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;
683 } else if (MenuOption->Sequence == 1) {
684 ASSERT (EraseLen >= 1);
685 FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;
686 }
687 } else {
688 QuestionValue->Value.u64 = EditValue;
689 PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
690 }
691
692 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
693 for (Loop = 0; Loop < EraseLen; Loop++) {
694 PrintAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");
695 }
696 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextHighlightColor) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor));
697
698 if (MenuOption->Sequence == 0) {
699 PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);
700 Column = MenuOption->OptCol + 1;
701 }
702
703 PrintStringAt (Column, Row, FormattedNumber);
704
705 if (!DateOrTime || MenuOption->Sequence == 2) {
706 PrintChar (RIGHT_NUMERIC_DELIMITER);
707 }
708 }
709
710 goto EnterCarriageReturn;
711 break;
712
713 case SCAN_UP:
714 case SCAN_DOWN:
715 goto EnterCarriageReturn;
716
717 case SCAN_ESC:
718 return EFI_DEVICE_ERROR;
719
720 default:
721 break;
722 }
723
724 break;
725
726 EnterCarriageReturn:
727
728 case CHAR_CARRIAGE_RETURN:
729 //
730 // Validate input value with Minimum value.
731 //
732 if (EditValue < Minimum) {
733 UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE);
734 break;
735 } else {
736 UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, FALSE);
737 }
738
739 //
740 // Store Edit value back to Question
741 //
742 if (Question->Operand == EFI_IFR_DATE_OP) {
743 switch (MenuOption->Sequence) {
744 case 0:
745 QuestionValue->Value.date.Month = (UINT8) EditValue;
746 break;
747
748 case 1:
749 QuestionValue->Value.date.Day = (UINT8) EditValue;
750 break;
751
752 case 2:
753 QuestionValue->Value.date.Year = (UINT16) EditValue;
754 break;
755
756 default:
757 break;
758 }
759 } else if (Question->Operand == EFI_IFR_TIME_OP) {
760 switch (MenuOption->Sequence) {
761 case 0:
762 QuestionValue->Value.time.Hour = (UINT8) EditValue;
763 break;
764
765 case 1:
766 QuestionValue->Value.time.Minute = (UINT8) EditValue;
767 break;
768
769 case 2:
770 QuestionValue->Value.time.Second = (UINT8) EditValue;
771 break;
772
773 default:
774 break;
775 }
776 } else {
777 //
778 // Numeric
779 //
780 QuestionValue->Value.u64 = EditValue;
781 }
782
783 //
784 // Adjust the value to the correct one.
785 // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01
786 // 2013.03.29 -> 2013.02.29 -> 2013.02.28
787 //
788 if (Question->Operand == EFI_IFR_DATE_OP &&
789 (MenuOption->Sequence == 0 || MenuOption->Sequence == 2)) {
790 AdjustQuestionValue (Question, (UINT8)MenuOption->Sequence);
791 }
792
793 //
794 // Check to see if the Value is something reasonable against consistency limitations.
795 // If not, let's kick the error specified.
796 //
797 Status = ValidateQuestion (FormSet, Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);
798 if (EFI_ERROR (Status)) {
799 //
800 // Input value is not valid, restore Question Value
801 //
802 GetQuestionValue (FormSet, Form, Question, GetSetValueWithEditBuffer);
803 } else {
804 SetQuestionValue (FormSet, Form, Question, GetSetValueWithEditBuffer);
805 if (!DateOrTime || (Question->Storage != NULL)) {
806 //
807 // NV flag is unnecessary for RTC type of Date/Time
808 //
809 UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
810 }
811 }
812
813 return Status;
814 break;
815
816 case CHAR_BACKSPACE:
817 if (ManualInput) {
818 if (Count == 0) {
819 break;
820 }
821 //
822 // Remove a character
823 //
824 EditValue = PreviousNumber[Count - 1];
825 UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, FALSE);
826 Count--;
827 Column--;
828 PrintAt (Column, Row, L" ");
829 }
830 break;
831
832 default:
833 if (ManualInput) {
834 if (HexInput) {
835 if ((Key.UnicodeChar >= L'0') && (Key.UnicodeChar <= L'9')) {
836 Digital = (UINT8) (Key.UnicodeChar - L'0');
837 } else if ((Key.UnicodeChar >= L'A') && (Key.UnicodeChar <= L'F')) {
838 Digital = (UINT8) (Key.UnicodeChar - L'A' + 0x0A);
839 } else if ((Key.UnicodeChar >= L'a') && (Key.UnicodeChar <= L'f')) {
840 Digital = (UINT8) (Key.UnicodeChar - L'a' + 0x0A);
841 } else {
842 UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE);
843 break;
844 }
845 } else {
846 if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {
847 UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE);
848 break;
849 }
850 }
851
852 //
853 // If Count exceed input width, there is no way more is valid
854 //
855 if (Count >= InputWidth) {
856 break;
857 }
858 //
859 // Someone typed something valid!
860 //
861 if (Count != 0) {
862 if (HexInput) {
863 EditValue = LShiftU64 (EditValue, 4) + Digital;
864 } else {
865 EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0');
866 }
867 } else {
868 if (HexInput) {
869 EditValue = Digital;
870 } else {
871 EditValue = Key.UnicodeChar - L'0';
872 }
873 }
874
875 if (EditValue > Maximum) {
876 UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE);
877 ASSERT (Count < sizeof (PreviousNumber) / sizeof (PreviousNumber[0]));
878 EditValue = PreviousNumber[Count];
879 break;
880 } else {
881 UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, FALSE);
882 }
883
884 Count++;
885 ASSERT (Count < (sizeof (PreviousNumber) / sizeof (PreviousNumber[0])));
886 PreviousNumber[Count] = EditValue;
887
888 PrintCharAt (Column, Row, Key.UnicodeChar);
889 Column++;
890 }
891 break;
892 }
893 } while (TRUE);
894
895 }
896
897
898 /**
899 Get selection for OneOf and OrderedList (Left/Right will be ignored).
900
901 @param Selection Pointer to current selection.
902 @param MenuOption Pointer to the current input menu.
903
904 @retval EFI_SUCCESS If Option input is processed successfully
905 @retval EFI_DEVICE_ERROR If operation fails
906
907 **/
908 EFI_STATUS
909 GetSelectionInputPopUp (
910 IN UI_MENU_SELECTION *Selection,
911 IN UI_MENU_OPTION *MenuOption
912 )
913 {
914 EFI_STATUS Status;
915 EFI_INPUT_KEY Key;
916 UINTN Index;
917 CHAR16 *StringPtr;
918 CHAR16 *TempStringPtr;
919 UINTN Index2;
920 UINTN TopOptionIndex;
921 UINTN HighlightOptionIndex;
922 UINTN Start;
923 UINTN End;
924 UINTN Top;
925 UINTN Bottom;
926 UINTN PopUpMenuLines;
927 UINTN MenuLinesInView;
928 UINTN PopUpWidth;
929 CHAR16 Character;
930 INT32 SavedAttribute;
931 BOOLEAN ShowDownArrow;
932 BOOLEAN ShowUpArrow;
933 UINTN DimensionsWidth;
934 LIST_ENTRY *Link;
935 BOOLEAN OrderedList;
936 UINT8 *ValueArray;
937 UINT8 ValueType;
938 EFI_HII_VALUE HiiValue;
939 EFI_HII_VALUE *HiiValueArray;
940 UINTN OptionCount;
941 QUESTION_OPTION *OneOfOption;
942 QUESTION_OPTION *CurrentOption;
943 FORM_BROWSER_STATEMENT *Question;
944 INTN Result;
945
946 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
947
948 ValueArray = NULL;
949 ValueType = 0;
950 CurrentOption = NULL;
951 ShowDownArrow = FALSE;
952 ShowUpArrow = FALSE;
953
954 StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);
955 ASSERT (StringPtr);
956
957 Question = MenuOption->ThisTag;
958 if (Question->Operand == EFI_IFR_ORDERED_LIST_OP) {
959 ValueArray = Question->BufferValue;
960 ValueType = Question->ValueType;
961 OrderedList = TRUE;
962 } else {
963 OrderedList = FALSE;
964 }
965
966 //
967 // Calculate Option count
968 //
969 if (OrderedList) {
970 for (Index = 0; Index < Question->MaxContainers; Index++) {
971 if (GetArrayData (ValueArray, ValueType, Index) == 0) {
972 break;
973 }
974 }
975
976 OptionCount = Index;
977 } else {
978 OptionCount = 0;
979 Link = GetFirstNode (&Question->OptionListHead);
980 while (!IsNull (&Question->OptionListHead, Link)) {
981 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
982
983 OptionCount++;
984
985 Link = GetNextNode (&Question->OptionListHead, Link);
986 }
987 }
988
989 //
990 // Prepare HiiValue array
991 //
992 HiiValueArray = AllocateZeroPool (OptionCount * sizeof (EFI_HII_VALUE));
993 ASSERT (HiiValueArray != NULL);
994 Link = GetFirstNode (&Question->OptionListHead);
995 for (Index = 0; Index < OptionCount; Index++) {
996 if (OrderedList) {
997 HiiValueArray[Index].Type = ValueType;
998 HiiValueArray[Index].Value.u64 = GetArrayData (ValueArray, ValueType, Index);
999 } else {
1000 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
1001 CopyMem (&HiiValueArray[Index], &OneOfOption->Value, sizeof (EFI_HII_VALUE));
1002 Link = GetNextNode (&Question->OptionListHead, Link);
1003 }
1004 }
1005
1006 //
1007 // Move Suppressed Option to list tail
1008 //
1009 PopUpMenuLines = 0;
1010 for (Index = 0; Index < OptionCount; Index++) {
1011 OneOfOption = ValueToOption (Question, &HiiValueArray[OptionCount - Index - 1]);
1012 if (OneOfOption == NULL) {
1013 return EFI_NOT_FOUND;
1014 }
1015
1016 RemoveEntryList (&OneOfOption->Link);
1017
1018 if ((OneOfOption->SuppressExpression != NULL) &&
1019 EvaluateExpressionList(OneOfOption->SuppressExpression, FALSE, NULL, NULL) != ExpressFalse) {
1020 //
1021 // This option is suppressed, insert to tail
1022 //
1023 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
1024 } else {
1025 //
1026 // Insert to head
1027 //
1028 InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);
1029
1030 PopUpMenuLines++;
1031 }
1032 }
1033
1034 //
1035 // Get the number of one of options present and its size
1036 //
1037 PopUpWidth = 0;
1038 HighlightOptionIndex = 0;
1039 Link = GetFirstNode (&Question->OptionListHead);
1040 for (Index = 0; Index < PopUpMenuLines; Index++) {
1041 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
1042
1043 StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);
1044 if (StrLen (StringPtr) > PopUpWidth) {
1045 PopUpWidth = StrLen (StringPtr);
1046 }
1047 FreePool (StringPtr);
1048
1049 if (!OrderedList && (CompareHiiValue (&Question->HiiValue, &OneOfOption->Value, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) {
1050 //
1051 // Find current selected Option for OneOf
1052 //
1053 HighlightOptionIndex = Index;
1054 }
1055
1056 Link = GetNextNode (&Question->OptionListHead, Link);
1057 }
1058
1059 //
1060 // Perform popup menu initialization.
1061 //
1062 PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;
1063
1064 SavedAttribute = gST->ConOut->Mode->Attribute;
1065 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
1066
1067 if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {
1068 PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;
1069 }
1070
1071 Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gScreenDimensions.LeftColumn;
1072 End = Start + PopUpWidth + POPUP_FRAME_WIDTH;
1073 Top = gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT;
1074 Bottom = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - gFooterHeight - 1;
1075
1076 MenuLinesInView = Bottom - Top - 1;
1077 if (MenuLinesInView >= PopUpMenuLines) {
1078 Top = Top + (MenuLinesInView - PopUpMenuLines) / 2;
1079 Bottom = Top + PopUpMenuLines + 1;
1080 } else {
1081 ShowDownArrow = TRUE;
1082 }
1083
1084 if (HighlightOptionIndex > (MenuLinesInView - 1)) {
1085 TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;
1086 } else {
1087 TopOptionIndex = 0;
1088 }
1089
1090 do {
1091 //
1092 // Clear that portion of the screen
1093 //
1094 ClearLines (Start, End, Top, Bottom, POPUP_TEXT | POPUP_BACKGROUND);
1095
1096 //
1097 // Draw "One of" pop-up menu
1098 //
1099 Character = BOXDRAW_DOWN_RIGHT;
1100 PrintCharAt (Start, Top, Character);
1101 for (Index = Start; Index + 2 < End; Index++) {
1102 if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {
1103 Character = GEOMETRICSHAPE_UP_TRIANGLE;
1104 } else {
1105 Character = BOXDRAW_HORIZONTAL;
1106 }
1107
1108 PrintChar (Character);
1109 }
1110
1111 Character = BOXDRAW_DOWN_LEFT;
1112 PrintChar (Character);
1113 Character = BOXDRAW_VERTICAL;
1114 for (Index = Top + 1; Index < Bottom; Index++) {
1115 PrintCharAt (Start, Index, Character);
1116 PrintCharAt (End - 1, Index, Character);
1117 }
1118
1119 //
1120 // Move to top Option
1121 //
1122 Link = GetFirstNode (&Question->OptionListHead);
1123 for (Index = 0; Index < TopOptionIndex; Index++) {
1124 Link = GetNextNode (&Question->OptionListHead, Link);
1125 }
1126
1127 //
1128 // Display the One of options
1129 //
1130 Index2 = Top + 1;
1131 for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {
1132 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
1133 Link = GetNextNode (&Question->OptionListHead, Link);
1134
1135 StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);
1136 ASSERT (StringPtr != NULL);
1137 //
1138 // If the string occupies multiple lines, truncate it to fit in one line,
1139 // and append a "..." for indication.
1140 //
1141 if (StrLen (StringPtr) > (PopUpWidth - 1)) {
1142 TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));
1143 ASSERT ( TempStringPtr != NULL );
1144 CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));
1145 FreePool (StringPtr);
1146 StringPtr = TempStringPtr;
1147 StrCat (StringPtr, L"...");
1148 }
1149
1150 if (Index == HighlightOptionIndex) {
1151 //
1152 // Highlight the selected one
1153 //
1154 CurrentOption = OneOfOption;
1155
1156 gST->ConOut->SetAttribute (gST->ConOut, PICKLIST_HIGHLIGHT_TEXT | PICKLIST_HIGHLIGHT_BACKGROUND);
1157 PrintStringAt (Start + 2, Index2, StringPtr);
1158 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
1159 } else {
1160 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
1161 PrintStringAt (Start + 2, Index2, StringPtr);
1162 }
1163
1164 Index2++;
1165 FreePool (StringPtr);
1166 }
1167
1168 Character = BOXDRAW_UP_RIGHT;
1169 PrintCharAt (Start, Bottom, Character);
1170 for (Index = Start; Index + 2 < End; Index++) {
1171 if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {
1172 Character = GEOMETRICSHAPE_DOWN_TRIANGLE;
1173 } else {
1174 Character = BOXDRAW_HORIZONTAL;
1175 }
1176
1177 PrintChar (Character);
1178 }
1179
1180 Character = BOXDRAW_UP_LEFT;
1181 PrintChar (Character);
1182
1183 //
1184 // Get User selection
1185 //
1186 Key.UnicodeChar = CHAR_NULL;
1187 if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {
1188 Key.ScanCode = gDirection;
1189 gDirection = 0;
1190 goto TheKey;
1191 }
1192
1193 Status = WaitForKeyStroke (&Key);
1194
1195 TheKey:
1196 switch (Key.UnicodeChar) {
1197 case '+':
1198 if (OrderedList) {
1199 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
1200 //
1201 // Highlight reaches the top of the popup window, scroll one menu item.
1202 //
1203 TopOptionIndex--;
1204 ShowDownArrow = TRUE;
1205 }
1206
1207 if (TopOptionIndex == 0) {
1208 ShowUpArrow = FALSE;
1209 }
1210
1211 if (HighlightOptionIndex > 0) {
1212 HighlightOptionIndex--;
1213
1214 ASSERT (CurrentOption != NULL);
1215 SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);
1216 }
1217 }
1218 break;
1219
1220 case '-':
1221 //
1222 // If an ordered list op-code, we will allow for a popup of +/- keys
1223 // to create an ordered list of items
1224 //
1225 if (OrderedList) {
1226 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
1227 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
1228 //
1229 // Highlight reaches the bottom of the popup window, scroll one menu item.
1230 //
1231 TopOptionIndex++;
1232 ShowUpArrow = TRUE;
1233 }
1234
1235 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
1236 ShowDownArrow = FALSE;
1237 }
1238
1239 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
1240 HighlightOptionIndex++;
1241
1242 ASSERT (CurrentOption != NULL);
1243 SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);
1244 }
1245 }
1246 break;
1247
1248 case CHAR_NULL:
1249 switch (Key.ScanCode) {
1250 case SCAN_UP:
1251 case SCAN_DOWN:
1252 if (Key.ScanCode == SCAN_UP) {
1253 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
1254 //
1255 // Highlight reaches the top of the popup window, scroll one menu item.
1256 //
1257 TopOptionIndex--;
1258 ShowDownArrow = TRUE;
1259 }
1260
1261 if (TopOptionIndex == 0) {
1262 ShowUpArrow = FALSE;
1263 }
1264
1265 if (HighlightOptionIndex > 0) {
1266 HighlightOptionIndex--;
1267 }
1268 } else {
1269 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
1270 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
1271 //
1272 // Highlight reaches the bottom of the popup window, scroll one menu item.
1273 //
1274 TopOptionIndex++;
1275 ShowUpArrow = TRUE;
1276 }
1277
1278 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
1279 ShowDownArrow = FALSE;
1280 }
1281
1282 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
1283 HighlightOptionIndex++;
1284 }
1285 }
1286 break;
1287
1288 case SCAN_ESC:
1289 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1290
1291 //
1292 // Restore link list order for orderedlist
1293 //
1294 if (OrderedList) {
1295 HiiValue.Type = ValueType;
1296 HiiValue.Value.u64 = 0;
1297 for (Index = 0; Index < Question->MaxContainers; Index++) {
1298 HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
1299 if (HiiValue.Value.u64 == 0) {
1300 break;
1301 }
1302
1303 OneOfOption = ValueToOption (Question, &HiiValue);
1304 if (OneOfOption == NULL) {
1305 return EFI_NOT_FOUND;
1306 }
1307
1308 RemoveEntryList (&OneOfOption->Link);
1309 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
1310 }
1311 }
1312
1313 FreePool (HiiValueArray);
1314 return EFI_DEVICE_ERROR;
1315
1316 default:
1317 break;
1318 }
1319
1320 break;
1321
1322 case CHAR_CARRIAGE_RETURN:
1323 //
1324 // return the current selection
1325 //
1326 if (OrderedList) {
1327 Index = 0;
1328 Link = GetFirstNode (&Question->OptionListHead);
1329 while (!IsNull (&Question->OptionListHead, Link)) {
1330 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
1331
1332 SetArrayData (ValueArray, ValueType, Index, OneOfOption->Value.Value.u64);
1333
1334 Index++;
1335 if (Index > Question->MaxContainers) {
1336 break;
1337 }
1338
1339 Link = GetNextNode (&Question->OptionListHead, Link);
1340 }
1341 } else {
1342 ASSERT (CurrentOption != NULL);
1343 CopyMem (&Question->HiiValue, &CurrentOption->Value, sizeof (EFI_HII_VALUE));
1344 }
1345
1346 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1347 FreePool (HiiValueArray);
1348
1349 Status = ValidateQuestion (Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);
1350 if (EFI_ERROR (Status)) {
1351 //
1352 // Input value is not valid, restore Question Value
1353 //
1354 GetQuestionValue (Selection->FormSet, Selection->Form, Question, GetSetValueWithEditBuffer);
1355 } else {
1356 SetQuestionValue (Selection->FormSet, Selection->Form, Question, GetSetValueWithEditBuffer);
1357 UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
1358 }
1359
1360 return Status;
1361
1362 default:
1363 break;
1364 }
1365 } while (TRUE);
1366
1367 }
1368
1369 /**
1370 Wait for a key to be pressed by user.
1371
1372 @param Key The key which is pressed by user.
1373
1374 @retval EFI_SUCCESS The function always completed successfully.
1375
1376 **/
1377 EFI_STATUS
1378 WaitForKeyStroke (
1379 OUT EFI_INPUT_KEY *Key
1380 )
1381 {
1382 EFI_STATUS Status;
1383
1384 do {
1385 UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0, 0);
1386 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, Key);
1387 } while (EFI_ERROR(Status));
1388
1389 return Status;
1390 }