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