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