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