]> code.delx.au - refind/blob - libeg/screen.c
Version 0.3.0 (beta) release. Adds "resolution" option to refind.conf.
[refind] / libeg / screen.c
1 /*
2 * libeg/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 "libegint.h"
38 #include "../refind/screen.h"
39 #include "refit_call_wrapper.h"
40
41 #include <efiUgaDraw.h>
42 /* #include <efiGraphicsOutput.h> */
43 #include <efiConsoleControl.h>
44
45 // Console defines and variables
46
47 static EFI_GUID ConsoleControlProtocolGuid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
48 static EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
49
50 static EFI_GUID UgaDrawProtocolGuid = EFI_UGA_DRAW_PROTOCOL_GUID;
51 static EFI_UGA_DRAW_PROTOCOL *UgaDraw = NULL;
52
53 static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
54 static EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;
55
56 static BOOLEAN egHasGraphics = FALSE;
57 static UINTN egScreenWidth = 800;
58 static UINTN egScreenHeight = 600;
59
60 //
61 // Screen handling
62 //
63
64 VOID egInitScreen(VOID)
65 {
66 EFI_STATUS Status = EFI_SUCCESS;
67 UINT32 UGAWidth, UGAHeight, UGADepth, UGARefreshRate;
68
69 // get protocols
70 Status = LibLocateProtocol(&ConsoleControlProtocolGuid, (VOID **) &ConsoleControl);
71 if (EFI_ERROR(Status))
72 ConsoleControl = NULL;
73
74 Status = LibLocateProtocol(&UgaDrawProtocolGuid, (VOID **) &UgaDraw);
75 if (EFI_ERROR(Status))
76 UgaDraw = NULL;
77
78 Status = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **) &GraphicsOutput);
79 if (EFI_ERROR(Status))
80 GraphicsOutput = NULL;
81
82 // get screen size
83 egHasGraphics = FALSE;
84 if (GraphicsOutput != NULL) {
85 egScreenWidth = GraphicsOutput->Mode->Info->HorizontalResolution;
86 egScreenHeight = GraphicsOutput->Mode->Info->VerticalResolution;
87 egHasGraphics = TRUE;
88 } else if (UgaDraw != NULL) {
89 Status = refit_call5_wrapper(UgaDraw->GetMode, UgaDraw, &UGAWidth, &UGAHeight, &UGADepth, &UGARefreshRate);
90 if (EFI_ERROR(Status)) {
91 UgaDraw = NULL; // graphics not available
92 } else {
93 egScreenWidth = UGAWidth;
94 egScreenHeight = UGAHeight;
95 egHasGraphics = TRUE;
96 }
97 }
98 }
99
100 // Sets the screen resolution to the specified value, if possible.
101 // If the specified value is not valid, displays a warning with the valid
102 // modes on UEFI systems, or silently fails on EFI 1.x systems. Note that
103 // this function attempts to set ANY screen resolution, even 0x0 or
104 // ridiculously large values.
105 // Returns TRUE if successful, FALSE if not.
106 BOOLEAN egSetScreenSize(IN UINTN ScreenWidth, IN UINTN ScreenHeight) {
107 EFI_STATUS Status = EFI_SUCCESS;
108 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
109 UINT32 ModeNum = 0;
110 UINTN Size;
111 BOOLEAN ModeSet = FALSE;
112 UINT32 UGAWidth, UGAHeight, UGADepth, UGARefreshRate;
113
114 if (GraphicsOutput != NULL) { // GOP mode (UEFI)
115 // Do a loop through the modes to see if the specified one is available;
116 // and if so, switch to it....
117 while ((Status == EFI_SUCCESS) && (!ModeSet)) {
118 Status = refit_call4_wrapper(GraphicsOutput->QueryMode, GraphicsOutput, ModeNum, &Size, &Info);
119 if ((Status == EFI_SUCCESS) && (Size >= sizeof(*Info)) &&
120 (Info->HorizontalResolution == ScreenWidth) && (Info->VerticalResolution == ScreenHeight)) {
121 Status = refit_call2_wrapper(GraphicsOutput->SetMode, GraphicsOutput, ModeNum);
122 ModeSet = (Status == EFI_SUCCESS);
123 } // if
124 ModeNum++;
125 } // while()
126
127 if (ModeSet) {
128 egScreenWidth = ScreenWidth;
129 egScreenHeight = ScreenHeight;
130 } else {// If unsuccessful, display an error message for the user....
131 Print(L"Error setting mode %d x %d; using default mode!\nAvailable modes are:\n", ScreenWidth, ScreenHeight);
132 ModeNum = 0;
133 Status = EFI_SUCCESS;
134 while (Status == EFI_SUCCESS) {
135 Status = refit_call4_wrapper(GraphicsOutput->QueryMode, GraphicsOutput, ModeNum, &Size, &Info);
136 if ((Status == EFI_SUCCESS) && (Size >= sizeof(*Info))) {
137 Print(L"Mode %d: %d x %d\n", ModeNum, Info->HorizontalResolution, Info->VerticalResolution);
138 } // else
139 ModeNum++;
140 } // while()
141 PauseForKey();
142 } // if()
143 } else if (UgaDraw != NULL) { // UGA mode (EFI 1.x)
144 // Try to use current color depth & refresh rate for new mode. Maybe not the best choice
145 // in all cases, but I don't know how to probe for alternatives....
146 Status = refit_call5_wrapper(UgaDraw->GetMode, UgaDraw, &UGAWidth, &UGAHeight, &UGADepth, &UGARefreshRate);
147 Status = refit_call5_wrapper(UgaDraw->SetMode, UgaDraw, ScreenWidth, ScreenHeight, UGADepth, UGARefreshRate);
148 if (Status == EFI_SUCCESS) {
149 egScreenWidth = ScreenWidth;
150 egScreenHeight = ScreenHeight;
151 ModeSet = TRUE;
152 } else {
153 // TODO: Find a list of supported modes and display it.
154 // NOTE: Below doesn't actually appear unless we explicitly switch to text mode.
155 // This is just a placeholder until something better can be done....
156 Print(L"Error setting mode %d x %d; unsupported mode!\n");
157 } // if/else
158 } // if/else if
159 return (ModeSet);
160 } // BOOLEAN egSetScreenSize()
161
162 VOID egGetScreenSize(OUT UINTN *ScreenWidth, OUT UINTN *ScreenHeight)
163 {
164 if (ScreenWidth != NULL)
165 *ScreenWidth = egScreenWidth;
166 if (ScreenHeight != NULL)
167 *ScreenHeight = egScreenHeight;
168 }
169
170 CHAR16 * egScreenDescription(VOID)
171 {
172 if (egHasGraphics) {
173 if (GraphicsOutput != NULL) {
174 return PoolPrint(L"Graphics Output (UEFI), %dx%d",
175 egScreenWidth, egScreenHeight);
176 } else if (UgaDraw != NULL) {
177 return PoolPrint(L"UGA Draw (EFI 1.10), %dx%d",
178 egScreenWidth, egScreenHeight);
179 } else {
180 return L"Internal Error";
181 }
182 } else {
183 return L"Text Console";
184 }
185 }
186
187 BOOLEAN egHasGraphicsMode(VOID)
188 {
189 return egHasGraphics;
190 }
191
192 BOOLEAN egIsGraphicsModeEnabled(VOID)
193 {
194 EFI_CONSOLE_CONTROL_SCREEN_MODE CurrentMode;
195
196 if (ConsoleControl != NULL) {
197 refit_call4_wrapper(ConsoleControl->GetMode, ConsoleControl, &CurrentMode, NULL, NULL);
198 return (CurrentMode == EfiConsoleControlScreenGraphics) ? TRUE : FALSE;
199 }
200
201 return FALSE;
202 }
203
204 VOID egSetGraphicsModeEnabled(IN BOOLEAN Enable)
205 {
206 EFI_CONSOLE_CONTROL_SCREEN_MODE CurrentMode;
207 EFI_CONSOLE_CONTROL_SCREEN_MODE NewMode;
208
209 if (ConsoleControl != NULL) {
210 refit_call4_wrapper(ConsoleControl->GetMode, ConsoleControl, &CurrentMode, NULL, NULL);
211
212 NewMode = Enable ? EfiConsoleControlScreenGraphics
213 : EfiConsoleControlScreenText;
214 if (CurrentMode != NewMode)
215 refit_call2_wrapper(ConsoleControl->SetMode, ConsoleControl, NewMode);
216 }
217 }
218
219 //
220 // Drawing to the screen
221 //
222
223 VOID egClearScreen(IN EG_PIXEL *Color)
224 {
225 EFI_UGA_PIXEL FillColor;
226
227 if (!egHasGraphics)
228 return;
229
230 FillColor.Red = Color->r;
231 FillColor.Green = Color->g;
232 FillColor.Blue = Color->b;
233 FillColor.Reserved = 0;
234
235 if (GraphicsOutput != NULL) {
236 // EFI_GRAPHICS_OUTPUT_BLT_PIXEL and EFI_UGA_PIXEL have the same
237 // layout, and the header from TianoCore actually defines them
238 // to be the same type.
239 refit_call10_wrapper(GraphicsOutput->Blt, GraphicsOutput, (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)&FillColor, EfiBltVideoFill,
240 0, 0, 0, 0, egScreenWidth, egScreenHeight, 0);
241 } else if (UgaDraw != NULL) {
242 refit_call10_wrapper(UgaDraw->Blt, UgaDraw, &FillColor, EfiUgaVideoFill,
243 0, 0, 0, 0, egScreenWidth, egScreenHeight, 0);
244 }
245 }
246
247 VOID egDrawImage(IN EG_IMAGE *Image, IN UINTN ScreenPosX, IN UINTN ScreenPosY)
248 {
249 if (!egHasGraphics)
250 return;
251
252 if (Image->HasAlpha) {
253 Image->HasAlpha = FALSE;
254 egSetPlane(PLPTR(Image, a), 0, Image->Width * Image->Height);
255 }
256
257 if (GraphicsOutput != NULL) {
258 refit_call10_wrapper(GraphicsOutput->Blt, GraphicsOutput, (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)Image->PixelData, EfiBltBufferToVideo,
259 0, 0, ScreenPosX, ScreenPosY, Image->Width, Image->Height, 0);
260 } else if (UgaDraw != NULL) {
261 refit_call10_wrapper(UgaDraw->Blt, UgaDraw, (EFI_UGA_PIXEL *)Image->PixelData, EfiUgaBltBufferToVideo,
262 0, 0, ScreenPosX, ScreenPosY, Image->Width, Image->Height, 0);
263 }
264 }
265
266 VOID egDrawImageArea(IN EG_IMAGE *Image,
267 IN UINTN AreaPosX, IN UINTN AreaPosY,
268 IN UINTN AreaWidth, IN UINTN AreaHeight,
269 IN UINTN ScreenPosX, IN UINTN ScreenPosY)
270 {
271 if (!egHasGraphics)
272 return;
273
274 egRestrictImageArea(Image, AreaPosX, AreaPosY, &AreaWidth, &AreaHeight);
275 if (AreaWidth == 0)
276 return;
277
278 if (Image->HasAlpha) {
279 Image->HasAlpha = FALSE;
280 egSetPlane(PLPTR(Image, a), 0, Image->Width * Image->Height);
281 }
282
283 if (GraphicsOutput != NULL) {
284 refit_call10_wrapper(GraphicsOutput->Blt, GraphicsOutput, (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)Image->PixelData, EfiBltBufferToVideo,
285 AreaPosX, AreaPosY, ScreenPosX, ScreenPosY, AreaWidth, AreaHeight, Image->Width * 4);
286 } else if (UgaDraw != NULL) {
287 refit_call10_wrapper(UgaDraw->Blt, UgaDraw, (EFI_UGA_PIXEL *)Image->PixelData, EfiUgaBltBufferToVideo,
288 AreaPosX, AreaPosY, ScreenPosX, ScreenPosY, AreaWidth, AreaHeight, Image->Width * 4);
289 }
290 }
291
292 //
293 // Make a screenshot
294 //
295
296 VOID egScreenShot(VOID)
297 {
298 EFI_STATUS Status;
299 EG_IMAGE *Image;
300 UINT8 *FileData;
301 UINTN FileDataLength;
302 UINTN Index;
303
304 if (!egHasGraphics)
305 return;
306
307 // allocate a buffer for the whole screen
308 Image = egCreateImage(egScreenWidth, egScreenHeight, FALSE);
309 if (Image == NULL) {
310 Print(L"Error egCreateImage returned NULL\n");
311 goto bailout_wait;
312 }
313
314 // get full screen image
315 if (GraphicsOutput != NULL) {
316 refit_call10_wrapper(GraphicsOutput->Blt, GraphicsOutput, (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)Image->PixelData, EfiBltVideoToBltBuffer,
317 0, 0, 0, 0, Image->Width, Image->Height, 0);
318 } else if (UgaDraw != NULL) {
319 refit_call10_wrapper(UgaDraw->Blt, UgaDraw, (EFI_UGA_PIXEL *)Image->PixelData, EfiUgaVideoToBltBuffer,
320 0, 0, 0, 0, Image->Width, Image->Height, 0);
321 }
322
323 // encode as BMP
324 egEncodeBMP(Image, &FileData, &FileDataLength);
325 egFreeImage(Image);
326 if (FileData == NULL) {
327 Print(L"Error egEncodeBMP returned NULL\n");
328 goto bailout_wait;
329 }
330
331 // save to file on the ESP
332 Status = egSaveFile(NULL, L"screenshot.bmp", FileData, FileDataLength);
333 FreePool(FileData);
334 if (EFI_ERROR(Status)) {
335 Print(L"Error egSaveFile: %x\n", Status);
336 goto bailout_wait;
337 }
338
339 return;
340
341 // DEBUG: switch to text mode
342 bailout_wait:
343 egSetGraphicsModeEnabled(FALSE);
344 refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &Index);
345 }
346
347 /* EOF */