OpenCore  1.0.4
OpenCore Bootloader
Loading...
Searching...
No Matches
CrScreenshotDxe.c
Go to the documentation of this file.
1/* CrScreenshotDxe.c
2
3Copyright (c) 2016, Nikolaj Schlej, All rights reserved.
4
5Redistribution and use in source and binary forms,
6with or without modification, are permitted provided that the following conditions are met:
7- Redistributions of source code must retain the above copyright notice,
8 this list of conditions and the following disclaimer.
9- Redistributions in binary form must reproduce the above copyright notice,
10 this list of conditions and the following disclaimer in the documentation
11 and/or other materials provided with the distribution.
12
13THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
17INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
18PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
21EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23*/
24
25#include <Uefi.h>
26#include <Library/BaseLib.h>
27#include <Library/DebugLib.h>
29#include <Library/OcPngLib.h>
30#include <Library/OcFileLib.h>
31#include <Library/OcMiscLib.h>
32#include <Library/PrintLib.h>
33#include <Library/TimerLib.h>
34#include <Library/UefiDriverEntryPoint.h>
35#include <Library/UefiBootServicesTableLib.h>
36#include <Library/UefiRuntimeServicesTableLib.h>
37
38#include <Protocol/AppleEvent.h>
39#include <Protocol/GraphicsOutput.h>
41#include <Protocol/SimpleTextInEx.h>
42#include <Protocol/SimpleFileSystem.h>
43
44//
45// Keyboard protocol arrival event.
46//
47STATIC EFI_EVENT mProtocolNotification;
48
49STATIC UINT64 mPreviousTime = 0;
50STATIC BOOLEAN mEnableMouseClick = FALSE;
51
52STATIC
53EFI_STATUS
54EFIAPI
56 IN UINT8 Red,
57 IN UINT8 Green,
58 IN UINT8 Blue
59 )
60{
61 //
62 // Determines the size of status square.
63 //
64 #define STATUS_SQUARE_SIDE 5
65
66 EFI_STATUS Status;
67 EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
68 EFI_GRAPHICS_OUTPUT_BLT_PIXEL Square[STATUS_SQUARE_SIDE * STATUS_SQUARE_SIDE];
69 EFI_GRAPHICS_OUTPUT_BLT_PIXEL Backup[STATUS_SQUARE_SIDE * STATUS_SQUARE_SIDE];
70 UINTN Index;
71
73 gST->ConsoleOutHandle,
75 (VOID **)&GraphicsOutput
76 );
77 if (EFI_ERROR (Status)) {
78 DEBUG ((DEBUG_INFO, "OCSCR: Graphics output protocol not found for status - %r\n", Status));
79 return EFI_UNSUPPORTED;
80 }
81
82 //
83 // Set square color.
84 //
85 for (Index = 0; Index < STATUS_SQUARE_SIDE * STATUS_SQUARE_SIDE; ++Index) {
86 Square[Index].Blue = Blue;
87 Square[Index].Green = Green;
88 Square[Index].Red = Red;
89 Square[Index].Reserved = 0x00;
90 }
91
92 //
93 // Backup current image.
94 //
95 GraphicsOutput->Blt (
96 GraphicsOutput,
97 Backup,
98 EfiBltVideoToBltBuffer,
99 0,
100 0,
101 0,
102 0,
105 0
106 );
107
108 //
109 // Draw the status square.
110 //
111 GraphicsOutput->Blt (
112 GraphicsOutput,
113 Square,
114 EfiBltBufferToVideo,
115 0,
116 0,
117 0,
118 0,
121 0
122 );
123
124 //
125 // Wait 500 ms.
126 //
127 gBS->Stall (500*1000);
128
129 //
130 // Restore the backup.
131 //
132 GraphicsOutput->Blt (
133 GraphicsOutput,
134 Backup,
135 EfiBltBufferToVideo,
136 0,
137 0,
138 0,
139 0,
142 0
143 );
144
145 return EFI_SUCCESS;
146}
147
148STATIC
149EFI_STATUS
150EFIAPI
152 IN EFI_KEY_DATA *KeyData
153 )
154{
155 EFI_FILE_PROTOCOL *Fs;
156 EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
157 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Image;
158 UINTN ImageSize;
159 VOID *PngFile;
160 UINTN PngFileSize;
161 EFI_STATUS Status;
162 UINT32 ScreenWidth;
163 UINT32 ScreenHeight;
164 CHAR16 FileName[16];
165 EFI_TIME Time;
166 UINTN Index;
167 UINT8 Temp;
168
169 //
170 // This is required to avoid assert (only noticeable on firmware compiled to
171 // assert) from gBS->RaiseTPL(TPL_CALLBACK) within the file system accesses
172 // below. Makes explicit what was happening anyway, which is that we're
173 // effectively lowering the TPL for a long running task, during a keyboard
174 // interrupt.
175 // REF: https://github.com/acidanthera/audk/blob/bcdcc4160d7460c46c08c9395aae81be44ef23a9/FatPkg/EnhancedFatDxe/Misc.c#L399
176 //
177 gBS->RestoreTPL (TPL_CALLBACK);
178
179 Status = OcFindWritableOcFileSystem (&Fs);
180 if (EFI_ERROR (Status)) {
181 DEBUG ((DEBUG_INFO, "OCSCR: Can't find writable FS - %r\n", Status));
182 ShowStatus (0xFF, 0xFF, 0x00);
183 return EFI_SUCCESS;
184 }
185
186 Status = OcHandleProtocolFallback (
187 gST->ConsoleOutHandle,
189 (VOID **)&GraphicsOutput
190 );
191 if (EFI_ERROR (Status)) {
192 DEBUG ((DEBUG_INFO, "OCSCR: Graphics output protocol not found for screen - %r\n", Status));
193 Fs->Close (Fs);
194 return EFI_SUCCESS;
195 }
196
197 //
198 // Set screen width, height and image size in pixels.
199 //
200 ScreenWidth = GraphicsOutput->Mode->Info->HorizontalResolution;
201 ScreenHeight = GraphicsOutput->Mode->Info->VerticalResolution;
202 ImageSize = (UINTN)ScreenWidth * ScreenHeight;
203
204 if (ImageSize == 0) {
205 DEBUG ((DEBUG_INFO, "OCSCR: Empty screen size\n"));
206 Fs->Close (Fs);
207 return EFI_SUCCESS;
208 }
209
210 //
211 // Get current time.
212 //
213 Status = gRT->GetTime (
214 &Time,
215 NULL
216 );
217 if (!EFI_ERROR (Status)) {
218 //
219 // Set file name to current day and time
220 //
221 UnicodeSPrint (
222 FileName,
223 sizeof (FileName),
224 L"%02d%02d%02d%02d.png",
225 Time.Day,
226 Time.Hour,
227 Time.Minute,
228 Time.Second
229 );
230 } else {
231 //
232 // Set file name to scrnshot.png
233 //
234 StrCpyS (FileName, sizeof (FileName), L"scrnshot.png");
235 }
236
237 //
238 // Allocate memory for screenshot.
239 //
240 Status = gBS->AllocatePool (
241 EfiBootServicesData,
242 ImageSize * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL),
243 (VOID **)&Image
244 );
245 if (EFI_ERROR (Status)) {
246 DEBUG ((DEBUG_INFO, "CRSCR: gBS->AllocatePool returned %r\n", Status));
247 ShowStatus (0xFF, 0x00, 0x00);
248 Fs->Close (Fs);
249 return EFI_SUCCESS;
250 }
251
252 //
253 // Take screenshot.
254 //
255 Status = GraphicsOutput->Blt (
256 GraphicsOutput,
257 Image,
258 EfiBltVideoToBltBuffer,
259 0,
260 0,
261 0,
262 0,
263 ScreenWidth,
264 ScreenHeight,
265 0
266 );
267 if (EFI_ERROR (Status)) {
268 DEBUG ((DEBUG_INFO, "CRSCR: GraphicsOutput->Blt returned %r\n", Status));
269 gBS->FreePool (Image);
270 ShowStatus (0xFF, 0x00, 0x00);
271 Fs->Close (Fs);
272 return EFI_SUCCESS;
273 }
274
275 //
276 // Convert BGR to RGBA with Alpha set to 0xFF.
277 //
278 for (Index = 0; Index < ImageSize; ++Index) {
279 Temp = Image[Index].Blue;
280 Image[Index].Blue = Image[Index].Red;
281 Image[Index].Red = Temp;
282 Image[Index].Reserved = 0xFF;
283 }
284
285 Status = OcEncodePng (
286 Image,
287 ScreenWidth,
288 ScreenHeight,
289 &PngFile,
290 &PngFileSize
291 );
292 gBS->FreePool (Image);
293 if (EFI_ERROR (Status)) {
294 DEBUG ((DEBUG_INFO, "CRSCR: OcEncodePng returned %r\n", Status));
295 ShowStatus (0xFF, 0x00, 0x00);
296 Fs->Close (Fs);
297 return EFI_SUCCESS;
298 }
299
300 //
301 // Write PNG image into the file.
302 //
303 Status = OcSetFileData (Fs, FileName, PngFile, (UINT32)PngFileSize);
304 gBS->FreePool (PngFile);
305 Fs->Close (Fs);
306 if (EFI_ERROR (Status)) {
307 DEBUG ((DEBUG_INFO, "CRSCR: OcEncodePng returned %r\n", Status));
308 ShowStatus (0xFF, 0x00, 0x00);
309 return EFI_SUCCESS;
310 }
311
312 //
313 // Show success.
314 //
315 ShowStatus (0x00, 0xFF, 0x00);
316
317 return EFI_SUCCESS;
318}
319
320STATIC
321VOID
322EFIAPI
324 IN APPLE_EVENT_INFORMATION *Information,
325 IN VOID *NotifyContext
326 )
327{
328 UINT64 CurrTime;
329
330 //
331 // Mark the context argument as used.
332 //
333 (VOID)NotifyContext;
334
335 //
336 // Ignore invalid information if it happened to arrive.
337 //
338 if (Information == NULL) {
339 return;
340 }
341
342 if ( ((Information->EventType & APPLE_EVENT_TYPE_KEY_DOWN) != 0)
343 && (Information->EventData.KeyData->AppleKeyCode == AppleHidUsbKbUsageKeyF10))
344 {
345 //
346 // Take a screenshot for F10.
347 //
348 TakeScreenshot (NULL);
349 }
350
351 if (mEnableMouseClick && ((Information->EventType & APPLE_EVENT_TYPE_MOUSE_CLICK) != 0)) {
352 //
353 // Debounce, prevents multiple detections from one click.
354 //
356 if (CurrTime - mPreviousTime > MS_TO_NANOSECONDS (1000ULL)) {
357 //
358 // Take a screenshot for mouse click.
359 //
360 TakeScreenshot (NULL);
362 }
363 }
364}
365
366STATIC
367EFI_STATUS
369 VOID
370 )
371{
372 EFI_STATUS Status;
373 UINTN HandleCount;
374 EFI_HANDLE *HandleBuffer;
375 UINTN Index;
376 EFI_KEY_DATA SimpleTextInExKeyStroke;
377 EFI_HANDLE SimpleTextInExHandle;
378 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx;
379 APPLE_EVENT_HANDLE AppleEventHandle;
380 APPLE_EVENT_PROTOCOL *AppleEvent;
381 BOOLEAN Installed;
382
383 Installed = FALSE;
384
385 //
386 // Locate compatible protocols, firstly try AppleEvent otherwise try SimpleTextInEx.
387 // This is because we want key swap to take precedence.
388 //
389 Status = gBS->LocateHandleBuffer (
390 ByProtocol,
392 NULL,
393 &HandleCount,
394 &HandleBuffer
395 );
396 if (!EFI_ERROR (Status)) {
397 for (Index = 0; Index < HandleCount; ++Index) {
398 Status = gBS->HandleProtocol (HandleBuffer[Index], &gAppleEventProtocolGuid, (VOID **)&AppleEvent);
399
400 if (EFI_ERROR (Status)) {
401 DEBUG ((
402 DEBUG_INFO,
403 "CRSCR: gBS->HandleProtocol[%u] AppleEvent returned %r\n",
404 (UINT32)Index,
405 Status
406 ));
407 continue;
408 }
409
410 if (AppleEvent->Revision < APPLE_EVENT_PROTOCOL_REVISION_MINIMUM) {
411 DEBUG ((
412 DEBUG_INFO,
413 "CRSCR: AppleEvent[%u] has outdated revision %u, expected %u\n",
414 (UINT32)Index,
415 (UINT32)AppleEvent->Revision,
417 ));
418 continue;
419 }
420
421 //
422 // Register key handler, which will later determine the combination.
423 //
424 Status = AppleEvent->RegisterHandler (
427 &AppleEventHandle,
428 NULL
429 );
430 if (!EFI_ERROR (Status)) {
431 Installed = TRUE;
432 }
433
434 DEBUG ((
435 DEBUG_INFO,
436 "CRSCR: AppleEvent->RegisterHandler[%u] returned %r\n",
437 (UINT32)Index,
438 Status
439 ));
440 }
441
442 gBS->FreePool (HandleBuffer);
443 } else {
444 DEBUG ((
445 DEBUG_INFO,
446 "CRSCR: gBS->LocateHandleBuffer AppleEvent returned %r\n",
447 Status
448 ));
449 }
450
451 if (!Installed) {
452 Status = gBS->LocateHandleBuffer (
453 ByProtocol,
454 &gEfiSimpleTextInputExProtocolGuid,
455 NULL,
456 &HandleCount,
457 &HandleBuffer
458 );
459 if (EFI_ERROR (Status)) {
460 DEBUG ((
461 DEBUG_INFO,
462 "CRSCR: gBS->LocateHandleBuffer SimpleTextInEx returned %r\n",
463 Status
464 ));
465 return EFI_UNSUPPORTED;
466 }
467
468 //
469 // Register key notification function for F10.
470 //
471 SimpleTextInExKeyStroke.Key.ScanCode = SCAN_F10;
472 SimpleTextInExKeyStroke.Key.UnicodeChar = 0;
473 SimpleTextInExKeyStroke.KeyState.KeyShiftState = 0;
474 SimpleTextInExKeyStroke.KeyState.KeyToggleState = 0;
475
476 for (Index = 0; Index < HandleCount; ++Index) {
477 Status = gBS->HandleProtocol (
478 HandleBuffer[Index],
479 &gEfiSimpleTextInputExProtocolGuid,
480 (VOID **)&SimpleTextInEx
481 );
482
483 if (EFI_ERROR (Status)) {
484 DEBUG ((
485 DEBUG_INFO,
486 "CRSCR: gBS->HandleProtocol[%u] SimpleTextInputEx returned %r\n",
487 (UINT32)Index,
488 Status
489 ));
490 continue;
491 }
492
493 Status = SimpleTextInEx->RegisterKeyNotify (
494 SimpleTextInEx,
495 &SimpleTextInExKeyStroke,
497 &SimpleTextInExHandle
498 );
499
500 if (!EFI_ERROR (Status)) {
501 Installed = TRUE;
502 }
503
504 DEBUG ((
505 DEBUG_INFO,
506 "CRSCR: SimpleTextInEx->RegisterKeyNotify[%u] returned %r\n",
507 (UINT32)Index,
508 Status
509 ));
510 }
511
512 gBS->FreePool (HandleBuffer);
513 }
514
515 return EFI_SUCCESS;
516}
517
518VOID
519EFIAPI
521 IN EFI_EVENT Event,
522 IN VOID *Context
523 )
524{
526 gBS->CloseEvent (mProtocolNotification);
527}
528
529EFI_STATUS
530EFIAPI
532 IN EFI_HANDLE ImageHandle,
533 IN EFI_SYSTEM_TABLE *SystemTable
534 )
535{
536 EFI_STATUS Status;
537 VOID *Registration;
538 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
539 OC_FLEX_ARRAY *ParsedLoadOptions;
540
541 //
542 // Parse optional driver params.
543 //
544 Status = gBS->HandleProtocol (
545 ImageHandle,
547 (VOID **)&LoadedImage
548 );
549
550 if (EFI_ERROR (Status)) {
551 return Status;
552 }
553
554 Status = OcParseLoadOptions (LoadedImage, &ParsedLoadOptions);
555 if (!EFI_ERROR (Status)) {
556 mEnableMouseClick = OcHasParsedVar (ParsedLoadOptions, L"--enable-mouse-click", OcStringFormatUnicode);
557
558 OcFlexArrayFree (&ParsedLoadOptions);
559 } else if (Status != EFI_NOT_FOUND) {
560 return Status;
561 }
562
563 Status = InstallKeyHandler ();
564 if (EFI_ERROR (Status)) {
565 Status = gBS->CreateEvent (
566 EVT_NOTIFY_SIGNAL,
567 TPL_CALLBACK,
569 NULL,
571 );
572
573 if (!EFI_ERROR (Status)) {
574 gBS->RegisterProtocolNotify (
575 &gEfiSimpleTextInputExProtocolGuid,
577 &Registration
578 );
579
580 gBS->RegisterProtocolNotify (
583 &Registration
584 );
585 } else {
586 DEBUG ((
587 DEBUG_INFO,
588 "CRSCR: Cannot create event for keyboard protocol arrivals %r\n",
589 Status
590 ));
591 }
592 }
593
594 return EFI_SUCCESS;
595}
#define APPLE_EVENT_TYPE_NONE
Definition AppleEvent.h:20
VOID * APPLE_EVENT_HANDLE
Definition AppleEvent.h:95
#define APPLE_EVENT_TYPE_MOUSE_CLICK
Definition AppleEvent.h:24
#define APPLE_EVENT_TYPE_KEY_DOWN
Definition AppleEvent.h:29
EFI_GUID gAppleEventProtocolGuid
#define APPLE_EVENT_PROTOCOL_REVISION_MINIMUM
Definition AppleEvent.h:103
@ AppleHidUsbKbUsageKeyF10
Definition AppleHid.h:170
STATIC EFI_STATUS EFIAPI ShowStatus(IN UINT8 Red, IN UINT8 Green, IN UINT8 Blue)
VOID EFIAPI InstallKeyHandlerWrapper(IN EFI_EVENT Event, IN VOID *Context)
STATIC EFI_STATUS EFIAPI TakeScreenshot(IN EFI_KEY_DATA *KeyData)
STATIC VOID EFIAPI AppleEventKeyHandler(IN APPLE_EVENT_INFORMATION *Information, IN VOID *NotifyContext)
#define STATUS_SQUARE_SIDE
EFI_STATUS EFIAPI CrScreenshotDxeEntry(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
STATIC EFI_EVENT mProtocolNotification
STATIC UINT64 mPreviousTime
STATIC EFI_STATUS InstallKeyHandler(VOID)
STATIC BOOLEAN mEnableMouseClick
EFI_STATUS OcParseLoadOptions(IN CONST EFI_LOADED_IMAGE_PROTOCOL *LoadedImage, OUT OC_FLEX_ARRAY **ParsedVars)
BOOLEAN OcHasParsedVar(IN CONST OC_FLEX_ARRAY *ParsedVars, IN CONST VOID *Name, IN CONST OC_STRING_FORMAT StringFormat)
EFI_SYSTEM_TABLE * gST
EFI_BOOT_SERVICES * gBS
EFI_STATUS OcSetFileData(IN EFI_FILE_PROTOCOL *WritableFs OPTIONAL, IN CONST CHAR16 *FileName, IN CONST VOID *Buffer, IN UINT32 Size)
EFI_STATUS OcFindWritableOcFileSystem(OUT EFI_FILE_PROTOCOL **FileSystem)
VOID OcFlexArrayFree(IN OUT OC_FLEX_ARRAY **FlexArray)
#define MS_TO_NANOSECONDS(x)
Definition OcMiscLib.h:37
EFI_STATUS OcHandleProtocolFallback(IN EFI_HANDLE Handle, IN EFI_GUID *Protocol, OUT VOID **Interface)
EFI_STATUS OcEncodePng(IN VOID *RawData, IN UINT32 Width, IN UINT32 Height, OUT VOID **Buffer, OUT UINTN *BufferSize)
Definition OcPng.c:118
@ OcStringFormatUnicode
Definition OcStringLib.h:51
EFI_RUNTIME_SERVICES * gRT
EFI_GUID gEfiGraphicsOutputProtocolGuid
EFI_GUID gEfiLoadedImageProtocolGuid
UINT64 EFIAPI GetTimeInNanoSecond(IN UINT64 Ticks)
UINT64 EFIAPI GetPerformanceCounter(VOID)