]> code.delx.au - refind/blob - refind/screen.c
Misc. changes, mostly to fix minor or rare bugs.
[refind] / refind / screen.c
1 /*
2 * refind/screen.c
3 * Screen handling functions
4 *
5 * Copyright (c) 2006 Christoph Pfisterer
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
18 * distribution.
19 *
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36 /*
37 * Modifications copyright (c) 2012-2014 Roderick W. Smith
38 *
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), a copy of which must be distributed
41 * with this source code or binaries made from it.
42 *
43 */
44
45 #include "global.h"
46 #include "screen.h"
47 #include "config.h"
48 #include "libegint.h"
49 #include "lib.h"
50 #include "menu.h"
51 #include "../include/refit_call_wrapper.h"
52
53 #include "../include/egemb_refind_banner.h"
54
55 // Console defines and variables
56
57 UINTN ConWidth;
58 UINTN ConHeight;
59 CHAR16 *BlankLine = NULL;
60
61 static VOID DrawScreenHeader(IN CHAR16 *Title);
62
63 // UGA defines and variables
64
65 UINTN UGAWidth;
66 UINTN UGAHeight;
67 BOOLEAN AllowGraphicsMode;
68
69 EG_PIXEL StdBackgroundPixel = { 0xbf, 0xbf, 0xbf, 0 };
70 EG_PIXEL MenuBackgroundPixel = { 0xbf, 0xbf, 0xbf, 0 };
71 EG_PIXEL DarkBackgroundPixel = { 0x0, 0x0, 0x0, 0 };
72
73 static BOOLEAN GraphicsScreenDirty;
74
75 // general defines and variables
76
77 static BOOLEAN haveError = FALSE;
78
79 static VOID PrepareBlankLine(VOID) {
80 UINTN i;
81
82 MyFreePool(BlankLine);
83 // make a buffer for a whole text line
84 BlankLine = AllocatePool((ConWidth + 1) * sizeof(CHAR16));
85 for (i = 0; i < ConWidth; i++)
86 BlankLine[i] = ' ';
87 BlankLine[i] = 0;
88 }
89
90 //
91 // Screen initialization and switching
92 //
93
94 VOID InitScreen(VOID)
95 {
96 // initialize libeg
97 egInitScreen();
98
99 if (egHasGraphicsMode()) {
100 egGetScreenSize(&UGAWidth, &UGAHeight);
101 AllowGraphicsMode = TRUE;
102 } else {
103 AllowGraphicsMode = FALSE;
104 egSetTextMode(GlobalConfig.RequestedTextMode);
105 egSetGraphicsModeEnabled(FALSE); // just to be sure we are in text mode
106 }
107 GraphicsScreenDirty = TRUE;
108
109 // disable cursor
110 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, FALSE);
111
112 // get size of text console
113 if (refit_call4_wrapper(ST->ConOut->QueryMode, ST->ConOut, ST->ConOut->Mode->Mode, &ConWidth, &ConHeight) != EFI_SUCCESS) {
114 // use default values on error
115 ConWidth = 80;
116 ConHeight = 25;
117 }
118
119 PrepareBlankLine();
120
121 // show the banner if in text mode
122 if (GlobalConfig.TextOnly && (GlobalConfig.ScreensaverTime != -1))
123 DrawScreenHeader(L"Initializing...");
124 }
125
126 // Set the screen resolution and mode (text vs. graphics).
127 VOID SetupScreen(VOID)
128 {
129 UINTN NewWidth, NewHeight;
130
131 // Convert mode number to horizontal & vertical resolution values
132 if ((GlobalConfig.RequestedScreenWidth > 0) && (GlobalConfig.RequestedScreenHeight == 0))
133 egGetResFromMode(&(GlobalConfig.RequestedScreenWidth), &(GlobalConfig.RequestedScreenHeight));
134
135 // Set the believed-to-be current resolution to the LOWER of the current
136 // believed-to-be resolution and the requested resolution. This is done to
137 // enable setting a lower-than-default resolution.
138 if ((GlobalConfig.RequestedScreenWidth > 0) && (GlobalConfig.RequestedScreenHeight > 0)) {
139 UGAWidth = (UGAWidth < GlobalConfig.RequestedScreenWidth) ? UGAWidth : GlobalConfig.RequestedScreenWidth;
140 UGAHeight = (UGAHeight < GlobalConfig.RequestedScreenHeight) ? UGAHeight : GlobalConfig.RequestedScreenHeight;
141 }
142
143 // Set text mode. If this requires increasing the size of the graphics mode, do so.
144 if (egSetTextMode(GlobalConfig.RequestedTextMode)) {
145 egGetScreenSize(&NewWidth, &NewHeight);
146 if ((NewWidth > UGAWidth) || (NewHeight > UGAHeight)) {
147 UGAWidth = NewWidth;
148 UGAHeight = NewHeight;
149 }
150 if ((UGAWidth > GlobalConfig.RequestedScreenWidth) || (UGAHeight > GlobalConfig.RequestedScreenHeight)) {
151 // Requested text mode forces us to use a bigger graphics mode
152 GlobalConfig.RequestedScreenWidth = UGAWidth;
153 GlobalConfig.RequestedScreenHeight = UGAHeight;
154 } // if
155 }
156
157 if (GlobalConfig.RequestedScreenWidth > 0) {
158 egSetScreenSize(&(GlobalConfig.RequestedScreenWidth), &(GlobalConfig.RequestedScreenHeight));
159 egGetScreenSize(&UGAWidth, &UGAHeight);
160 } // if user requested a particular screen resolution
161
162 if (GlobalConfig.TextOnly) {
163 // switch to text mode if requested
164 AllowGraphicsMode = FALSE;
165 SwitchToText(FALSE);
166
167 } else if (AllowGraphicsMode) {
168 // clear screen and show banner
169 // (now we know we'll stay in graphics mode)
170 SwitchToGraphics();
171 if (GlobalConfig.ScreensaverTime != -1) {
172 BltClearScreen(TRUE);
173 } else { // start with screen blanked
174 GraphicsScreenDirty = TRUE;
175 }
176 }
177 } // VOID SetupScreen()
178
179 VOID SwitchToText(IN BOOLEAN CursorEnabled)
180 {
181 egSetGraphicsModeEnabled(FALSE);
182 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, CursorEnabled);
183 // get size of text console
184 if (refit_call4_wrapper(ST->ConOut->QueryMode, ST->ConOut, ST->ConOut->Mode->Mode, &ConWidth, &ConHeight) != EFI_SUCCESS) {
185 // use default values on error
186 ConWidth = 80;
187 ConHeight = 25;
188 }
189 PrepareBlankLine();
190 }
191
192 VOID SwitchToGraphics(VOID)
193 {
194 if (AllowGraphicsMode && !egIsGraphicsModeEnabled()) {
195 egSetGraphicsModeEnabled(TRUE);
196 GraphicsScreenDirty = TRUE;
197 }
198 }
199
200 //
201 // Screen control for running tools
202 //
203
204 VOID BeginTextScreen(IN CHAR16 *Title)
205 {
206 DrawScreenHeader(Title);
207 SwitchToText(FALSE);
208
209 // reset error flag
210 haveError = FALSE;
211 }
212
213 VOID FinishTextScreen(IN BOOLEAN WaitAlways)
214 {
215 if (haveError || WaitAlways) {
216 PauseForKey();
217 SwitchToText(FALSE);
218 }
219
220 // reset error flag
221 haveError = FALSE;
222 }
223
224 VOID BeginExternalScreen(IN BOOLEAN UseGraphicsMode, IN CHAR16 *Title)
225 {
226 if (!AllowGraphicsMode)
227 UseGraphicsMode = FALSE;
228
229 if (UseGraphicsMode) {
230 SwitchToGraphics();
231 BltClearScreen(FALSE);
232 } else {
233 egClearScreen(&DarkBackgroundPixel);
234 DrawScreenHeader(Title);
235 SwitchToText(TRUE);
236 }
237
238 // reset error flag
239 haveError = FALSE;
240 }
241
242 VOID FinishExternalScreen(VOID)
243 {
244 // make sure we clean up later
245 GraphicsScreenDirty = TRUE;
246
247 if (haveError) {
248 SwitchToText(FALSE);
249 PauseForKey();
250 }
251
252 // Reset the screen resolution, in case external program changed it....
253 SetupScreen();
254
255 // reset error flag
256 haveError = FALSE;
257 }
258
259 VOID TerminateScreen(VOID)
260 {
261 // clear text screen
262 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
263 refit_call1_wrapper(ST->ConOut->ClearScreen, ST->ConOut);
264
265 // enable cursor
266 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, TRUE);
267 }
268
269 static VOID DrawScreenHeader(IN CHAR16 *Title)
270 {
271 UINTN y;
272
273 // clear to black background
274 egClearScreen(&DarkBackgroundPixel); // first clear in graphics mode
275 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
276 refit_call1_wrapper(ST->ConOut->ClearScreen, ST->ConOut); // then clear in text mode
277
278 // paint header background
279 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BANNER);
280 for (y = 0; y < 3; y++) {
281 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, y);
282 Print(BlankLine);
283 }
284
285 // print header text
286 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 3, 1);
287 Print(L"rEFInd - %s", Title);
288
289 // reposition cursor
290 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
291 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, 4);
292 }
293
294 //
295 // Keyboard input
296 //
297
298 BOOLEAN ReadAllKeyStrokes(VOID)
299 {
300 BOOLEAN GotKeyStrokes;
301 EFI_STATUS Status;
302 EFI_INPUT_KEY key;
303
304 GotKeyStrokes = FALSE;
305 for (;;) {
306 Status = refit_call2_wrapper(ST->ConIn->ReadKeyStroke, ST->ConIn, &key);
307 if (Status == EFI_SUCCESS) {
308 GotKeyStrokes = TRUE;
309 continue;
310 }
311 break;
312 }
313 return GotKeyStrokes;
314 }
315
316 VOID PauseForKey(VOID)
317 {
318 UINTN index;
319
320 Print(L"\n* Hit any key to continue *");
321
322 if (ReadAllKeyStrokes()) { // remove buffered key strokes
323 refit_call1_wrapper(BS->Stall, 5000000); // 5 seconds delay
324 ReadAllKeyStrokes(); // empty the buffer again
325 }
326
327 refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &index);
328 ReadAllKeyStrokes(); // empty the buffer to protect the menu
329
330 Print(L"\n");
331 }
332
333 #if REFIT_DEBUG > 0
334 VOID DebugPause(VOID)
335 {
336 // show console and wait for key
337 SwitchToText(FALSE);
338 PauseForKey();
339
340 // reset error flag
341 haveError = FALSE;
342 }
343 #endif
344
345 VOID EndlessIdleLoop(VOID)
346 {
347 UINTN index;
348
349 for (;;) {
350 ReadAllKeyStrokes();
351 refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &index);
352 }
353 }
354
355 //
356 // Error handling
357 //
358
359 #ifdef __MAKEWITH_GNUEFI
360 BOOLEAN CheckFatalError(IN EFI_STATUS Status, IN CHAR16 *where)
361 {
362 CHAR16 ErrorName[64];
363
364 if (!EFI_ERROR(Status))
365 return FALSE;
366
367 StatusToString(ErrorName, Status);
368 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_ERROR);
369 Print(L"Fatal Error: %s %s\n", ErrorName, where);
370 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
371 haveError = TRUE;
372
373 //BS->Exit(ImageHandle, ExitStatus, ExitDataSize, ExitData);
374
375 return TRUE;
376 }
377
378 BOOLEAN CheckError(IN EFI_STATUS Status, IN CHAR16 *where)
379 {
380 CHAR16 ErrorName[64];
381
382 if (!EFI_ERROR(Status))
383 return FALSE;
384
385 StatusToString(ErrorName, Status);
386 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_ERROR);
387 Print(L"Error: %s %s\n", ErrorName, where);
388 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
389 haveError = TRUE;
390
391 return TRUE;
392 }
393 #else
394 BOOLEAN CheckFatalError(IN EFI_STATUS Status, IN CHAR16 *where)
395 {
396 // CHAR16 ErrorName[64];
397
398 if (!EFI_ERROR(Status))
399 return FALSE;
400
401 gST->ConOut->SetAttribute (gST->ConOut, ATTR_ERROR);
402 Print(L"Fatal Error: %r %s\n", Status, where);
403 gST->ConOut->SetAttribute (gST->ConOut, ATTR_BASIC);
404 haveError = TRUE;
405
406 //gBS->Exit(ImageHandle, ExitStatus, ExitDataSize, ExitData);
407
408 return TRUE;
409 }
410
411 BOOLEAN CheckError(IN EFI_STATUS Status, IN CHAR16 *where)
412 {
413 if (!EFI_ERROR(Status))
414 return FALSE;
415
416 gST->ConOut->SetAttribute (gST->ConOut, ATTR_ERROR);
417 Print(L"Error: %r %s\n", Status, where);
418 gST->ConOut->SetAttribute (gST->ConOut, ATTR_BASIC);
419 haveError = TRUE;
420
421 return TRUE;
422 }
423 #endif
424
425 //
426 // Graphics functions
427 //
428
429 VOID SwitchToGraphicsAndClear(VOID)
430 {
431 SwitchToGraphics();
432 if (GraphicsScreenDirty)
433 BltClearScreen(TRUE);
434 }
435
436 VOID BltClearScreen(BOOLEAN ShowBanner)
437 {
438 static EG_IMAGE *Banner = NULL;
439 EG_IMAGE *NewBanner = NULL;
440 INTN BannerPosX, BannerPosY;
441 EG_PIXEL Black = { 0x0, 0x0, 0x0, 0 };
442
443 if (ShowBanner && !(GlobalConfig.HideUIFlags & HIDEUI_FLAG_BANNER)) {
444 // load banner on first call
445 if (Banner == NULL) {
446 if (GlobalConfig.BannerFileName)
447 Banner = egLoadImage(SelfDir, GlobalConfig.BannerFileName, FALSE);
448 if (Banner == NULL)
449 Banner = egPrepareEmbeddedImage(&egemb_refind_banner, FALSE);
450 }
451
452 if (Banner) {
453 if (GlobalConfig.BannerScale == BANNER_FILLSCREEN) {
454 if ((Banner->Height != UGAHeight) || (Banner->Width != UGAWidth)) {
455 NewBanner = egScaleImage(Banner, UGAWidth, UGAHeight);
456 } // if
457 } else if ((Banner->Width > UGAWidth) || (Banner->Height > UGAHeight)) {
458 NewBanner = egCropImage(Banner, 0, 0, (Banner->Width > UGAWidth) ? UGAWidth : Banner->Width,
459 (Banner->Height > UGAHeight) ? UGAHeight : Banner->Height);
460 } // if/elseif
461 if (NewBanner) {
462 MyFreePool(Banner);
463 Banner = NewBanner;
464 }
465 MenuBackgroundPixel = Banner->PixelData[0];
466 } // if Banner exists
467
468 // clear and draw banner
469 if (GlobalConfig.ScreensaverTime != -1)
470 egClearScreen(&MenuBackgroundPixel);
471 else
472 egClearScreen(&Black);
473
474 if (Banner != NULL) {
475 BannerPosX = (Banner->Width < UGAWidth) ? ((UGAWidth - Banner->Width) / 2) : 0;
476 BannerPosY = (INTN) (ComputeRow0PosY() / 2) - (INTN) Banner->Height;
477 if (BannerPosY < 0)
478 BannerPosY = 0;
479 GlobalConfig.BannerBottomEdge = BannerPosY + Banner->Height;
480 if (GlobalConfig.ScreensaverTime != -1)
481 BltImage(Banner, (UINTN) BannerPosX, (UINTN) BannerPosY);
482 }
483
484 } else { // not showing banner
485 // clear to menu background color
486 egClearScreen(&MenuBackgroundPixel);
487 }
488
489 GraphicsScreenDirty = FALSE;
490 egFreeImage(GlobalConfig.ScreenBackground);
491 GlobalConfig.ScreenBackground = egCopyScreen();
492 } // VOID BltClearScreen()
493
494
495 VOID BltImage(IN EG_IMAGE *Image, IN UINTN XPos, IN UINTN YPos)
496 {
497 egDrawImage(Image, XPos, YPos);
498 GraphicsScreenDirty = TRUE;
499 }
500
501 VOID BltImageAlpha(IN EG_IMAGE *Image, IN UINTN XPos, IN UINTN YPos, IN EG_PIXEL *BackgroundPixel)
502 {
503 EG_IMAGE *CompImage;
504
505 // compose on background
506 CompImage = egCreateFilledImage(Image->Width, Image->Height, FALSE, BackgroundPixel);
507 egComposeImage(CompImage, Image, 0, 0);
508
509 // blit to screen and clean up
510 egDrawImage(CompImage, XPos, YPos);
511 egFreeImage(CompImage);
512 GraphicsScreenDirty = TRUE;
513 }
514
515 // VOID BltImageComposite(IN EG_IMAGE *BaseImage, IN EG_IMAGE *TopImage, IN UINTN XPos, IN UINTN YPos)
516 // {
517 // UINTN TotalWidth, TotalHeight, CompWidth, CompHeight, OffsetX, OffsetY;
518 // EG_IMAGE *CompImage;
519 //
520 // // initialize buffer with base image
521 // CompImage = egCopyImage(BaseImage);
522 // TotalWidth = BaseImage->Width;
523 // TotalHeight = BaseImage->Height;
524 //
525 // // place the top image
526 // CompWidth = TopImage->Width;
527 // if (CompWidth > TotalWidth)
528 // CompWidth = TotalWidth;
529 // OffsetX = (TotalWidth - CompWidth) >> 1;
530 // CompHeight = TopImage->Height;
531 // if (CompHeight > TotalHeight)
532 // CompHeight = TotalHeight;
533 // OffsetY = (TotalHeight - CompHeight) >> 1;
534 // egComposeImage(CompImage, TopImage, OffsetX, OffsetY);
535 //
536 // // blit to screen and clean up
537 // egDrawImage(CompImage, XPos, YPos);
538 // egFreeImage(CompImage);
539 // GraphicsScreenDirty = TRUE;
540 // }
541
542 VOID BltImageCompositeBadge(IN EG_IMAGE *BaseImage, IN EG_IMAGE *TopImage, IN EG_IMAGE *BadgeImage, IN UINTN XPos, IN UINTN YPos)
543 {
544 UINTN TotalWidth = 0, TotalHeight = 0, CompWidth = 0, CompHeight = 0, OffsetX = 0, OffsetY = 0;
545 EG_IMAGE *CompImage = NULL;
546
547 // initialize buffer with base image
548 if (BaseImage != NULL) {
549 CompImage = egCopyImage(BaseImage);
550 TotalWidth = BaseImage->Width;
551 TotalHeight = BaseImage->Height;
552 }
553
554 // place the top image
555 if ((TopImage != NULL) && (CompImage != NULL)) {
556 CompWidth = TopImage->Width;
557 if (CompWidth > TotalWidth)
558 CompWidth = TotalWidth;
559 OffsetX = (TotalWidth - CompWidth) >> 1;
560 CompHeight = TopImage->Height;
561 if (CompHeight > TotalHeight)
562 CompHeight = TotalHeight;
563 OffsetY = (TotalHeight - CompHeight) >> 1;
564 egComposeImage(CompImage, TopImage, OffsetX, OffsetY);
565 }
566
567 // place the badge image
568 if (BadgeImage != NULL && CompImage != NULL && (BadgeImage->Width + 8) < CompWidth && (BadgeImage->Height + 8) < CompHeight) {
569 OffsetX += CompWidth - 8 - BadgeImage->Width;
570 OffsetY += CompHeight - 8 - BadgeImage->Height;
571 egComposeImage(CompImage, BadgeImage, OffsetX, OffsetY);
572 }
573
574 // blit to screen and clean up
575 if (CompImage != NULL) {
576 if (CompImage->HasAlpha)
577 egDrawImageWithTransparency(CompImage, NULL, XPos, YPos, CompImage->Width, CompImage->Height);
578 else
579 egDrawImage(CompImage, XPos, YPos);
580 egFreeImage(CompImage);
581 GraphicsScreenDirty = TRUE;
582 }
583 }
584
585 // Line-editing functions borrowed from gummiboot (cursor_left(), cursor_right(), & line_edit()).
586 // gummiboot is copyright (c) 2012 by Kay Sievers <kay.sievers@vrfy.org> and Harald Hoyer
587 // <harald@redhat.com> and is licensed under the LGPL 2.1.
588
589 static void cursor_left(UINTN *cursor, UINTN *first)
590 {
591 if ((*cursor) > 0)
592 (*cursor)--;
593 else if ((*first) > 0)
594 (*first)--;
595 }
596
597 static void cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len)
598 {
599 if ((*cursor)+2 < x_max)
600 (*cursor)++;
601 else if ((*first) + (*cursor) < len)
602 (*first)++;
603 }
604
605 BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max) {
606 CHAR16 *line;
607 UINTN size;
608 UINTN len;
609 UINTN first;
610 UINTN y_pos = 3;
611 CHAR16 *print;
612 UINTN cursor;
613 BOOLEAN exit;
614 BOOLEAN enter;
615
616 DrawScreenHeader(L"Line Editor");
617 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, (ConWidth - 71) / 2, ConHeight - 1);
618 refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut,
619 L"Use cursor keys to edit, Esc to exit, Enter to boot with edited options");
620
621 if (!line_in)
622 line_in = L"";
623 size = StrLen(line_in) + 1024;
624 line = AllocatePool(size * sizeof(CHAR16));
625 StrCpy(line, line_in);
626 len = StrLen(line);
627 print = AllocatePool(x_max * sizeof(CHAR16));
628
629 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, TRUE);
630
631 first = 0;
632 cursor = 0;
633 enter = FALSE;
634 exit = FALSE;
635 while (!exit) {
636 UINTN index;
637 EFI_STATUS err;
638 EFI_INPUT_KEY key;
639 UINTN i;
640
641 i = len - first;
642 if (i >= x_max-2)
643 i = x_max-2;
644 CopyMem(print, line + first, i * sizeof(CHAR16));
645 print[i++] = ' ';
646 print[i] = '\0';
647
648 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, y_pos);
649 refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, print);
650 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos);
651
652 refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &index);
653 err = refit_call2_wrapper(ST->ConIn->ReadKeyStroke, ST->ConIn, &key);
654 if (EFI_ERROR(err))
655 continue;
656
657 switch (key.ScanCode) {
658 case SCAN_ESC:
659 exit = TRUE;
660 break;
661 case SCAN_HOME:
662 cursor = 0;
663 first = 0;
664 continue;
665 case SCAN_END:
666 cursor = len;
667 if (cursor >= x_max) {
668 cursor = x_max-2;
669 first = len - (x_max-2);
670 }
671 continue;
672 case SCAN_UP:
673 while((first + cursor) && line[first + cursor] == ' ')
674 cursor_left(&cursor, &first);
675 while((first + cursor) && line[first + cursor] != ' ')
676 cursor_left(&cursor, &first);
677 while((first + cursor) && line[first + cursor] == ' ')
678 cursor_left(&cursor, &first);
679 if (first + cursor != len && first + cursor)
680 cursor_right(&cursor, &first, x_max, len);
681 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos);
682 continue;
683 case SCAN_DOWN:
684 while(line[first + cursor] && line[first + cursor] == ' ')
685 cursor_right(&cursor, &first, x_max, len);
686 while(line[first + cursor] && line[first + cursor] != ' ')
687 cursor_right(&cursor, &first, x_max, len);
688 while(line[first + cursor] && line[first + cursor] == ' ')
689 cursor_right(&cursor, &first, x_max, len);
690 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos);
691 continue;
692 case SCAN_RIGHT:
693 if (first + cursor == len)
694 continue;
695 cursor_right(&cursor, &first, x_max, len);
696 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos);
697 continue;
698 case SCAN_LEFT:
699 cursor_left(&cursor, &first);
700 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos);
701 continue;
702 case SCAN_DELETE:
703 if (len == 0)
704 continue;
705 if (first + cursor == len)
706 continue;
707 for (i = first + cursor; i < len; i++)
708 line[i] = line[i+1];
709 line[len-1] = ' ';
710 len--;
711 continue;
712 }
713
714 switch (key.UnicodeChar) {
715 case CHAR_LINEFEED:
716 case CHAR_CARRIAGE_RETURN:
717 *line_out = line;
718 line = NULL;
719 enter = TRUE;
720 exit = TRUE;
721 break;
722 case CHAR_BACKSPACE:
723 if (len == 0)
724 continue;
725 if (first == 0 && cursor == 0)
726 continue;
727 for (i = first + cursor-1; i < len; i++)
728 line[i] = line[i+1];
729 len--;
730 if (cursor > 0)
731 cursor--;
732 if (cursor > 0 || first == 0)
733 continue;
734 /* show full line if it fits */
735 if (len < x_max-2) {
736 cursor = first;
737 first = 0;
738 continue;
739 }
740 /* jump left to see what we delete */
741 if (first > 10) {
742 first -= 10;
743 cursor = 10;
744 } else {
745 cursor = first;
746 first = 0;
747 }
748 continue;
749 case '\t':
750 case ' ' ... '~':
751 case 0x80 ... 0xffff:
752 if (len+1 == size)
753 continue;
754 for (i = len; i > first + cursor; i--)
755 line[i] = line[i-1];
756 line[first + cursor] = key.UnicodeChar;
757 len++;
758 line[len] = '\0';
759 if (cursor+2 < x_max)
760 cursor++;
761 else if (first + cursor < len)
762 first++;
763 continue;
764 }
765 }
766
767 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, FALSE);
768 FreePool(print);
769 FreePool(line);
770 return enter;
771 } /* BOOLEAN line_edit() */