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