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