Translate

Archives

Two UEFI Screenshot Utilities Added To UEFI-Utilities-2019

I have had a very rudimentary UEFI utility for taking screenshots from within the UEFI shell since circa 2012. Recently, I decided to update and polish this utility. The end result is two seperate screenshot capturing utilities – a UEFI shell command line utility (ScreenShot.efi) and a UEFI driver-based screenshot utility (ScreenshotDriver.efi). These are both available on GitHub in my UEFI-Utilities-2019 repository.

In this post, I describe the key functions (AKA components) of these two utilities. Note that this post loads slowly because of the inclusion of some fullscreen BMP images generated by the utilities – so apologies in advance.

This is the key snippet of code in both utilities:

Status = ShowStatus( Gop, Yellow, StartX, StartY, Width, Height ); 
gBS->Stall(500*1000);
Status = SnapShot( Gop , StartX, StartY, Width, Height );
if (EFI_ERROR(Status)) {
    Status = ShowStatus( Gop, Red, StartX, StartY, Width, Height ); 
} else {
    Status = ShowStatus( Gop, Lime, StartX, StartY, Width, Height ); 
}


This code displays 4 small 10 pixel squares (size is configurable) designating the top left, top right, bottom left and bottom right of the rectangular area of the screen which will be snapshot-ed. Initially the squares are yellow. They change to green if the screenshot is successfully saved to a file, otherwise they change to red to denote that an error occurred and no screenshot was saved.

Here is the source code for ShowStatus function:

EFI_STATUS
ShowStatus( EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop,
            UINT8 Color, 
            UINTN StartX, 
            UINTN StartY,
            UINTN Width, 
            UINTN Height ) 
{
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL Square[STATUS_SQUARE_SIDE * STATUS_SQUARE_SIDE];
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL BackupTL[STATUS_SQUARE_SIDE * STATUS_SQUARE_SIDE];
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL BackupTR[STATUS_SQUARE_SIDE * STATUS_SQUARE_SIDE];
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL BackupBL[STATUS_SQUARE_SIDE * STATUS_SQUARE_SIDE];
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL BackupBR[STATUS_SQUARE_SIDE * STATUS_SQUARE_SIDE];
    
    // set square color
    for (UINTN i = 0 ; i < STATUS_SQUARE_SIDE * STATUS_SQUARE_SIDE; i++) {
        Square[i].Blue = EfiGraphicsColors[Color].Blue;
        Square[i].Green = EfiGraphicsColors[Color].Green;
        Square[i].Red = EfiGraphicsColors[Color].Red;
        Square[i].Reserved = 0x00;
    }
    
    Width = Width - STATUS_SQUARE_SIDE -1;
    Height = Height - STATUS_SQUARE_SIDE -1;
    
    // backup current squares
    Gop->Blt(Gop, BackupTL, EfiBltVideoToBltBuffer, StartX, StartY, 0, 0, STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0);
    Gop->Blt(Gop, BackupTR, EfiBltVideoToBltBuffer, StartX + Width, StartY, 0, 0, STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0);
    Gop->Blt(Gop, BackupBL, EfiBltVideoToBltBuffer, StartX, StartY + Height, 0, 0, STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0);
    Gop->Blt(Gop, BackupBR, EfiBltVideoToBltBuffer, StartX + Width, StartY + Height, 0, 0, STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0);
        
    // draw status square
    Gop->Blt(Gop, Square, EfiBltBufferToVideo, 0, 0, StartX, StartY, STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0);
    Gop->Blt(Gop, Square, EfiBltBufferToVideo, 0, 0, StartX + Width, StartY, STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0);
    Gop->Blt(Gop, Square, EfiBltBufferToVideo, 0, 0, StartX, StartY + Height, STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0);
    Gop->Blt(Gop, Square, EfiBltBufferToVideo, 0, 0, StartX + Width, StartY + Height, STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0);
        
    // wait 500ms
    gBS->Stall(500*1000);
        
    // restore squares from backups
    Gop->Blt(Gop, BackupTL, EfiBltBufferToVideo, 0, 0, StartX, StartY, STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0);
    Gop->Blt(Gop, BackupTR, EfiBltBufferToVideo, 0, 0, StartX + Width, StartY, STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0);
    Gop->Blt(Gop, BackupBL, EfiBltBufferToVideo, 0, 0, StartX, StartY + Height, STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0);
    Gop->Blt(Gop, BackupBR, EfiBltBufferToVideo, 0, 0, StartX + Width, StartY + Height, STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0);
    
    return EFI_SUCCESS;
}


As you can see, it makes extensive use of the GOP (Graphic Output Protocol) blt (block transfer) functionality to copy data between the video framebuffer and allocated temporary memory using the EfiBltVideoToBltBuffer and EfiBltBufferToVideo parameters.

Here is the source code for the function that takes the actual screenshot:

EFI_STATUS
SnapShot( EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop, 
          UINTN                        StartX, 
          UINTN                        StartY,
          UINTN                        Width, 
          UINTN                        Height ) 
{
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer = NULL;
    EFI_STATUS Status = EFI_SUCCESS;
    UINTN      ImageSize;  

    ImageSize = Width * Height;
            
    // allocate memory for snapshot
    BltBuffer = AllocateZeroPool( sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * ImageSize );
    if (BltBuffer == NULL) {
        Print(L"ERROR: BltBuffer. No memory resources\n");
        return EFI_OUT_OF_RESOURCES;
    }

    // take screenshot
    Status = Gop->Blt( Gop, BltBuffer, EfiBltVideoToBltBuffer, StartX, StartY, 0, 0, Width, Height, 0 );
    if (EFI_ERROR(Status)) {
        Print(L"ERROR: Gop->Blt [%d]\n", Status);
        FreePool( BltBuffer );
        return Status;
    }
            
    PrepareBMPFile( BltBuffer, Width, Height );

    return Status;
}


Again, note the use of GOP blt with the EfiBltVideoToBltBuffer parameter to capture the contents of the designated screen area as a 32-bit bitmap into BltBuffer.

The next task after capturing a bitmap of the designated screen area is to create the contents of the BMP file in memory before writting it out to disk. This is done in the PrepareBMPFile function as shown below:

EFI_STATUS
PrepareBMPFile( EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer,
                UINT32 Width, 
                UINT32 Height )
{
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Pixel;
    BMP_IMAGE_HEADER  *BmpHeader;
    EFI_STATUS        Status = EFI_SUCCESS;
    EFI_TIME          Time;
    CHAR16            FileName[40]; 
    UINT8             *FileData;
    UINTN             FileDataLength;
    UINT8             *ImagePtr;
    UINT8             *ImagePtrBase;
    UINTN             ImageLineOffset;
    UINTN             x, y;

    ImageLineOffset = Width * 3;
    if ((ImageLineOffset % 4) != 0) {
        ImageLineOffset = ImageLineOffset + (4 - (ImageLineOffset % 4));
    }

    // allocate buffer for data
    FileDataLength = sizeof(BMP_IMAGE_HEADER) + Height * ImageLineOffset;
    FileData = AllocateZeroPool( FileDataLength );
    if (FileData == NULL) {
        FreePool( BltBuffer );
        Print(L"ERROR: AllocateZeroPool. No memony resources\n");
        return EFI_OUT_OF_RESOURCES;
    }
    
    // fill header
    BmpHeader = (BMP_IMAGE_HEADER *)FileData;
    BmpHeader->CharB = 'B';
    BmpHeader->CharM = 'M';
    BmpHeader->Size = (UINT32)FileDataLength;
    BmpHeader->ImageOffset = sizeof(BMP_IMAGE_HEADER);
    BmpHeader->HeaderSize = 40;
    BmpHeader->PixelWidth = Width;
    BmpHeader->PixelHeight = Height;
    BmpHeader->Planes = 1;
    BmpHeader->BitPerPixel = 24;
    BmpHeader->CompressionType = 0;
    BmpHeader->XPixelsPerMeter = 0;
    BmpHeader->YPixelsPerMeter = 0;
    
    // fill pixel buffer
    ImagePtrBase = FileData + BmpHeader->ImageOffset;
    for (y = 0; y < Height; y++) {
        ImagePtr = ImagePtrBase;
        ImagePtrBase += ImageLineOffset;
        Pixel = BltBuffer + (Height - 1 - y) * Width;
        
        for (x = 0; x < Width; x++) {
            *ImagePtr++ = Pixel->Blue;
            *ImagePtr++ = Pixel->Green;
            *ImagePtr++ = Pixel->Red;
            Pixel++;
        }
    }

    FreePool(BltBuffer);

    Status = gRT->GetTime(&Time, NULL);
    if (!EFI_ERROR(Status)) {
        UnicodeSPrint( FileName, 62, L"screenshot-%04d%02d%02d-%02d%02d%02d.bmp", 
                       Time.Year, Time.Month, Time.Day, Time.Hour, Time.Minute, Time.Second );
    } else {
        UnicodeSPrint( FileName, 30, L"screenshot.bmp" );
    }
    
    SaveImage( FileName, FileData, FileDataLength );

    FreePool( FileData );

    return Status;
}

Finally the in-memory copy of the BMP file is written out to disk in the current directory by the SaveImage function:

EFI_STATUS 
SaveImage( CHAR16 *FileName,
           UINT8  *FileData,
           UINTN  FileDataLength )
{
    SHELL_FILE_HANDLE FileHandle = NULL;
    EFI_STATUS        Status = EFI_SUCCESS;
    CONST CHAR16      *CurDir = NULL;
    CONST CHAR16      *PathName = NULL;
    CHAR16            *FullPath = NULL;
    UINTN             Length = 0;
    
    CurDir = gEfiShellProtocol->GetCurDir(NULL);
    if (CurDir == NULL) {
        Print(L"ERROR: Cannot retrieve current directory\n");
	return EFI_NOT_FOUND;
    }
    PathName = CurDir;
    StrnCatGrow(&FullPath, &Length, PathName, 0);
    StrnCatGrow(&FullPath, &Length, L"\\", 0);
    StrnCatGrow(&FullPath, &Length, FileName, 0);
#ifdef DEBUG
    Print(L"FullPath: [%s]\n", FullPath);
#endif

    Status = gEfiShellProtocol->OpenFileByName( FullPath,
                                                &FileHandle,
                                                EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE);
    if (EFI_ERROR(Status)) {
        Print(L"ERROR: OpenFileByName [%s] [%d]\n", FullPath, Status);
        return Status;
    } 

    // BufferSize = FileDataLength;
    Status = gEfiShellProtocol->WriteFile( FileHandle, 
                                           &FileDataLength, 
                                           FileData );
    gEfiShellProtocol->CloseFile( FileHandle );

    if (EFI_ERROR(Status)) {
        Print(L"ERROR: Saving image to file: %x\n", Status);
    } else {
        Print(L"Successfully saved image to %s\n", FullPath);
    }

    return Status;
}


Could I have directly accessed the screen framebuffer instead of using the functionality provided by GOP’s blt? Absolutely. However I felt that using the GOP blt functionality was probably the more portable approach.

I also decided to see if I could convert ScreenShot into a UEFI driver so that a user could take multiple screenshots using a hotkey combination.

Here is the basic framework for the ScreenshotDriver UEFI driver:

#define SCREENSHOTDRIVER_VERSION 0x1

EFI_DRIVER_BINDING_PROTOCOL gScreenshotDriverBinding = {
    ScreenshotDriverBindingSupported,
    ScreenshotDriverBindingStart,
    ScreenshotDriverBindingStop,
    SCREENSHOTDRIVER_VERSION,
    NULL,
    NULL
};

GLOBAL_REMOVE_IF_UNREFERENCED
EFI_COMPONENT_NAME2_PROTOCOL gScreenshotDriverComponentName2 = {
    (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) ScreenshotDriverComponentNameGetDriverName,
    (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) ScreenshotDriverComponentNameGetControllerName,
    "en"
};

GLOBAL_REMOVE_IF_UNREFERENCED 
EFI_UNICODE_STRING_TABLE mScreenshotDriverNameTable[] = {
    { "en", (CHAR16 *) L"ScreenshotDriver" },
    { NULL , NULL }
};

GLOBAL_REMOVE_IF_UNREFERENCED
EFI_DRIVER_DIAGNOSTICS2_PROTOCOL gScreenshotDriverDiagnostics2 = {
    ScreenshotDriverRunDiagnostics,
    "en"
}; 

GLOBAL_REMOVE_IF_UNREFERENCED
EFI_DRIVER_CONFIGURATION2_PROTOCOL gScreenshotDriverConfiguration2 = {
    ScreenshotDriverConfigurationSetOptions,
    ScreenshotDriverConfigurationOptionsValid,
    ScreenshotDriverConfigurationForceDefaults,
    "en"
};


EFI_STATUS
ScreenshotDriverConfigurationSetOptions( EFI_DRIVER_CONFIGURATION2_PROTOCOL       *This,
                                         EFI_HANDLE                               ControllerHandle,
                                         EFI_HANDLE                               ChildHandle,
                                         CHAR8                                    *Language,
                                         EFI_DRIVER_CONFIGURATION_ACTION_REQUIRED *ActionRequired )
{
    return EFI_UNSUPPORTED;
}


EFI_STATUS
ScreenshotDriverConfigurationOptionsValid( EFI_DRIVER_CONFIGURATION2_PROTOCOL *This,
                                           EFI_HANDLE                         ControllerHandle,
                                           EFI_HANDLE                         ChildHandle )
{
    return EFI_UNSUPPORTED;
}


EFI_STATUS
ScreenshotDriverConfigurationForceDefaults( EFI_DRIVER_CONFIGURATION2_PROTOCOL       *This,
                                            EFI_HANDLE                               ControllerHandle,
                                            EFI_HANDLE                               ChildHandle,
                                            UINT32                                   DefaultType,
                                            EFI_DRIVER_CONFIGURATION_ACTION_REQUIRED *ActionRequired )
{
    return EFI_UNSUPPORTED;
}


EFI_STATUS
ScreenshotDriverRunDiagnostics( EFI_DRIVER_DIAGNOSTICS2_PROTOCOL *This,
                                EFI_HANDLE                       ControllerHandle,
                                EFI_HANDLE                       ChildHandle, 
                                EFI_DRIVER_DIAGNOSTIC_TYPE       DiagnosticType, 
                                CHAR8                            *Language,
                                EFI_GUID                         **ErrorType, 
                                UINTN                            *BufferSize,
                                CHAR16                           **Buffer )
{
    return EFI_UNSUPPORTED;
}


//
//  Retrieve user readable name of the driver
//
EFI_STATUS
ScreenshotDriverComponentNameGetDriverName( EFI_COMPONENT_NAME2_PROTOCOL *This,
                                            CHAR8                        *Language,
                                            CHAR16                       **DriverName )
{
    return LookupUnicodeString2( Language,
                                 This->SupportedLanguages,
                                 mScreenshotDriverNameTable,
                                 DriverName,
                                 (BOOLEAN)(This == &gScreenshotDriverComponentName2) );
}


//
//  Retrieve user readable name of controller being managed by a driver
//
EFI_STATUS
ScreenshotDriverComponentNameGetControllerName( EFI_COMPONENT_NAME2_PROTOCOL *This,
                                                EFI_HANDLE                   ControllerHandle,
                                                EFI_HANDLE                   ChildHandle,
                                                CHAR8                        *Language,
                                                CHAR16                       **ControllerName )
{
    return EFI_UNSUPPORTED;
}


//
//  Start this driver on Controller
//
EFI_STATUS
ScreenshotDriverBindingStart( EFI_DRIVER_BINDING_PROTOCOL *This,
                              EFI_HANDLE                  Controller,
                              EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath )
{
    return EFI_UNSUPPORTED;
}


//
//  Stop this driver on ControllerHandle.
//
EFI_STATUS
ScreenshotDriverBindingStop( EFI_DRIVER_BINDING_PROTOCOL *This,
                             EFI_HANDLE                  Controller,
                             UINTN                       NumberOfChildren,
                             EFI_HANDLE                  *ChildHandleBuffer )
{
    return EFI_UNSUPPORTED;
}


//
//  See if this driver supports ControllerHandle.
//
EFI_STATUS
ScreenshotDriverBindingSupported( EFI_DRIVER_BINDING_PROTOCOL *This,
                                  EFI_HANDLE                  Controller,
                                  EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath )
{
    return EFI_UNSUPPORTED;
}


Note that I only used version 2 of the ComponentName, Diagnostics and Configuration driver options.

Here is the source code for the driver entry point:

FI_STATUS
EFIAPI
ScreenshotDriverEntryPoint( EFI_HANDLE ImageHandle,
                            EFI_SYSTEM_TABLE *SystemTable )
{
    EFI_GUID gEfiSimpleTextInputExProtocolGuid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
    EFI_STATUS                        Status;
    EFI_KEY_DATA                      SimpleTextInExKeyStroke;
    EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx;

    // install driver model protocol(s).
    Status = EfiLibInstallAllDriverProtocols2( ImageHandle,
                                               SystemTable,
                                               &gScreenshotDriverBinding,
                                               ImageHandle,
                                               NULL,
                                               &gScreenshotDriverComponentName2,
                                               NULL,
                                               &gScreenshotDriverConfiguration2,
                                               NULL,
                                               &gScreenshotDriverDiagnostics2 );

    if (EFI_ERROR(Status)) {
        DEBUG((DEBUG_ERROR, "EfiLibInstallDriverBindingComponentName2 [%d]\n", Status));
        return Status;
    }

    // set key combination to be LEFTCTRL+LEFTALT+F12
    SimpleTextInExKeyStroke.Key.ScanCode = SCAN_F12;
    SimpleTextInExKeyStroke.Key.UnicodeChar = 0;
    SimpleTextInExKeyStroke.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID | EFI_LEFT_CONTROL_PRESSED | EFI_LEFT_ALT_PRESSED;
    SimpleTextInExKeyStroke.KeyState.KeyToggleState = 0;

    Status = gBS->HandleProtocol( gST->ConsoleInHandle, 
                                  &gEfiSimpleTextInputExProtocolGuid,
                                  (VOID **) &SimpleTextInEx );
    if (EFI_ERROR (Status)) {
        DEBUG((DEBUG_ERROR, "SimpleTextInputEx handle not found via HandleProtocol\n"));
        return Status;
    }

    // register key notification function
    Status = SimpleTextInEx->RegisterKeyNotify( SimpleTextInEx,
                                                &SimpleTextInExKeyStroke,
                                                TakeScreenShot,
                                                &SimpleTextInExHandle );
    if (EFI_ERROR (Status)) {
        DEBUG((DEBUG_ERROR, "SimpleTextInEx->RegisterKeyNotify returned %d\n", Status));
        return Status;
    }

    return EFI_SUCCESS;
}


From the above code you can see that the hotkey combination to take a screenshot is LEFTCTRL+LEFTALT+F12. Note that the order of passing parameters to EfiLibInstallAllDriverProtocols2 is critical; NULL indicates that a specific driver protocol is not supported.

Here is the code that unloads the ScreenshotDriver:

EFI_STATUS
EFIAPI
ScreenshotDriverUnload( EFI_HANDLE ImageHandle )
{
    EFI_GUID gEfiSimpleTextInputExProtocolGuid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
    EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx;
    EFI_STATUS Status = EFI_SUCCESS;
    UINTN      Index;
    UINTN      HandleCount = 0;
    EFI_HANDLE *HandleBuffer = NULL;

    Status = gBS->HandleProtocol( gST->ConsoleInHandle, 
                                  &gEfiSimpleTextInputExProtocolGuid, 
                                  (VOID **) &SimpleTextInEx );
    if (EFI_ERROR (Status)) {
        DEBUG((DEBUG_ERROR, "SimpleTextInputEx handle not found via HandleProtocol\n"));
        return Status;
    }

    // unregister key notification function
    Status = SimpleTextInEx->UnregisterKeyNotify( SimpleTextInEx,
                                                  SimpleTextInExHandle );
    if (EFI_ERROR (Status)) {
        DEBUG((DEBUG_ERROR, "SimpleTextInEx->UnregisterKeyNotify returned %d\n", Status));
        return Status;
    }

    // get list of all the handles in the handle database.
    Status = gBS->LocateHandleBuffer( AllHandles,
                                      NULL,
                                      NULL,
                                      &HandleCount,
                                      &HandleBuffer );
    if (EFI_ERROR (Status)) {
        DEBUG((DEBUG_ERROR, "LocateHandleBuffer [%d]\n", Status));
        return Status;
    }

    for (Index = 0; Index < HandleCount; Index++) {
        Status = gBS->DisconnectController( HandleBuffer[Index],
                                            ImageHandle,
                                            NULL );
    }

    if (HandleBuffer != NULL) {
        FreePool( HandleBuffer );
    }

    // uninstall protocols installed in the driver entry point
    Status = gBS->UninstallMultipleProtocolInterface( ImageHandle,
                                                       &gEfiDriverBindingProtocolGuid, &gScreenshotDriverBinding,
                                                       &gEfiComponentName2ProtocolGuid, &gScreenshotDriverComponentName2,
                                                       &gEfiDriverConfiguration2ProtocolGuid, gScreenshotDriverConfiguration2, 
                                                       &gEfiDriverDiagnostics2ProtocolGuid, &gScreenshotDriverDiagnostics2, 
                                                       NULL );
    if (EFI_ERROR (Status)) {
        DEBUG((DEBUG_ERROR, "UninstallMultipleProtocolInterfaces returned %d\n", Status));
        return Status;
    }

    return EFI_SUCCESS;
}


Note that the order in which you pass the protocol parameter pairs to UninstallMultipleProtocolInterface is also critical. It must be Binding, followed by ComponentName, followed by Configuration, followed by Diagnostics.

Here is the INF for this driver:

[Defines]
  INF_VERSION                    = 1.25
  BASE_NAME                      = ScreenshotDriver 
  FILE_GUID                      = 4ea87c57-7795-5ded-1055-747010f3ce51
  MODULE_TYPE                    = UEFI_DRIVER
  VERSION_STRING                 = 0.9
  ENTRY_POINT                    = ScreenshotDriverEntryPoint
  UNLOAD_IMAGE                   = ScreenshotDriverUnload
  VALID_ARCHITECTURES            = X64

[Packages]
  MdePkg/MdePkg.dec
  MdeModulePkg/MdeModulePkg.dec

[LibraryClasses]
  UefiBootServicesTableLib
  UefiRuntimeServicesTableLib
  UefiDriverEntryPoint
  DebugLib
  PrintLib
  ShellLib
  BaseLib
  BaseMemoryLib
  UefiLib

[Sources]
  ScreenshotDriver.c
  ScreenshotDriver.h

[Packages]
  MdePkg/MdePkg.dec
  ShellPkg/ShellPkg.dec

[Depex]
  gEfiGraphicsOutputProtocolGuid AND
  gEfiSimpleTextInputExProtocolGuid

[Pcd]


Note that the module type is defined as UEFI_DRIVER, the ENTRY_POINT and UNLOAD_IMAGE keywords are mandatory as is the Depex section.

Here is a BMP file produced by ScreenShot when fullscreen (the default) was specified:

Here is a BMP file produced by ScreenshotDriver when loaded and the LEFTCTRL+LEFTALT+F12 hotkey combination pressed:

Here is a BMP file produced by ScreenShot when a specific rectangular part of the screen was specified:

I plan to enhance the functionality of these two utilities over time, especially better driver support. One feature I would like to add is the ability to generate a compressed BMP file. Another feature I am considering is support for generating a JPEG-encoded file. If you have other feature requests, please let me know.

Meanwhile, enjoy!

4 comments to Two UEFI Screenshot Utilities Added To UEFI-Utilities-2019