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