/** @file This file is part of OpenCanopy, OpenCore GUI. Copyright (c) 2018-2019, Download-Fritz. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "OpenCanopy.h" #include "BmfLib.h" #include "GuiApp.h" GLOBAL_REMOVE_IF_UNREFERENCED BOOT_PICKER_GUI_CONTEXT mGuiContext; // // FIXME: Should not be global here. // STATIC EFI_GRAPHICS_OUTPUT_BLT_PIXEL mBackgroundPixel; STATIC EFI_GRAPHICS_OUTPUT_BLT_PIXEL mHighlightPixel = {0xAF, 0xAF, 0xAF, 0x32}; CONST GUI_IMAGE mBackgroundImage = { 1, 1, &mBackgroundPixel }; STATIC CONST CHAR8 * mLabelNames[LABEL_NUM_TOTAL] = { [LABEL_GENERIC_HDD] = "EFIBoot", [LABEL_APPLE] = "Apple", [LABEL_APPLE_RECOVERY] = "AppleRecv", [LABEL_APPLE_TIME_MACHINE] = "AppleTM", [LABEL_WINDOWS] = "Windows", [LABEL_OTHER] = "Other", [LABEL_TOOL] = "Tool", [LABEL_RESET_NVRAM] = "ResetNVRAM", [LABEL_SHELL] = "Shell" }; STATIC CONST CHAR8 * mIconNames[ICON_NUM_TOTAL] = { [ICON_CURSOR] = "Cursor", [ICON_SELECTED] = "Selected", [ICON_SELECTOR] = "Selector", [ICON_GENERIC_HDD] = "HardDrive", [ICON_APPLE] = "Apple", [ICON_APPLE_RECOVERY] = "AppleRecv", [ICON_APPLE_TIME_MACHINE] = "AppleTM", [ICON_WINDOWS] = "Windows", [ICON_OTHER] = "Other", [ICON_TOOL] = "Tool", [ICON_RESET_NVRAM] = "ResetNVRAM", [ICON_SHELL] = "Shell" }; STATIC VOID InternalSafeFreePool ( IN CONST VOID *Memory ) { if (Memory != NULL) { FreePool ((VOID *)Memory); } } STATIC VOID InternalContextDestruct ( IN OUT BOOT_PICKER_GUI_CONTEXT *Context ) { UINT32 Index; UINT32 Index2; for (Index = 0; Index < ICON_NUM_TOTAL; ++Index) { for (Index2 = 0; Index2 < ICON_TYPE_COUNT; ++Index2) { InternalSafeFreePool (Context->Icons[Index][Index2].Buffer); } } for (Index = 0; Index < LABEL_NUM_TOTAL; ++Index) { InternalSafeFreePool (Context->Labels[Index].Buffer); } InternalSafeFreePool (Context->FontContext.FontImage.Buffer); /* InternalSafeFreePool (Context->Poof[0].Buffer); InternalSafeFreePool (Context->Poof[1].Buffer); InternalSafeFreePool (Context->Poof[2].Buffer); InternalSafeFreePool (Context->Poof[3].Buffer); InternalSafeFreePool (Context->Poof[4].Buffer); */ } STATIC EFI_STATUS LoadImageFileFromStorage ( OUT GUI_IMAGE *Images, IN OC_STORAGE_CONTEXT *Storage, IN CONST CHAR8 *ImageFilePath, IN UINT8 Scale, IN UINT32 MatchWidth, IN UINT32 MatchHeight, IN BOOLEAN Icon, IN BOOLEAN Old, IN BOOLEAN AllowLessSize ) { EFI_STATUS Status; CHAR16 Path[OC_STORAGE_SAFE_PATH_MAX]; UINT8 *FileData; UINT32 FileSize; UINT32 ImageCount; UINT32 Index; ASSERT (ImageFilePath != NULL); ASSERT (Scale == 1 || Scale == 2); ImageCount = Icon ? ICON_TYPE_COUNT : 1; ///< Icons can be external. for (Index = 0; Index < ImageCount; ++Index) { Status = OcUnicodeSafeSPrint ( Path, sizeof (Path), OPEN_CORE_IMAGE_PATH L"%a%a%a.icns", Old ? "Old" : "", Index > 0 ? "Ext" : "", ImageFilePath ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_WARN, "OCUI: Cannot fit %a\n", ImageFilePath)); return EFI_OUT_OF_RESOURCES; } Status = EFI_NOT_FOUND; if (OcStorageExistsFileUnicode (Storage, Path)) { FileData = OcStorageReadFileUnicode (Storage, Path, &FileSize); if (FileData != NULL && FileSize > 0) { Status = GuiIcnsToImageIcon ( &Images[Index], FileData, FileSize, Scale, MatchWidth, MatchHeight, AllowLessSize ); } if (FileData != NULL) { FreePool (FileData); } } if (EFI_ERROR (Status)) { DEBUG (( DEBUG_INFO, "OCUI: Failed to load image (%u/%u) %s old:%d icon:%d - %r\n", Index+1, ImageCount, Path, Old, Icon, Status )); return Index == ICON_TYPE_BASE ? EFI_NOT_FOUND : EFI_SUCCESS; } } return EFI_SUCCESS; } STATIC EFI_STATUS LoadLabelFileFromStorageForScale ( IN OC_STORAGE_CONTEXT *Storage, IN CONST CHAR8 *LabelFilePath, IN UINT8 Scale, OUT VOID **FileData, OUT UINT32 *FileSize ) { EFI_STATUS Status; CHAR16 Path[OC_STORAGE_SAFE_PATH_MAX]; ASSERT (Scale == 1 || Scale == 2); Status = OcUnicodeSafeSPrint ( Path, sizeof (Path), OPEN_CORE_LABEL_PATH L"%a.%a", LabelFilePath, Scale == 2 ? "l2x" : "lbl" ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_WARN, "OCUI: Cannot fit %a\n", LabelFilePath)); return EFI_OUT_OF_RESOURCES; } *FileData = OcStorageReadFileUnicode (Storage, Path, FileSize); if (*FileData == NULL) { DEBUG ((DEBUG_WARN, "OCUI: Failed to load %s\n", Path)); return EFI_NOT_FOUND; } if (*FileSize == 0) { FreePool (*FileData); DEBUG ((DEBUG_WARN, "OCUI: Empty %s\n", Path)); return EFI_NOT_FOUND; } return EFI_SUCCESS; } EFI_STATUS LoadLabelFromStorage ( IN OC_STORAGE_CONTEXT *Storage, IN CONST CHAR8 *ImageFilePath, IN UINT8 Scale, IN BOOLEAN Inverted, OUT GUI_IMAGE *Image ) { VOID *ImageData; UINT32 ImageSize; EFI_STATUS Status; ASSERT (Scale == 1 || Scale == 2); Status = LoadLabelFileFromStorageForScale (Storage, ImageFilePath, Scale, &ImageData, &ImageSize); if (EFI_ERROR (Status)) { return Status; } Status = GuiLabelToImage (Image, ImageData, ImageSize, Scale, Inverted); FreePool (ImageData); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_WARN, "OCUI: Failed to decode label %a - %r\n", ImageFilePath, Status)); } return Status; } EFI_STATUS InternalContextConstruct ( OUT BOOT_PICKER_GUI_CONTEXT *Context, IN OC_STORAGE_CONTEXT *Storage, IN OC_PICKER_CONTEXT *Picker ) { EFI_STATUS Status; EFI_USER_INTERFACE_THEME_PROTOCOL *UiTheme; VOID *FontImage; VOID *FontData; UINT32 FontImageSize; UINT32 FontDataSize; UINTN UiScaleSize; UINT32 Index; UINT32 ImageDimension; BOOLEAN Old; BOOLEAN Result; ASSERT (Context != NULL); Context->Scale = 1; UiScaleSize = sizeof (Context->Scale); Status = gRT->GetVariable ( APPLE_UI_SCALE_VARIABLE_NAME, &gAppleVendorVariableGuid, NULL, &UiScaleSize, (VOID *) &Context->Scale ); if (EFI_ERROR (Status) || Context->Scale != 2) { Context->Scale = 1; } Status = gBS->LocateProtocol ( &gEfiUserInterfaceThemeProtocolGuid, NULL, (VOID **) &UiTheme ); if (!EFI_ERROR (Status)) { Status = UiTheme->GetBackgroundColor (&Context->BackgroundColor.Raw); } if (EFI_ERROR (Status)) { Context->BackgroundColor.Raw = APPLE_COLOR_SYRAH_BLACK; } // // Set background colour with full opacity. // mBackgroundPixel.Red = Context->BackgroundColor.Pixel.Red; mBackgroundPixel.Green = Context->BackgroundColor.Pixel.Green; mBackgroundPixel.Blue = Context->BackgroundColor.Pixel.Blue; mBackgroundPixel.Reserved = 0xFF; Old = Context->BackgroundColor.Raw == APPLE_COLOR_LIGHT_GRAY; if ((Picker->PickerAttributes & OC_ATTR_USE_ALTERNATE_ICONS) != 0) { Old = !Old; } if (Context->BackgroundColor.Raw == APPLE_COLOR_SYRAH_BLACK) { Context->LightBackground = FALSE; } else if (Context->BackgroundColor.Raw == APPLE_COLOR_LIGHT_GRAY) { Context->LightBackground = TRUE; } else { // // FIXME: Support proper W3C formula. // Context->LightBackground = (Context->BackgroundColor.Pixel.Red * 299U + Context->BackgroundColor.Pixel.Green * 587U + Context->BackgroundColor.Pixel.Blue * 114U) >= 186000; } Context->BootEntry = NULL; Status = EFI_SUCCESS; for (Index = 0; Index < ICON_NUM_TOTAL; ++Index) { if (Index == ICON_CURSOR) { ImageDimension = MAX_CURSOR_DIMENSION; } else if (Index == ICON_SELECTED) { ImageDimension = BOOT_SELECTOR_BACKGROUND_DIMENSION; } else if (Index == ICON_SELECTOR) { ImageDimension = BOOT_SELECTOR_BUTTON_DIMENSION; } else { ImageDimension = BOOT_ENTRY_ICON_DIMENSION; } Status = LoadImageFileFromStorage ( Context->Icons[Index], Storage, mIconNames[Index], Context->Scale, ImageDimension, ImageDimension, Index >= ICON_NUM_SYS, Old, Index == ICON_CURSOR ); if (!EFI_ERROR (Status) && Index == ICON_SELECTOR) { Status = GuiCreateHighlightedImage ( &Context->Icons[Index][ICON_TYPE_HELD], &Context->Icons[Index][ICON_TYPE_BASE], &mHighlightPixel ); } // // For generic disk icon being able to distinguish internal and external // disk icons is a security requirement. These icons are used whenever // 'typed' external icons are not available. // if (!EFI_ERROR (Status) && Index == ICON_GENERIC_HDD && Context->Icons[Index][ICON_TYPE_EXTERNAL].Buffer == NULL) { Status = EFI_NOT_FOUND; DEBUG ((DEBUG_WARN, "OCUI: Missing external disk icon\n")); break; } if (EFI_ERROR (Status) && Index < ICON_NUM_MANDATORY) { break; } Status = EFI_SUCCESS; } if (!EFI_ERROR (Status)) { for (Index = 0; Index < LABEL_NUM_TOTAL; ++Index) { Status |= LoadLabelFromStorage ( Storage, mLabelNames[Index], Context->Scale, Context->LightBackground, &Context->Labels[Index] ); } } if (EFI_ERROR (Status)) { DEBUG ((DEBUG_WARN, "OCUI: Failed to load images\n")); InternalContextDestruct (Context); return EFI_UNSUPPORTED; } if (Context->Scale == 2) { FontImage = OcStorageReadFileUnicode (Storage, OPEN_CORE_FONT_PATH L"Font_2x.png", &FontImageSize); FontData = OcStorageReadFileUnicode (Storage, OPEN_CORE_FONT_PATH L"Font_2x.bin", &FontDataSize); } else { FontImage = OcStorageReadFileUnicode (Storage, OPEN_CORE_FONT_PATH L"Font_1x.png", &FontImageSize); FontData = OcStorageReadFileUnicode (Storage, OPEN_CORE_FONT_PATH L"Font_1x.bin", &FontDataSize); } if (FontImage != NULL && FontData != NULL) { Result = GuiFontConstruct ( &Context->FontContext, FontImage, FontImageSize, FontData, FontDataSize ); if (Context->FontContext.BmfContext.Height != BOOT_ENTRY_LABEL_HEIGHT * Context->Scale) { DEBUG(( DEBUG_WARN, "OCUI: Font has height %d instead of %d\n", Context->FontContext.BmfContext.Height, BOOT_ENTRY_LABEL_HEIGHT * Context->Scale )); Result = FALSE; } } else { Result = FALSE; } if (!Result) { DEBUG ((DEBUG_WARN, "OCUI: Font init failed\n")); InternalContextDestruct (Context); return EFI_UNSUPPORTED; } return EFI_SUCCESS; } CONST GUI_IMAGE * InternalGetCursorImage ( IN OUT GUI_SCREEN_CURSOR *This, IN VOID *Context ) { CONST BOOT_PICKER_GUI_CONTEXT *GuiContext; ASSERT (This != NULL); ASSERT (Context != NULL); GuiContext = (CONST BOOT_PICKER_GUI_CONTEXT *)Context; return &GuiContext->Icons[ICON_CURSOR][ICON_TYPE_BASE]; }