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.
50 #include "../include/refit_call_wrapper.h"
52 #include "../include/egemb_refind_banner.h"
54 // Console defines and variables
58 CHAR16
*BlankLine
= NULL
;
60 static VOID
DrawScreenHeader(IN CHAR16
*Title
);
62 // UGA defines and variables
66 BOOLEAN AllowGraphicsMode
;
68 EG_PIXEL StdBackgroundPixel
= { 0xbf, 0xbf, 0xbf, 0 };
69 EG_PIXEL MenuBackgroundPixel
= { 0xbf, 0xbf, 0xbf, 0 };
70 EG_PIXEL DarkBackgroundPixel
= { 0x0, 0x0, 0x0, 0 };
72 static BOOLEAN GraphicsScreenDirty
;
74 // general defines and variables
76 static BOOLEAN haveError
= FALSE
;
78 static VOID
PrepareBlankLine(VOID
) {
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
++)
90 // Screen initialization and switching
98 if (egHasGraphicsMode()) {
99 egGetScreenSize(&UGAWidth
, &UGAHeight
);
100 AllowGraphicsMode
= TRUE
;
102 AllowGraphicsMode
= FALSE
;
103 egSetGraphicsModeEnabled(FALSE
); // just to be sure we are in text mode
105 GraphicsScreenDirty
= TRUE
;
108 refit_call2_wrapper(ST
->ConOut
->EnableCursor
, ST
->ConOut
, FALSE
);
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
119 // show the banner if in text mode
120 if (GlobalConfig
.TextOnly
)
121 DrawScreenHeader(L
"Initializing...");
124 VOID
SetupScreen(VOID
)
126 UINTN NewWidth
, NewHeight
;
128 if ((GlobalConfig
.RequestedScreenWidth
> 0) && (GlobalConfig
.RequestedScreenHeight
> 0)) {
129 UGAWidth
= (UGAWidth
< GlobalConfig
.RequestedScreenWidth
) ? UGAWidth
: GlobalConfig
.RequestedScreenWidth
;
130 UGAHeight
= (UGAHeight
< GlobalConfig
.RequestedScreenHeight
) ? UGAHeight
: GlobalConfig
.RequestedScreenHeight
;
132 if (egSetTextMode(GlobalConfig
.RequestedTextMode
)) {
133 egGetScreenSize(&NewWidth
, &NewHeight
);
134 if ((NewWidth
> UGAWidth
) || (NewHeight
> UGAHeight
)) {
136 UGAHeight
= NewHeight
;
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
;
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
152 if (GlobalConfig
.TextOnly
) {
153 // switch to text mode if requested
154 AllowGraphicsMode
= FALSE
;
157 } else if (AllowGraphicsMode
) {
158 // clear screen and show banner
159 // (now we know we'll stay in graphics mode)
161 BltClearScreen(TRUE
);
163 } // VOID SetupScreen()
165 VOID
SwitchToText(IN BOOLEAN CursorEnabled
)
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
178 VOID
SwitchToGraphics(VOID
)
180 if (AllowGraphicsMode
&& !egIsGraphicsModeEnabled()) {
181 egSetGraphicsModeEnabled(TRUE
);
182 GraphicsScreenDirty
= TRUE
;
187 // Screen control for running tools
190 VOID
BeginTextScreen(IN CHAR16
*Title
)
192 DrawScreenHeader(Title
);
199 VOID
FinishTextScreen(IN BOOLEAN WaitAlways
)
201 if (haveError
|| WaitAlways
) {
210 VOID
BeginExternalScreen(IN BOOLEAN UseGraphicsMode
, IN CHAR16
*Title
)
212 if (!AllowGraphicsMode
)
213 UseGraphicsMode
= FALSE
;
215 if (UseGraphicsMode
) {
217 BltClearScreen(FALSE
);
219 egClearScreen(&DarkBackgroundPixel
);
220 DrawScreenHeader(Title
);
228 VOID
FinishExternalScreen(VOID
)
230 // make sure we clean up later
231 GraphicsScreenDirty
= TRUE
;
238 // Reset the screen resolution, in case external program changed it....
245 VOID
TerminateScreen(VOID
)
248 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
249 refit_call1_wrapper(ST
->ConOut
->ClearScreen
, ST
->ConOut
);
252 refit_call2_wrapper(ST
->ConOut
->EnableCursor
, ST
->ConOut
, TRUE
);
255 static VOID
DrawScreenHeader(IN CHAR16
*Title
)
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
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
);
272 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 3, 1);
273 Print(L
"rEFInd - %s", Title
);
276 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
277 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 0, 4);
284 static BOOLEAN
ReadAllKeyStrokes(VOID
)
286 BOOLEAN GotKeyStrokes
;
290 GotKeyStrokes
= FALSE
;
292 Status
= refit_call2_wrapper(ST
->ConIn
->ReadKeyStroke
, ST
->ConIn
, &key
);
293 if (Status
== EFI_SUCCESS
) {
294 GotKeyStrokes
= TRUE
;
299 return GotKeyStrokes
;
302 VOID
PauseForKey(VOID
)
306 Print(L
"\n* Hit any key to continue *");
308 if (ReadAllKeyStrokes()) { // remove buffered key strokes
309 refit_call1_wrapper(BS
->Stall
, 5000000); // 5 seconds delay
310 ReadAllKeyStrokes(); // empty the buffer again
313 refit_call3_wrapper(BS
->WaitForEvent
, 1, &ST
->ConIn
->WaitForKey
, &index
);
314 ReadAllKeyStrokes(); // empty the buffer to protect the menu
320 VOID
DebugPause(VOID
)
322 // show console and wait for key
331 VOID
EndlessIdleLoop(VOID
)
337 refit_call3_wrapper(BS
->WaitForEvent
, 1, &ST
->ConIn
->WaitForKey
, &index
);
345 #ifdef __MAKEWITH_GNUEFI
346 BOOLEAN
CheckFatalError(IN EFI_STATUS Status
, IN CHAR16
*where
)
348 CHAR16 ErrorName
[64];
350 if (!EFI_ERROR(Status
))
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
);
359 //BS->Exit(ImageHandle, ExitStatus, ExitDataSize, ExitData);
364 BOOLEAN
CheckError(IN EFI_STATUS Status
, IN CHAR16
*where
)
366 CHAR16 ErrorName
[64];
368 if (!EFI_ERROR(Status
))
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
);
380 BOOLEAN
CheckFatalError(IN EFI_STATUS Status
, IN CHAR16
*where
)
382 // CHAR16 ErrorName[64];
384 if (!EFI_ERROR(Status
))
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
);
393 //gBS->Exit(ImageHandle, ExitStatus, ExitDataSize, ExitData);
398 BOOLEAN
CheckError(IN EFI_STATUS Status
, IN CHAR16
*where
)
400 // CHAR16 ErrorName[64];
402 if (!EFI_ERROR(Status
))
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
);
416 // Graphics functions
419 VOID
SwitchToGraphicsAndClear(VOID
)
422 if (GraphicsScreenDirty
)
423 BltClearScreen(TRUE
);
426 VOID
BltClearScreen(IN BOOLEAN ShowBanner
)
428 static EG_IMAGE
*Banner
= NULL
;
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
);
436 Banner
= egLoadImage(SelfDir
, GlobalConfig
.BannerFileName
, FALSE
);
438 MenuBackgroundPixel
= Banner
->PixelData
[0];
441 // clear and draw banner
442 egClearScreen(&MenuBackgroundPixel
);
444 BltImage(Banner
, (UGAWidth
- Banner
->Width
) >> 1,
445 ((UGAHeight
- LAYOUT_TOTAL_HEIGHT
) >> 1) + LAYOUT_BANNER_HEIGHT
- Banner
->Height
);
448 // clear to standard background color
449 egClearScreen(&StdBackgroundPixel
);
452 GraphicsScreenDirty
= FALSE
;
455 VOID
BltImage(IN EG_IMAGE
*Image
, IN UINTN XPos
, IN UINTN YPos
)
457 egDrawImage(Image
, XPos
, YPos
);
458 GraphicsScreenDirty
= TRUE
;
461 VOID
BltImageAlpha(IN EG_IMAGE
*Image
, IN UINTN XPos
, IN UINTN YPos
, IN EG_PIXEL
*BackgroundPixel
)
465 // compose on background
466 CompImage
= egCreateFilledImage(Image
->Width
, Image
->Height
, FALSE
, BackgroundPixel
);
467 egComposeImage(CompImage
, Image
, 0, 0);
469 // blit to screen and clean up
470 egDrawImage(CompImage
, XPos
, YPos
);
471 egFreeImage(CompImage
);
472 GraphicsScreenDirty
= TRUE
;
475 // VOID BltImageComposite(IN EG_IMAGE *BaseImage, IN EG_IMAGE *TopImage, IN UINTN XPos, IN UINTN YPos)
477 // UINTN TotalWidth, TotalHeight, CompWidth, CompHeight, OffsetX, OffsetY;
478 // EG_IMAGE *CompImage;
480 // // initialize buffer with base image
481 // CompImage = egCopyImage(BaseImage);
482 // TotalWidth = BaseImage->Width;
483 // TotalHeight = BaseImage->Height;
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);
496 // // blit to screen and clean up
497 // egDrawImage(CompImage, XPos, YPos);
498 // egFreeImage(CompImage);
499 // GraphicsScreenDirty = TRUE;
502 VOID
BltImageCompositeBadge(IN EG_IMAGE
*BaseImage
, IN EG_IMAGE
*TopImage
, IN EG_IMAGE
*BadgeImage
, IN UINTN XPos
, IN UINTN YPos
)
504 UINTN TotalWidth
= 0, TotalHeight
= 0, CompWidth
= 0, CompHeight
= 0, OffsetX
= 0, OffsetY
= 0;
505 EG_IMAGE
*CompImage
= NULL
;
507 // initialize buffer with base image
508 if (BaseImage
!= NULL
) {
509 CompImage
= egCopyImage(BaseImage
);
510 TotalWidth
= BaseImage
->Width
;
511 TotalHeight
= BaseImage
->Height
;
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
);
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
);
534 // blit to screen and clean up
535 egDrawImage(CompImage
, XPos
, YPos
);
536 egFreeImage(CompImage
);
537 GraphicsScreenDirty
= TRUE
;
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.
544 static void cursor_left(UINTN
*cursor
, UINTN
*first
)
548 else if ((*first
) > 0)
552 static void cursor_right(UINTN
*cursor
, UINTN
*first
, UINTN x_max
, UINTN len
)
554 if ((*cursor
)+2 < x_max
)
556 else if ((*first
) + (*cursor
) < len
)
560 BOOLEAN
line_edit(CHAR16
*line_in
, CHAR16
**line_out
, UINTN x_max
) {
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");
578 size
= StrLen(line_in
) + 1024;
579 line
= AllocatePool(size
* sizeof(CHAR16
));
580 StrCpy(line
, line_in
);
582 print
= AllocatePool(x_max
* sizeof(CHAR16
));
584 refit_call2_wrapper(ST
->ConOut
->EnableCursor
, ST
->ConOut
, TRUE
);
599 CopyMem(print
, line
+ first
, i
* sizeof(CHAR16
));
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
);
607 refit_call3_wrapper(BS
->WaitForEvent
, 1, &ST
->ConIn
->WaitForKey
, &index
);
608 err
= refit_call2_wrapper(ST
->ConIn
->ReadKeyStroke
, ST
->ConIn
, &key
);
612 switch (key
.ScanCode
) {
622 if (cursor
>= x_max
) {
624 first
= len
- (x_max
-2);
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
);
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
);
648 if (first
+ cursor
== len
)
650 cursor_right(&cursor
, &first
, x_max
, len
);
651 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, cursor
, y_pos
);
654 cursor_left(&cursor
, &first
);
655 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, cursor
, y_pos
);
660 if (first
+ cursor
== len
)
662 for (i
= first
+ cursor
; i
< len
; i
++)
669 switch (key
.UnicodeChar
) {
671 case CHAR_CARRIAGE_RETURN
:
680 if (first
== 0 && cursor
== 0)
682 for (i
= first
+ cursor
-1; i
< len
; i
++)
687 if (cursor
> 0 || first
== 0)
689 /* show full line if it fits */
695 /* jump left to see what we delete */
706 case 0x80 ... 0xffff:
709 for (i
= len
; i
> first
+ cursor
; i
--)
711 line
[first
+ cursor
] = key
.UnicodeChar
;
714 if (cursor
+2 < x_max
)
716 else if (first
+ cursor
< len
)
722 refit_call2_wrapper(ST
->ConOut
->EnableCursor
, ST
->ConOut
, FALSE
);
726 } /* BOOLEAN line_edit() */