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