]> code.delx.au - refind/blob - refind/screen.c
Fixed screen-clearing bug; display fs type & size rather than blank
[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 #include "global.h"
38 #include "screen.h"
39 #include "config.h"
40 #include "libegint.h"
41 #include "lib.h"
42 #include "../include/refit_call_wrapper.h"
43
44 #include "../include/egemb_refind_banner.h"
45
46 // Console defines and variables
47
48 UINTN ConWidth;
49 UINTN ConHeight;
50 CHAR16 *BlankLine = NULL;
51
52 static VOID DrawScreenHeader(IN CHAR16 *Title);
53
54 // UGA defines and variables
55
56 UINTN UGAWidth;
57 UINTN UGAHeight;
58 BOOLEAN AllowGraphicsMode;
59
60 EG_PIXEL StdBackgroundPixel = { 0xbf, 0xbf, 0xbf, 0 };
61 EG_PIXEL MenuBackgroundPixel = { 0xbf, 0xbf, 0xbf, 0 };
62 EG_PIXEL DarkBackgroundPixel = { 0x0, 0x0, 0x0, 0 };
63
64 static BOOLEAN GraphicsScreenDirty;
65
66 // general defines and variables
67
68 static BOOLEAN haveError = FALSE;
69
70 static VOID PrepareBlankLine(VOID) {
71 UINTN i;
72
73 MyFreePool(BlankLine);
74 // make a buffer for a whole text line
75 BlankLine = AllocatePool((ConWidth + 1) * sizeof(CHAR16));
76 for (i = 0; i < ConWidth; i++)
77 BlankLine[i] = ' ';
78 BlankLine[i] = 0;
79 }
80
81 //
82 // Screen initialization and switching
83 //
84
85 VOID InitScreen(VOID)
86 {
87 // initialize libeg
88 egInitScreen();
89
90 if (egHasGraphicsMode()) {
91 egGetScreenSize(&UGAWidth, &UGAHeight);
92 AllowGraphicsMode = TRUE;
93 } else {
94 AllowGraphicsMode = FALSE;
95 egSetGraphicsModeEnabled(FALSE); // just to be sure we are in text mode
96 }
97 GraphicsScreenDirty = TRUE;
98
99 // disable cursor
100 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, FALSE);
101
102 // get size of text console
103 if (refit_call4_wrapper(ST->ConOut->QueryMode, ST->ConOut, ST->ConOut->Mode->Mode, &ConWidth, &ConHeight) != EFI_SUCCESS) {
104 // use default values on error
105 ConWidth = 80;
106 ConHeight = 25;
107 }
108
109 PrepareBlankLine();
110
111 // show the banner (even when in graphics mode)
112 DrawScreenHeader(L"Initializing...");
113 }
114
115 VOID SetupScreen(VOID)
116 {
117 GlobalConfig.RequestedTextMode = egSetTextMode(GlobalConfig.RequestedTextMode);
118
119 if ((GlobalConfig.RequestedScreenWidth > 0) &&
120 (egSetScreenSize(&GlobalConfig.RequestedScreenWidth, &GlobalConfig.RequestedScreenHeight))) {
121 UGAWidth = GlobalConfig.RequestedScreenWidth;
122 UGAHeight = GlobalConfig.RequestedScreenHeight;
123 } // if user requested a particular screen resolution
124
125 if (GlobalConfig.TextOnly) {
126 // switch to text mode if requested
127 AllowGraphicsMode = FALSE;
128 SwitchToText(FALSE);
129
130 } else if (AllowGraphicsMode) {
131 // clear screen and show banner
132 // (now we know we'll stay in graphics mode)
133 SwitchToGraphics();
134 BltClearScreen(TRUE);
135 }
136 }
137
138 VOID SwitchToText(IN BOOLEAN CursorEnabled)
139 {
140 egSetGraphicsModeEnabled(FALSE);
141 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, CursorEnabled);
142 // get size of text console
143 if (refit_call4_wrapper(ST->ConOut->QueryMode, ST->ConOut, ST->ConOut->Mode->Mode, &ConWidth, &ConHeight) != EFI_SUCCESS) {
144 // use default values on error
145 ConWidth = 80;
146 ConHeight = 25;
147 }
148 PrepareBlankLine();
149 }
150
151 VOID SwitchToGraphics(VOID)
152 {
153 if (AllowGraphicsMode && !egIsGraphicsModeEnabled()) {
154 egSetGraphicsModeEnabled(TRUE);
155 GraphicsScreenDirty = TRUE;
156 }
157 }
158
159 //
160 // Screen control for running tools
161 //
162
163 VOID BeginTextScreen(IN CHAR16 *Title)
164 {
165 DrawScreenHeader(Title);
166 SwitchToText(FALSE);
167
168 // reset error flag
169 haveError = FALSE;
170 }
171
172 VOID FinishTextScreen(IN BOOLEAN WaitAlways)
173 {
174 if (haveError || WaitAlways) {
175 PauseForKey();
176 SwitchToText(FALSE);
177 }
178
179 // reset error flag
180 haveError = FALSE;
181 }
182
183 VOID BeginExternalScreen(IN BOOLEAN UseGraphicsMode, IN CHAR16 *Title)
184 {
185 if (!AllowGraphicsMode)
186 UseGraphicsMode = FALSE;
187
188 if (UseGraphicsMode) {
189 SwitchToGraphics();
190 BltClearScreen(FALSE);
191 } else {
192 egClearScreen(&DarkBackgroundPixel);
193 DrawScreenHeader(Title);
194 } // if/else
195
196 if (!UseGraphicsMode)
197 SwitchToText(TRUE);
198
199 // reset error flag
200 haveError = FALSE;
201 }
202
203 VOID FinishExternalScreen(VOID)
204 {
205 // make sure we clean up later
206 GraphicsScreenDirty = TRUE;
207
208 if (haveError) {
209 SwitchToText(FALSE);
210 PauseForKey();
211 }
212
213 // Reset the screen resolution, in case external program changed it....
214 SetupScreen();
215
216 // reset error flag
217 haveError = FALSE;
218 }
219
220 VOID TerminateScreen(VOID)
221 {
222 // clear text screen
223 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
224 refit_call1_wrapper(ST->ConOut->ClearScreen, ST->ConOut);
225
226 // enable cursor
227 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, TRUE);
228 }
229
230 static VOID DrawScreenHeader(IN CHAR16 *Title)
231 {
232 UINTN y;
233
234 // clear to black background
235 egClearScreen(&DarkBackgroundPixel); // first clear in graphics mode
236 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
237 refit_call1_wrapper(ST->ConOut->ClearScreen, ST->ConOut); // then clear in text mode
238
239 // paint header background
240 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BANNER);
241 for (y = 0; y < 3; y++) {
242 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, y);
243 Print(BlankLine);
244 }
245
246 // print header text
247 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 3, 1);
248 Print(L"rEFInd - %s", Title);
249
250 // reposition cursor
251 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
252 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, 4);
253 }
254
255 //
256 // Keyboard input
257 //
258
259 static BOOLEAN ReadAllKeyStrokes(VOID)
260 {
261 BOOLEAN GotKeyStrokes;
262 EFI_STATUS Status;
263 EFI_INPUT_KEY key;
264
265 GotKeyStrokes = FALSE;
266 for (;;) {
267 Status = refit_call2_wrapper(ST->ConIn->ReadKeyStroke, ST->ConIn, &key);
268 if (Status == EFI_SUCCESS) {
269 GotKeyStrokes = TRUE;
270 continue;
271 }
272 break;
273 }
274 return GotKeyStrokes;
275 }
276
277 VOID PauseForKey(VOID)
278 {
279 UINTN index;
280
281 Print(L"\n* Hit any key to continue *");
282
283 if (ReadAllKeyStrokes()) { // remove buffered key strokes
284 refit_call1_wrapper(BS->Stall, 5000000); // 5 seconds delay
285 ReadAllKeyStrokes(); // empty the buffer again
286 }
287
288 refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &index);
289 ReadAllKeyStrokes(); // empty the buffer to protect the menu
290
291 Print(L"\n");
292 }
293
294 #if REFIT_DEBUG > 0
295 VOID DebugPause(VOID)
296 {
297 // show console and wait for key
298 SwitchToText(FALSE);
299 PauseForKey();
300
301 // reset error flag
302 haveError = FALSE;
303 }
304 #endif
305
306 VOID EndlessIdleLoop(VOID)
307 {
308 UINTN index;
309
310 for (;;) {
311 ReadAllKeyStrokes();
312 refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &index);
313 }
314 }
315
316 //
317 // Error handling
318 //
319
320 #ifdef __MAKEWITH_GNUEFI
321 BOOLEAN CheckFatalError(IN EFI_STATUS Status, IN CHAR16 *where)
322 {
323 CHAR16 ErrorName[64];
324
325 if (!EFI_ERROR(Status))
326 return FALSE;
327
328 StatusToString(ErrorName, Status);
329 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_ERROR);
330 Print(L"Fatal Error: %s %s\n", ErrorName, where);
331 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
332 haveError = TRUE;
333
334 //BS->Exit(ImageHandle, ExitStatus, ExitDataSize, ExitData);
335
336 return TRUE;
337 }
338
339 BOOLEAN CheckError(IN EFI_STATUS Status, IN CHAR16 *where)
340 {
341 CHAR16 ErrorName[64];
342
343 if (!EFI_ERROR(Status))
344 return FALSE;
345
346 StatusToString(ErrorName, Status);
347 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_ERROR);
348 Print(L"Error: %s %s\n", ErrorName, where);
349 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
350 haveError = TRUE;
351
352 return TRUE;
353 }
354 #else
355 BOOLEAN CheckFatalError(IN EFI_STATUS Status, IN CHAR16 *where)
356 {
357 // CHAR16 ErrorName[64];
358
359 if (!EFI_ERROR(Status))
360 return FALSE;
361
362 // StatusToString(ErrorName, Status);
363 gST->ConOut->SetAttribute (gST->ConOut, ATTR_ERROR);
364 Print(L"Fatal Error: %r %s\n", Status, where);
365 gST->ConOut->SetAttribute (gST->ConOut, ATTR_BASIC);
366 haveError = TRUE;
367
368 //gBS->Exit(ImageHandle, ExitStatus, ExitDataSize, ExitData);
369
370 return TRUE;
371 }
372
373 BOOLEAN CheckError(IN EFI_STATUS Status, IN CHAR16 *where)
374 {
375 // CHAR16 ErrorName[64];
376
377 if (!EFI_ERROR(Status))
378 return FALSE;
379
380 // StatusToString(ErrorName, Status);
381 gST->ConOut->SetAttribute (gST->ConOut, ATTR_ERROR);
382 Print(L"Error: %r %s\n", Status, where);
383 gST->ConOut->SetAttribute (gST->ConOut, ATTR_BASIC);
384 haveError = TRUE;
385
386 return TRUE;
387 }
388 #endif
389
390 //
391 // Graphics functions
392 //
393
394 VOID SwitchToGraphicsAndClear(VOID)
395 {
396 SwitchToGraphics();
397 if (GraphicsScreenDirty)
398 BltClearScreen(TRUE);
399 }
400
401 VOID BltClearScreen(IN BOOLEAN ShowBanner)
402 {
403 static EG_IMAGE *Banner = NULL;
404
405 if (ShowBanner && !(GlobalConfig.HideUIFlags & HIDEUI_FLAG_BANNER)) {
406 // load banner on first call
407 if (Banner == NULL) {
408 if (GlobalConfig.BannerFileName == NULL)
409 Banner = egPrepareEmbeddedImage(&egemb_refind_banner, FALSE);
410 else
411 Banner = egLoadImage(SelfDir, GlobalConfig.BannerFileName, FALSE);
412 if (Banner != NULL)
413 MenuBackgroundPixel = Banner->PixelData[0];
414 }
415
416 // clear and draw banner
417 egClearScreen(&MenuBackgroundPixel);
418 if (Banner != NULL)
419 BltImage(Banner, (UGAWidth - Banner->Width) >> 1,
420 ((UGAHeight - LAYOUT_TOTAL_HEIGHT) >> 1) + LAYOUT_BANNER_HEIGHT - Banner->Height);
421
422 } else {
423 // clear to standard background color
424 egClearScreen(&StdBackgroundPixel);
425 }
426
427 GraphicsScreenDirty = FALSE;
428 }
429
430 VOID BltImage(IN EG_IMAGE *Image, IN UINTN XPos, IN UINTN YPos)
431 {
432 egDrawImage(Image, XPos, YPos);
433 GraphicsScreenDirty = TRUE;
434 }
435
436 VOID BltImageAlpha(IN EG_IMAGE *Image, IN UINTN XPos, IN UINTN YPos, IN EG_PIXEL *BackgroundPixel)
437 {
438 EG_IMAGE *CompImage;
439
440 // compose on background
441 CompImage = egCreateFilledImage(Image->Width, Image->Height, FALSE, BackgroundPixel);
442 egComposeImage(CompImage, Image, 0, 0);
443
444 // blit to screen and clean up
445 egDrawImage(CompImage, XPos, YPos);
446 egFreeImage(CompImage);
447 GraphicsScreenDirty = TRUE;
448 }
449
450 // VOID BltImageComposite(IN EG_IMAGE *BaseImage, IN EG_IMAGE *TopImage, IN UINTN XPos, IN UINTN YPos)
451 // {
452 // UINTN TotalWidth, TotalHeight, CompWidth, CompHeight, OffsetX, OffsetY;
453 // EG_IMAGE *CompImage;
454 //
455 // // initialize buffer with base image
456 // CompImage = egCopyImage(BaseImage);
457 // TotalWidth = BaseImage->Width;
458 // TotalHeight = BaseImage->Height;
459 //
460 // // place the top image
461 // CompWidth = TopImage->Width;
462 // if (CompWidth > TotalWidth)
463 // CompWidth = TotalWidth;
464 // OffsetX = (TotalWidth - CompWidth) >> 1;
465 // CompHeight = TopImage->Height;
466 // if (CompHeight > TotalHeight)
467 // CompHeight = TotalHeight;
468 // OffsetY = (TotalHeight - CompHeight) >> 1;
469 // egComposeImage(CompImage, TopImage, OffsetX, OffsetY);
470 //
471 // // blit to screen and clean up
472 // egDrawImage(CompImage, XPos, YPos);
473 // egFreeImage(CompImage);
474 // GraphicsScreenDirty = TRUE;
475 // }
476
477 VOID BltImageCompositeBadge(IN EG_IMAGE *BaseImage, IN EG_IMAGE *TopImage, IN EG_IMAGE *BadgeImage, IN UINTN XPos, IN UINTN YPos)
478 {
479 UINTN TotalWidth = 0, TotalHeight = 0, CompWidth = 0, CompHeight = 0, OffsetX = 0, OffsetY = 0;
480 EG_IMAGE *CompImage = NULL;
481
482 // initialize buffer with base image
483 if (BaseImage != NULL) {
484 CompImage = egCopyImage(BaseImage);
485 TotalWidth = BaseImage->Width;
486 TotalHeight = BaseImage->Height;
487 }
488
489 // place the top image
490 if ((TopImage != NULL) && (CompImage != NULL)) {
491 CompWidth = TopImage->Width;
492 if (CompWidth > TotalWidth)
493 CompWidth = TotalWidth;
494 OffsetX = (TotalWidth - CompWidth) >> 1;
495 CompHeight = TopImage->Height;
496 if (CompHeight > TotalHeight)
497 CompHeight = TotalHeight;
498 OffsetY = (TotalHeight - CompHeight) >> 1;
499 egComposeImage(CompImage, TopImage, OffsetX, OffsetY);
500 }
501
502 // place the badge image
503 if (BadgeImage != NULL && CompImage != NULL && (BadgeImage->Width + 8) < CompWidth && (BadgeImage->Height + 8) < CompHeight) {
504 OffsetX += CompWidth - 8 - BadgeImage->Width;
505 OffsetY += CompHeight - 8 - BadgeImage->Height;
506 egComposeImage(CompImage, BadgeImage, OffsetX, OffsetY);
507 }
508
509 // blit to screen and clean up
510 egDrawImage(CompImage, XPos, YPos);
511 egFreeImage(CompImage);
512 GraphicsScreenDirty = TRUE;
513 }
514
515 // Line-editing functions borrowed from gummiboot (cursor_left(), cursor_right(), & line_edit()).
516 // gummiboot is copyright (c) 2012 by Kay Sievers <kay.sievers@vrfy.org> and Harald Hoyer
517 // <harald@redhat.com> and is licensed under the LGPL 2.1.
518
519 static void cursor_left(UINTN *cursor, UINTN *first)
520 {
521 if ((*cursor) > 0)
522 (*cursor)--;
523 else if ((*first) > 0)
524 (*first)--;
525 }
526
527 static void cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len)
528 {
529 if ((*cursor)+2 < x_max)
530 (*cursor)++;
531 else if ((*first) + (*cursor) < len)
532 (*first)++;
533 }
534
535 BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max) {
536 CHAR16 *line;
537 UINTN size;
538 UINTN len;
539 UINTN first;
540 UINTN y_pos = 3;
541 CHAR16 *print;
542 UINTN cursor;
543 BOOLEAN exit;
544 BOOLEAN enter;
545
546 DrawScreenHeader(L"Line Editor");
547 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, (ConWidth - 71) / 2, ConHeight - 1);
548 refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut,
549 L"Use cursor keys to edit, Esc to exit, Enter to boot with edited options");
550
551 if (!line_in)
552 line_in = L"";
553 size = StrLen(line_in) + 1024;
554 line = AllocatePool(size * sizeof(CHAR16));
555 StrCpy(line, line_in);
556 len = StrLen(line);
557 print = AllocatePool(x_max * sizeof(CHAR16));
558
559 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, TRUE);
560
561 first = 0;
562 cursor = 0;
563 enter = FALSE;
564 exit = FALSE;
565 while (!exit) {
566 UINTN index;
567 EFI_STATUS err;
568 EFI_INPUT_KEY key;
569 UINTN i;
570
571 i = len - first;
572 if (i >= x_max-2)
573 i = x_max-2;
574 CopyMem(print, line + first, i * sizeof(CHAR16));
575 print[i++] = ' ';
576 print[i] = '\0';
577
578 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, y_pos);
579 refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, print);
580 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos);
581
582 refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &index);
583 err = refit_call2_wrapper(ST->ConIn->ReadKeyStroke, ST->ConIn, &key);
584 if (EFI_ERROR(err))
585 continue;
586
587 switch (key.ScanCode) {
588 case SCAN_ESC:
589 exit = TRUE;
590 break;
591 case SCAN_HOME:
592 cursor = 0;
593 first = 0;
594 continue;
595 case SCAN_END:
596 cursor = len;
597 if (cursor >= x_max) {
598 cursor = x_max-2;
599 first = len - (x_max-2);
600 }
601 continue;
602 case SCAN_UP:
603 while((first + cursor) && line[first + cursor] == ' ')
604 cursor_left(&cursor, &first);
605 while((first + cursor) && line[first + cursor] != ' ')
606 cursor_left(&cursor, &first);
607 while((first + cursor) && line[first + cursor] == ' ')
608 cursor_left(&cursor, &first);
609 if (first + cursor != len && first + cursor)
610 cursor_right(&cursor, &first, x_max, len);
611 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos);
612 continue;
613 case SCAN_DOWN:
614 while(line[first + cursor] && line[first + cursor] == ' ')
615 cursor_right(&cursor, &first, x_max, len);
616 while(line[first + cursor] && line[first + cursor] != ' ')
617 cursor_right(&cursor, &first, x_max, len);
618 while(line[first + cursor] && line[first + cursor] == ' ')
619 cursor_right(&cursor, &first, x_max, len);
620 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos);
621 continue;
622 case SCAN_RIGHT:
623 if (first + cursor == len)
624 continue;
625 cursor_right(&cursor, &first, x_max, len);
626 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos);
627 continue;
628 case SCAN_LEFT:
629 cursor_left(&cursor, &first);
630 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos);
631 continue;
632 case SCAN_DELETE:
633 if (len == 0)
634 continue;
635 if (first + cursor == len)
636 continue;
637 for (i = first + cursor; i < len; i++)
638 line[i] = line[i+1];
639 line[len-1] = ' ';
640 len--;
641 continue;
642 }
643
644 switch (key.UnicodeChar) {
645 case CHAR_LINEFEED:
646 case CHAR_CARRIAGE_RETURN:
647 *line_out = line;
648 line = NULL;
649 enter = TRUE;
650 exit = TRUE;
651 break;
652 case CHAR_BACKSPACE:
653 if (len == 0)
654 continue;
655 if (first == 0 && cursor == 0)
656 continue;
657 for (i = first + cursor-1; i < len; i++)
658 line[i] = line[i+1];
659 len--;
660 if (cursor > 0)
661 cursor--;
662 if (cursor > 0 || first == 0)
663 continue;
664 /* show full line if it fits */
665 if (len < x_max-2) {
666 cursor = first;
667 first = 0;
668 continue;
669 }
670 /* jump left to see what we delete */
671 if (first > 10) {
672 first -= 10;
673 cursor = 10;
674 } else {
675 cursor = first;
676 first = 0;
677 }
678 continue;
679 case '\t':
680 case ' ' ... '~':
681 case 0x80 ... 0xffff:
682 if (len+1 == size)
683 continue;
684 for (i = len; i > first + cursor; i--)
685 line[i] = line[i-1];
686 line[first + cursor] = key.UnicodeChar;
687 len++;
688 line[len] = '\0';
689 if (cursor+2 < x_max)
690 cursor++;
691 else if (first + cursor < len)
692 first++;
693 continue;
694 }
695 }
696
697 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, FALSE);
698 FreePool(print);
699 FreePool(line);
700 return enter;
701 } /* BOOLEAN line_edit() */