3 * Screen handling functions
5 * Copyright (c) 2006 Christoph Pfisterer
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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
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.
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.
37 * Modifications copyright (c) 2012 Roderick W. Smith
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.
51 #include "../include/refit_call_wrapper.h"
53 #include "../include/egemb_refind_banner.h"
55 // Console defines and variables
59 CHAR16
*BlankLine
= NULL
;
61 static VOID
DrawScreenHeader(IN CHAR16
*Title
);
63 // UGA defines and variables
67 BOOLEAN AllowGraphicsMode
;
69 EG_PIXEL StdBackgroundPixel
= { 0xbf, 0xbf, 0xbf, 0 };
70 EG_PIXEL MenuBackgroundPixel
= { 0xbf, 0xbf, 0xbf, 0 };
71 EG_PIXEL DarkBackgroundPixel
= { 0x0, 0x0, 0x0, 0 };
73 static BOOLEAN GraphicsScreenDirty
;
75 // general defines and variables
77 static BOOLEAN haveError
= FALSE
;
79 static VOID
PrepareBlankLine(VOID
) {
82 MyFreePool(BlankLine
);
83 // make a buffer for a whole text line
84 BlankLine
= AllocatePool((ConWidth
+ 1) * sizeof(CHAR16
));
85 for (i
= 0; i
< ConWidth
; i
++)
91 // Screen initialization and switching
99 if (egHasGraphicsMode()) {
100 egGetScreenSize(&UGAWidth
, &UGAHeight
);
101 AllowGraphicsMode
= TRUE
;
103 AllowGraphicsMode
= FALSE
;
104 egSetTextMode(GlobalConfig
.RequestedTextMode
);
105 egSetGraphicsModeEnabled(FALSE
); // just to be sure we are in text mode
107 GraphicsScreenDirty
= TRUE
;
110 refit_call2_wrapper(ST
->ConOut
->EnableCursor
, ST
->ConOut
, FALSE
);
112 // get size of text console
113 if (refit_call4_wrapper(ST
->ConOut
->QueryMode
, ST
->ConOut
, ST
->ConOut
->Mode
->Mode
, &ConWidth
, &ConHeight
) != EFI_SUCCESS
) {
114 // use default values on error
121 // show the banner if in text mode
122 if (GlobalConfig
.TextOnly
&& (GlobalConfig
.ScreensaverTime
!= -1))
123 DrawScreenHeader(L
"Initializing...");
126 // Set the screen resolution and mode (text vs. graphics).
127 VOID
SetupScreen(VOID
)
129 UINTN NewWidth
, NewHeight
;
131 // Convert mode number to horizontal & vertical resolution values
132 if ((GlobalConfig
.RequestedScreenWidth
> 0) && (GlobalConfig
.RequestedScreenHeight
== 0))
133 egGetResFromMode(&(GlobalConfig
.RequestedScreenWidth
), &(GlobalConfig
.RequestedScreenHeight
));
135 // Set the believed-to-be current resolution to the LOWER of the current
136 // believed-to-be resolution and the requested resolution. This is done to
137 // enable setting a lower-than-default resolution.
138 if ((GlobalConfig
.RequestedScreenWidth
> 0) && (GlobalConfig
.RequestedScreenHeight
> 0)) {
139 UGAWidth
= (UGAWidth
< GlobalConfig
.RequestedScreenWidth
) ? UGAWidth
: GlobalConfig
.RequestedScreenWidth
;
140 UGAHeight
= (UGAHeight
< GlobalConfig
.RequestedScreenHeight
) ? UGAHeight
: GlobalConfig
.RequestedScreenHeight
;
143 // Set text mode. If this requires increasing the size of the graphics mode, do so.
144 if (egSetTextMode(GlobalConfig
.RequestedTextMode
)) {
145 egGetScreenSize(&NewWidth
, &NewHeight
);
146 if ((NewWidth
> UGAWidth
) || (NewHeight
> UGAHeight
)) {
148 UGAHeight
= NewHeight
;
150 if ((UGAWidth
> GlobalConfig
.RequestedScreenWidth
) || (UGAHeight
> GlobalConfig
.RequestedScreenHeight
)) {
151 // Requested text mode forces us to use a bigger graphics mode
152 GlobalConfig
.RequestedScreenWidth
= UGAWidth
;
153 GlobalConfig
.RequestedScreenHeight
= UGAHeight
;
157 if (GlobalConfig
.RequestedScreenWidth
> 0) {
158 egSetScreenSize(&(GlobalConfig
.RequestedScreenWidth
), &(GlobalConfig
.RequestedScreenHeight
));
159 egGetScreenSize(&UGAWidth
, &UGAHeight
);
160 } // if user requested a particular screen resolution
162 if (GlobalConfig
.TextOnly
) {
163 // switch to text mode if requested
164 AllowGraphicsMode
= FALSE
;
167 } else if (AllowGraphicsMode
) {
168 // clear screen and show banner
169 // (now we know we'll stay in graphics mode)
171 if (GlobalConfig
.ScreensaverTime
!= -1) {
172 BltClearScreen(TRUE
);
173 } else { // start with screen blanked
174 GraphicsScreenDirty
= TRUE
;
177 } // VOID SetupScreen()
179 VOID
SwitchToText(IN BOOLEAN CursorEnabled
)
181 egSetGraphicsModeEnabled(FALSE
);
182 refit_call2_wrapper(ST
->ConOut
->EnableCursor
, ST
->ConOut
, CursorEnabled
);
183 // get size of text console
184 if (refit_call4_wrapper(ST
->ConOut
->QueryMode
, ST
->ConOut
, ST
->ConOut
->Mode
->Mode
, &ConWidth
, &ConHeight
) != EFI_SUCCESS
) {
185 // use default values on error
192 VOID
SwitchToGraphics(VOID
)
194 if (AllowGraphicsMode
&& !egIsGraphicsModeEnabled()) {
195 egSetGraphicsModeEnabled(TRUE
);
196 GraphicsScreenDirty
= TRUE
;
201 // Screen control for running tools
204 VOID
BeginTextScreen(IN CHAR16
*Title
)
206 DrawScreenHeader(Title
);
213 VOID
FinishTextScreen(IN BOOLEAN WaitAlways
)
215 if (haveError
|| WaitAlways
) {
224 VOID
BeginExternalScreen(IN BOOLEAN UseGraphicsMode
, IN CHAR16
*Title
)
226 if (!AllowGraphicsMode
)
227 UseGraphicsMode
= FALSE
;
229 if (UseGraphicsMode
) {
231 BltClearScreen(FALSE
);
233 egClearScreen(&DarkBackgroundPixel
);
234 DrawScreenHeader(Title
);
242 VOID
FinishExternalScreen(VOID
)
244 // make sure we clean up later
245 GraphicsScreenDirty
= TRUE
;
252 // Reset the screen resolution, in case external program changed it....
259 VOID
TerminateScreen(VOID
)
262 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
263 refit_call1_wrapper(ST
->ConOut
->ClearScreen
, ST
->ConOut
);
266 refit_call2_wrapper(ST
->ConOut
->EnableCursor
, ST
->ConOut
, TRUE
);
269 static VOID
DrawScreenHeader(IN CHAR16
*Title
)
273 // clear to black background
274 egClearScreen(&DarkBackgroundPixel
); // first clear in graphics mode
275 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
276 refit_call1_wrapper(ST
->ConOut
->ClearScreen
, ST
->ConOut
); // then clear in text mode
278 // paint header background
279 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BANNER
);
280 for (y
= 0; y
< 3; y
++) {
281 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 0, y
);
286 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 3, 1);
287 Print(L
"rEFInd - %s", Title
);
290 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
291 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 0, 4);
298 BOOLEAN
ReadAllKeyStrokes(VOID
)
300 BOOLEAN GotKeyStrokes
;
304 GotKeyStrokes
= FALSE
;
306 Status
= refit_call2_wrapper(ST
->ConIn
->ReadKeyStroke
, ST
->ConIn
, &key
);
307 if (Status
== EFI_SUCCESS
) {
308 GotKeyStrokes
= TRUE
;
313 return GotKeyStrokes
;
316 VOID
PauseForKey(VOID
)
320 Print(L
"\n* Hit any key to continue *");
322 if (ReadAllKeyStrokes()) { // remove buffered key strokes
323 refit_call1_wrapper(BS
->Stall
, 5000000); // 5 seconds delay
324 ReadAllKeyStrokes(); // empty the buffer again
327 refit_call3_wrapper(BS
->WaitForEvent
, 1, &ST
->ConIn
->WaitForKey
, &index
);
328 ReadAllKeyStrokes(); // empty the buffer to protect the menu
334 VOID
DebugPause(VOID
)
336 // show console and wait for key
345 VOID
EndlessIdleLoop(VOID
)
351 refit_call3_wrapper(BS
->WaitForEvent
, 1, &ST
->ConIn
->WaitForKey
, &index
);
359 #ifdef __MAKEWITH_GNUEFI
360 BOOLEAN
CheckFatalError(IN EFI_STATUS Status
, IN CHAR16
*where
)
362 CHAR16 ErrorName
[64];
364 if (!EFI_ERROR(Status
))
367 StatusToString(ErrorName
, Status
);
368 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
369 Print(L
"Fatal Error: %s %s\n", ErrorName
, where
);
370 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
373 //BS->Exit(ImageHandle, ExitStatus, ExitDataSize, ExitData);
378 BOOLEAN
CheckError(IN EFI_STATUS Status
, IN CHAR16
*where
)
380 CHAR16 ErrorName
[64];
382 if (!EFI_ERROR(Status
))
385 StatusToString(ErrorName
, Status
);
386 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
387 Print(L
"Error: %s %s\n", ErrorName
, where
);
388 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
394 BOOLEAN
CheckFatalError(IN EFI_STATUS Status
, IN CHAR16
*where
)
396 // CHAR16 ErrorName[64];
398 if (!EFI_ERROR(Status
))
401 gST
->ConOut
->SetAttribute (gST
->ConOut
, ATTR_ERROR
);
402 Print(L
"Fatal Error: %r %s\n", Status
, where
);
403 gST
->ConOut
->SetAttribute (gST
->ConOut
, ATTR_BASIC
);
406 //gBS->Exit(ImageHandle, ExitStatus, ExitDataSize, ExitData);
411 BOOLEAN
CheckError(IN EFI_STATUS Status
, IN CHAR16
*where
)
413 if (!EFI_ERROR(Status
))
416 gST
->ConOut
->SetAttribute (gST
->ConOut
, ATTR_ERROR
);
417 Print(L
"Error: %r %s\n", Status
, where
);
418 gST
->ConOut
->SetAttribute (gST
->ConOut
, ATTR_BASIC
);
426 // Graphics functions
429 VOID
SwitchToGraphicsAndClear(VOID
)
432 if (GraphicsScreenDirty
)
433 BltClearScreen(TRUE
);
436 VOID
BltClearScreen(IN BOOLEAN ShowBanner
)
438 static EG_IMAGE
*Banner
= NULL
, *CroppedBanner
;
439 INTN BannerPosX
, BannerPosY
;
440 EG_PIXEL Black
= { 0x0, 0x0, 0x0, 0 };
442 if (ShowBanner
&& !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_BANNER
)) {
443 // load banner on first call
444 if (Banner
== NULL
) {
445 if (GlobalConfig
.BannerFileName
== NULL
) {
446 Banner
= egPrepareEmbeddedImage(&egemb_refind_banner
, FALSE
);
448 Banner
= egLoadImage(SelfDir
, GlobalConfig
.BannerFileName
, FALSE
);
449 if (Banner
&& ((Banner
->Width
> UGAWidth
) || (Banner
->Height
> UGAHeight
))) {
450 CroppedBanner
= egCropImage(Banner
, 0, 0, (Banner
->Width
> UGAWidth
) ? UGAWidth
: Banner
->Width
,
451 (Banner
->Height
> UGAHeight
) ? UGAHeight
: Banner
->Height
);
453 Banner
= CroppedBanner
;
454 } // if image too big
455 if (Banner
== NULL
) {
456 Banner
= egPrepareEmbeddedImage(&egemb_refind_banner
, FALSE
);
457 } // if unusable image
460 MenuBackgroundPixel
= Banner
->PixelData
[0];
463 // clear and draw banner
464 if (GlobalConfig
.ScreensaverTime
!= -1)
465 egClearScreen(&MenuBackgroundPixel
);
467 egClearScreen(&Black
);
469 if (Banner
!= NULL
) {
470 BannerPosX
= (Banner
->Width
< UGAWidth
) ? ((UGAWidth
- Banner
->Width
) / 2) : 0;
471 BannerPosY
= (INTN
) (ComputeRow0PosY() / 2) - (INTN
) Banner
->Height
;
474 GlobalConfig
.BannerBottomEdge
= BannerPosY
+ Banner
->Height
;
475 if (GlobalConfig
.ScreensaverTime
!= -1)
476 BltImage(Banner
, (UINTN
) BannerPosX
, (UINTN
) BannerPosY
);
480 // clear to menu background color
481 egClearScreen(&MenuBackgroundPixel
);
484 GraphicsScreenDirty
= FALSE
;
485 egFreeImage(GlobalConfig
.ScreenBackground
);
486 GlobalConfig
.ScreenBackground
= egCopyScreen();
487 } /* VOID BltClearScreen() */
489 VOID
BltImage(IN EG_IMAGE
*Image
, IN UINTN XPos
, IN UINTN YPos
)
491 egDrawImage(Image
, XPos
, YPos
);
492 GraphicsScreenDirty
= TRUE
;
495 VOID
BltImageAlpha(IN EG_IMAGE
*Image
, IN UINTN XPos
, IN UINTN YPos
, IN EG_PIXEL
*BackgroundPixel
)
499 // compose on background
500 CompImage
= egCreateFilledImage(Image
->Width
, Image
->Height
, FALSE
, BackgroundPixel
);
501 egComposeImage(CompImage
, Image
, 0, 0);
503 // blit to screen and clean up
504 egDrawImage(CompImage
, XPos
, YPos
);
505 egFreeImage(CompImage
);
506 GraphicsScreenDirty
= TRUE
;
509 // VOID BltImageComposite(IN EG_IMAGE *BaseImage, IN EG_IMAGE *TopImage, IN UINTN XPos, IN UINTN YPos)
511 // UINTN TotalWidth, TotalHeight, CompWidth, CompHeight, OffsetX, OffsetY;
512 // EG_IMAGE *CompImage;
514 // // initialize buffer with base image
515 // CompImage = egCopyImage(BaseImage);
516 // TotalWidth = BaseImage->Width;
517 // TotalHeight = BaseImage->Height;
519 // // place the top image
520 // CompWidth = TopImage->Width;
521 // if (CompWidth > TotalWidth)
522 // CompWidth = TotalWidth;
523 // OffsetX = (TotalWidth - CompWidth) >> 1;
524 // CompHeight = TopImage->Height;
525 // if (CompHeight > TotalHeight)
526 // CompHeight = TotalHeight;
527 // OffsetY = (TotalHeight - CompHeight) >> 1;
528 // egComposeImage(CompImage, TopImage, OffsetX, OffsetY);
530 // // blit to screen and clean up
531 // egDrawImage(CompImage, XPos, YPos);
532 // egFreeImage(CompImage);
533 // GraphicsScreenDirty = TRUE;
536 VOID
BltImageCompositeBadge(IN EG_IMAGE
*BaseImage
, IN EG_IMAGE
*TopImage
, IN EG_IMAGE
*BadgeImage
, IN UINTN XPos
, IN UINTN YPos
)
538 UINTN TotalWidth
= 0, TotalHeight
= 0, CompWidth
= 0, CompHeight
= 0, OffsetX
= 0, OffsetY
= 0;
539 EG_IMAGE
*CompImage
= NULL
;
541 // initialize buffer with base image
542 if (BaseImage
!= NULL
) {
543 CompImage
= egCopyImage(BaseImage
);
544 TotalWidth
= BaseImage
->Width
;
545 TotalHeight
= BaseImage
->Height
;
548 // place the top image
549 if ((TopImage
!= NULL
) && (CompImage
!= NULL
)) {
550 CompWidth
= TopImage
->Width
;
551 if (CompWidth
> TotalWidth
)
552 CompWidth
= TotalWidth
;
553 OffsetX
= (TotalWidth
- CompWidth
) >> 1;
554 CompHeight
= TopImage
->Height
;
555 if (CompHeight
> TotalHeight
)
556 CompHeight
= TotalHeight
;
557 OffsetY
= (TotalHeight
- CompHeight
) >> 1;
558 egComposeImage(CompImage
, TopImage
, OffsetX
, OffsetY
);
561 // place the badge image
562 if (BadgeImage
!= NULL
&& CompImage
!= NULL
&& (BadgeImage
->Width
+ 8) < CompWidth
&& (BadgeImage
->Height
+ 8) < CompHeight
) {
563 OffsetX
+= CompWidth
- 8 - BadgeImage
->Width
;
564 OffsetY
+= CompHeight
- 8 - BadgeImage
->Height
;
565 egComposeImage(CompImage
, BadgeImage
, OffsetX
, OffsetY
);
568 // blit to screen and clean up
569 if (CompImage
->HasAlpha
)
570 egDrawImageWithTransparency(CompImage
, NULL
, XPos
, YPos
, CompImage
->Width
, CompImage
->Height
);
572 egDrawImage(CompImage
, XPos
, YPos
);
573 egFreeImage(CompImage
);
574 GraphicsScreenDirty
= TRUE
;
577 // Line-editing functions borrowed from gummiboot (cursor_left(), cursor_right(), & line_edit()).
578 // gummiboot is copyright (c) 2012 by Kay Sievers <kay.sievers@vrfy.org> and Harald Hoyer
579 // <harald@redhat.com> and is licensed under the LGPL 2.1.
581 static void cursor_left(UINTN
*cursor
, UINTN
*first
)
585 else if ((*first
) > 0)
589 static void cursor_right(UINTN
*cursor
, UINTN
*first
, UINTN x_max
, UINTN len
)
591 if ((*cursor
)+2 < x_max
)
593 else if ((*first
) + (*cursor
) < len
)
597 BOOLEAN
line_edit(CHAR16
*line_in
, CHAR16
**line_out
, UINTN x_max
) {
608 DrawScreenHeader(L
"Line Editor");
609 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, (ConWidth
- 71) / 2, ConHeight
- 1);
610 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
,
611 L
"Use cursor keys to edit, Esc to exit, Enter to boot with edited options");
615 size
= StrLen(line_in
) + 1024;
616 line
= AllocatePool(size
* sizeof(CHAR16
));
617 StrCpy(line
, line_in
);
619 print
= AllocatePool(x_max
* sizeof(CHAR16
));
621 refit_call2_wrapper(ST
->ConOut
->EnableCursor
, ST
->ConOut
, TRUE
);
636 CopyMem(print
, line
+ first
, i
* sizeof(CHAR16
));
640 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 0, y_pos
);
641 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, print
);
642 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, cursor
, y_pos
);
644 refit_call3_wrapper(BS
->WaitForEvent
, 1, &ST
->ConIn
->WaitForKey
, &index
);
645 err
= refit_call2_wrapper(ST
->ConIn
->ReadKeyStroke
, ST
->ConIn
, &key
);
649 switch (key
.ScanCode
) {
659 if (cursor
>= x_max
) {
661 first
= len
- (x_max
-2);
665 while((first
+ cursor
) && line
[first
+ cursor
] == ' ')
666 cursor_left(&cursor
, &first
);
667 while((first
+ cursor
) && line
[first
+ cursor
] != ' ')
668 cursor_left(&cursor
, &first
);
669 while((first
+ cursor
) && line
[first
+ cursor
] == ' ')
670 cursor_left(&cursor
, &first
);
671 if (first
+ cursor
!= len
&& first
+ cursor
)
672 cursor_right(&cursor
, &first
, x_max
, len
);
673 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, cursor
, y_pos
);
676 while(line
[first
+ cursor
] && line
[first
+ cursor
] == ' ')
677 cursor_right(&cursor
, &first
, x_max
, len
);
678 while(line
[first
+ cursor
] && line
[first
+ cursor
] != ' ')
679 cursor_right(&cursor
, &first
, x_max
, len
);
680 while(line
[first
+ cursor
] && line
[first
+ cursor
] == ' ')
681 cursor_right(&cursor
, &first
, x_max
, len
);
682 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, cursor
, y_pos
);
685 if (first
+ cursor
== len
)
687 cursor_right(&cursor
, &first
, x_max
, len
);
688 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, cursor
, y_pos
);
691 cursor_left(&cursor
, &first
);
692 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, cursor
, y_pos
);
697 if (first
+ cursor
== len
)
699 for (i
= first
+ cursor
; i
< len
; i
++)
706 switch (key
.UnicodeChar
) {
708 case CHAR_CARRIAGE_RETURN
:
717 if (first
== 0 && cursor
== 0)
719 for (i
= first
+ cursor
-1; i
< len
; i
++)
724 if (cursor
> 0 || first
== 0)
726 /* show full line if it fits */
732 /* jump left to see what we delete */
743 case 0x80 ... 0xffff:
746 for (i
= len
; i
> first
+ cursor
; i
--)
748 line
[first
+ cursor
] = key
.UnicodeChar
;
751 if (cursor
+2 < x_max
)
753 else if (first
+ cursor
< len
)
759 refit_call2_wrapper(ST
->ConOut
->EnableCursor
, ST
->ConOut
, FALSE
);
763 } /* BOOLEAN line_edit() */