/** @file * * Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved. * Copyright (c) 2018, Andrei Warkentin * * SPDX-License-Identifier: BSD-2-Clause-Patent * **/ /* * Loosely based on CrScreenShotDxe (https://github.com/LongSoft/CrScreenshotDxe). * * Copyright (c) 2016, Nikolaj Schlej, All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "DisplayDxe.h" #include #include #include #include /* * ShowStatus defs. */ #define STATUS_SQUARE_SIDE 5 #define STATUS_YELLOW 0xff, 0xff, 0x00 #define STATUS_GREEN 0x00, 0xff, 0x00 #define STATUS_BLUE 0x00, 0x00, 0xff #define STATUS_RED 0xff, 0x00, 0x00 EFI_STATUS ShowStatus ( IN EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput, IN UINT8 Red, IN UINT8 Green, IN UINT8 Blue ) { UINTN Index; EFI_GRAPHICS_OUTPUT_BLT_PIXEL Square[STATUS_SQUARE_SIDE * STATUS_SQUARE_SIDE]; EFI_GRAPHICS_OUTPUT_BLT_PIXEL Backup[STATUS_SQUARE_SIDE * STATUS_SQUARE_SIDE]; for (Index = 0; Index < STATUS_SQUARE_SIDE * STATUS_SQUARE_SIDE; Index++) { Square[Index].Blue = Blue; Square[Index].Green = Green; Square[Index].Red = Red; Square[Index].Reserved = 0x00; } // Backup current image. GraphicsOutput->Blt (GraphicsOutput, Backup, EfiBltVideoToBltBuffer, 0, 0, 0, 0, STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0); // Draw the status square. GraphicsOutput->Blt (GraphicsOutput, Square, EfiBltBufferToVideo, 0, 0, 0, 0, STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0); // Wait 500ms. gBS->Stall (500 * 1000); // Restore the backup. GraphicsOutput->Blt (GraphicsOutput, Backup, EfiBltBufferToVideo, 0, 0, 0, 0, STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0); return EFI_SUCCESS; } STATIC EFI_STATUS FindWritableFs ( OUT EFI_FILE_PROTOCOL **WritableFs ) { EFI_FILE_PROTOCOL *Fs = NULL; EFI_HANDLE *HandleBuffer = NULL; UINTN HandleCount; UINTN Index; EFI_STATUS Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &HandleCount, &HandleBuffer); if (EFI_ERROR (Status)) { return Status; } for (Index = 0; Index < HandleCount; Index++) { EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFs = NULL; EFI_FILE_PROTOCOL *File = NULL; Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiSimpleFileSystemProtocolGuid, (VOID**)&SimpleFs); if (EFI_ERROR (Status)) { ASSERT_EFI_ERROR (Status); /* * Not supposed to happen. */ continue; } Status = SimpleFs->OpenVolume (SimpleFs, &Fs); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a OpenVolume[%u] returned %r\n", __FUNCTION__, Index, Status)); continue; } Status = Fs->Open (Fs, &File, L"--------.---", EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a Open[%u] returned %r\n", __FUNCTION__, Index, Status)); continue; } /* * Okay, we have a writable filesystem! */ Fs->Delete (File); *WritableFs = Fs; Status = EFI_SUCCESS; break; } if (HandleBuffer) { FreePool (HandleBuffer); } return Status; } STATIC VOID TakeScreenshot ( VOID ) { VOID *BmpImage = NULL; EFI_FILE_PROTOCOL *Fs = NULL; EFI_FILE_PROTOCOL *File = NULL; EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = &gDisplayProto; EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Image = NULL; EFI_STATUS Status; CHAR16 FileName[8 + 1 + 3 + 1]; UINT32 ScreenWidth; UINT32 ScreenHeight; UINTN ImageSize; UINTN BmpSize; UINTN Index; EFI_TIME Time; Status = FindWritableFs (&Fs); if (EFI_ERROR (Status)) { ShowStatus (GraphicsOutput, STATUS_YELLOW); } ScreenWidth = GraphicsOutput->Mode->Info->HorizontalResolution; ScreenHeight = GraphicsOutput->Mode->Info->VerticalResolution; ImageSize = ScreenWidth * ScreenHeight; Status = gRT->GetTime (&Time, NULL); if (!EFI_ERROR (Status)) { UnicodeSPrint (FileName, sizeof (FileName), L"%02d%02d%02d%02d.bmp", Time.Day, Time.Hour, Time.Minute, Time.Second); } else { UnicodeSPrint (FileName, sizeof (FileName), L"scrnshot.bmp"); } Image = AllocatePool (ImageSize * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); if (Image == NULL) { Status = EFI_OUT_OF_RESOURCES; ShowStatus (GraphicsOutput, STATUS_RED); goto Done; } Status = GraphicsOutput->Blt (GraphicsOutput, Image, EfiBltVideoToBltBuffer, 0, 0, 0, 0, ScreenWidth, ScreenHeight, 0); if (EFI_ERROR (Status)) { ShowStatus (GraphicsOutput, STATUS_RED); goto Done; } for (Index = 0; Index < ImageSize; Index++) { if (Image[Index].Red != 0x00 || Image[Index].Green != 0x00 || Image[Index].Blue != 0x00) { break; } } if (Index == ImageSize) { ShowStatus (GraphicsOutput, STATUS_BLUE); goto Done; } Status = TranslateGopBltToBmp (Image, ScreenHeight, ScreenWidth, &BmpImage, (UINT32*)&BmpSize); if (EFI_ERROR (Status)) { ShowStatus (GraphicsOutput, STATUS_RED); goto Done; } Status = Fs->Open (Fs, &File, FileName, EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0); if (EFI_ERROR (Status)) { ShowStatus (GraphicsOutput, STATUS_RED); goto Done; } Status = File->Write (File, &BmpSize, BmpImage); File->Close (File); if (EFI_ERROR (Status)) { ShowStatus (GraphicsOutput, STATUS_RED); goto Done; } ShowStatus (GraphicsOutput, STATUS_GREEN); Done: if (BmpImage != NULL) { FreePool (BmpImage); } if (Image != NULL) { FreePool (Image); } } STATIC EFI_STATUS EFIAPI ScreenshotKeyHandler ( IN EFI_KEY_DATA *KeyData ) { TakeScreenshot (); return EFI_SUCCESS; } STATIC EFI_STATUS ProcessScreenshotHandler ( IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx ) { EFI_STATUS Status; EFI_HANDLE Handle; EFI_KEY_DATA ScreenshotKey; /* * LCtrl+LAlt+F12 */ ScreenshotKey.Key.ScanCode = SCAN_F12; ScreenshotKey.Key.UnicodeChar = 0; ScreenshotKey.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID | EFI_LEFT_CONTROL_PRESSED | EFI_LEFT_ALT_PRESSED; ScreenshotKey.KeyState.KeyToggleState = 0; Status = SimpleTextInEx->RegisterKeyNotify ( SimpleTextInEx, &ScreenshotKey, ScreenshotKeyHandler, &Handle ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: couldn't register key notification: %r\n", __FUNCTION__, Status)); return Status; } return EFI_SUCCESS; } STATIC VOID ProcessScreenshotHandlers ( VOID ) { UINTN Index; EFI_STATUS Status; UINTN HandleCount; EFI_HANDLE *HandleBuffer; EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx; Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleTextInputExProtocolGuid, NULL, &HandleCount, &HandleBuffer); if (EFI_ERROR (Status)) { return; } for (Index = 0; Index < HandleCount; Index++) { Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiSimpleTextInputExProtocolGuid, (VOID**)&SimpleTextInEx); if (EFI_ERROR (Status)) { ASSERT_EFI_ERROR (Status); /* * Not supposed to happen. */ continue; } Status = ProcessScreenshotHandler (SimpleTextInEx); if (EFI_ERROR (Status)) { continue; } } } STATIC VOID EFIAPI OnTextInExInstall ( IN EFI_EVENT Event, IN VOID *Context ) { ProcessScreenshotHandlers (); } VOID RegisterScreenshotHandlers ( VOID ) { EFI_STATUS Status; EFI_EVENT TextInExInstallEvent; VOID *TextInExInstallRegistration; ProcessScreenshotHandlers (); Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, OnTextInExInstall, NULL, &TextInExInstallEvent); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: couldn't create protocol install event: %r\n", __FUNCTION__, Status)); return; } Status = gBS->RegisterProtocolNotify (&gEfiSimpleTextInputExProtocolGuid, TextInExInstallEvent, &TextInExInstallRegistration); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: couldn't register protocol install notify: %r\n", __FUNCTION__, Status)); return; } }