/** @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 "../OpenCanopy.h" #include "../BmfLib.h" #include "../GuiApp.h" #include "BootPicker.h" extern GUI_OBJ mBootPickerView; extern GUI_VOLUME_PICKER mBootPicker; extern GUI_OBJ_CLICKABLE mBootPickerSelector; extern CONST GUI_IMAGE mBackgroundImage; STATIC UINT8 mBootPickerOpacity = 0xFF; // STATIC UINT8 mBootPickerImageIndex = 0; BOOLEAN GuiClickableIsHit ( IN CONST GUI_IMAGE *Image, IN INT64 OffsetX, IN INT64 OffsetY ) { UINT32 RowOffset; UINT32 IndexX; ASSERT (Image != NULL); ASSERT (Image->Buffer != NULL); if (OffsetX < 0 || OffsetX >= Image->Width || OffsetY < 0 || OffsetY >= Image->Height) { return FALSE; } RowOffset = (UINT32)OffsetY * Image->Width; if (Image->Buffer[RowOffset + OffsetX].Reserved != 0) { return TRUE; } for (IndexX = 0; IndexX < OffsetX; ++IndexX) { if (Image->Buffer[RowOffset + IndexX].Reserved != 0) { break; } } if (IndexX == OffsetX) { return FALSE; } for (IndexX = Image->Width - 1; IndexX > OffsetX; --IndexX) { if (Image->Buffer[RowOffset + IndexX].Reserved != 0) { break; } } if (IndexX == OffsetX) { return FALSE; } return TRUE; } VOID InternalBootPickerViewDraw ( IN OUT GUI_OBJ *This, IN OUT GUI_DRAWING_CONTEXT *DrawContext, IN BOOT_PICKER_GUI_CONTEXT *Context, IN INT64 BaseX, IN INT64 BaseY, IN UINT32 OffsetX, IN UINT32 OffsetY, IN UINT32 Width, IN UINT32 Height, IN BOOLEAN RequestDraw ) { ASSERT (This != NULL); ASSERT (DrawContext != NULL); ASSERT (Context != NULL); GuiDrawToBuffer ( &mBackgroundImage, 0xFF, TRUE, DrawContext, BaseX, BaseY, OffsetX, OffsetY, Width, Height, TRUE ); GuiObjDrawDelegate ( This, DrawContext, Context, BaseX, BaseY, OffsetX, OffsetY, Width, Height, FALSE ); } VOID InternalBootPickerViewKeyEvent ( IN OUT GUI_OBJ *This, IN OUT GUI_DRAWING_CONTEXT *DrawContext, IN BOOT_PICKER_GUI_CONTEXT *Context, IN INT64 BaseX, IN INT64 BaseY, IN INTN Key, IN BOOLEAN Modifier ) { ASSERT (This != NULL); ASSERT (DrawContext != NULL); // // Consider moving between multiple panes with UP/DOWN and store the current // view within the object - for now, hardcoding this is enough. // ASSERT (mBootPicker.Hdr.Obj.KeyEvent != NULL); mBootPicker.Hdr.Obj.KeyEvent ( &mBootPicker.Hdr.Obj, DrawContext, Context, BaseX + mBootPicker.Hdr.Obj.OffsetX, BaseY + mBootPicker.Hdr.Obj.OffsetY, Key, Modifier ); } VOID InternalBootPickerSelectEntry ( IN OUT GUI_VOLUME_PICKER *This, IN GUI_VOLUME_ENTRY *NewEntry ) { CONST GUI_OBJ *VolumeEntryObj; LIST_ENTRY *SelectorNode; GUI_OBJ_CHILD *Selector; ASSERT (This != NULL); ASSERT (NewEntry != NULL); ASSERT (IsNodeInList (&This->Hdr.Obj.Children, &NewEntry->Hdr.Link)); This->SelectedEntry = NewEntry; VolumeEntryObj = &NewEntry->Hdr.Obj; SelectorNode = This->Hdr.Obj.Children.BackLink; ASSERT (SelectorNode != NULL); Selector = BASE_CR (SelectorNode, GUI_OBJ_CHILD, Link); ASSERT (Selector->Obj.Width <= VolumeEntryObj->Width); ASSERT_EQUALS (This->Hdr.Obj.Height, Selector->Obj.OffsetY + Selector->Obj.Height); Selector->Obj.OffsetX = VolumeEntryObj->OffsetX; Selector->Obj.OffsetX += (VolumeEntryObj->Width - Selector->Obj.Width) / 2; } VOID InternalBootPickerChangeEntry ( IN OUT GUI_VOLUME_PICKER *This, IN OUT GUI_DRAWING_CONTEXT *DrawContext, IN INT64 BaseX, IN INT64 BaseY, IN GUI_VOLUME_ENTRY *NewEntry ) { GUI_VOLUME_ENTRY *PrevEntry; ASSERT (This != NULL); ASSERT (DrawContext != NULL); ASSERT (NewEntry != NULL); ASSERT (IsNodeInList (&This->Hdr.Obj.Children, &NewEntry->Hdr.Link)); // // The caller must guarantee the entry is actually new for performance // reasons. // ASSERT (This->SelectedEntry != NewEntry); // // Redraw the two now (un-)selected entries. // PrevEntry = This->SelectedEntry; InternalBootPickerSelectEntry (This, NewEntry); // // To redraw the entry *and* the selector, draw the entire height of the // Picker object. For this, the height just reach from the top of the entries // to the bottom of the selector. // GuiDrawScreen ( DrawContext, BaseX + NewEntry->Hdr.Obj.OffsetX, BaseY + NewEntry->Hdr.Obj.OffsetY, NewEntry->Hdr.Obj.Width, This->Hdr.Obj.Height, TRUE ); GuiDrawScreen ( DrawContext, BaseX + PrevEntry->Hdr.Obj.OffsetX, BaseY + PrevEntry->Hdr.Obj.OffsetY, PrevEntry->Hdr.Obj.Width, This->Hdr.Obj.Height, TRUE ); } VOID InternalBootPickerKeyEvent ( IN OUT GUI_OBJ *This, IN OUT GUI_DRAWING_CONTEXT *DrawContext, IN BOOT_PICKER_GUI_CONTEXT *GuiContext, IN INT64 BaseX, IN INT64 BaseY, IN INTN Key, IN BOOLEAN Modifier ) { GUI_VOLUME_PICKER *Picker; GUI_VOLUME_ENTRY *PrevEntry; LIST_ENTRY *NextLink; GUI_VOLUME_ENTRY *NextEntry; ASSERT (This != NULL); ASSERT (GuiContext != NULL); ASSERT (DrawContext != NULL); Picker = BASE_CR (This, GUI_VOLUME_PICKER, Hdr.Obj); PrevEntry = Picker->SelectedEntry; ASSERT (PrevEntry != NULL); if (Key == OC_INPUT_RIGHT) { NextLink = GetNextNode ( &Picker->Hdr.Obj.Children, &PrevEntry->Hdr.Link ); // // Edge-case: The last child is the selector button. // if (This->Children.BackLink != NextLink) { // // Redraw the two now (un-)selected entries. // NextEntry = BASE_CR (NextLink, GUI_VOLUME_ENTRY, Hdr.Link); InternalBootPickerChangeEntry (Picker, DrawContext, BaseX, BaseY, NextEntry); } } else if (Key == OC_INPUT_LEFT) { NextLink = GetPreviousNode ( &Picker->Hdr.Obj.Children, &PrevEntry->Hdr.Link ); if (!IsNull (&This->Children, NextLink)) { // // Redraw the two now (un-)selected entries. // NextEntry = BASE_CR (NextLink, GUI_VOLUME_ENTRY, Hdr.Link); InternalBootPickerChangeEntry (Picker, DrawContext, BaseX, BaseY, NextEntry); } } else if (Key == OC_INPUT_CONTINUE) { ASSERT (Picker->SelectedEntry != NULL); Picker->SelectedEntry->Context->SetDefault = Modifier; GuiContext->BootEntry = Picker->SelectedEntry->Context; } else if (mBootPickerOpacity != 0xFF) { // // FIXME: Other keys are not allowed when boot picker is partially transparent. // return; } if (Key == OC_INPUT_MORE) { GuiContext->HideAuxiliary = FALSE; GuiContext->Refresh = TRUE; } else if (Key == OC_INPUT_ABORTED) { GuiContext->Refresh = TRUE; } } VOID GuiDrawChildImage ( IN CONST GUI_IMAGE *Image, IN UINT8 Opacity, IN OUT GUI_DRAWING_CONTEXT *DrawContext, IN INT64 ParentBaseX, IN INT64 ParentBaseY, IN INT64 ChildBaseX, IN INT64 ChildBaseY, IN UINT32 OffsetX, IN UINT32 OffsetY, IN UINT32 Width, IN UINT32 Height, IN BOOLEAN RequestDraw ) { BOOLEAN Result; ASSERT (Image != NULL); ASSERT (DrawContext != NULL); Result = GuiClipChildBounds ( ChildBaseX, Image->Width, &OffsetX, &Width ); if (Result) { Result = GuiClipChildBounds ( ChildBaseY, Image->Height, &OffsetY, &Height ); if (Result) { ASSERT (Image->Width > OffsetX); ASSERT (Image->Height > OffsetY); ASSERT (Image->Buffer != NULL); GuiDrawToBuffer ( Image, Opacity, FALSE, DrawContext, ParentBaseX + ChildBaseX, ParentBaseY + ChildBaseY, OffsetX, OffsetY, Width, Height, RequestDraw ); } } } STATIC VOID InternalBootPickerEntryDraw ( IN OUT GUI_OBJ *This, IN OUT GUI_DRAWING_CONTEXT *DrawContext, IN BOOT_PICKER_GUI_CONTEXT *Context, IN INT64 BaseX, IN INT64 BaseY, IN UINT32 OffsetX, IN UINT32 OffsetY, IN UINT32 Width, IN UINT32 Height, IN BOOLEAN RequestDraw ) { CONST GUI_VOLUME_ENTRY *Entry; CONST GUI_IMAGE *EntryIcon; CONST GUI_IMAGE *Label; ASSERT (This != NULL); ASSERT (DrawContext != NULL); ASSERT (Context != NULL); Entry = BASE_CR (This, GUI_VOLUME_ENTRY, Hdr.Obj); /*if (mBootPickerImageIndex < 5) { EntryIcon = &((BOOT_PICKER_GUI_CONTEXT *) DrawContext->GuiContext)->Poof[mBootPickerImageIndex]; } else */{ EntryIcon = &Entry->EntryIcon; } Label = &Entry->Label; ASSERT_EQUALS (This->Width, BOOT_ENTRY_DIMENSION * DrawContext->Scale); ASSERT_EQUALS (This->Height, BOOT_ENTRY_HEIGHT * DrawContext->Scale); // // Draw the icon horizontally centered. // ASSERT (EntryIcon != NULL); ASSERT_EQUALS (EntryIcon->Width, BOOT_ENTRY_ICON_DIMENSION * DrawContext->Scale); ASSERT_EQUALS (EntryIcon->Height, BOOT_ENTRY_ICON_DIMENSION * DrawContext->Scale); GuiDrawChildImage ( EntryIcon, mBootPickerOpacity, DrawContext, BaseX, BaseY, (BOOT_ENTRY_DIMENSION - BOOT_ENTRY_ICON_DIMENSION) * DrawContext->Scale / 2, (BOOT_ENTRY_DIMENSION - BOOT_ENTRY_ICON_DIMENSION) * DrawContext->Scale / 2, OffsetX, OffsetY, Width, Height, RequestDraw ); // // Draw the label horizontally centered. // // // FIXME: Apple allows the label to be up to 340px wide, // but OpenCanopy can't display it now (it would overlap adjacent entries) // //ASSERT (Label->Width <= BOOT_ENTRY_DIMENSION * DrawContext->Scale); ASSERT (Label->Height <= BOOT_ENTRY_LABEL_HEIGHT * DrawContext->Scale); GuiDrawChildImage ( Label, mBootPickerOpacity, DrawContext, BaseX, BaseY, (BOOT_ENTRY_DIMENSION * DrawContext->Scale - Label->Width) / 2, (BOOT_ENTRY_DIMENSION + BOOT_ENTRY_LABEL_SPACE + BOOT_ENTRY_LABEL_HEIGHT) * DrawContext->Scale - Label->Height, OffsetX, OffsetY, Width, Height, RequestDraw ); // // There should be no children. // ASSERT (IsListEmpty (&This->Children)); } STATIC GUI_OBJ * InternalBootPickerEntryPtrEvent ( IN OUT GUI_OBJ *This, IN OUT GUI_DRAWING_CONTEXT *DrawContext, IN BOOT_PICKER_GUI_CONTEXT *Context, IN GUI_PTR_EVENT Event, IN INT64 BaseX, IN INT64 BaseY, IN INT64 OffsetX, IN INT64 OffsetY ) { STATIC BOOLEAN SameIter = FALSE; GUI_VOLUME_ENTRY *Entry; BOOLEAN IsHit; ASSERT (Event == GuiPointerPrimaryDown || Event == GuiPointerPrimaryHold || Event == GuiPointerPrimaryUp); if (Event == GuiPointerPrimaryHold) { return This; } if (OffsetX < (BOOT_ENTRY_DIMENSION - BOOT_ENTRY_ICON_DIMENSION) * DrawContext->Scale / 2 || OffsetY < (BOOT_ENTRY_DIMENSION - BOOT_ENTRY_ICON_DIMENSION) * DrawContext->Scale / 2) { return This; } Entry = BASE_CR (This, GUI_VOLUME_ENTRY, Hdr.Obj); IsHit = GuiClickableIsHit ( &Entry->EntryIcon, OffsetX - (BOOT_ENTRY_DIMENSION - BOOT_ENTRY_ICON_DIMENSION) * DrawContext->Scale / 2, OffsetY - (BOOT_ENTRY_DIMENSION - BOOT_ENTRY_ICON_DIMENSION) * DrawContext->Scale / 2 ); if (!IsHit) { return This; } if (Event == GuiPointerPrimaryDown) { if (mBootPicker.SelectedEntry != Entry) { ASSERT (Entry->Hdr.Parent == &mBootPicker.Hdr.Obj); InternalBootPickerChangeEntry ( &mBootPicker, DrawContext, BaseX - This->OffsetX, BaseY - This->OffsetY, Entry ); SameIter = TRUE; } } else { // // This must be ensured because the UI directs Move/Up events to the object // Down had been sent to. // ASSERT (mBootPicker.SelectedEntry == Entry); if (SameIter) { SameIter = FALSE; } else { Context->BootEntry = Entry->Context; } } // // There should be no children. // ASSERT (IsListEmpty (&This->Children)); return This; } VOID InternalBootPickerSelectorDraw ( IN OUT GUI_OBJ *This, IN OUT GUI_DRAWING_CONTEXT *DrawContext, IN BOOT_PICKER_GUI_CONTEXT *Context, IN INT64 BaseX, IN INT64 BaseY, IN UINT32 OffsetX, IN UINT32 OffsetY, IN UINT32 Width, IN UINT32 Height, IN BOOLEAN RequestDraw ) { CONST GUI_OBJ_CLICKABLE *Clickable; CONST GUI_IMAGE *BackgroundImage; CONST GUI_IMAGE *ButtonImage; ASSERT (This != NULL); ASSERT (DrawContext != NULL); ASSERT (Context != NULL); Clickable = BASE_CR (This, GUI_OBJ_CLICKABLE, Hdr.Obj); ASSERT_EQUALS (This->Width, BOOT_SELECTOR_WIDTH * DrawContext->Scale); ASSERT_EQUALS (This->Height, BOOT_SELECTOR_HEIGHT * DrawContext->Scale); BackgroundImage = &Context->Icons[ICON_SELECTED][ICON_TYPE_BASE]; ASSERT_EQUALS (BackgroundImage->Width, BOOT_SELECTOR_BACKGROUND_DIMENSION * DrawContext->Scale); ASSERT_EQUALS (BackgroundImage->Height, BOOT_SELECTOR_BACKGROUND_DIMENSION * DrawContext->Scale); ASSERT (BackgroundImage->Buffer != NULL); // // Background starts at (0,0) and is as wide as This. // if (OffsetY < BOOT_SELECTOR_BACKGROUND_DIMENSION * DrawContext->Scale) { GuiDrawToBuffer ( BackgroundImage, mBootPickerOpacity, FALSE, DrawContext, BaseX, BaseY, OffsetX, OffsetY, Width, Height, RequestDraw ); } ButtonImage = Clickable->CurrentImage; ASSERT (ButtonImage != NULL); ASSERT_EQUALS (ButtonImage->Width , BOOT_SELECTOR_BUTTON_DIMENSION * DrawContext->Scale); ASSERT_EQUALS (ButtonImage->Height, BOOT_SELECTOR_BUTTON_DIMENSION * DrawContext->Scale); ASSERT (ButtonImage->Buffer != NULL); GuiDrawChildImage ( ButtonImage, mBootPickerOpacity, DrawContext, BaseX, BaseY, (BOOT_SELECTOR_BACKGROUND_DIMENSION - BOOT_SELECTOR_BUTTON_DIMENSION) * DrawContext->Scale / 2, (BOOT_SELECTOR_BACKGROUND_DIMENSION + BOOT_SELECTOR_BUTTON_SPACE) * DrawContext->Scale, OffsetX, OffsetY, Width, Height, RequestDraw ); // // There should be no children. // ASSERT (IsListEmpty (&This->Children)); } GUI_OBJ * InternalBootPickerSelectorPtrEvent ( IN OUT GUI_OBJ *This, IN OUT GUI_DRAWING_CONTEXT *DrawContext, IN BOOT_PICKER_GUI_CONTEXT *Context, IN GUI_PTR_EVENT Event, IN INT64 BaseX, IN INT64 BaseY, IN INT64 OffsetX, IN INT64 OffsetY ) { GUI_OBJ_CLICKABLE *Clickable; CONST GUI_IMAGE *ButtonImage; BOOLEAN IsHit; ASSERT (This != NULL); ASSERT (DrawContext != NULL); ASSERT (Context != NULL); // // There should be no children. // ASSERT (IsListEmpty (&This->Children)); Clickable = BASE_CR (This, GUI_OBJ_CLICKABLE, Hdr.Obj); ButtonImage = &Context->Icons[ICON_SELECTOR][ICON_TYPE_BASE]; ASSERT (Event == GuiPointerPrimaryDown || Event == GuiPointerPrimaryHold || Event == GuiPointerPrimaryUp); if (OffsetX >= (BOOT_SELECTOR_BACKGROUND_DIMENSION - BOOT_SELECTOR_BUTTON_DIMENSION) * DrawContext->Scale / 2 && OffsetY >= (BOOT_SELECTOR_BACKGROUND_DIMENSION + BOOT_SELECTOR_BUTTON_SPACE) * DrawContext->Scale) { IsHit = GuiClickableIsHit ( ButtonImage, OffsetX - (BOOT_SELECTOR_BACKGROUND_DIMENSION - BOOT_SELECTOR_BUTTON_DIMENSION) * DrawContext->Scale / 2, OffsetY - (BOOT_SELECTOR_BACKGROUND_DIMENSION + BOOT_SELECTOR_BUTTON_SPACE) * DrawContext->Scale ); if (IsHit) { if (Event == GuiPointerPrimaryUp) { ASSERT (mBootPicker.SelectedEntry != NULL); Context->BootEntry = mBootPicker.SelectedEntry->Context; } else { ButtonImage = &Context->Icons[ICON_SELECTOR][ICON_TYPE_HELD]; } } } if (Clickable->CurrentImage != ButtonImage) { Clickable->CurrentImage = ButtonImage; GuiRedrawObject (This, DrawContext, BaseX, BaseY, TRUE); } return This; } GLOBAL_REMOVE_IF_UNREFERENCED GUI_OBJ_CLICKABLE mBootPickerSelector = { { INITIALIZE_LIST_HEAD_VARIABLE (mBootPicker.Hdr.Obj.Children), &mBootPicker.Hdr.Obj, { 0, 0, BOOT_SELECTOR_WIDTH, BOOT_SELECTOR_HEIGHT, InternalBootPickerSelectorDraw, InternalBootPickerSelectorPtrEvent, NULL, INITIALIZE_LIST_HEAD_VARIABLE (mBootPickerSelector.Hdr.Obj.Children) } }, NULL, NULL }; GLOBAL_REMOVE_IF_UNREFERENCED GUI_VOLUME_PICKER mBootPicker = { { INITIALIZE_LIST_HEAD_VARIABLE (mBootPickerView.Children), &mBootPickerView, { 0, 0, 0, BOOT_SELECTOR_HEIGHT, GuiObjDrawDelegate, GuiObjDelegatePtrEvent, InternalBootPickerKeyEvent, INITIALIZE_LIST_HEAD_VARIABLE (mBootPickerSelector.Hdr.Link) } }, NULL }; GLOBAL_REMOVE_IF_UNREFERENCED GUI_OBJ mBootPickerView = { 0, 0, 0, 0, InternalBootPickerViewDraw, GuiObjDelegatePtrEvent, InternalBootPickerViewKeyEvent, INITIALIZE_LIST_HEAD_VARIABLE (mBootPicker.Hdr.Link) }; STATIC EFI_STATUS CopyLabel ( OUT GUI_IMAGE *Destination, IN CONST GUI_IMAGE *Source ) { Destination->Width = Source->Width; Destination->Height = Source->Height; Destination->Buffer = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) AllocateCopyPool ( sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * Source->Width * Source->Height, Source->Buffer ); if (Destination->Buffer == NULL) { return EFI_OUT_OF_RESOURCES; } return EFI_SUCCESS; } EFI_STATUS BootPickerEntriesAdd ( IN OC_PICKER_CONTEXT *Context, IN CONST BOOT_PICKER_GUI_CONTEXT *GuiContext, IN OC_BOOT_ENTRY *Entry, IN BOOLEAN Default ) { EFI_STATUS Status; GUI_VOLUME_ENTRY *VolumeEntry; CONST GUI_IMAGE *SuggestedIcon; LIST_ENTRY *ListEntry; CONST GUI_VOLUME_ENTRY *PrevEntry; UINT32 IconFileSize; UINT32 IconTypeIndex; VOID *IconFileData; BOOLEAN UseVolumeIcon; BOOLEAN UseDiskLabel; BOOLEAN UseGenericLabel; BOOLEAN Result; ASSERT (GuiContext != NULL); ASSERT (Entry != NULL); DEBUG ((DEBUG_INFO, "OCUI: Console attributes: %d\n", Context->ConsoleAttributes)); UseVolumeIcon = (Context->PickerAttributes & OC_ATTR_USE_VOLUME_ICON) != 0; UseDiskLabel = (Context->PickerAttributes & OC_ATTR_USE_DISK_LABEL_FILE) != 0; UseGenericLabel = (Context->PickerAttributes & OC_ATTR_USE_GENERIC_LABEL_IMAGE) != 0; DEBUG ((DEBUG_INFO, "OCUI: UseDiskLabel: %d, UseGenericLabel: %d\n", UseDiskLabel, UseGenericLabel)); VolumeEntry = AllocateZeroPool (sizeof (*VolumeEntry)); if (VolumeEntry == NULL) { return EFI_OUT_OF_RESOURCES; } if (UseDiskLabel) { Status = OcGetBootEntryLabelImage ( Context, Entry, GuiContext->Scale, &IconFileData, &IconFileSize ); if (!EFI_ERROR (Status)) { Status = GuiLabelToImage ( &VolumeEntry->Label, IconFileData, IconFileSize, GuiContext->Scale, GuiContext->LightBackground ); } } else { Status = EFI_UNSUPPORTED; } if (EFI_ERROR (Status) && UseGenericLabel) { switch (Entry->Type) { case OC_BOOT_APPLE_OS: Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_APPLE]); break; case OC_BOOT_APPLE_FW_UPDATE: case OC_BOOT_APPLE_RECOVERY: Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_APPLE_RECOVERY]); break; case OC_BOOT_APPLE_TIME_MACHINE: Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_APPLE_TIME_MACHINE]); break; case OC_BOOT_WINDOWS: Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_WINDOWS]); break; case OC_BOOT_EXTERNAL_OS: Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_OTHER]); break; case OC_BOOT_RESET_NVRAM: Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_RESET_NVRAM]); break; case OC_BOOT_EXTERNAL_TOOL: if (StrStr (Entry->Name, OC_MENU_RESET_NVRAM_ENTRY) != NULL) { Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_RESET_NVRAM]); } else if (StrStr (Entry->Name, OC_MENU_UEFI_SHELL_ENTRY) != NULL) { Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_SHELL]); } else { Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_TOOL]); } break; case OC_BOOT_UNKNOWN: Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_GENERIC_HDD]); break; default: DEBUG ((DEBUG_WARN, "OCUI: Entry kind %d unsupported for label\n", Entry->Type)); return EFI_UNSUPPORTED; } } if (EFI_ERROR (Status)) { Result = GuiGetLabel ( &VolumeEntry->Label, &GuiContext->FontContext, Entry->Name, StrLen (Entry->Name), GuiContext->LightBackground ); if (!Result) { DEBUG ((DEBUG_WARN, "OCUI: label failed\n")); return EFI_UNSUPPORTED; } } VolumeEntry->Context = Entry; if (UseVolumeIcon) { Status = OcGetBootEntryIcon (Context, Entry, &IconFileData, &IconFileSize); if (!EFI_ERROR (Status)) { Status = GuiIcnsToImageIcon ( &VolumeEntry->EntryIcon, IconFileData, IconFileSize, GuiContext->Scale, BOOT_ENTRY_ICON_DIMENSION, BOOT_ENTRY_ICON_DIMENSION, FALSE ); FreePool (IconFileData); if (!EFI_ERROR (Status)) { VolumeEntry->CustomIcon = TRUE; } else { DEBUG ((DEBUG_INFO, "OCUI: Failed to convert icon - %r\n", Status)); } } } else { Status = EFI_UNSUPPORTED; } if (EFI_ERROR (Status)) { SuggestedIcon = NULL; IconTypeIndex = Entry->IsExternal ? ICON_TYPE_EXTERNAL : ICON_TYPE_BASE; switch (Entry->Type) { case OC_BOOT_APPLE_OS: SuggestedIcon = &GuiContext->Icons[ICON_APPLE][IconTypeIndex]; break; case OC_BOOT_APPLE_FW_UPDATE: case OC_BOOT_APPLE_RECOVERY: SuggestedIcon = &GuiContext->Icons[ICON_APPLE_RECOVERY][IconTypeIndex]; if (SuggestedIcon->Buffer == NULL) { SuggestedIcon = &GuiContext->Icons[ICON_APPLE][IconTypeIndex]; } break; case OC_BOOT_APPLE_TIME_MACHINE: SuggestedIcon = &GuiContext->Icons[ICON_APPLE_TIME_MACHINE][IconTypeIndex]; if (SuggestedIcon->Buffer == NULL) { SuggestedIcon = &GuiContext->Icons[ICON_APPLE][IconTypeIndex]; } break; case OC_BOOT_WINDOWS: SuggestedIcon = &GuiContext->Icons[ICON_WINDOWS][IconTypeIndex]; break; case OC_BOOT_EXTERNAL_OS: SuggestedIcon = &GuiContext->Icons[ICON_OTHER][IconTypeIndex]; break; case OC_BOOT_RESET_NVRAM: SuggestedIcon = &GuiContext->Icons[ICON_RESET_NVRAM][IconTypeIndex]; if (SuggestedIcon->Buffer == NULL) { SuggestedIcon = &GuiContext->Icons[ICON_TOOL][IconTypeIndex]; } break; case OC_BOOT_EXTERNAL_TOOL: if (StrStr (Entry->Name, OC_MENU_RESET_NVRAM_ENTRY) != NULL) { SuggestedIcon = &GuiContext->Icons[ICON_RESET_NVRAM][IconTypeIndex]; } else if (StrStr (Entry->Name, OC_MENU_UEFI_SHELL_ENTRY) != NULL) { SuggestedIcon = &GuiContext->Icons[ICON_SHELL][IconTypeIndex]; } if (SuggestedIcon == NULL || SuggestedIcon->Buffer == NULL) { SuggestedIcon = &GuiContext->Icons[ICON_TOOL][IconTypeIndex]; } break; case OC_BOOT_UNKNOWN: SuggestedIcon = &GuiContext->Icons[ICON_GENERIC_HDD][IconTypeIndex]; break; default: DEBUG ((DEBUG_WARN, "OCUI: Entry kind %d unsupported for icon\n", Entry->Type)); return EFI_UNSUPPORTED; } ASSERT (SuggestedIcon != NULL); if (SuggestedIcon->Buffer == NULL) { SuggestedIcon = &GuiContext->Icons[ICON_GENERIC_HDD][IconTypeIndex]; } CopyMem (&VolumeEntry->EntryIcon, SuggestedIcon, sizeof (VolumeEntry->EntryIcon)); } VolumeEntry->Hdr.Parent = &mBootPicker.Hdr.Obj; VolumeEntry->Hdr.Obj.Width = BOOT_ENTRY_WIDTH * GuiContext->Scale; VolumeEntry->Hdr.Obj.Height = BOOT_ENTRY_HEIGHT * GuiContext->Scale; VolumeEntry->Hdr.Obj.Draw = InternalBootPickerEntryDraw; VolumeEntry->Hdr.Obj.PtrEvent = InternalBootPickerEntryPtrEvent; InitializeListHead (&VolumeEntry->Hdr.Obj.Children); // // The last entry is always the selector. // ListEntry = mBootPicker.Hdr.Obj.Children.BackLink; ASSERT (ListEntry == &mBootPickerSelector.Hdr.Link); ListEntry = ListEntry->BackLink; ASSERT (ListEntry != NULL); if (!IsNull (&mBootPicker.Hdr.Obj.Children, ListEntry)) { PrevEntry = BASE_CR (ListEntry, GUI_VOLUME_ENTRY, Hdr.Link); VolumeEntry->Hdr.Obj.OffsetX = PrevEntry->Hdr.Obj.OffsetX + (BOOT_ENTRY_DIMENSION + BOOT_ENTRY_SPACE) * GuiContext->Scale; } InsertHeadList (ListEntry, &VolumeEntry->Hdr.Link); mBootPicker.Hdr.Obj.Width += (BOOT_ENTRY_WIDTH + BOOT_ENTRY_SPACE) * GuiContext->Scale; mBootPicker.Hdr.Obj.OffsetX -= (BOOT_ENTRY_WIDTH + BOOT_ENTRY_SPACE) * GuiContext->Scale / 2; if (Default) { InternalBootPickerSelectEntry (&mBootPicker, VolumeEntry); } return EFI_SUCCESS; } VOID InternalBootPickerEntryDestruct ( IN GUI_VOLUME_ENTRY *Entry ) { ASSERT (Entry != NULL); ASSERT (Entry->Label.Buffer != NULL); if (Entry->CustomIcon) { FreePool (Entry->EntryIcon.Buffer); } FreePool (Entry->Label.Buffer); FreePool (Entry); } BOOLEAN InternalBootPickerExitLoop ( IN BOOT_PICKER_GUI_CONTEXT *Context ) { ASSERT (Context != NULL); return Context->BootEntry != NULL || Context->Refresh; } STATIC GUI_INTERPOLATION mBpAnimInfoOpacity; VOID InitBpAnimOpacity ( IN GUI_INTERPOL_TYPE Type, IN UINT64 StartTime, IN UINT64 Duration ) { mBpAnimInfoOpacity.Type = Type; mBpAnimInfoOpacity.StartTime = StartTime; mBpAnimInfoOpacity.Duration = Duration; mBpAnimInfoOpacity.StartValue = 0; mBpAnimInfoOpacity.EndValue = 0xFF; mBootPickerOpacity = 0; } BOOLEAN InternalBootPickerAnimateOpacity ( IN BOOT_PICKER_GUI_CONTEXT *Context, IN OUT GUI_DRAWING_CONTEXT *DrawContext, IN UINT64 CurrentTime ) { ASSERT (DrawContext != NULL); mBootPickerOpacity = (UINT8)GuiGetInterpolatedValue (&mBpAnimInfoOpacity, CurrentTime); GuiRedrawObject ( &mBootPicker.Hdr.Obj, DrawContext, mBootPicker.Hdr.Obj.OffsetX, mBootPicker.Hdr.Obj.OffsetY, TRUE ); if (mBootPickerOpacity == mBpAnimInfoOpacity.EndValue) { return TRUE; /*UINT32 OrigVal = mBpAnimInfoOpacity.EndValue; mBpAnimInfoOpacity.EndValue = mBpAnimInfoOpacity.StartValue; mBpAnimInfoOpacity.StartValue = OrigVal; mBpAnimInfoOpacity.StartTime = CurrentTime;*/ } return FALSE; } STATIC GUI_INTERPOLATION mBpAnimInfoImageList; VOID InitBpAnimImageList ( IN GUI_INTERPOL_TYPE Type, IN UINT64 StartTime, IN UINT64 Duration ) { mBpAnimInfoImageList.Type = Type; mBpAnimInfoImageList.StartTime = StartTime; mBpAnimInfoImageList.Duration = Duration; mBpAnimInfoImageList.StartValue = 0; mBpAnimInfoImageList.EndValue = 5; mBootPickerOpacity = 0; } BOOLEAN InternalBootPickerAnimateImageList ( IN BOOT_PICKER_GUI_CONTEXT *Context, IN OUT GUI_DRAWING_CONTEXT *DrawContext, IN UINT64 CurrentTime ) { #if 0 GUI_VOLUME_ENTRY *Entry; CONST GUI_IMAGE *EntryIcon; Entry = BASE_CR (&mBootPicker.Hdr.Obj, GUI_VOLUME_ENTRY, Hdr.Obj); EntryIcon = &Entry->EntryIcon; mBootPickerImageIndex++; mBootPickerImageIndex = (UINT8)GuiGetInterpolatedValue (&mBpAnimInfoImageList, CurrentTime); Entry->EntryIcon = &((GUI_IMAGE*)Context)[mBootPickerImageIndex]; GuiRedrawObject ( &mBootPicker.Hdr.Obj, DrawContext, mBootPicker.Hdr.Obj.OffsetX, mBootPicker.Hdr.Obj.OffsetY, TRUE ); if (mBootPickerImageIndex == mBpAnimInfoImageList.EndValue) { return TRUE; } #endif return FALSE; } STATIC GUI_INTERPOLATION mBpAnimInfoSinMove; VOID InitBpAnimSinMov ( IN GUI_INTERPOL_TYPE Type, IN UINT64 StartTime, IN UINT64 Duration ) { mBpAnimInfoSinMove.Type = Type; mBpAnimInfoSinMove.StartTime = StartTime; mBpAnimInfoSinMove.Duration = Duration; mBpAnimInfoSinMove.StartValue = 0; mBpAnimInfoSinMove.EndValue = 35; } BOOLEAN InternalBootPickerAnimateSinMov ( IN BOOT_PICKER_GUI_CONTEXT *Context, IN OUT GUI_DRAWING_CONTEXT *DrawContext, IN UINT64 CurrentTime ) { STATIC BOOLEAN First = TRUE; STATIC BOOLEAN Minus = TRUE; STATIC INT64 InitOffsetX = 0; INT64 OldOffsetX; UINT32 InterpolVal; ASSERT (DrawContext != NULL); OldOffsetX = mBootPicker.Hdr.Obj.OffsetX; if (First) { First = FALSE; InitOffsetX = OldOffsetX + 35; } InterpolVal = GuiGetInterpolatedValue (&mBpAnimInfoSinMove, CurrentTime); if (Minus) { mBootPicker.Hdr.Obj.OffsetX = InitOffsetX - InterpolVal; } else { mBootPicker.Hdr.Obj.OffsetX = InitOffsetX + InterpolVal; } GuiDrawScreen ( DrawContext, MIN (OldOffsetX, mBootPicker.Hdr.Obj.OffsetX), mBootPicker.Hdr.Obj.OffsetY, (UINT32)(mBootPicker.Hdr.Obj.Width + ABS (OldOffsetX - mBootPicker.Hdr.Obj.OffsetX)), mBootPicker.Hdr.Obj.Height, TRUE ); if (InterpolVal == mBpAnimInfoSinMove.EndValue) { return TRUE; /*Minus = !Minus; InitOffsetX = mBootPicker.Hdr.Obj.OffsetX; mBpAnimInfoSinMove.StartTime = CurrentTime;*/ } return FALSE; } EFI_STATUS BootPickerViewInitialize ( OUT GUI_DRAWING_CONTEXT *DrawContext, IN BOOT_PICKER_GUI_CONTEXT *GuiContext, IN GUI_CURSOR_GET_IMAGE GetCursorImage ) { ASSERT (DrawContext != NULL); ASSERT (GuiContext != NULL); ASSERT (GetCursorImage != NULL); GuiViewInitialize ( DrawContext, &mBootPickerView, GetCursorImage, InternalBootPickerExitLoop, GuiContext ); DrawContext->Scale = GuiContext->Scale; mBootPickerSelector.ClickImage = &GuiContext->Icons[ICON_SELECTOR][ICON_TYPE_BASE]; mBootPickerSelector.CurrentImage = &GuiContext->Icons[ICON_SELECTOR][ICON_TYPE_BASE]; mBootPickerSelector.Hdr.Obj.OffsetX = 0; mBootPickerSelector.Hdr.Obj.OffsetY = 0; mBootPickerSelector.Hdr.Obj.Height = BOOT_SELECTOR_HEIGHT * GuiContext->Scale; mBootPickerSelector.Hdr.Obj.Width = BOOT_SELECTOR_WIDTH * GuiContext->Scale; mBootPicker.Hdr.Obj.Height = BOOT_SELECTOR_HEIGHT * GuiContext->Scale; mBootPicker.Hdr.Obj.Width = 0; mBootPicker.Hdr.Obj.OffsetX = mBootPickerView.Width / 2; mBootPicker.Hdr.Obj.OffsetY = (mBootPickerView.Height - mBootPicker.Hdr.Obj.Height) / 2; // TODO: animations should be tied to UI objects, not global // Each object has its own list of animations. // How to animate addition of one or more boot entries? // 1. MOVE: // - Calculate the delta by which each entry moves to the left or to the right. // ∆i = (N(added after) - N(added before)) // Conditions for delta function: // if (!GuiContext->DoneIntroAnimation) { InitBpAnimSinMov (GuiInterpolTypeSmooth, 0, 25); STATIC GUI_ANIMATION PickerAnim; PickerAnim.Context = NULL; PickerAnim.Animate = InternalBootPickerAnimateSinMov; InsertHeadList (&DrawContext->Animations, &PickerAnim.Link); InitBpAnimOpacity (GuiInterpolTypeSmooth, 0, 25); STATIC GUI_ANIMATION PickerAnim2; PickerAnim2.Context = NULL; PickerAnim2.Animate = InternalBootPickerAnimateOpacity; InsertHeadList (&DrawContext->Animations, &PickerAnim2.Link); GuiContext->DoneIntroAnimation = TRUE; } /* InitBpAnimImageList(GuiInterpolTypeLinear, 25, 25); STATIC GUI_ANIMATION PoofAnim; PoofAnim.Context = GuiContext->Poof; PoofAnim.Animate = InternalBootPickerAnimateImageList; InsertHeadList(&DrawContext->Animations, &PoofAnim.Link); */ return EFI_SUCCESS; } VOID BootPickerViewDeinitialize ( IN OUT GUI_DRAWING_CONTEXT *DrawContext, IN OUT BOOT_PICKER_GUI_CONTEXT *GuiContext ) { LIST_ENTRY *ListEntry; LIST_ENTRY *NextEntry; GUI_VOLUME_ENTRY *BootEntry; CONST GUI_SCREEN_CURSOR *ScreenCursor; ListEntry = mBootPicker.Hdr.Obj.Children.BackLink; ASSERT (ListEntry == &mBootPickerSelector.Hdr.Link); // // Last entry is always the selector, which is special and cannot be freed. // ListEntry = ListEntry->BackLink; while (!IsNull (&mBootPicker.Hdr.Obj.Children, ListEntry)) { NextEntry = ListEntry->BackLink; RemoveEntryList (ListEntry); BootEntry = BASE_CR (ListEntry, GUI_VOLUME_ENTRY, Hdr.Link); InternalBootPickerEntryDestruct (BootEntry); ListEntry = NextEntry; } ScreenCursor = GuiViewCurrentCursor (DrawContext); GuiContext->CursorDefaultX = ScreenCursor->X; GuiContext->CursorDefaultY = ScreenCursor->Y; GuiViewDeinitialize (DrawContext); }