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.
41 #include "../include/refit_call_wrapper.h"
43 #include "../include/egemb_refind_banner.h"
45 // Console defines and variables
51 static VOID
DrawScreenHeader(IN CHAR16
*Title
);
53 // UGA defines and variables
57 BOOLEAN AllowGraphicsMode
;
59 EG_PIXEL StdBackgroundPixel
= { 0xbf, 0xbf, 0xbf, 0 };
60 EG_PIXEL MenuBackgroundPixel
= { 0xbf, 0xbf, 0xbf, 0 };
62 static BOOLEAN GraphicsScreenDirty
;
64 // general defines and variables
66 static BOOLEAN haveError
= FALSE
;
69 // Screen initialization and switching
79 if (egHasGraphicsMode()) {
80 egGetScreenSize(&UGAWidth
, &UGAHeight
);
81 AllowGraphicsMode
= TRUE
;
83 AllowGraphicsMode
= FALSE
;
84 egSetGraphicsModeEnabled(FALSE
); // just to be sure we are in text mode
86 GraphicsScreenDirty
= TRUE
;
89 refit_call2_wrapper(ST
->ConOut
->EnableCursor
, ST
->ConOut
, FALSE
);
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
98 // make a buffer for a whole text line
99 BlankLine
= AllocatePool((ConWidth
+ 1) * sizeof(CHAR16
));
100 for (i
= 0; i
< ConWidth
; i
++)
104 // show the banner (even when in graphics mode)
105 DrawScreenHeader(L
"Initializing...");
108 VOID
SetupScreen(VOID
)
110 if ((GlobalConfig
.RequestedScreenWidth
> 0) && (GlobalConfig
.RequestedScreenHeight
> 0) &&
111 egSetScreenSize(GlobalConfig
.RequestedScreenWidth
, GlobalConfig
.RequestedScreenHeight
)) {
112 UGAWidth
= GlobalConfig
.RequestedScreenWidth
;
113 UGAHeight
= GlobalConfig
.RequestedScreenHeight
;
114 } // if user requested a particular screen resolution
116 if (GlobalConfig
.TextOnly
) {
117 // switch to text mode if requested
118 AllowGraphicsMode
= FALSE
;
121 } else if (AllowGraphicsMode
) {
122 // clear screen and show banner
123 // (now we know we'll stay in graphics mode)
125 BltClearScreen(TRUE
);
129 VOID
SwitchToText(IN BOOLEAN CursorEnabled
)
131 egSetGraphicsModeEnabled(FALSE
);
132 refit_call2_wrapper(ST
->ConOut
->EnableCursor
, ST
->ConOut
, CursorEnabled
);
133 // get size of text console
134 if (refit_call4_wrapper(ST
->ConOut
->QueryMode
, ST
->ConOut
, ST
->ConOut
->Mode
->Mode
, &ConWidth
, &ConHeight
) != EFI_SUCCESS
) {
135 // use default values on error
141 VOID
SwitchToGraphics(VOID
)
143 if (AllowGraphicsMode
&& !egIsGraphicsModeEnabled()) {
144 egSetGraphicsModeEnabled(TRUE
);
145 GraphicsScreenDirty
= TRUE
;
150 // Screen control for running tools
153 VOID
BeginTextScreen(IN CHAR16
*Title
)
155 DrawScreenHeader(Title
);
162 VOID
FinishTextScreen(IN BOOLEAN WaitAlways
)
164 if (haveError
|| WaitAlways
) {
173 VOID
BeginExternalScreen(IN BOOLEAN UseGraphicsMode
, IN CHAR16
*Title
)
175 EG_PIXEL DarkBackgroundPixel
= { 0x0, 0x0, 0x0, 0 };
177 if (!AllowGraphicsMode
)
178 UseGraphicsMode
= FALSE
;
180 if (UseGraphicsMode
) {
182 BltClearScreen(FALSE
);
184 egClearScreen(&DarkBackgroundPixel
);
185 DrawScreenHeader(Title
);
189 // DrawScreenHeader(Title);
191 if (!UseGraphicsMode
)
198 VOID
FinishExternalScreen(VOID
)
200 // make sure we clean up later
201 GraphicsScreenDirty
= TRUE
;
212 VOID
TerminateScreen(VOID
)
215 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
216 refit_call1_wrapper(ST
->ConOut
->ClearScreen
, ST
->ConOut
);
219 refit_call2_wrapper(ST
->ConOut
->EnableCursor
, ST
->ConOut
, TRUE
);
222 static VOID
DrawScreenHeader(IN CHAR16
*Title
)
226 // clear to black background
227 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
228 refit_call1_wrapper(ST
->ConOut
->ClearScreen
, ST
->ConOut
);
230 // paint header background
231 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BANNER
);
232 for (y
= 0; y
< 3; y
++) {
233 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 0, y
);
238 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 3, 1);
239 Print(L
"rEFInd - %s", Title
);
242 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
243 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 0, 4);
250 static BOOLEAN
ReadAllKeyStrokes(VOID
)
252 BOOLEAN GotKeyStrokes
;
256 GotKeyStrokes
= FALSE
;
258 Status
= refit_call2_wrapper(ST
->ConIn
->ReadKeyStroke
, ST
->ConIn
, &key
);
259 if (Status
== EFI_SUCCESS
) {
260 GotKeyStrokes
= TRUE
;
265 return GotKeyStrokes
;
268 VOID
PauseForKey(VOID
)
272 Print(L
"\n* Hit any key to continue *");
274 if (ReadAllKeyStrokes()) { // remove buffered key strokes
275 refit_call1_wrapper(BS
->Stall
, 5000000); // 5 seconds delay
276 ReadAllKeyStrokes(); // empty the buffer again
279 refit_call3_wrapper(BS
->WaitForEvent
, 1, &ST
->ConIn
->WaitForKey
, &index
);
280 ReadAllKeyStrokes(); // empty the buffer to protect the menu
286 VOID
DebugPause(VOID
)
288 // show console and wait for key
297 VOID
EndlessIdleLoop(VOID
)
303 refit_call3_wrapper(BS
->WaitForEvent
, 1, &ST
->ConIn
->WaitForKey
, &index
);
311 #ifdef __MAKEWITH_GNUEFI
312 BOOLEAN
CheckFatalError(IN EFI_STATUS Status
, IN CHAR16
*where
)
314 CHAR16 ErrorName
[64];
316 if (!EFI_ERROR(Status
))
319 StatusToString(ErrorName
, Status
);
320 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
321 Print(L
"Fatal Error: %s %s\n", ErrorName
, where
);
322 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
325 //BS->Exit(ImageHandle, ExitStatus, ExitDataSize, ExitData);
330 BOOLEAN
CheckError(IN EFI_STATUS Status
, IN CHAR16
*where
)
332 CHAR16 ErrorName
[64];
334 if (!EFI_ERROR(Status
))
337 StatusToString(ErrorName
, Status
);
338 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
339 Print(L
"Error: %s %s\n", ErrorName
, where
);
340 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
346 BOOLEAN
CheckFatalError(IN EFI_STATUS Status
, IN CHAR16
*where
)
348 // CHAR16 ErrorName[64];
350 if (!EFI_ERROR(Status
))
353 // StatusToString(ErrorName, Status);
354 gST
->ConOut
->SetAttribute (gST
->ConOut
, ATTR_ERROR
);
355 Print(L
"Fatal Error: %r %s\n", Status
, where
);
356 gST
->ConOut
->SetAttribute (gST
->ConOut
, ATTR_BASIC
);
359 //gBS->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 gST
->ConOut
->SetAttribute (gST
->ConOut
, ATTR_ERROR
);
373 Print(L
"Error: %r %s\n", Status
, where
);
374 gST
->ConOut
->SetAttribute (gST
->ConOut
, ATTR_BASIC
);
382 // Graphics functions
385 VOID
SwitchToGraphicsAndClear(VOID
)
388 if (GraphicsScreenDirty
)
389 BltClearScreen(TRUE
);
392 VOID
BltClearScreen(IN BOOLEAN ShowBanner
)
394 static EG_IMAGE
*Banner
= NULL
;
396 if (ShowBanner
&& !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_BANNER
)) {
397 // load banner on first call
398 if (Banner
== NULL
) {
399 if (GlobalConfig
.BannerFileName
== NULL
)
400 Banner
= egPrepareEmbeddedImage(&egemb_refind_banner
, FALSE
);
402 Banner
= egLoadImage(SelfDir
, GlobalConfig
.BannerFileName
, FALSE
);
404 MenuBackgroundPixel
= Banner
->PixelData
[0];
407 // clear and draw banner
408 egClearScreen(&MenuBackgroundPixel
);
410 BltImage(Banner
, (UGAWidth
- Banner
->Width
) >> 1,
411 ((UGAHeight
- LAYOUT_TOTAL_HEIGHT
) >> 1) + LAYOUT_BANNER_HEIGHT
- Banner
->Height
);
414 // clear to standard background color
415 egClearScreen(&StdBackgroundPixel
);
418 GraphicsScreenDirty
= FALSE
;
421 VOID
BltImage(IN EG_IMAGE
*Image
, IN UINTN XPos
, IN UINTN YPos
)
423 egDrawImage(Image
, XPos
, YPos
);
424 GraphicsScreenDirty
= TRUE
;
427 VOID
BltImageAlpha(IN EG_IMAGE
*Image
, IN UINTN XPos
, IN UINTN YPos
, IN EG_PIXEL
*BackgroundPixel
)
431 // compose on background
432 CompImage
= egCreateFilledImage(Image
->Width
, Image
->Height
, FALSE
, BackgroundPixel
);
433 egComposeImage(CompImage
, Image
, 0, 0);
435 // blit to screen and clean up
436 egDrawImage(CompImage
, XPos
, YPos
);
437 egFreeImage(CompImage
);
438 GraphicsScreenDirty
= TRUE
;
441 // VOID BltImageComposite(IN EG_IMAGE *BaseImage, IN EG_IMAGE *TopImage, IN UINTN XPos, IN UINTN YPos)
443 // UINTN TotalWidth, TotalHeight, CompWidth, CompHeight, OffsetX, OffsetY;
444 // EG_IMAGE *CompImage;
446 // // initialize buffer with base image
447 // CompImage = egCopyImage(BaseImage);
448 // TotalWidth = BaseImage->Width;
449 // TotalHeight = BaseImage->Height;
451 // // place the top image
452 // CompWidth = TopImage->Width;
453 // if (CompWidth > TotalWidth)
454 // CompWidth = TotalWidth;
455 // OffsetX = (TotalWidth - CompWidth) >> 1;
456 // CompHeight = TopImage->Height;
457 // if (CompHeight > TotalHeight)
458 // CompHeight = TotalHeight;
459 // OffsetY = (TotalHeight - CompHeight) >> 1;
460 // egComposeImage(CompImage, TopImage, OffsetX, OffsetY);
462 // // blit to screen and clean up
463 // egDrawImage(CompImage, XPos, YPos);
464 // egFreeImage(CompImage);
465 // GraphicsScreenDirty = TRUE;
468 VOID
BltImageCompositeBadge(IN EG_IMAGE
*BaseImage
, IN EG_IMAGE
*TopImage
, IN EG_IMAGE
*BadgeImage
, IN UINTN XPos
, IN UINTN YPos
)
470 UINTN TotalWidth
= 0, TotalHeight
= 0, CompWidth
= 0, CompHeight
= 0, OffsetX
= 0, OffsetY
= 0;
471 EG_IMAGE
*CompImage
= NULL
;
473 // initialize buffer with base image
474 if (BaseImage
!= NULL
) {
475 CompImage
= egCopyImage(BaseImage
);
476 TotalWidth
= BaseImage
->Width
;
477 TotalHeight
= BaseImage
->Height
;
480 // place the top image
481 if ((TopImage
!= NULL
) && (CompImage
!= NULL
)) {
482 CompWidth
= TopImage
->Width
;
483 if (CompWidth
> TotalWidth
)
484 CompWidth
= TotalWidth
;
485 OffsetX
= (TotalWidth
- CompWidth
) >> 1;
486 CompHeight
= TopImage
->Height
;
487 if (CompHeight
> TotalHeight
)
488 CompHeight
= TotalHeight
;
489 OffsetY
= (TotalHeight
- CompHeight
) >> 1;
490 egComposeImage(CompImage
, TopImage
, OffsetX
, OffsetY
);
493 // place the badge image
494 if (BadgeImage
!= NULL
&& CompImage
!= NULL
&& (BadgeImage
->Width
+ 8) < CompWidth
&& (BadgeImage
->Height
+ 8) < CompHeight
) {
495 OffsetX
+= CompWidth
- 8 - BadgeImage
->Width
;
496 OffsetY
+= CompHeight
- 8 - BadgeImage
->Height
;
497 egComposeImage(CompImage
, BadgeImage
, OffsetX
, OffsetY
);
500 // blit to screen and clean up
501 egDrawImage(CompImage
, XPos
, YPos
);
502 egFreeImage(CompImage
);
503 GraphicsScreenDirty
= TRUE
;
506 // Line-editing functions borrowed from gummiboot (cursor_left(), cursor_right(), & line_edit()).
507 // gummiboot is copyright (c) 2012 by Kay Sievers <kay.sievers@vrfy.org> and Harald Hoyer
508 // <harald@redhat.com> and is licensed under the LGPL 2.1.
510 static void cursor_left(UINTN
*cursor
, UINTN
*first
)
514 else if ((*first
) > 0)
518 static void cursor_right(UINTN
*cursor
, UINTN
*first
, UINTN x_max
, UINTN len
)
520 if ((*cursor
)+2 < x_max
)
522 else if ((*first
) + (*cursor
) < len
)
526 BOOLEAN
line_edit(CHAR16
*line_in
, CHAR16
**line_out
, UINTN x_max
, UINTN y_pos
) {
538 size
= StrLen(line_in
) + 1024;
539 line
= AllocatePool(size
* sizeof(CHAR16
));
540 StrCpy(line
, line_in
);
542 print
= AllocatePool(x_max
* sizeof(CHAR16
));
544 refit_call2_wrapper(ST
->ConOut
->EnableCursor
, ST
->ConOut
, TRUE
);
559 CopyMem(print
, line
+ first
, i
* sizeof(CHAR16
));
563 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 0, y_pos
);
564 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, print
);
565 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, cursor
, y_pos
);
567 refit_call3_wrapper(BS
->WaitForEvent
, 1, &ST
->ConIn
->WaitForKey
, &index
);
568 err
= refit_call2_wrapper(ST
->ConIn
->ReadKeyStroke
, ST
->ConIn
, &key
);
572 switch (key
.ScanCode
) {
582 if (cursor
>= x_max
) {
584 first
= len
- (x_max
-2);
588 while((first
+ cursor
) && line
[first
+ cursor
] == ' ')
589 cursor_left(&cursor
, &first
);
590 while((first
+ cursor
) && line
[first
+ cursor
] != ' ')
591 cursor_left(&cursor
, &first
);
592 while((first
+ cursor
) && line
[first
+ cursor
] == ' ')
593 cursor_left(&cursor
, &first
);
594 if (first
+ cursor
!= len
&& first
+ cursor
)
595 cursor_right(&cursor
, &first
, x_max
, len
);
596 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, cursor
, y_pos
);
599 while(line
[first
+ cursor
] && line
[first
+ cursor
] == ' ')
600 cursor_right(&cursor
, &first
, x_max
, len
);
601 while(line
[first
+ cursor
] && line
[first
+ cursor
] != ' ')
602 cursor_right(&cursor
, &first
, x_max
, len
);
603 while(line
[first
+ cursor
] && line
[first
+ cursor
] == ' ')
604 cursor_right(&cursor
, &first
, x_max
, len
);
605 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, cursor
, y_pos
);
608 if (first
+ cursor
== len
)
610 cursor_right(&cursor
, &first
, x_max
, len
);
611 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, cursor
, y_pos
);
614 cursor_left(&cursor
, &first
);
615 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, cursor
, y_pos
);
620 if (first
+ cursor
== len
)
622 for (i
= first
+ cursor
; i
< len
; i
++)
629 switch (key
.UnicodeChar
) {
631 case CHAR_CARRIAGE_RETURN
:
640 if (first
== 0 && cursor
== 0)
642 for (i
= first
+ cursor
-1; i
< len
; i
++)
647 if (cursor
> 0 || first
== 0)
649 /* show full line if it fits */
655 /* jump left to see what we delete */
666 case 0x80 ... 0xffff:
669 for (i
= len
; i
> first
+ cursor
; i
--)
671 line
[first
+ cursor
] = key
.UnicodeChar
;
674 if (cursor
+2 < x_max
)
676 else if (first
+ cursor
< len
)
682 refit_call2_wrapper(ST
->ConOut
->EnableCursor
, ST
->ConOut
, FALSE
);
686 } /* BOOLEAN line_edit() */