]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/SetupBrowserDxe/InputHandler.c
Add pointer check for NULL before dereference it.
[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 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 // Store Edit value back to Question
731 //
732 if (Question->Operand == EFI_IFR_DATE_OP) {
733 switch (MenuOption->Sequence) {
734 case 0:
735 QuestionValue->Value.date.Month = (UINT8) EditValue;
736 break;
737
738 case 1:
739 QuestionValue->Value.date.Day = (UINT8) EditValue;
740 break;
741
742 case 2:
743 QuestionValue->Value.date.Year = (UINT16) EditValue;
744 break;
745
746 default:
747 break;
748 }
749 } else if (Question->Operand == EFI_IFR_TIME_OP) {
750 switch (MenuOption->Sequence) {
751 case 0:
752 QuestionValue->Value.time.Hour = (UINT8) EditValue;
753 break;
754
755 case 1:
756 QuestionValue->Value.time.Minute = (UINT8) EditValue;
757 break;
758
759 case 2:
760 QuestionValue->Value.time.Second = (UINT8) EditValue;
761 break;
762
763 default:
764 break;
765 }
766 } else {
767 //
768 // Numeric
769 //
770 QuestionValue->Value.u64 = EditValue;
771 }
772
773 //
774 // Adjust the value to the correct one.
775 // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01
776 // 2013.03.29 -> 2013.02.29 -> 2013.02.28
777 //
778 if (Question->Operand == EFI_IFR_DATE_OP &&
779 (MenuOption->Sequence == 0 || MenuOption->Sequence == 2)) {
780 AdjustQuestionValue (Question, (UINT8)MenuOption->Sequence);
781 }
782
783 //
784 // Check to see if the Value is something reasonable against consistency limitations.
785 // If not, let's kick the error specified.
786 //
787 Status = ValidateQuestion (FormSet, Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);
788 if (EFI_ERROR (Status)) {
789 //
790 // Input value is not valid, restore Question Value
791 //
792 GetQuestionValue (FormSet, Form, Question, TRUE);
793 } else {
794 SetQuestionValue (FormSet, Form, Question, TRUE);
795 if (!DateOrTime || (Question->Storage != NULL)) {
796 //
797 // NV flag is unnecessary for RTC type of Date/Time
798 //
799 UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
800 }
801 }
802
803 return Status;
804 break;
805
806 case CHAR_BACKSPACE:
807 if (ManualInput) {
808 if (Count == 0) {
809 break;
810 }
811 //
812 // Remove a character
813 //
814 EditValue = PreviousNumber[Count - 1];
815 UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, FALSE);
816 Count--;
817 Column--;
818 PrintAt (Column, Row, L" ");
819 }
820 break;
821
822 default:
823 if (ManualInput) {
824 if (HexInput) {
825 if ((Key.UnicodeChar >= L'0') && (Key.UnicodeChar <= L'9')) {
826 Digital = (UINT8) (Key.UnicodeChar - L'0');
827 } else if ((Key.UnicodeChar >= L'A') && (Key.UnicodeChar <= L'F')) {
828 Digital = (UINT8) (Key.UnicodeChar - L'A' + 0x0A);
829 } else if ((Key.UnicodeChar >= L'a') && (Key.UnicodeChar <= L'f')) {
830 Digital = (UINT8) (Key.UnicodeChar - L'a' + 0x0A);
831 } else {
832 UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE);
833 break;
834 }
835 } else {
836 if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {
837 UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE);
838 break;
839 }
840 }
841
842 //
843 // If Count exceed input width, there is no way more is valid
844 //
845 if (Count >= InputWidth) {
846 break;
847 }
848 //
849 // Someone typed something valid!
850 //
851 if (Count != 0) {
852 if (HexInput) {
853 EditValue = LShiftU64 (EditValue, 4) + Digital;
854 } else {
855 EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0');
856 }
857 } else {
858 if (HexInput) {
859 EditValue = Digital;
860 } else {
861 EditValue = Key.UnicodeChar - L'0';
862 }
863 }
864
865 if (EditValue > Maximum) {
866 UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE);
867 ASSERT (Count < sizeof (PreviousNumber) / sizeof (PreviousNumber[0]));
868 EditValue = PreviousNumber[Count];
869 break;
870 } else {
871 UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, FALSE);
872 }
873
874 Count++;
875 ASSERT (Count < (sizeof (PreviousNumber) / sizeof (PreviousNumber[0])));
876 PreviousNumber[Count] = EditValue;
877
878 PrintCharAt (Column, Row, Key.UnicodeChar);
879 Column++;
880 }
881 break;
882 }
883 } while (TRUE);
884
885 }
886
887
888 /**
889 Get selection for OneOf and OrderedList (Left/Right will be ignored).
890
891 @param Selection Pointer to current selection.
892 @param MenuOption Pointer to the current input menu.
893
894 @retval EFI_SUCCESS If Option input is processed successfully
895 @retval EFI_DEVICE_ERROR If operation fails
896
897 **/
898 EFI_STATUS
899 GetSelectionInputPopUp (
900 IN UI_MENU_SELECTION *Selection,
901 IN UI_MENU_OPTION *MenuOption
902 )
903 {
904 EFI_STATUS Status;
905 EFI_INPUT_KEY Key;
906 UINTN Index;
907 CHAR16 *StringPtr;
908 CHAR16 *TempStringPtr;
909 UINTN Index2;
910 UINTN TopOptionIndex;
911 UINTN HighlightOptionIndex;
912 UINTN Start;
913 UINTN End;
914 UINTN Top;
915 UINTN Bottom;
916 UINTN PopUpMenuLines;
917 UINTN MenuLinesInView;
918 UINTN PopUpWidth;
919 CHAR16 Character;
920 INT32 SavedAttribute;
921 BOOLEAN ShowDownArrow;
922 BOOLEAN ShowUpArrow;
923 UINTN DimensionsWidth;
924 LIST_ENTRY *Link;
925 BOOLEAN OrderedList;
926 UINT8 *ValueArray;
927 UINT8 ValueType;
928 EFI_HII_VALUE HiiValue;
929 EFI_HII_VALUE *HiiValueArray;
930 UINTN OptionCount;
931 QUESTION_OPTION *OneOfOption;
932 QUESTION_OPTION *CurrentOption;
933 FORM_BROWSER_STATEMENT *Question;
934
935 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
936
937 ValueArray = NULL;
938 ValueType = 0;
939 CurrentOption = NULL;
940 ShowDownArrow = FALSE;
941 ShowUpArrow = FALSE;
942
943 StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);
944 ASSERT (StringPtr);
945
946 Question = MenuOption->ThisTag;
947 if (Question->Operand == EFI_IFR_ORDERED_LIST_OP) {
948 ValueArray = Question->BufferValue;
949 ValueType = Question->ValueType;
950 OrderedList = TRUE;
951 } else {
952 OrderedList = FALSE;
953 }
954
955 //
956 // Calculate Option count
957 //
958 if (OrderedList) {
959 for (Index = 0; Index < Question->MaxContainers; Index++) {
960 if (GetArrayData (ValueArray, ValueType, Index) == 0) {
961 break;
962 }
963 }
964
965 OptionCount = Index;
966 } else {
967 OptionCount = 0;
968 Link = GetFirstNode (&Question->OptionListHead);
969 while (!IsNull (&Question->OptionListHead, Link)) {
970 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
971
972 OptionCount++;
973
974 Link = GetNextNode (&Question->OptionListHead, Link);
975 }
976 }
977
978 //
979 // Prepare HiiValue array
980 //
981 HiiValueArray = AllocateZeroPool (OptionCount * sizeof (EFI_HII_VALUE));
982 ASSERT (HiiValueArray != NULL);
983 Link = GetFirstNode (&Question->OptionListHead);
984 for (Index = 0; Index < OptionCount; Index++) {
985 if (OrderedList) {
986 HiiValueArray[Index].Type = ValueType;
987 HiiValueArray[Index].Value.u64 = GetArrayData (ValueArray, ValueType, Index);
988 } else {
989 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
990 CopyMem (&HiiValueArray[Index], &OneOfOption->Value, sizeof (EFI_HII_VALUE));
991 Link = GetNextNode (&Question->OptionListHead, Link);
992 }
993 }
994
995 //
996 // Move Suppressed Option to list tail
997 //
998 PopUpMenuLines = 0;
999 for (Index = 0; Index < OptionCount; Index++) {
1000 OneOfOption = ValueToOption (Question, &HiiValueArray[OptionCount - Index - 1]);
1001 if (OneOfOption == NULL) {
1002 return EFI_NOT_FOUND;
1003 }
1004
1005 RemoveEntryList (&OneOfOption->Link);
1006
1007 if ((OneOfOption->SuppressExpression != NULL) &&
1008 (OneOfOption->SuppressExpression->Result.Value.b)) {
1009 //
1010 // This option is suppressed, insert to tail
1011 //
1012 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
1013 } else {
1014 //
1015 // Insert to head
1016 //
1017 InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);
1018
1019 PopUpMenuLines++;
1020 }
1021 }
1022
1023 //
1024 // Get the number of one of options present and its size
1025 //
1026 PopUpWidth = 0;
1027 HighlightOptionIndex = 0;
1028 Link = GetFirstNode (&Question->OptionListHead);
1029 for (Index = 0; Index < PopUpMenuLines; Index++) {
1030 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
1031
1032 StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);
1033 if (StrLen (StringPtr) > PopUpWidth) {
1034 PopUpWidth = StrLen (StringPtr);
1035 }
1036 FreePool (StringPtr);
1037
1038 if (!OrderedList && CompareHiiValue (&Question->HiiValue, &OneOfOption->Value, NULL) == 0) {
1039 //
1040 // Find current selected Option for OneOf
1041 //
1042 HighlightOptionIndex = Index;
1043 }
1044
1045 Link = GetNextNode (&Question->OptionListHead, Link);
1046 }
1047
1048 //
1049 // Perform popup menu initialization.
1050 //
1051 PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;
1052
1053 SavedAttribute = gST->ConOut->Mode->Attribute;
1054 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
1055
1056 if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {
1057 PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;
1058 }
1059
1060 Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gScreenDimensions.LeftColumn;
1061 End = Start + PopUpWidth + POPUP_FRAME_WIDTH;
1062 Top = gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT;
1063 Bottom = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - gFooterHeight - 1;
1064
1065 MenuLinesInView = Bottom - Top - 1;
1066 if (MenuLinesInView >= PopUpMenuLines) {
1067 Top = Top + (MenuLinesInView - PopUpMenuLines) / 2;
1068 Bottom = Top + PopUpMenuLines + 1;
1069 } else {
1070 ShowDownArrow = TRUE;
1071 }
1072
1073 if (HighlightOptionIndex > (MenuLinesInView - 1)) {
1074 TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;
1075 } else {
1076 TopOptionIndex = 0;
1077 }
1078
1079 do {
1080 //
1081 // Clear that portion of the screen
1082 //
1083 ClearLines (Start, End, Top, Bottom, POPUP_TEXT | POPUP_BACKGROUND);
1084
1085 //
1086 // Draw "One of" pop-up menu
1087 //
1088 Character = BOXDRAW_DOWN_RIGHT;
1089 PrintCharAt (Start, Top, Character);
1090 for (Index = Start; Index + 2 < End; Index++) {
1091 if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {
1092 Character = GEOMETRICSHAPE_UP_TRIANGLE;
1093 } else {
1094 Character = BOXDRAW_HORIZONTAL;
1095 }
1096
1097 PrintChar (Character);
1098 }
1099
1100 Character = BOXDRAW_DOWN_LEFT;
1101 PrintChar (Character);
1102 Character = BOXDRAW_VERTICAL;
1103 for (Index = Top + 1; Index < Bottom; Index++) {
1104 PrintCharAt (Start, Index, Character);
1105 PrintCharAt (End - 1, Index, Character);
1106 }
1107
1108 //
1109 // Move to top Option
1110 //
1111 Link = GetFirstNode (&Question->OptionListHead);
1112 for (Index = 0; Index < TopOptionIndex; Index++) {
1113 Link = GetNextNode (&Question->OptionListHead, Link);
1114 }
1115
1116 //
1117 // Display the One of options
1118 //
1119 Index2 = Top + 1;
1120 for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {
1121 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
1122 Link = GetNextNode (&Question->OptionListHead, Link);
1123
1124 StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);
1125 ASSERT (StringPtr != NULL);
1126 //
1127 // If the string occupies multiple lines, truncate it to fit in one line,
1128 // and append a "..." for indication.
1129 //
1130 if (StrLen (StringPtr) > (PopUpWidth - 1)) {
1131 TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));
1132 ASSERT ( TempStringPtr != NULL );
1133 CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));
1134 FreePool (StringPtr);
1135 StringPtr = TempStringPtr;
1136 StrCat (StringPtr, L"...");
1137 }
1138
1139 if (Index == HighlightOptionIndex) {
1140 //
1141 // Highlight the selected one
1142 //
1143 CurrentOption = OneOfOption;
1144
1145 gST->ConOut->SetAttribute (gST->ConOut, PICKLIST_HIGHLIGHT_TEXT | PICKLIST_HIGHLIGHT_BACKGROUND);
1146 PrintStringAt (Start + 2, Index2, StringPtr);
1147 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
1148 } else {
1149 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
1150 PrintStringAt (Start + 2, Index2, StringPtr);
1151 }
1152
1153 Index2++;
1154 FreePool (StringPtr);
1155 }
1156
1157 Character = BOXDRAW_UP_RIGHT;
1158 PrintCharAt (Start, Bottom, Character);
1159 for (Index = Start; Index + 2 < End; Index++) {
1160 if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {
1161 Character = GEOMETRICSHAPE_DOWN_TRIANGLE;
1162 } else {
1163 Character = BOXDRAW_HORIZONTAL;
1164 }
1165
1166 PrintChar (Character);
1167 }
1168
1169 Character = BOXDRAW_UP_LEFT;
1170 PrintChar (Character);
1171
1172 //
1173 // Get User selection
1174 //
1175 Key.UnicodeChar = CHAR_NULL;
1176 if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {
1177 Key.ScanCode = gDirection;
1178 gDirection = 0;
1179 goto TheKey;
1180 }
1181
1182 Status = WaitForKeyStroke (&Key);
1183
1184 TheKey:
1185 switch (Key.UnicodeChar) {
1186 case '+':
1187 if (OrderedList) {
1188 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
1189 //
1190 // Highlight reaches the top of the popup window, scroll one menu item.
1191 //
1192 TopOptionIndex--;
1193 ShowDownArrow = TRUE;
1194 }
1195
1196 if (TopOptionIndex == 0) {
1197 ShowUpArrow = FALSE;
1198 }
1199
1200 if (HighlightOptionIndex > 0) {
1201 HighlightOptionIndex--;
1202
1203 ASSERT (CurrentOption != NULL);
1204 SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);
1205 }
1206 }
1207 break;
1208
1209 case '-':
1210 //
1211 // If an ordered list op-code, we will allow for a popup of +/- keys
1212 // to create an ordered list of items
1213 //
1214 if (OrderedList) {
1215 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
1216 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
1217 //
1218 // Highlight reaches the bottom of the popup window, scroll one menu item.
1219 //
1220 TopOptionIndex++;
1221 ShowUpArrow = TRUE;
1222 }
1223
1224 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
1225 ShowDownArrow = FALSE;
1226 }
1227
1228 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
1229 HighlightOptionIndex++;
1230
1231 ASSERT (CurrentOption != NULL);
1232 SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);
1233 }
1234 }
1235 break;
1236
1237 case CHAR_NULL:
1238 switch (Key.ScanCode) {
1239 case SCAN_UP:
1240 case SCAN_DOWN:
1241 if (Key.ScanCode == SCAN_UP) {
1242 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
1243 //
1244 // Highlight reaches the top of the popup window, scroll one menu item.
1245 //
1246 TopOptionIndex--;
1247 ShowDownArrow = TRUE;
1248 }
1249
1250 if (TopOptionIndex == 0) {
1251 ShowUpArrow = FALSE;
1252 }
1253
1254 if (HighlightOptionIndex > 0) {
1255 HighlightOptionIndex--;
1256 }
1257 } else {
1258 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
1259 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
1260 //
1261 // Highlight reaches the bottom of the popup window, scroll one menu item.
1262 //
1263 TopOptionIndex++;
1264 ShowUpArrow = TRUE;
1265 }
1266
1267 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
1268 ShowDownArrow = FALSE;
1269 }
1270
1271 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
1272 HighlightOptionIndex++;
1273 }
1274 }
1275 break;
1276
1277 case SCAN_ESC:
1278 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1279
1280 //
1281 // Restore link list order for orderedlist
1282 //
1283 if (OrderedList) {
1284 HiiValue.Type = ValueType;
1285 HiiValue.Value.u64 = 0;
1286 for (Index = 0; Index < Question->MaxContainers; Index++) {
1287 HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
1288 if (HiiValue.Value.u64 == 0) {
1289 break;
1290 }
1291
1292 OneOfOption = ValueToOption (Question, &HiiValue);
1293 if (OneOfOption == NULL) {
1294 return EFI_NOT_FOUND;
1295 }
1296
1297 RemoveEntryList (&OneOfOption->Link);
1298 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
1299 }
1300 }
1301
1302 FreePool (HiiValueArray);
1303 return EFI_DEVICE_ERROR;
1304
1305 default:
1306 break;
1307 }
1308
1309 break;
1310
1311 case CHAR_CARRIAGE_RETURN:
1312 //
1313 // return the current selection
1314 //
1315 if (OrderedList) {
1316 Index = 0;
1317 Link = GetFirstNode (&Question->OptionListHead);
1318 while (!IsNull (&Question->OptionListHead, Link)) {
1319 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
1320
1321 SetArrayData (ValueArray, ValueType, Index, OneOfOption->Value.Value.u64);
1322
1323 Index++;
1324 if (Index > Question->MaxContainers) {
1325 break;
1326 }
1327
1328 Link = GetNextNode (&Question->OptionListHead, Link);
1329 }
1330 } else {
1331 ASSERT (CurrentOption != NULL);
1332 CopyMem (&Question->HiiValue, &CurrentOption->Value, sizeof (EFI_HII_VALUE));
1333 }
1334
1335 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1336 FreePool (HiiValueArray);
1337
1338 Status = ValidateQuestion (Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);
1339 if (EFI_ERROR (Status)) {
1340 //
1341 // Input value is not valid, restore Question Value
1342 //
1343 GetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
1344 } else {
1345 SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
1346 UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
1347 }
1348
1349 return Status;
1350
1351 default:
1352 break;
1353 }
1354 } while (TRUE);
1355
1356 }
1357
1358 /**
1359 Wait for a key to be pressed by user.
1360
1361 @param Key The key which is pressed by user.
1362
1363 @retval EFI_SUCCESS The function always completed successfully.
1364
1365 **/
1366 EFI_STATUS
1367 WaitForKeyStroke (
1368 OUT EFI_INPUT_KEY *Key
1369 )
1370 {
1371 EFI_STATUS Status;
1372
1373 do {
1374 UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0, 0);
1375 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, Key);
1376 } while (EFI_ERROR(Status));
1377
1378 return Status;
1379 }