VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/SetupBrowserDxe/InputHandler.c@ 54985

Last change on this file since 54985 was 48674, checked in by vboxsync, 11 years ago

EFI: Export newly imported tinaocore UEFI sources to OSE.

  • Property svn:eol-style set to native
File size: 39.2 KB
Line 
1/** @file
2Implementation for handling user input from the User Interfaces.
3
4Copyright (c) 2004 - 2012, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials
6are licensed and made available under the terms and conditions of the BSD License
7which accompanies this distribution. The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT 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**/
29EFI_STATUS
30ReadString (
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**/
285VOID
286AdjustQuestionValue (
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**/
352EFI_STATUS
353GetNumericInput (
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
610TheKey2:
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
726EnterCarriageReturn:
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, TRUE);
803 } else {
804 SetQuestionValue (FormSet, Form, Question, TRUE);
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**/
908EFI_STATUS
909GetSelectionInputPopUp (
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
1195TheKey:
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, TRUE);
1355 } else {
1356 SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
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**/
1377EFI_STATUS
1378WaitForKeyStroke (
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}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette