]> code.delx.au - refind/blob - refind/screen.c
New "editor" option to the "hideui" configuration file token.
[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 // show the header
197 // DrawScreenHeader(Title);
198
199 if (!UseGraphicsMode)
200 SwitchToText(TRUE);
201
202 // reset error flag
203 haveError = FALSE;
204 }
205
206 VOID FinishExternalScreen(VOID)
207 {
208 // Reset the screen resolution, in case external program changed it....
209 SetupScreen();
210
211 // make sure we clean up later
212 GraphicsScreenDirty = TRUE;
213
214 if (haveError) {
215 SwitchToText(FALSE);
216 PauseForKey();
217 }
218
219 // reset error flag
220 haveError = FALSE;
221 }
222
223 VOID TerminateScreen(VOID)
224 {
225 // clear text screen
226 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
227 refit_call1_wrapper(ST->ConOut->ClearScreen, ST->ConOut);
228
229 // enable cursor
230 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, TRUE);
231 }
232
233 static VOID DrawScreenHeader(IN CHAR16 *Title)
234 {
235 UINTN y;
236
237 // clear to black background
238 egClearScreen(&DarkBackgroundPixel); // first clear in graphics mode
239 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
240 refit_call1_wrapper(ST->ConOut->ClearScreen, ST->ConOut); // then clear in text mode
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() */