]> code.delx.au - refind/blob - refind/screen.c
Added transparency support for built-in selection icon.
[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 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)
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 BltClearScreen(TRUE);
172 }
173 } // VOID SetupScreen()
174
175 VOID SwitchToText(IN BOOLEAN CursorEnabled)
176 {
177 egSetGraphicsModeEnabled(FALSE);
178 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, CursorEnabled);
179 // get size of text console
180 if (refit_call4_wrapper(ST->ConOut->QueryMode, ST->ConOut, ST->ConOut->Mode->Mode, &ConWidth, &ConHeight) != EFI_SUCCESS) {
181 // use default values on error
182 ConWidth = 80;
183 ConHeight = 25;
184 }
185 PrepareBlankLine();
186 }
187
188 VOID SwitchToGraphics(VOID)
189 {
190 if (AllowGraphicsMode && !egIsGraphicsModeEnabled()) {
191 egSetGraphicsModeEnabled(TRUE);
192 GraphicsScreenDirty = TRUE;
193 }
194 }
195
196 //
197 // Screen control for running tools
198 //
199
200 VOID BeginTextScreen(IN CHAR16 *Title)
201 {
202 DrawScreenHeader(Title);
203 SwitchToText(FALSE);
204
205 // reset error flag
206 haveError = FALSE;
207 }
208
209 VOID FinishTextScreen(IN BOOLEAN WaitAlways)
210 {
211 if (haveError || WaitAlways) {
212 PauseForKey();
213 SwitchToText(FALSE);
214 }
215
216 // reset error flag
217 haveError = FALSE;
218 }
219
220 VOID BeginExternalScreen(IN BOOLEAN UseGraphicsMode, IN CHAR16 *Title)
221 {
222 if (!AllowGraphicsMode)
223 UseGraphicsMode = FALSE;
224
225 if (UseGraphicsMode) {
226 SwitchToGraphics();
227 BltClearScreen(FALSE);
228 } else {
229 egClearScreen(&DarkBackgroundPixel);
230 DrawScreenHeader(Title);
231 SwitchToText(TRUE);
232 }
233
234 // reset error flag
235 haveError = FALSE;
236 }
237
238 VOID FinishExternalScreen(VOID)
239 {
240 // make sure we clean up later
241 GraphicsScreenDirty = TRUE;
242
243 if (haveError) {
244 SwitchToText(FALSE);
245 PauseForKey();
246 }
247
248 // Reset the screen resolution, in case external program changed it....
249 SetupScreen();
250
251 // reset error flag
252 haveError = FALSE;
253 }
254
255 VOID TerminateScreen(VOID)
256 {
257 // clear text screen
258 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
259 refit_call1_wrapper(ST->ConOut->ClearScreen, ST->ConOut);
260
261 // enable cursor
262 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, TRUE);
263 }
264
265 static VOID DrawScreenHeader(IN CHAR16 *Title)
266 {
267 UINTN y;
268
269 // clear to black background
270 egClearScreen(&DarkBackgroundPixel); // first clear in graphics mode
271 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
272 refit_call1_wrapper(ST->ConOut->ClearScreen, ST->ConOut); // then clear in text mode
273
274 // paint header background
275 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BANNER);
276 for (y = 0; y < 3; y++) {
277 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, y);
278 Print(BlankLine);
279 }
280
281 // print header text
282 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 3, 1);
283 Print(L"rEFInd - %s", Title);
284
285 // reposition cursor
286 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
287 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, 4);
288 }
289
290 //
291 // Keyboard input
292 //
293
294 static BOOLEAN ReadAllKeyStrokes(VOID)
295 {
296 BOOLEAN GotKeyStrokes;
297 EFI_STATUS Status;
298 EFI_INPUT_KEY key;
299
300 GotKeyStrokes = FALSE;
301 for (;;) {
302 Status = refit_call2_wrapper(ST->ConIn->ReadKeyStroke, ST->ConIn, &key);
303 if (Status == EFI_SUCCESS) {
304 GotKeyStrokes = TRUE;
305 continue;
306 }
307 break;
308 }
309 return GotKeyStrokes;
310 }
311
312 VOID PauseForKey(VOID)
313 {
314 UINTN index;
315
316 Print(L"\n* Hit any key to continue *");
317
318 if (ReadAllKeyStrokes()) { // remove buffered key strokes
319 refit_call1_wrapper(BS->Stall, 5000000); // 5 seconds delay
320 ReadAllKeyStrokes(); // empty the buffer again
321 }
322
323 refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &index);
324 ReadAllKeyStrokes(); // empty the buffer to protect the menu
325
326 Print(L"\n");
327 }
328
329 #if REFIT_DEBUG > 0
330 VOID DebugPause(VOID)
331 {
332 // show console and wait for key
333 SwitchToText(FALSE);
334 PauseForKey();
335
336 // reset error flag
337 haveError = FALSE;
338 }
339 #endif
340
341 VOID EndlessIdleLoop(VOID)
342 {
343 UINTN index;
344
345 for (;;) {
346 ReadAllKeyStrokes();
347 refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &index);
348 }
349 }
350
351 //
352 // Error handling
353 //
354
355 #ifdef __MAKEWITH_GNUEFI
356 BOOLEAN CheckFatalError(IN EFI_STATUS Status, IN CHAR16 *where)
357 {
358 CHAR16 ErrorName[64];
359
360 if (!EFI_ERROR(Status))
361 return FALSE;
362
363 StatusToString(ErrorName, Status);
364 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_ERROR);
365 Print(L"Fatal Error: %s %s\n", ErrorName, where);
366 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
367 haveError = TRUE;
368
369 //BS->Exit(ImageHandle, ExitStatus, ExitDataSize, ExitData);
370
371 return TRUE;
372 }
373
374 BOOLEAN CheckError(IN EFI_STATUS Status, IN CHAR16 *where)
375 {
376 CHAR16 ErrorName[64];
377
378 if (!EFI_ERROR(Status))
379 return FALSE;
380
381 StatusToString(ErrorName, Status);
382 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_ERROR);
383 Print(L"Error: %s %s\n", ErrorName, where);
384 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
385 haveError = TRUE;
386
387 return TRUE;
388 }
389 #else
390 BOOLEAN CheckFatalError(IN EFI_STATUS Status, IN CHAR16 *where)
391 {
392 // CHAR16 ErrorName[64];
393
394 if (!EFI_ERROR(Status))
395 return FALSE;
396
397 // StatusToString(ErrorName, Status);
398 gST->ConOut->SetAttribute (gST->ConOut, ATTR_ERROR);
399 Print(L"Fatal Error: %r %s\n", Status, where);
400 gST->ConOut->SetAttribute (gST->ConOut, ATTR_BASIC);
401 haveError = TRUE;
402
403 //gBS->Exit(ImageHandle, ExitStatus, ExitDataSize, ExitData);
404
405 return TRUE;
406 }
407
408 BOOLEAN CheckError(IN EFI_STATUS Status, IN CHAR16 *where)
409 {
410 // CHAR16 ErrorName[64];
411
412 if (!EFI_ERROR(Status))
413 return FALSE;
414
415 // StatusToString(ErrorName, Status);
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(IN BOOLEAN ShowBanner)
437 {
438 static EG_IMAGE *Banner = NULL, *CroppedBanner;
439 INTN BannerPosX, BannerPosY;
440
441 if (ShowBanner && !(GlobalConfig.HideUIFlags & HIDEUI_FLAG_BANNER)) {
442 // load banner on first call
443 if (Banner == NULL) {
444 if (GlobalConfig.BannerFileName == NULL) {
445 Banner = egPrepareEmbeddedImage(&egemb_refind_banner, FALSE);
446 } else {
447 Banner = egLoadImage(SelfDir, GlobalConfig.BannerFileName, FALSE);
448 if (Banner && ((Banner->Width > UGAWidth) || (Banner->Height > UGAHeight))) {
449 CroppedBanner = egCropImage(Banner, 0, 0, (Banner->Width > UGAWidth) ? UGAWidth : Banner->Width,
450 (Banner->Height > UGAHeight) ? UGAHeight : Banner->Height);
451 MyFreePool(Banner);
452 Banner = CroppedBanner;
453 } // if image too big
454 if (Banner == NULL) {
455 Banner = egPrepareEmbeddedImage(&egemb_refind_banner, FALSE);
456 } // if unusable image
457 }
458 if (Banner != NULL)
459 MenuBackgroundPixel = Banner->PixelData[0];
460 }
461
462 // clear and draw banner
463 egClearScreen(&MenuBackgroundPixel);
464 if (Banner != NULL) {
465 BannerPosX = (Banner->Width < UGAWidth) ? ((UGAWidth - Banner->Width) / 2) : 0;
466 BannerPosY = (ComputeRow0PosX() / 2) - Banner->Height;
467 // BannerPosY = ComputeRow0PosX() - Banner->Height - LAYOUT_BANNER_YGAP;
468 if (BannerPosY < 0)
469 BannerPosY = 0;
470 BltImage(Banner, (UINTN) BannerPosX, (UINTN) BannerPosY);
471 }
472
473 } else {
474 // clear to standard background color
475 egClearScreen(&StdBackgroundPixel);
476 }
477
478 GraphicsScreenDirty = FALSE;
479 egFreeImage(GlobalConfig.ScreenBackground);
480 GlobalConfig.ScreenBackground = egCopyScreen();
481 }
482
483 VOID BltImage(IN EG_IMAGE *Image, IN UINTN XPos, IN UINTN YPos)
484 {
485 egDrawImage(Image, XPos, YPos);
486 GraphicsScreenDirty = TRUE;
487 }
488
489 VOID BltImageAlpha(IN EG_IMAGE *Image, IN UINTN XPos, IN UINTN YPos, IN EG_PIXEL *BackgroundPixel)
490 {
491 EG_IMAGE *CompImage;
492
493 // compose on background
494 CompImage = egCreateFilledImage(Image->Width, Image->Height, FALSE, BackgroundPixel);
495 egComposeImage(CompImage, Image, 0, 0);
496
497 // blit to screen and clean up
498 egDrawImage(CompImage, XPos, YPos);
499 egFreeImage(CompImage);
500 GraphicsScreenDirty = TRUE;
501 }
502
503 // VOID BltImageComposite(IN EG_IMAGE *BaseImage, IN EG_IMAGE *TopImage, IN UINTN XPos, IN UINTN YPos)
504 // {
505 // UINTN TotalWidth, TotalHeight, CompWidth, CompHeight, OffsetX, OffsetY;
506 // EG_IMAGE *CompImage;
507 //
508 // // initialize buffer with base image
509 // CompImage = egCopyImage(BaseImage);
510 // TotalWidth = BaseImage->Width;
511 // TotalHeight = BaseImage->Height;
512 //
513 // // place the top image
514 // CompWidth = TopImage->Width;
515 // if (CompWidth > TotalWidth)
516 // CompWidth = TotalWidth;
517 // OffsetX = (TotalWidth - CompWidth) >> 1;
518 // CompHeight = TopImage->Height;
519 // if (CompHeight > TotalHeight)
520 // CompHeight = TotalHeight;
521 // OffsetY = (TotalHeight - CompHeight) >> 1;
522 // egComposeImage(CompImage, TopImage, OffsetX, OffsetY);
523 //
524 // // blit to screen and clean up
525 // egDrawImage(CompImage, XPos, YPos);
526 // egFreeImage(CompImage);
527 // GraphicsScreenDirty = TRUE;
528 // }
529
530 VOID BltImageCompositeBadge(IN EG_IMAGE *BaseImage, IN EG_IMAGE *TopImage, IN EG_IMAGE *BadgeImage, IN UINTN XPos, IN UINTN YPos)
531 {
532 UINTN TotalWidth = 0, TotalHeight = 0, CompWidth = 0, CompHeight = 0, OffsetX = 0, OffsetY = 0;
533 EG_IMAGE *CompImage = NULL;
534
535 // initialize buffer with base image
536 if (BaseImage != NULL) {
537 CompImage = egCopyImage(BaseImage);
538 TotalWidth = BaseImage->Width;
539 TotalHeight = BaseImage->Height;
540 }
541
542 // place the top image
543 if ((TopImage != NULL) && (CompImage != NULL)) {
544 CompWidth = TopImage->Width;
545 if (CompWidth > TotalWidth)
546 CompWidth = TotalWidth;
547 OffsetX = (TotalWidth - CompWidth) >> 1;
548 CompHeight = TopImage->Height;
549 if (CompHeight > TotalHeight)
550 CompHeight = TotalHeight;
551 OffsetY = (TotalHeight - CompHeight) >> 1;
552 egComposeImage(CompImage, TopImage, OffsetX, OffsetY);
553 }
554
555 // place the badge image
556 if (BadgeImage != NULL && CompImage != NULL && (BadgeImage->Width + 8) < CompWidth && (BadgeImage->Height + 8) < CompHeight) {
557 OffsetX += CompWidth - 8 - BadgeImage->Width;
558 OffsetY += CompHeight - 8 - BadgeImage->Height;
559 egComposeImage(CompImage, BadgeImage, OffsetX, OffsetY);
560 }
561
562 // blit to screen and clean up
563 if (CompImage->HasAlpha)
564 egDrawImageWithTransparency(CompImage, NULL, XPos, YPos, CompImage->Width, CompImage->Height);
565 else
566 egDrawImage(CompImage, XPos, YPos);
567 egFreeImage(CompImage);
568 GraphicsScreenDirty = TRUE;
569 }
570
571 // Line-editing functions borrowed from gummiboot (cursor_left(), cursor_right(), & line_edit()).
572 // gummiboot is copyright (c) 2012 by Kay Sievers <kay.sievers@vrfy.org> and Harald Hoyer
573 // <harald@redhat.com> and is licensed under the LGPL 2.1.
574
575 static void cursor_left(UINTN *cursor, UINTN *first)
576 {
577 if ((*cursor) > 0)
578 (*cursor)--;
579 else if ((*first) > 0)
580 (*first)--;
581 }
582
583 static void cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len)
584 {
585 if ((*cursor)+2 < x_max)
586 (*cursor)++;
587 else if ((*first) + (*cursor) < len)
588 (*first)++;
589 }
590
591 BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max) {
592 CHAR16 *line;
593 UINTN size;
594 UINTN len;
595 UINTN first;
596 UINTN y_pos = 3;
597 CHAR16 *print;
598 UINTN cursor;
599 BOOLEAN exit;
600 BOOLEAN enter;
601
602 DrawScreenHeader(L"Line Editor");
603 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, (ConWidth - 71) / 2, ConHeight - 1);
604 refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut,
605 L"Use cursor keys to edit, Esc to exit, Enter to boot with edited options");
606
607 if (!line_in)
608 line_in = L"";
609 size = StrLen(line_in) + 1024;
610 line = AllocatePool(size * sizeof(CHAR16));
611 StrCpy(line, line_in);
612 len = StrLen(line);
613 print = AllocatePool(x_max * sizeof(CHAR16));
614
615 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, TRUE);
616
617 first = 0;
618 cursor = 0;
619 enter = FALSE;
620 exit = FALSE;
621 while (!exit) {
622 UINTN index;
623 EFI_STATUS err;
624 EFI_INPUT_KEY key;
625 UINTN i;
626
627 i = len - first;
628 if (i >= x_max-2)
629 i = x_max-2;
630 CopyMem(print, line + first, i * sizeof(CHAR16));
631 print[i++] = ' ';
632 print[i] = '\0';
633
634 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, y_pos);
635 refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, print);
636 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos);
637
638 refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &index);
639 err = refit_call2_wrapper(ST->ConIn->ReadKeyStroke, ST->ConIn, &key);
640 if (EFI_ERROR(err))
641 continue;
642
643 switch (key.ScanCode) {
644 case SCAN_ESC:
645 exit = TRUE;
646 break;
647 case SCAN_HOME:
648 cursor = 0;
649 first = 0;
650 continue;
651 case SCAN_END:
652 cursor = len;
653 if (cursor >= x_max) {
654 cursor = x_max-2;
655 first = len - (x_max-2);
656 }
657 continue;
658 case SCAN_UP:
659 while((first + cursor) && line[first + cursor] == ' ')
660 cursor_left(&cursor, &first);
661 while((first + cursor) && line[first + cursor] != ' ')
662 cursor_left(&cursor, &first);
663 while((first + cursor) && line[first + cursor] == ' ')
664 cursor_left(&cursor, &first);
665 if (first + cursor != len && first + cursor)
666 cursor_right(&cursor, &first, x_max, len);
667 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos);
668 continue;
669 case SCAN_DOWN:
670 while(line[first + cursor] && line[first + cursor] == ' ')
671 cursor_right(&cursor, &first, x_max, len);
672 while(line[first + cursor] && line[first + cursor] != ' ')
673 cursor_right(&cursor, &first, x_max, len);
674 while(line[first + cursor] && line[first + cursor] == ' ')
675 cursor_right(&cursor, &first, x_max, len);
676 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos);
677 continue;
678 case SCAN_RIGHT:
679 if (first + cursor == len)
680 continue;
681 cursor_right(&cursor, &first, x_max, len);
682 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos);
683 continue;
684 case SCAN_LEFT:
685 cursor_left(&cursor, &first);
686 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos);
687 continue;
688 case SCAN_DELETE:
689 if (len == 0)
690 continue;
691 if (first + cursor == len)
692 continue;
693 for (i = first + cursor; i < len; i++)
694 line[i] = line[i+1];
695 line[len-1] = ' ';
696 len--;
697 continue;
698 }
699
700 switch (key.UnicodeChar) {
701 case CHAR_LINEFEED:
702 case CHAR_CARRIAGE_RETURN:
703 *line_out = line;
704 line = NULL;
705 enter = TRUE;
706 exit = TRUE;
707 break;
708 case CHAR_BACKSPACE:
709 if (len == 0)
710 continue;
711 if (first == 0 && cursor == 0)
712 continue;
713 for (i = first + cursor-1; i < len; i++)
714 line[i] = line[i+1];
715 len--;
716 if (cursor > 0)
717 cursor--;
718 if (cursor > 0 || first == 0)
719 continue;
720 /* show full line if it fits */
721 if (len < x_max-2) {
722 cursor = first;
723 first = 0;
724 continue;
725 }
726 /* jump left to see what we delete */
727 if (first > 10) {
728 first -= 10;
729 cursor = 10;
730 } else {
731 cursor = first;
732 first = 0;
733 }
734 continue;
735 case '\t':
736 case ' ' ... '~':
737 case 0x80 ... 0xffff:
738 if (len+1 == size)
739 continue;
740 for (i = len; i > first + cursor; i--)
741 line[i] = line[i-1];
742 line[first + cursor] = key.UnicodeChar;
743 len++;
744 line[len] = '\0';
745 if (cursor+2 < x_max)
746 cursor++;
747 else if (first + cursor < len)
748 first++;
749 continue;
750 }
751 }
752
753 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, FALSE);
754 FreePool(print);
755 FreePool(line);
756 return enter;
757 } /* BOOLEAN line_edit() */