]> code.delx.au - refind/blob - refind/screen.c
TianoCore build support; new use_graphics_for refind.conf 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 #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 SwitchToText(IN BOOLEAN CursorEnabled);
52 static VOID SwitchToGraphics(VOID);
53 static VOID DrawScreenHeader(IN CHAR16 *Title);
54
55 // UGA defines and variables
56
57 UINTN UGAWidth;
58 UINTN UGAHeight;
59 BOOLEAN AllowGraphicsMode;
60
61 EG_PIXEL StdBackgroundPixel = { 0xbf, 0xbf, 0xbf, 0 };
62 EG_PIXEL MenuBackgroundPixel = { 0xbf, 0xbf, 0xbf, 0 };
63
64 static BOOLEAN GraphicsScreenDirty;
65
66 // general defines and variables
67
68 static BOOLEAN haveError = FALSE;
69
70 //
71 // Screen initialization and switching
72 //
73
74 VOID InitScreen(VOID)
75 {
76 UINTN i;
77
78 // initialize libeg
79 egInitScreen();
80
81 if (egHasGraphicsMode()) {
82 egGetScreenSize(&UGAWidth, &UGAHeight);
83 AllowGraphicsMode = TRUE;
84 } else {
85 AllowGraphicsMode = FALSE;
86 egSetGraphicsModeEnabled(FALSE); // just to be sure we are in text mode
87 }
88 GraphicsScreenDirty = TRUE;
89
90 // disable cursor
91 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, FALSE);
92
93 // get size of text console
94 if (refit_call4_wrapper(ST->ConOut->QueryMode, ST->ConOut, ST->ConOut->Mode->Mode, &ConWidth, &ConHeight) != EFI_SUCCESS) {
95 // use default values on error
96 ConWidth = 80;
97 ConHeight = 25;
98 }
99
100 // make a buffer for a whole text line
101 BlankLine = AllocatePool((ConWidth + 1) * sizeof(CHAR16));
102 for (i = 0; i < ConWidth; i++)
103 BlankLine[i] = ' ';
104 BlankLine[i] = 0;
105
106 // show the banner (even when in graphics mode)
107 DrawScreenHeader(L"Initializing...");
108 }
109
110 VOID SetupScreen(VOID)
111 {
112 if (GlobalConfig.TextOnly) {
113 // switch to text mode if requested
114 AllowGraphicsMode = FALSE;
115 SwitchToText(FALSE);
116
117 } else if (AllowGraphicsMode) {
118 // clear screen and show banner
119 // (now we know we'll stay in graphics mode)
120 if ((GlobalConfig.RequestedScreenWidth > 0) && (GlobalConfig.RequestedScreenHeight > 0) &&
121 egSetScreenSize(GlobalConfig.RequestedScreenWidth, GlobalConfig.RequestedScreenHeight)) {
122 UGAWidth = GlobalConfig.RequestedScreenWidth;
123 UGAHeight = GlobalConfig.RequestedScreenHeight;
124 } // if user requested a particular screen resolution
125 SwitchToGraphics();
126 BltClearScreen(TRUE);
127 }
128 }
129
130 static VOID SwitchToText(IN BOOLEAN CursorEnabled)
131 {
132 egSetGraphicsModeEnabled(FALSE);
133 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, CursorEnabled);
134 }
135
136 static VOID SwitchToGraphics(VOID)
137 {
138 if (AllowGraphicsMode && !egIsGraphicsModeEnabled()) {
139 egSetGraphicsModeEnabled(TRUE);
140 GraphicsScreenDirty = TRUE;
141 }
142 }
143
144 //
145 // Screen control for running tools
146 //
147
148 VOID BeginTextScreen(IN CHAR16 *Title)
149 {
150 DrawScreenHeader(Title);
151 SwitchToText(FALSE);
152
153 // reset error flag
154 haveError = FALSE;
155 }
156
157 VOID FinishTextScreen(IN BOOLEAN WaitAlways)
158 {
159 if (haveError || WaitAlways) {
160 SwitchToText(FALSE);
161 PauseForKey();
162 }
163
164 // reset error flag
165 haveError = FALSE;
166 }
167
168 VOID BeginExternalScreen(IN BOOLEAN UseGraphicsMode, IN CHAR16 *Title)
169 {
170 EG_PIXEL DarkBackgroundPixel = { 0x0, 0x0, 0x0, 0 };
171
172 if (!AllowGraphicsMode)
173 UseGraphicsMode = FALSE;
174
175 if (UseGraphicsMode) {
176 SwitchToGraphics();
177 BltClearScreen(FALSE);
178 } else {
179 egClearScreen(&DarkBackgroundPixel);
180 DrawScreenHeader(Title);
181 } // if/else
182
183 // show the header
184 // DrawScreenHeader(Title);
185
186 if (!UseGraphicsMode)
187 SwitchToText(TRUE);
188
189 // reset error flag
190 haveError = FALSE;
191 }
192
193 VOID FinishExternalScreen(VOID)
194 {
195 // make sure we clean up later
196 GraphicsScreenDirty = TRUE;
197
198 if (haveError) {
199 SwitchToText(FALSE);
200 PauseForKey();
201 }
202
203 // reset error flag
204 haveError = FALSE;
205 }
206
207 VOID TerminateScreen(VOID)
208 {
209 // clear text screen
210 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
211 refit_call1_wrapper(ST->ConOut->ClearScreen, ST->ConOut);
212
213 // enable cursor
214 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, TRUE);
215 }
216
217 static VOID DrawScreenHeader(IN CHAR16 *Title)
218 {
219 UINTN y;
220
221 // clear to black background
222 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
223 refit_call1_wrapper(ST->ConOut->ClearScreen, ST->ConOut);
224
225 // paint header background
226 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BANNER);
227 for (y = 0; y < 3; y++) {
228 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, y);
229 Print(BlankLine);
230 }
231
232 // print header text
233 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 3, 1);
234 Print(L"rEFInd - %s", Title);
235
236 // reposition cursor
237 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
238 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, 4);
239 }
240
241 //
242 // Keyboard input
243 //
244
245 static BOOLEAN ReadAllKeyStrokes(VOID)
246 {
247 BOOLEAN GotKeyStrokes;
248 EFI_STATUS Status;
249 EFI_INPUT_KEY key;
250
251 GotKeyStrokes = FALSE;
252 for (;;) {
253 Status = refit_call2_wrapper(ST->ConIn->ReadKeyStroke, ST->ConIn, &key);
254 if (Status == EFI_SUCCESS) {
255 GotKeyStrokes = TRUE;
256 continue;
257 }
258 break;
259 }
260 return GotKeyStrokes;
261 }
262
263 VOID PauseForKey(VOID)
264 {
265 UINTN index;
266
267 Print(L"\n* Hit any key to continue *");
268
269 if (ReadAllKeyStrokes()) { // remove buffered key strokes
270 refit_call1_wrapper(BS->Stall, 5000000); // 5 seconds delay
271 ReadAllKeyStrokes(); // empty the buffer again
272 }
273
274 refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &index);
275 ReadAllKeyStrokes(); // empty the buffer to protect the menu
276
277 Print(L"\n");
278 }
279
280 #if REFIT_DEBUG > 0
281 VOID DebugPause(VOID)
282 {
283 // show console and wait for key
284 SwitchToText(FALSE);
285 PauseForKey();
286
287 // reset error flag
288 haveError = FALSE;
289 }
290 #endif
291
292 VOID EndlessIdleLoop(VOID)
293 {
294 UINTN index;
295
296 for (;;) {
297 ReadAllKeyStrokes();
298 refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &index);
299 }
300 }
301
302 //
303 // Error handling
304 //
305
306 #ifdef __MAKEWITH_GNUEFI
307 BOOLEAN CheckFatalError(IN EFI_STATUS Status, IN CHAR16 *where)
308 {
309 CHAR16 ErrorName[64];
310
311 if (!EFI_ERROR(Status))
312 return FALSE;
313
314 StatusToString(ErrorName, Status);
315 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_ERROR);
316 Print(L"Fatal Error: %s %s\n", ErrorName, where);
317 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
318 haveError = TRUE;
319
320 //BS->Exit(ImageHandle, ExitStatus, ExitDataSize, ExitData);
321
322 return TRUE;
323 }
324
325 BOOLEAN CheckError(IN EFI_STATUS Status, IN CHAR16 *where)
326 {
327 CHAR16 ErrorName[64];
328
329 if (!EFI_ERROR(Status))
330 return FALSE;
331
332 StatusToString(ErrorName, Status);
333 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_ERROR);
334 Print(L"Error: %s %s\n", ErrorName, where);
335 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
336 haveError = TRUE;
337
338 return TRUE;
339 }
340 #else
341 BOOLEAN CheckFatalError(IN EFI_STATUS Status, IN CHAR16 *where)
342 {
343 // CHAR16 ErrorName[64];
344
345 if (!EFI_ERROR(Status))
346 return FALSE;
347
348 // StatusToString(ErrorName, Status);
349 gST->ConOut->SetAttribute (gST->ConOut, ATTR_ERROR);
350 Print(L"Fatal Error: %r %s\n", Status, where);
351 gST->ConOut->SetAttribute (gST->ConOut, ATTR_BASIC);
352 haveError = TRUE;
353
354 //gBS->Exit(ImageHandle, ExitStatus, ExitDataSize, ExitData);
355
356 return TRUE;
357 }
358
359 BOOLEAN CheckError(IN EFI_STATUS Status, IN CHAR16 *where)
360 {
361 // CHAR16 ErrorName[64];
362
363 if (!EFI_ERROR(Status))
364 return FALSE;
365
366 // StatusToString(ErrorName, Status);
367 gST->ConOut->SetAttribute (gST->ConOut, ATTR_ERROR);
368 Print(L"Error: %r %s\n", Status, where);
369 gST->ConOut->SetAttribute (gST->ConOut, ATTR_BASIC);
370 haveError = TRUE;
371
372 return TRUE;
373 }
374 #endif
375
376 //
377 // Graphics functions
378 //
379
380 VOID SwitchToGraphicsAndClear(VOID)
381 {
382 SwitchToGraphics();
383 if (GraphicsScreenDirty)
384 BltClearScreen(TRUE);
385 }
386
387 VOID BltClearScreen(IN BOOLEAN ShowBanner)
388 {
389 static EG_IMAGE *Banner = NULL;
390
391 if (ShowBanner && !(GlobalConfig.HideUIFlags & HIDEUI_FLAG_BANNER)) {
392 // load banner on first call
393 if (Banner == NULL) {
394 if (GlobalConfig.BannerFileName == NULL)
395 Banner = egPrepareEmbeddedImage(&egemb_refind_banner, FALSE);
396 else
397 Banner = egLoadImage(SelfDir, GlobalConfig.BannerFileName, FALSE);
398 if (Banner != NULL)
399 MenuBackgroundPixel = Banner->PixelData[0];
400 }
401
402 // clear and draw banner
403 egClearScreen(&MenuBackgroundPixel);
404 if (Banner != NULL)
405 BltImage(Banner, (UGAWidth - Banner->Width) >> 1,
406 ((UGAHeight - LAYOUT_TOTAL_HEIGHT) >> 1) + LAYOUT_BANNER_HEIGHT - Banner->Height);
407
408 } else {
409 // clear to standard background color
410 egClearScreen(&StdBackgroundPixel);
411 }
412
413 GraphicsScreenDirty = FALSE;
414 }
415
416 VOID BltImage(IN EG_IMAGE *Image, IN UINTN XPos, IN UINTN YPos)
417 {
418 egDrawImage(Image, XPos, YPos);
419 GraphicsScreenDirty = TRUE;
420 }
421
422 VOID BltImageAlpha(IN EG_IMAGE *Image, IN UINTN XPos, IN UINTN YPos, IN EG_PIXEL *BackgroundPixel)
423 {
424 EG_IMAGE *CompImage;
425
426 // compose on background
427 CompImage = egCreateFilledImage(Image->Width, Image->Height, FALSE, BackgroundPixel);
428 egComposeImage(CompImage, Image, 0, 0);
429
430 // blit to screen and clean up
431 egDrawImage(CompImage, XPos, YPos);
432 egFreeImage(CompImage);
433 GraphicsScreenDirty = TRUE;
434 }
435
436 // VOID BltImageComposite(IN EG_IMAGE *BaseImage, IN EG_IMAGE *TopImage, IN UINTN XPos, IN UINTN YPos)
437 // {
438 // UINTN TotalWidth, TotalHeight, CompWidth, CompHeight, OffsetX, OffsetY;
439 // EG_IMAGE *CompImage;
440 //
441 // // initialize buffer with base image
442 // CompImage = egCopyImage(BaseImage);
443 // TotalWidth = BaseImage->Width;
444 // TotalHeight = BaseImage->Height;
445 //
446 // // place the top image
447 // CompWidth = TopImage->Width;
448 // if (CompWidth > TotalWidth)
449 // CompWidth = TotalWidth;
450 // OffsetX = (TotalWidth - CompWidth) >> 1;
451 // CompHeight = TopImage->Height;
452 // if (CompHeight > TotalHeight)
453 // CompHeight = TotalHeight;
454 // OffsetY = (TotalHeight - CompHeight) >> 1;
455 // egComposeImage(CompImage, TopImage, OffsetX, OffsetY);
456 //
457 // // blit to screen and clean up
458 // egDrawImage(CompImage, XPos, YPos);
459 // egFreeImage(CompImage);
460 // GraphicsScreenDirty = TRUE;
461 // }
462
463 VOID BltImageCompositeBadge(IN EG_IMAGE *BaseImage, IN EG_IMAGE *TopImage, IN EG_IMAGE *BadgeImage, IN UINTN XPos, IN UINTN YPos)
464 {
465 UINTN TotalWidth = 0, TotalHeight = 0, CompWidth = 0, CompHeight = 0, OffsetX = 0, OffsetY = 0;
466 EG_IMAGE *CompImage = NULL;
467
468 // initialize buffer with base image
469 if (BaseImage != NULL) {
470 CompImage = egCopyImage(BaseImage);
471 TotalWidth = BaseImage->Width;
472 TotalHeight = BaseImage->Height;
473 }
474
475 // place the top image
476 if ((TopImage != NULL) && (CompImage != NULL)) {
477 CompWidth = TopImage->Width;
478 if (CompWidth > TotalWidth)
479 CompWidth = TotalWidth;
480 OffsetX = (TotalWidth - CompWidth) >> 1;
481 CompHeight = TopImage->Height;
482 if (CompHeight > TotalHeight)
483 CompHeight = TotalHeight;
484 OffsetY = (TotalHeight - CompHeight) >> 1;
485 egComposeImage(CompImage, TopImage, OffsetX, OffsetY);
486 }
487
488 // place the badge image
489 if (BadgeImage != NULL && CompImage != NULL && (BadgeImage->Width + 8) < CompWidth && (BadgeImage->Height + 8) < CompHeight) {
490 OffsetX += CompWidth - 8 - BadgeImage->Width;
491 OffsetY += CompHeight - 8 - BadgeImage->Height;
492 egComposeImage(CompImage, BadgeImage, OffsetX, OffsetY);
493 }
494
495 // blit to screen and clean up
496 egDrawImage(CompImage, XPos, YPos);
497 egFreeImage(CompImage);
498 GraphicsScreenDirty = TRUE;
499 }