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