]> code.delx.au - refind/blob - refind/screen.c
Modified SIP/CSR feature to work ON MACS when the csr-active-config
[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 * Modifications copyright (c) 2012-2015 Roderick W. Smith
38 *
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), or (at your option) any later version.
41 *
42 */
43 /*
44 * This program is free software: you can redistribute it and/or modify
45 * it under the terms of the GNU General Public License as published by
46 * the Free Software Foundation, either version 3 of the License, or
47 * (at your option) any later version.
48 *
49 * This program is distributed in the hope that it will be useful,
50 * but WITHOUT ANY WARRANTY; without even the implied warranty of
51 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
52 * GNU General Public License for more details.
53 *
54 * You should have received a copy of the GNU General Public License
55 * along with this program. If not, see <http://www.gnu.org/licenses/>.
56 */
57
58 #include "global.h"
59 #include "screen.h"
60 #include "config.h"
61 #include "libegint.h"
62 #include "lib.h"
63 #include "menu.h"
64 #include "../include/refit_call_wrapper.h"
65
66 #include "../include/egemb_refind_banner.h"
67
68 // Console defines and variables
69
70 UINTN ConWidth;
71 UINTN ConHeight;
72 CHAR16 *BlankLine = NULL;
73
74 // UGA defines and variables
75
76 UINTN UGAWidth;
77 UINTN UGAHeight;
78 BOOLEAN AllowGraphicsMode;
79
80 EG_PIXEL StdBackgroundPixel = { 0xbf, 0xbf, 0xbf, 0 };
81 EG_PIXEL MenuBackgroundPixel = { 0xbf, 0xbf, 0xbf, 0 };
82 EG_PIXEL DarkBackgroundPixel = { 0x0, 0x0, 0x0, 0 };
83
84 static BOOLEAN GraphicsScreenDirty;
85
86 // general defines and variables
87
88 static BOOLEAN haveError = FALSE;
89
90 static VOID PrepareBlankLine(VOID) {
91 UINTN i;
92
93 MyFreePool(BlankLine);
94 // make a buffer for a whole text line
95 BlankLine = AllocatePool((ConWidth + 1) * sizeof(CHAR16));
96 for (i = 0; i < ConWidth; i++)
97 BlankLine[i] = ' ';
98 BlankLine[i] = 0;
99 }
100
101 //
102 // Screen initialization and switching
103 //
104
105 VOID InitScreen(VOID)
106 {
107 // initialize libeg
108 egInitScreen();
109
110 if (egHasGraphicsMode()) {
111 egGetScreenSize(&UGAWidth, &UGAHeight);
112 AllowGraphicsMode = TRUE;
113 } else {
114 AllowGraphicsMode = FALSE;
115 egSetTextMode(GlobalConfig.RequestedTextMode);
116 egSetGraphicsModeEnabled(FALSE); // just to be sure we are in text mode
117 }
118 GraphicsScreenDirty = TRUE;
119
120 // disable cursor
121 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, FALSE);
122
123 // get size of text console
124 if (refit_call4_wrapper(ST->ConOut->QueryMode, ST->ConOut, ST->ConOut->Mode->Mode, &ConWidth, &ConHeight) != EFI_SUCCESS) {
125 // use default values on error
126 ConWidth = 80;
127 ConHeight = 25;
128 }
129
130 PrepareBlankLine();
131
132 // show the banner if in text mode
133 if (GlobalConfig.TextOnly && (GlobalConfig.ScreensaverTime != -1))
134 DrawScreenHeader(L"Initializing...");
135 }
136
137 // Set the screen resolution and mode (text vs. graphics).
138 VOID SetupScreen(VOID)
139 {
140 UINTN NewWidth, NewHeight;
141
142 // Convert mode number to horizontal & vertical resolution values
143 if ((GlobalConfig.RequestedScreenWidth > 0) && (GlobalConfig.RequestedScreenHeight == 0))
144 egGetResFromMode(&(GlobalConfig.RequestedScreenWidth), &(GlobalConfig.RequestedScreenHeight));
145
146 // Set the believed-to-be current resolution to the LOWER of the current
147 // believed-to-be resolution and the requested resolution. This is done to
148 // enable setting a lower-than-default resolution.
149 if ((GlobalConfig.RequestedScreenWidth > 0) && (GlobalConfig.RequestedScreenHeight > 0)) {
150 UGAWidth = (UGAWidth < GlobalConfig.RequestedScreenWidth) ? UGAWidth : GlobalConfig.RequestedScreenWidth;
151 UGAHeight = (UGAHeight < GlobalConfig.RequestedScreenHeight) ? UGAHeight : GlobalConfig.RequestedScreenHeight;
152 }
153
154 // Set text mode. If this requires increasing the size of the graphics mode, do so.
155 if (egSetTextMode(GlobalConfig.RequestedTextMode)) {
156 egGetScreenSize(&NewWidth, &NewHeight);
157 if ((NewWidth > UGAWidth) || (NewHeight > UGAHeight)) {
158 UGAWidth = NewWidth;
159 UGAHeight = NewHeight;
160 }
161 if ((UGAWidth > GlobalConfig.RequestedScreenWidth) || (UGAHeight > GlobalConfig.RequestedScreenHeight)) {
162 // Requested text mode forces us to use a bigger graphics mode
163 GlobalConfig.RequestedScreenWidth = UGAWidth;
164 GlobalConfig.RequestedScreenHeight = UGAHeight;
165 } // if
166 }
167
168 if (GlobalConfig.RequestedScreenWidth > 0) {
169 egSetScreenSize(&(GlobalConfig.RequestedScreenWidth), &(GlobalConfig.RequestedScreenHeight));
170 egGetScreenSize(&UGAWidth, &UGAHeight);
171 } // if user requested a particular screen resolution
172
173 if (GlobalConfig.TextOnly) {
174 // switch to text mode if requested
175 AllowGraphicsMode = FALSE;
176 SwitchToText(FALSE);
177
178 } else if (AllowGraphicsMode) {
179 // clear screen and show banner
180 // (now we know we'll stay in graphics mode)
181 SwitchToGraphics();
182 if (GlobalConfig.ScreensaverTime != -1) {
183 BltClearScreen(TRUE);
184 } else { // start with screen blanked
185 GraphicsScreenDirty = TRUE;
186 }
187 }
188 } // VOID SetupScreen()
189
190 VOID SwitchToText(IN BOOLEAN CursorEnabled)
191 {
192 egSetGraphicsModeEnabled(FALSE);
193 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, CursorEnabled);
194 // get size of text console
195 if (refit_call4_wrapper(ST->ConOut->QueryMode, ST->ConOut, ST->ConOut->Mode->Mode, &ConWidth, &ConHeight) != EFI_SUCCESS) {
196 // use default values on error
197 ConWidth = 80;
198 ConHeight = 25;
199 }
200 PrepareBlankLine();
201 }
202
203 VOID SwitchToGraphics(VOID)
204 {
205 if (AllowGraphicsMode && !egIsGraphicsModeEnabled()) {
206 egSetGraphicsModeEnabled(TRUE);
207 GraphicsScreenDirty = TRUE;
208 }
209 }
210
211 //
212 // Screen control for running tools
213 //
214
215 VOID BeginTextScreen(IN CHAR16 *Title)
216 {
217 DrawScreenHeader(Title);
218 SwitchToText(FALSE);
219
220 // reset error flag
221 haveError = FALSE;
222 }
223
224 VOID FinishTextScreen(IN BOOLEAN WaitAlways)
225 {
226 if (haveError || WaitAlways) {
227 PauseForKey();
228 SwitchToText(FALSE);
229 }
230
231 // reset error flag
232 haveError = FALSE;
233 }
234
235 VOID BeginExternalScreen(IN BOOLEAN UseGraphicsMode, IN CHAR16 *Title)
236 {
237 if (!AllowGraphicsMode)
238 UseGraphicsMode = FALSE;
239
240 if (UseGraphicsMode) {
241 SwitchToGraphics();
242 BltClearScreen(FALSE);
243 } else {
244 egClearScreen(&DarkBackgroundPixel);
245 DrawScreenHeader(Title);
246 SwitchToText(TRUE);
247 }
248
249 // reset error flag
250 haveError = FALSE;
251 }
252
253 VOID FinishExternalScreen(VOID)
254 {
255 // make sure we clean up later
256 GraphicsScreenDirty = TRUE;
257
258 if (haveError) {
259 SwitchToText(FALSE);
260 PauseForKey();
261 }
262
263 // Reset the screen resolution, in case external program changed it....
264 SetupScreen();
265
266 // reset error flag
267 haveError = FALSE;
268 }
269
270 VOID TerminateScreen(VOID)
271 {
272 // clear text screen
273 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
274 refit_call1_wrapper(ST->ConOut->ClearScreen, ST->ConOut);
275
276 // enable cursor
277 refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, TRUE);
278 }
279
280 VOID DrawScreenHeader(IN CHAR16 *Title)
281 {
282 UINTN y;
283
284 // clear to black background
285 egClearScreen(&DarkBackgroundPixel); // first clear in graphics mode
286 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
287 refit_call1_wrapper(ST->ConOut->ClearScreen, ST->ConOut); // then clear in text mode
288
289 // paint header background
290 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BANNER);
291 for (y = 0; y < 3; y++) {
292 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, y);
293 Print(BlankLine);
294 }
295
296 // print header text
297 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 3, 1);
298 Print(L"rEFInd - %s", Title);
299
300 // reposition cursor
301 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
302 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, 4);
303 }
304
305 //
306 // Keyboard input
307 //
308
309 BOOLEAN ReadAllKeyStrokes(VOID)
310 {
311 BOOLEAN GotKeyStrokes;
312 EFI_STATUS Status;
313 EFI_INPUT_KEY key;
314
315 GotKeyStrokes = FALSE;
316 for (;;) {
317 Status = refit_call2_wrapper(ST->ConIn->ReadKeyStroke, ST->ConIn, &key);
318 if (Status == EFI_SUCCESS) {
319 GotKeyStrokes = TRUE;
320 continue;
321 }
322 break;
323 }
324 return GotKeyStrokes;
325 }
326
327 VOID PauseForKey(VOID)
328 {
329 UINTN index;
330
331 Print(L"\n* Hit any key to continue *");
332
333 if (ReadAllKeyStrokes()) { // remove buffered key strokes
334 refit_call1_wrapper(BS->Stall, 5000000); // 5 seconds delay
335 ReadAllKeyStrokes(); // empty the buffer again
336 }
337
338 refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &index);
339 ReadAllKeyStrokes(); // empty the buffer to protect the menu
340
341 Print(L"\n");
342 }
343
344 // Pause a specified number of seconds
345 VOID PauseSeconds(UINTN Seconds) {
346 refit_call1_wrapper(BS->Stall, 1000000 * Seconds);
347 } // VOID PauseSeconds()
348
349 #if REFIT_DEBUG > 0
350 VOID DebugPause(VOID)
351 {
352 // show console and wait for key
353 SwitchToText(FALSE);
354 PauseForKey();
355
356 // reset error flag
357 haveError = FALSE;
358 }
359 #endif
360
361 VOID EndlessIdleLoop(VOID)
362 {
363 UINTN index;
364
365 for (;;) {
366 ReadAllKeyStrokes();
367 refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &index);
368 }
369 }
370
371 //
372 // Error handling
373 //
374
375 #ifdef __MAKEWITH_GNUEFI
376 BOOLEAN CheckFatalError(IN EFI_STATUS Status, IN CHAR16 *where)
377 {
378 CHAR16 ErrorName[64];
379
380 if (!EFI_ERROR(Status))
381 return FALSE;
382
383 StatusToString(ErrorName, Status);
384 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_ERROR);
385 Print(L"Fatal Error: %s %s\n", ErrorName, where);
386 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
387 haveError = TRUE;
388
389 //BS->Exit(ImageHandle, ExitStatus, ExitDataSize, ExitData);
390
391 return TRUE;
392 }
393
394 BOOLEAN CheckError(IN EFI_STATUS Status, IN CHAR16 *where)
395 {
396 CHAR16 ErrorName[64];
397
398 if (!EFI_ERROR(Status))
399 return FALSE;
400
401 StatusToString(ErrorName, Status);
402 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_ERROR);
403 Print(L"Error: %s %s\n", ErrorName, where);
404 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
405 haveError = TRUE;
406
407 return TRUE;
408 }
409 #else
410 BOOLEAN CheckFatalError(IN EFI_STATUS Status, IN CHAR16 *where)
411 {
412 // CHAR16 ErrorName[64];
413
414 if (!EFI_ERROR(Status))
415 return FALSE;
416
417 gST->ConOut->SetAttribute (gST->ConOut, ATTR_ERROR);
418 Print(L"Fatal Error: %r %s\n", Status, where);
419 gST->ConOut->SetAttribute (gST->ConOut, ATTR_BASIC);
420 haveError = TRUE;
421
422 //gBS->Exit(ImageHandle, ExitStatus, ExitDataSize, ExitData);
423
424 return TRUE;
425 }
426
427 BOOLEAN CheckError(IN EFI_STATUS Status, IN CHAR16 *where)
428 {
429 if (!EFI_ERROR(Status))
430 return FALSE;
431
432 gST->ConOut->SetAttribute (gST->ConOut, ATTR_ERROR);
433 Print(L"Error: %r %s\n", Status, where);
434 gST->ConOut->SetAttribute (gST->ConOut, ATTR_BASIC);
435 haveError = TRUE;
436
437 return TRUE;
438 }
439 #endif
440
441 //
442 // Graphics functions
443 //
444
445 VOID SwitchToGraphicsAndClear(VOID)
446 {
447 SwitchToGraphics();
448 if (GraphicsScreenDirty)
449 BltClearScreen(TRUE);
450 }
451
452 VOID BltClearScreen(BOOLEAN ShowBanner)
453 {
454 static EG_IMAGE *Banner = NULL;
455 EG_IMAGE *NewBanner = NULL;
456 INTN BannerPosX, BannerPosY;
457 EG_PIXEL Black = { 0x0, 0x0, 0x0, 0 };
458
459 if (ShowBanner && !(GlobalConfig.HideUIFlags & HIDEUI_FLAG_BANNER)) {
460 // load banner on first call
461 if (Banner == NULL) {
462 if (GlobalConfig.BannerFileName)
463 Banner = egLoadImage(SelfDir, GlobalConfig.BannerFileName, FALSE);
464 if (Banner == NULL)
465 Banner = egPrepareEmbeddedImage(&egemb_refind_banner, FALSE);
466 }
467
468 if (Banner) {
469 if (GlobalConfig.BannerScale == BANNER_FILLSCREEN) {
470 if ((Banner->Height != UGAHeight) || (Banner->Width != UGAWidth)) {
471 NewBanner = egScaleImage(Banner, UGAWidth, UGAHeight);
472 } // if
473 } else if ((Banner->Width > UGAWidth) || (Banner->Height > UGAHeight)) {
474 NewBanner = egCropImage(Banner, 0, 0, (Banner->Width > UGAWidth) ? UGAWidth : Banner->Width,
475 (Banner->Height > UGAHeight) ? UGAHeight : Banner->Height);
476 } // if/elseif
477 if (NewBanner) {
478 MyFreePool(Banner);
479 Banner = NewBanner;
480 }
481 MenuBackgroundPixel = Banner->PixelData[0];
482 } // if Banner exists
483
484 // clear and draw banner
485 if (GlobalConfig.ScreensaverTime != -1)
486 egClearScreen(&MenuBackgroundPixel);
487 else
488 egClearScreen(&Black);
489
490 if (Banner != NULL) {
491 BannerPosX = (Banner->Width < UGAWidth) ? ((UGAWidth - Banner->Width) / 2) : 0;
492 BannerPosY = (INTN) (ComputeRow0PosY() / 2) - (INTN) Banner->Height;
493 if (BannerPosY < 0)
494 BannerPosY = 0;
495 GlobalConfig.BannerBottomEdge = BannerPosY + Banner->Height;
496 if (GlobalConfig.ScreensaverTime != -1)
497 BltImage(Banner, (UINTN) BannerPosX, (UINTN) BannerPosY);
498 }
499
500 } else { // not showing banner
501 // clear to menu background color
502 egClearScreen(&MenuBackgroundPixel);
503 }
504
505 GraphicsScreenDirty = FALSE;
506 egFreeImage(GlobalConfig.ScreenBackground);
507 GlobalConfig.ScreenBackground = egCopyScreen();
508 } // VOID BltClearScreen()
509
510
511 VOID BltImage(IN EG_IMAGE *Image, IN UINTN XPos, IN UINTN YPos)
512 {
513 egDrawImage(Image, XPos, YPos);
514 GraphicsScreenDirty = TRUE;
515 }
516
517 VOID BltImageAlpha(IN EG_IMAGE *Image, IN UINTN XPos, IN UINTN YPos, IN EG_PIXEL *BackgroundPixel)
518 {
519 EG_IMAGE *CompImage;
520
521 // compose on background
522 CompImage = egCreateFilledImage(Image->Width, Image->Height, FALSE, BackgroundPixel);
523 egComposeImage(CompImage, Image, 0, 0);
524
525 // blit to screen and clean up
526 egDrawImage(CompImage, XPos, YPos);
527 egFreeImage(CompImage);
528 GraphicsScreenDirty = TRUE;
529 }
530
531 // VOID BltImageComposite(IN EG_IMAGE *BaseImage, IN EG_IMAGE *TopImage, IN UINTN XPos, IN UINTN YPos)
532 // {
533 // UINTN TotalWidth, TotalHeight, CompWidth, CompHeight, OffsetX, OffsetY;
534 // EG_IMAGE *CompImage;
535 //
536 // // initialize buffer with base image
537 // CompImage = egCopyImage(BaseImage);
538 // TotalWidth = BaseImage->Width;
539 // TotalHeight = BaseImage->Height;
540 //
541 // // place the top image
542 // CompWidth = TopImage->Width;
543 // if (CompWidth > TotalWidth)
544 // CompWidth = TotalWidth;
545 // OffsetX = (TotalWidth - CompWidth) >> 1;
546 // CompHeight = TopImage->Height;
547 // if (CompHeight > TotalHeight)
548 // CompHeight = TotalHeight;
549 // OffsetY = (TotalHeight - CompHeight) >> 1;
550 // egComposeImage(CompImage, TopImage, OffsetX, OffsetY);
551 //
552 // // blit to screen and clean up
553 // egDrawImage(CompImage, XPos, YPos);
554 // egFreeImage(CompImage);
555 // GraphicsScreenDirty = TRUE;
556 // }
557
558 VOID BltImageCompositeBadge(IN EG_IMAGE *BaseImage, IN EG_IMAGE *TopImage, IN EG_IMAGE *BadgeImage, IN UINTN XPos, IN UINTN YPos)
559 {
560 UINTN TotalWidth = 0, TotalHeight = 0, CompWidth = 0, CompHeight = 0, OffsetX = 0, OffsetY = 0;
561 EG_IMAGE *CompImage = NULL;
562
563 // initialize buffer with base image
564 if (BaseImage != NULL) {
565 CompImage = egCopyImage(BaseImage);
566 TotalWidth = BaseImage->Width;
567 TotalHeight = BaseImage->Height;
568 }
569
570 // place the top image
571 if ((TopImage != NULL) && (CompImage != NULL)) {
572 CompWidth = TopImage->Width;
573 if (CompWidth > TotalWidth)
574 CompWidth = TotalWidth;
575 OffsetX = (TotalWidth - CompWidth) >> 1;
576 CompHeight = TopImage->Height;
577 if (CompHeight > TotalHeight)
578 CompHeight = TotalHeight;
579 OffsetY = (TotalHeight - CompHeight) >> 1;
580 egComposeImage(CompImage, TopImage, OffsetX, OffsetY);
581 }
582
583 // place the badge image
584 if (BadgeImage != NULL && CompImage != NULL && (BadgeImage->Width + 8) < CompWidth && (BadgeImage->Height + 8) < CompHeight) {
585 OffsetX += CompWidth - 8 - BadgeImage->Width;
586 OffsetY += CompHeight - 8 - BadgeImage->Height;
587 egComposeImage(CompImage, BadgeImage, OffsetX, OffsetY);
588 }
589
590 // blit to screen and clean up
591 if (CompImage != NULL) {
592 if (CompImage->HasAlpha)
593 egDrawImageWithTransparency(CompImage, NULL, XPos, YPos, CompImage->Width, CompImage->Height);
594 else
595 egDrawImage(CompImage, XPos, YPos);
596 egFreeImage(CompImage);
597 GraphicsScreenDirty = TRUE;
598 }
599 }