mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
2097 lines
61 KiB
C
2097 lines
61 KiB
C
/** @file
|
|
This file is part of OpenCanopy, OpenCore GUI.
|
|
|
|
Copyright (c) 2018-2019, Download-Fritz. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-3-Clause
|
|
**/
|
|
|
|
#include <Uefi.h>
|
|
|
|
#include <IndustryStandard/AppleIcon.h>
|
|
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/UefiRuntimeServicesTableLib.h>
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/OcBootManagementLib.h>
|
|
#include <Library/OcPngLib.h>
|
|
#include <Library/OcStorageLib.h>
|
|
#include <Library/OcMiscLib.h>
|
|
|
|
#include "../OpenCanopy.h"
|
|
#include "../BmfLib.h"
|
|
#include "../GuiApp.h"
|
|
#include "../GuiIo.h"
|
|
#include "BootPicker.h"
|
|
|
|
#include "Common.h"
|
|
#include "../Blending.h"
|
|
|
|
#define BOOT_LABEL_WRAPAROUND_PADDING 30U
|
|
#define BOOT_LABEL_SCROLLING_HOLD_TIME 180U
|
|
|
|
#define BOOT_LABEL_SHADOW_WIDTH 8U
|
|
#define BOOT_LABEL_SHADOW_HEIGHT BOOT_ENTRY_LABEL_HEIGHT
|
|
|
|
extern GUI_KEY_CONTEXT *mKeyContext;
|
|
|
|
extern GUI_VOLUME_PICKER mBootPicker;
|
|
extern GUI_OBJ_CHILD mBootPickerContainer;
|
|
extern GUI_OBJ_CHILD mBootPickerSelectorContainer;
|
|
extern GUI_OBJ_CHILD mBootPickerSelectorBackground;
|
|
extern GUI_OBJ_CLICKABLE mBootPickerSelectorButton;
|
|
extern GUI_OBJ_CLICKABLE mBootPickerRightScroll;
|
|
extern GUI_OBJ_CLICKABLE mBootPickerLeftScroll;
|
|
extern CONST GUI_IMAGE mBackgroundImage;
|
|
|
|
// STATIC UINT8 mBootPickerImageIndex = 0;
|
|
|
|
extern INT64 mBackgroundImageOffsetX;
|
|
extern INT64 mBackgroundImageOffsetY;
|
|
|
|
STATIC UINT32 mBootPickerLabelScrollHoldTime = 0;
|
|
|
|
STATIC GUI_OBJ *mBootPickerFocusList[] = {
|
|
&mBootPicker.Hdr.Obj,
|
|
&mCommonRestart.Hdr.Obj,
|
|
&mCommonShutDown.Hdr.Obj
|
|
};
|
|
|
|
STATIC GUI_OBJ *mBootPickerFocusListMinimal[] = {
|
|
&mBootPicker.Hdr.Obj
|
|
};
|
|
|
|
STATIC
|
|
GUI_VOLUME_ENTRY *
|
|
InternalGetVolumeEntry (
|
|
IN UINT32 Index
|
|
)
|
|
{
|
|
ASSERT (Index < mBootPicker.Hdr.Obj.NumChildren);
|
|
return (GUI_VOLUME_ENTRY *)(
|
|
mBootPicker.Hdr.Obj.Children[Index]
|
|
);
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
InternalRedrawVolumeLabel (
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN CONST GUI_VOLUME_ENTRY *Entry
|
|
)
|
|
{
|
|
GuiRequestDrawCrop (
|
|
DrawContext,
|
|
mBootPickerContainer.Obj.OffsetX + mBootPicker.Hdr.Obj.OffsetX + Entry->Hdr.Obj.OffsetX,
|
|
(mBootPickerContainer.Obj.OffsetY + mBootPicker.Hdr.Obj.OffsetY + Entry->Hdr.Obj.OffsetY + Entry->Hdr.Obj.Height - Entry->Label.Height),
|
|
Entry->Hdr.Obj.Width,
|
|
Entry->Label.Height
|
|
);
|
|
}
|
|
|
|
BOOLEAN
|
|
InternalBootPickerAnimateLabel (
|
|
IN BOOT_PICKER_GUI_CONTEXT *Context,
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN UINT64 CurrentTime
|
|
)
|
|
{
|
|
GUI_VOLUME_ENTRY *Entry;
|
|
|
|
ASSERT (DrawContext != NULL);
|
|
|
|
if (mBootPickerLabelScrollHoldTime < BOOT_LABEL_SCROLLING_HOLD_TIME) {
|
|
++mBootPickerLabelScrollHoldTime;
|
|
return FALSE;
|
|
}
|
|
|
|
Entry = InternalGetVolumeEntry (mBootPicker.SelectedIndex);
|
|
if (Entry->Label.Width <= Entry->Hdr.Obj.Width) {
|
|
return FALSE;
|
|
}
|
|
|
|
Entry->ShowLeftShadow = TRUE;
|
|
Entry->LabelOffset -= DrawContext->Scale;
|
|
|
|
if (Entry->LabelOffset <= -(INT16)Entry->Label.Width) {
|
|
//
|
|
// Hide the left shadow once the first label is hidden.
|
|
//
|
|
Entry->ShowLeftShadow = FALSE;
|
|
//
|
|
// If the second drawn label reaches the front, switch back to the first.
|
|
//
|
|
if (Entry->LabelOffset <= -(INT16)(Entry->Label.Width + BOOT_LABEL_WRAPAROUND_PADDING * DrawContext->Scale)) {
|
|
Entry->LabelOffset = 0;
|
|
mBootPickerLabelScrollHoldTime = 0;
|
|
}
|
|
}
|
|
|
|
InternalRedrawVolumeLabel (DrawContext, Entry);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
STATIC GUI_ANIMATION mBootPickerLabelAnimation = {
|
|
{ NULL, NULL },
|
|
NULL,
|
|
InternalBootPickerAnimateLabel
|
|
};
|
|
|
|
VOID
|
|
InternalStartAnimateLabel (
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN CONST GUI_VOLUME_ENTRY *Entry
|
|
)
|
|
{
|
|
ASSERT (!IsNodeInList (&DrawContext->Animations, &mBootPickerLabelAnimation.Link));
|
|
//
|
|
// Reset the boot entry label scrolling timer.
|
|
//
|
|
mBootPickerLabelScrollHoldTime = 0;
|
|
|
|
if (Entry->Label.Width > Entry->Hdr.Obj.Width) {
|
|
//
|
|
// Add the animation if the next entry needs scrolling.
|
|
//
|
|
InsertHeadList (&DrawContext->Animations, &mBootPickerLabelAnimation.Link);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
InternalStopAnimateLabel (
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN OUT GUI_VOLUME_ENTRY *Entry
|
|
)
|
|
{
|
|
if (Entry->Label.Width > Entry->Hdr.Obj.Width) {
|
|
//
|
|
// Reset the label of the old entry back to its default position.
|
|
//
|
|
if (Entry->LabelOffset != 0) {
|
|
Entry->ShowLeftShadow = FALSE;
|
|
Entry->LabelOffset = 0;
|
|
InternalRedrawVolumeLabel (DrawContext, Entry);
|
|
}
|
|
|
|
RemoveEntryList (&mBootPickerLabelAnimation.Link);
|
|
InitializeListHead (&mBootPickerLabelAnimation.Link);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
InternalBootPickerSelectEntry (
|
|
IN OUT GUI_VOLUME_PICKER *This,
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN UINT32 NewIndex
|
|
)
|
|
{
|
|
GUI_VOLUME_ENTRY *OldEntry;
|
|
CONST GUI_VOLUME_ENTRY *NewEntry;
|
|
|
|
ASSERT (This != NULL);
|
|
ASSERT (NewIndex < mBootPicker.Hdr.Obj.NumChildren);
|
|
|
|
OldEntry = InternalGetVolumeEntry (This->SelectedIndex);
|
|
This->SelectedIndex = NewIndex;
|
|
NewEntry = InternalGetVolumeEntry (NewIndex);
|
|
|
|
ASSERT (NewEntry->Hdr.Obj.Width <= mBootPickerSelectorContainer.Obj.Width);
|
|
ASSERT_EQUALS (This->Hdr.Obj.Height, mBootPickerSelectorContainer.Obj.OffsetY + mBootPickerSelectorContainer.Obj.Height);
|
|
|
|
mBootPickerSelectorContainer.Obj.OffsetX = mBootPicker.Hdr.Obj.OffsetX + NewEntry->Hdr.Obj.OffsetX - (mBootPickerSelectorContainer.Obj.Width - NewEntry->Hdr.Obj.Width) / 2;
|
|
|
|
if (DrawContext != NULL) {
|
|
if (OldEntry->Label.Width > OldEntry->Hdr.Obj.Width) {
|
|
ASSERT (IsNodeInList (&DrawContext->Animations, &mBootPickerLabelAnimation.Link));
|
|
}
|
|
|
|
InternalStopAnimateLabel (DrawContext, OldEntry);
|
|
InternalStartAnimateLabel (DrawContext, NewEntry);
|
|
//
|
|
// Set voice timeout to N frames from now.
|
|
//
|
|
DrawContext->GuiContext->AudioPlaybackTimeout = OC_VOICE_OVER_IDLE_TIMEOUT_MS;
|
|
DrawContext->GuiContext->VoAction = CanopyVoSelectedEntry;
|
|
DrawContext->GuiContext->BootEntry = NewEntry->Context;
|
|
}
|
|
}
|
|
|
|
INT64
|
|
InternelBootPickerScrollSelected (
|
|
VOID
|
|
)
|
|
{
|
|
CONST GUI_VOLUME_ENTRY *SelectedEntry;
|
|
INT64 EntryOffsetX;
|
|
UINT32 EntryWidth;
|
|
|
|
ASSERT (mBootPicker.SelectedIndex < mBootPicker.Hdr.Obj.NumChildren);
|
|
|
|
//
|
|
// No scroll required if nothing off screen
|
|
//
|
|
if (mBootPicker.Hdr.Obj.Width <= mBootPickerContainer.Obj.Width) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// If the selected entry is outside of the view, scroll it accordingly.
|
|
//
|
|
SelectedEntry = InternalGetVolumeEntry (mBootPicker.SelectedIndex);
|
|
EntryOffsetX = mBootPicker.Hdr.Obj.OffsetX + SelectedEntry->Hdr.Obj.OffsetX - (mBootPickerSelectorContainer.Obj.Width - SelectedEntry->Hdr.Obj.Width) / 2;
|
|
EntryWidth = SelectedEntry->Hdr.Obj.Width + (mBootPickerSelectorContainer.Obj.Width - SelectedEntry->Hdr.Obj.Width);
|
|
|
|
if (EntryOffsetX < 0) {
|
|
return -EntryOffsetX;
|
|
}
|
|
|
|
if (EntryOffsetX + EntryWidth > mBootPickerContainer.Obj.Width) {
|
|
return -((EntryOffsetX + EntryWidth) - mBootPickerContainer.Obj.Width);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
VOID
|
|
InternalUpdateScrollButtons (
|
|
VOID
|
|
)
|
|
{
|
|
if (mBootPicker.Hdr.Obj.OffsetX < 0) {
|
|
mBootPickerLeftScroll.Hdr.Obj.Opacity = 0xFF;
|
|
} else {
|
|
mBootPickerLeftScroll.Hdr.Obj.Opacity = 0;
|
|
}
|
|
|
|
if (mBootPicker.Hdr.Obj.OffsetX + mBootPicker.Hdr.Obj.Width > mBootPickerContainer.Obj.Width) {
|
|
mBootPickerRightScroll.Hdr.Obj.Opacity = 0xFF;
|
|
} else {
|
|
mBootPickerRightScroll.Hdr.Obj.Opacity = 0;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
InternalBootPickerScroll (
|
|
IN OUT GUI_VOLUME_PICKER *This,
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN INT64 BaseX,
|
|
IN INT64 BaseY,
|
|
IN INT64 ScrollOffset
|
|
)
|
|
{
|
|
INT64 ScrollX;
|
|
INT64 ScrollY;
|
|
|
|
mBootPicker.Hdr.Obj.OffsetX += ScrollOffset;
|
|
mBootPickerSelectorContainer.Obj.OffsetX += ScrollOffset;
|
|
InternalUpdateScrollButtons ();
|
|
//
|
|
// The entry list has been scrolled, the entire horizontal space to also cover
|
|
// the scroll buttons.
|
|
//
|
|
DEBUG_CODE_BEGIN ();
|
|
GuiGetBaseCoords (
|
|
&mBootPickerLeftScroll.Hdr.Obj,
|
|
DrawContext,
|
|
&ScrollX,
|
|
&ScrollY
|
|
);
|
|
ASSERT (ScrollY >= BaseY);
|
|
ASSERT (ScrollY + mBootPickerLeftScroll.Hdr.Obj.Height <= BaseY + This->Hdr.Obj.Height);
|
|
|
|
GuiGetBaseCoords (
|
|
&mBootPickerRightScroll.Hdr.Obj,
|
|
DrawContext,
|
|
&ScrollX,
|
|
&ScrollY
|
|
);
|
|
ASSERT (ScrollY >= BaseY);
|
|
ASSERT (ScrollY + mBootPickerRightScroll.Hdr.Obj.Height <= BaseY + This->Hdr.Obj.Height);
|
|
DEBUG_CODE_END ();
|
|
//
|
|
// The container is constructed such that it is always fully visible.
|
|
//
|
|
ASSERT (This->Hdr.Obj.Height <= mBootPickerContainer.Obj.Height);
|
|
ASSERT (BaseY + This->Hdr.Obj.Height <= DrawContext->Screen.Height);
|
|
|
|
GuiRequestDraw (
|
|
0,
|
|
(UINT32)BaseY,
|
|
DrawContext->Screen.Width,
|
|
This->Hdr.Obj.Height
|
|
);
|
|
}
|
|
|
|
VOID
|
|
InternalBootPickerChangeEntry (
|
|
IN OUT GUI_VOLUME_PICKER *This,
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN INT64 BaseX,
|
|
IN INT64 BaseY,
|
|
IN UINT32 NewIndex
|
|
)
|
|
{
|
|
INT64 ScrollOffset;
|
|
INT64 OldSelectorOffsetX;
|
|
|
|
ASSERT (This != NULL);
|
|
ASSERT (DrawContext != NULL);
|
|
ASSERT (NewIndex < This->Hdr.Obj.NumChildren);
|
|
//
|
|
// The caller must guarantee the entry is actually new for performance
|
|
// reasons.
|
|
//
|
|
ASSERT (This->SelectedIndex != NewIndex);
|
|
|
|
OldSelectorOffsetX = mBootPickerSelectorContainer.Obj.OffsetX;
|
|
InternalBootPickerSelectEntry (This, DrawContext, NewIndex);
|
|
|
|
ScrollOffset = InternelBootPickerScrollSelected ();
|
|
if (ScrollOffset == 0) {
|
|
GuiRequestDrawCrop (
|
|
DrawContext,
|
|
mBootPickerContainer.Obj.OffsetX + OldSelectorOffsetX,
|
|
mBootPickerContainer.Obj.OffsetY + mBootPickerSelectorContainer.Obj.OffsetY,
|
|
mBootPickerSelectorContainer.Obj.Width,
|
|
mBootPickerSelectorContainer.Obj.Height
|
|
);
|
|
|
|
GuiRequestDrawCrop (
|
|
DrawContext,
|
|
mBootPickerContainer.Obj.OffsetX + mBootPickerSelectorContainer.Obj.OffsetX,
|
|
mBootPickerContainer.Obj.OffsetY + mBootPickerSelectorContainer.Obj.OffsetY,
|
|
mBootPickerSelectorContainer.Obj.Width,
|
|
mBootPickerSelectorContainer.Obj.Height
|
|
);
|
|
} else {
|
|
InternalBootPickerScroll (
|
|
This,
|
|
DrawContext,
|
|
BaseX,
|
|
BaseY,
|
|
ScrollOffset
|
|
);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
InternalBootPickerKeyEvent (
|
|
IN OUT GUI_OBJ *This,
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN BOOT_PICKER_GUI_CONTEXT *GuiContext,
|
|
IN CONST GUI_KEY_EVENT *KeyEvent
|
|
)
|
|
{
|
|
GUI_VOLUME_PICKER *Picker;
|
|
INT64 BaseX;
|
|
INT64 BaseY;
|
|
CONST GUI_VOLUME_ENTRY *SelectedEntry;
|
|
UINT8 ImageId;
|
|
|
|
ASSERT (This != NULL);
|
|
ASSERT (GuiContext != NULL);
|
|
ASSERT (DrawContext != NULL);
|
|
|
|
if ((KeyEvent->OcModifiers & OC_MODIFIERS_SET_DEFAULT) == 0) {
|
|
ImageId = ICON_SELECTOR;
|
|
} else {
|
|
ImageId = ICON_SET_DEFAULT;
|
|
}
|
|
|
|
if (mBootPickerSelectorButton.ImageId != ImageId) {
|
|
mBootPickerSelectorButton.ImageId = ImageId;
|
|
GuiRequestDraw (
|
|
(UINT32)(mBootPickerContainer.Obj.OffsetX + mBootPickerSelectorContainer.Obj.OffsetX + mBootPickerSelectorButton.Hdr.Obj.OffsetX),
|
|
(UINT32)(mBootPickerContainer.Obj.OffsetY + mBootPickerSelectorContainer.Obj.OffsetY + mBootPickerSelectorButton.Hdr.Obj.OffsetY),
|
|
mBootPickerSelectorButton.Hdr.Obj.Width,
|
|
mBootPickerSelectorButton.Hdr.Obj.Height
|
|
);
|
|
}
|
|
|
|
Picker = BASE_CR (This, GUI_VOLUME_PICKER, Hdr.Obj);
|
|
|
|
BaseX = mBootPickerContainer.Obj.OffsetX + mBootPicker.Hdr.Obj.OffsetX;
|
|
BaseY = mBootPickerContainer.Obj.OffsetY + mBootPicker.Hdr.Obj.OffsetY;
|
|
|
|
if (KeyEvent->OcKeyCode == OC_INPUT_RIGHT) {
|
|
if (mBootPicker.Hdr.Obj.NumChildren > 1) {
|
|
InternalBootPickerChangeEntry (
|
|
Picker,
|
|
DrawContext,
|
|
BaseX,
|
|
BaseY,
|
|
mBootPicker.SelectedIndex + 1 < mBootPicker.Hdr.Obj.NumChildren
|
|
? mBootPicker.SelectedIndex + 1
|
|
: 0
|
|
);
|
|
}
|
|
} else if (KeyEvent->OcKeyCode == OC_INPUT_LEFT) {
|
|
ASSERT (mBootPicker.Hdr.Obj.NumChildren > 0);
|
|
if (mBootPicker.Hdr.Obj.NumChildren > 1) {
|
|
InternalBootPickerChangeEntry (
|
|
Picker,
|
|
DrawContext,
|
|
BaseX,
|
|
BaseY,
|
|
mBootPicker.SelectedIndex > 0
|
|
? mBootPicker.SelectedIndex - 1
|
|
: mBootPicker.Hdr.Obj.NumChildren - 1
|
|
);
|
|
}
|
|
} else if (KeyEvent->OcKeyCode == OC_INPUT_CONTINUE) {
|
|
if (mBootPicker.Hdr.Obj.NumChildren > 0) {
|
|
SelectedEntry = InternalGetVolumeEntry (mBootPicker.SelectedIndex);
|
|
SelectedEntry->Context->SetDefault = (KeyEvent->OcModifiers & OC_MODIFIERS_SET_DEFAULT) != 0;
|
|
GuiContext->ReadyToBoot = TRUE;
|
|
ASSERT (GuiContext->BootEntry == SelectedEntry->Context);
|
|
}
|
|
} else if (mBootPickerContainer.Obj.Opacity != 0xFF) {
|
|
//
|
|
// FIXME: Other keys are not allowed when boot picker is partially transparent.
|
|
//
|
|
return;
|
|
}
|
|
|
|
if (KeyEvent->OcKeyCode == OC_INPUT_MORE) {
|
|
//
|
|
// Match Builtin picker logic here: only refresh if the keypress makes a change
|
|
//
|
|
if (GuiContext->HideAuxiliary) {
|
|
GuiContext->HideAuxiliary = FALSE;
|
|
GuiContext->Refresh = TRUE;
|
|
DrawContext->GuiContext->PickerContext->PlayAudioFile (
|
|
DrawContext->GuiContext->PickerContext,
|
|
OC_VOICE_OVER_AUDIO_FILE_SHOW_AUXILIARY,
|
|
OC_VOICE_OVER_AUDIO_BASE_TYPE_OPEN_CORE,
|
|
FALSE
|
|
);
|
|
}
|
|
} else if (KeyEvent->OcKeyCode == OC_INPUT_ABORTED) {
|
|
GuiContext->Refresh = TRUE;
|
|
DrawContext->GuiContext->PickerContext->PlayAudioFile (
|
|
DrawContext->GuiContext->PickerContext,
|
|
OC_VOICE_OVER_AUDIO_FILE_RELOADING,
|
|
OC_VOICE_OVER_AUDIO_BASE_TYPE_OPEN_CORE,
|
|
FALSE
|
|
);
|
|
}
|
|
}
|
|
|
|
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 UINT8 Opacity
|
|
)
|
|
{
|
|
UINT32 ShadowWidth;
|
|
UINT32 ShadowStart;
|
|
UINT32 ShadowEnd;
|
|
UINT32 LeftShadowSize;
|
|
UINT32 RightShadowSize1;
|
|
UINT32 RightShadowSize2;
|
|
UINT32 LabelWidth;
|
|
UINT32 ColumnOffset;
|
|
UINT8 ShadowOpacity;
|
|
INT64 LabelOffset;
|
|
|
|
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;
|
|
//
|
|
// Draw the icon horizontally centered.
|
|
//
|
|
ASSERT (EntryIcon != NULL);
|
|
ASSERT_EQUALS (EntryIcon->Width, This->Width);
|
|
|
|
if (OffsetY < EntryIcon->Height) {
|
|
GuiDrawToBuffer (
|
|
EntryIcon,
|
|
Opacity,
|
|
DrawContext,
|
|
BaseX,
|
|
BaseY,
|
|
OffsetX,
|
|
OffsetY,
|
|
Width,
|
|
Height
|
|
);
|
|
}
|
|
|
|
ASSERT (Label->Height <= BOOT_ENTRY_LABEL_HEIGHT * DrawContext->Scale);
|
|
|
|
if (Entry->Label.Width <= This->Width) {
|
|
//
|
|
// Draw the label horizontally centered.
|
|
//
|
|
GuiDrawChildImage (
|
|
Label,
|
|
Opacity,
|
|
DrawContext,
|
|
BaseX,
|
|
BaseY,
|
|
Entry->LabelOffset,
|
|
This->Height - Label->Height,
|
|
OffsetX,
|
|
OffsetY,
|
|
Width,
|
|
Height
|
|
);
|
|
} else {
|
|
//
|
|
// Draw scrolling label with fade-in and fade-out.
|
|
//
|
|
ShadowWidth = BOOT_LABEL_SHADOW_WIDTH * DrawContext->Scale;
|
|
|
|
if (!Entry->ShowLeftShadow) {
|
|
LeftShadowSize = 0;
|
|
} else {
|
|
//
|
|
// Draw left shadow one column at a time.
|
|
//
|
|
ShadowStart = OffsetX; // <- No left clip, unsigned
|
|
|
|
if (OffsetX + Width > ShadowWidth) {
|
|
ShadowEnd = ShadowWidth;
|
|
} else {
|
|
ShadowEnd = OffsetX + Width;
|
|
}
|
|
|
|
if (ShadowEnd <= ShadowStart) {
|
|
LeftShadowSize = 0;
|
|
} else {
|
|
LeftShadowSize = ShadowEnd - ShadowStart;
|
|
|
|
for (ColumnOffset = ShadowStart; ColumnOffset < ShadowEnd; ++ColumnOffset) {
|
|
ShadowOpacity = (UINT8)(((ColumnOffset + 1) * 0xFF) / (ShadowWidth + 1));
|
|
|
|
ShadowOpacity = RGB_APPLY_OPACITY (
|
|
Opacity,
|
|
ShadowOpacity
|
|
);
|
|
|
|
GuiDrawChildImage (
|
|
Label,
|
|
ShadowOpacity,
|
|
DrawContext,
|
|
BaseX,
|
|
BaseY,
|
|
Entry->LabelOffset,
|
|
This->Height - Label->Height,
|
|
ColumnOffset,
|
|
OffsetY,
|
|
1,
|
|
Height
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the label needs scrolling, draw a second one to get a wraparound effect.
|
|
//
|
|
|
|
//
|
|
// Draw right shadow one column at a time.
|
|
//
|
|
if (OffsetX < This->Width - ShadowWidth) {
|
|
ShadowStart = This->Width - ShadowWidth;
|
|
} else {
|
|
ShadowStart = OffsetX;
|
|
}
|
|
|
|
if (OffsetX + Width > This->Width) {
|
|
ShadowEnd = This->Width;
|
|
} else {
|
|
ShadowEnd = OffsetX + Width;
|
|
}
|
|
|
|
if (ShadowEnd <= ShadowStart) {
|
|
RightShadowSize1 = 0;
|
|
RightShadowSize2 = 0;
|
|
} else {
|
|
//
|
|
// Right shadow needs to be drawn on first or second label depending on
|
|
// position of scroll, but never on both because of the padding between them.
|
|
//
|
|
STATIC_ASSERT (BOOT_LABEL_WRAPAROUND_PADDING >= BOOT_LABEL_SHADOW_WIDTH, "additional code required for wraparound padding < shadow width");
|
|
|
|
LabelOffset = Entry->LabelOffset + Entry->Label.Width + BOOT_LABEL_WRAPAROUND_PADDING * DrawContext->Scale;
|
|
if (LabelOffset > This->Width) {
|
|
LabelOffset = Entry->LabelOffset;
|
|
RightShadowSize1 = ShadowEnd - ShadowStart;
|
|
RightShadowSize2 = 0;
|
|
} else {
|
|
RightShadowSize1 = 0;
|
|
RightShadowSize2 = ShadowEnd - ShadowStart;
|
|
}
|
|
|
|
for (ColumnOffset = ShadowStart; ColumnOffset < ShadowEnd; ++ColumnOffset) {
|
|
ShadowOpacity = (UINT8)(((ColumnOffset - (This->Width - ShadowWidth) + 1) * 0xFF) / (ShadowWidth + 1));
|
|
|
|
ShadowOpacity = RGB_APPLY_OPACITY (
|
|
Opacity,
|
|
0xFF - ShadowOpacity
|
|
);
|
|
|
|
GuiDrawChildImage (
|
|
Label,
|
|
ShadowOpacity,
|
|
DrawContext,
|
|
BaseX,
|
|
BaseY,
|
|
LabelOffset,
|
|
This->Height - Label->Height,
|
|
ColumnOffset,
|
|
OffsetY,
|
|
1,
|
|
Height
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Draw non-shaded part of first label.
|
|
//
|
|
LabelWidth = Width - MIN (Width, LeftShadowSize);
|
|
LabelWidth = LabelWidth - MIN (LabelWidth, RightShadowSize1);
|
|
if (LabelWidth > 0) {
|
|
GuiDrawChildImage (
|
|
Label,
|
|
Opacity,
|
|
DrawContext,
|
|
BaseX,
|
|
BaseY,
|
|
Entry->LabelOffset,
|
|
This->Height - Label->Height,
|
|
OffsetX + LeftShadowSize,
|
|
OffsetY,
|
|
LabelWidth,
|
|
Height
|
|
);
|
|
}
|
|
|
|
//
|
|
// Draw non-shaded part of second label.
|
|
//
|
|
LabelWidth = Width - MIN (Width, RightShadowSize2);
|
|
if (LabelWidth > 0) {
|
|
GuiDrawChildImage (
|
|
Label,
|
|
Opacity,
|
|
DrawContext,
|
|
BaseX,
|
|
BaseY,
|
|
(INT64)Entry->LabelOffset + Entry->Label.Width + BOOT_LABEL_WRAPAROUND_PADDING * DrawContext->Scale,
|
|
This->Height - Label->Height,
|
|
OffsetX,
|
|
OffsetY,
|
|
LabelWidth,
|
|
Height
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// There should be no children.
|
|
//
|
|
ASSERT (This->NumChildren == 0);
|
|
}
|
|
|
|
STATIC
|
|
GUI_OBJ *
|
|
InternalBootPickerEntryPtrEvent (
|
|
IN OUT GUI_OBJ *This,
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN BOOT_PICKER_GUI_CONTEXT *Context,
|
|
IN INT64 BaseX,
|
|
IN INT64 BaseY,
|
|
IN CONST GUI_PTR_EVENT *Event
|
|
)
|
|
{
|
|
GUI_VOLUME_ENTRY *Entry;
|
|
BOOLEAN IsHit;
|
|
UINT32 OffsetX;
|
|
UINT32 OffsetY;
|
|
|
|
OffsetX = (UINT32)(Event->Pos.Pos.X - BaseX);
|
|
OffsetY = (UINT32)(Event->Pos.Pos.Y - BaseY);
|
|
|
|
ASSERT (
|
|
Event->Type == GuiPointerPrimaryDown
|
|
|| Event->Type == GuiPointerPrimaryUp
|
|
|| Event->Type == GuiPointerPrimaryDoubleClick
|
|
);
|
|
|
|
Entry = BASE_CR (This, GUI_VOLUME_ENTRY, Hdr.Obj);
|
|
|
|
IsHit = GuiClickableIsHit (
|
|
&Entry->EntryIcon,
|
|
OffsetX,
|
|
OffsetY
|
|
);
|
|
if (!IsHit) {
|
|
return This;
|
|
}
|
|
|
|
if (Event->Type == GuiPointerPrimaryDown) {
|
|
if (mBootPicker.SelectedIndex != Entry->Index) {
|
|
ASSERT (Entry->Hdr.Parent == &mBootPicker.Hdr.Obj);
|
|
InternalBootPickerChangeEntry (
|
|
&mBootPicker,
|
|
DrawContext,
|
|
BaseX - This->OffsetX,
|
|
BaseY - This->OffsetY,
|
|
Entry->Index
|
|
);
|
|
}
|
|
} else if (Event->Type == GuiPointerPrimaryDoubleClick) {
|
|
//
|
|
// This must be ensured because the UI directs Move/Up events to the object
|
|
// Down had been sent to.
|
|
//
|
|
ASSERT (mBootPicker.SelectedIndex == Entry->Index);
|
|
Context->ReadyToBoot = TRUE;
|
|
}
|
|
|
|
//
|
|
// There should be no children.
|
|
//
|
|
ASSERT (This->NumChildren == 0);
|
|
return This;
|
|
}
|
|
|
|
VOID
|
|
InternalBootPickerSelectorBackgroundDraw (
|
|
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 UINT8 Opacity
|
|
)
|
|
{
|
|
CONST GUI_IMAGE *BackgroundImage;
|
|
|
|
ASSERT (This != NULL);
|
|
ASSERT (DrawContext != NULL);
|
|
ASSERT (Context != NULL);
|
|
|
|
BackgroundImage = &Context->Icons[ICON_SELECTED][ICON_TYPE_BASE];
|
|
|
|
ASSERT_EQUALS (This->Width, BackgroundImage->Width);
|
|
ASSERT_EQUALS (This->Height, BackgroundImage->Height);
|
|
|
|
GuiDrawToBuffer (
|
|
BackgroundImage,
|
|
Opacity,
|
|
DrawContext,
|
|
BaseX,
|
|
BaseY,
|
|
OffsetX,
|
|
OffsetY,
|
|
Width,
|
|
Height
|
|
);
|
|
//
|
|
// There should be no children.
|
|
//
|
|
ASSERT (This->NumChildren == 0);
|
|
}
|
|
|
|
GUI_OBJ *
|
|
InternalBootPickerSelectorPtrEvent (
|
|
IN OUT GUI_OBJ *This,
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN BOOT_PICKER_GUI_CONTEXT *Context,
|
|
IN INT64 BaseX,
|
|
IN INT64 BaseY,
|
|
IN CONST GUI_PTR_EVENT *Event
|
|
)
|
|
{
|
|
UINT8 Result;
|
|
|
|
Result = InternalCommonSimpleButtonPtrEvent (
|
|
This,
|
|
DrawContext,
|
|
Context,
|
|
BaseX,
|
|
BaseY,
|
|
Event
|
|
);
|
|
switch (Result) {
|
|
case CommonPtrNotHit:
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
case CommonPtrAction:
|
|
{
|
|
Context->ReadyToBoot = TRUE;
|
|
//
|
|
// Falthrough to 'hit' case.
|
|
//
|
|
}
|
|
|
|
case CommonPtrHit:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return This;
|
|
}
|
|
|
|
GUI_OBJ *
|
|
InternalBootPickerLeftScrollPtrEvent (
|
|
IN OUT GUI_OBJ *This,
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN BOOT_PICKER_GUI_CONTEXT *Context,
|
|
IN INT64 BaseX,
|
|
IN INT64 BaseY,
|
|
IN CONST GUI_PTR_EVENT *Event
|
|
)
|
|
{
|
|
UINT8 Result;
|
|
|
|
INT64 BootPickerX;
|
|
INT64 BootPickerY;
|
|
CONST GUI_VOLUME_ENTRY *SelectedEntry;
|
|
|
|
if (This->Opacity == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
Result = InternalCommonSimpleButtonPtrEvent (
|
|
This,
|
|
DrawContext,
|
|
Context,
|
|
BaseX,
|
|
BaseY,
|
|
Event
|
|
);
|
|
switch (Result) {
|
|
case CommonPtrNotHit:
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
case CommonPtrAction:
|
|
{
|
|
//
|
|
// The view can only be scrolled when there are off-screen entries.
|
|
//
|
|
GuiGetBaseCoords (
|
|
&mBootPicker.Hdr.Obj,
|
|
DrawContext,
|
|
&BootPickerX,
|
|
&BootPickerY
|
|
);
|
|
//
|
|
// Scroll the boot entry view by one spot.
|
|
//
|
|
InternalBootPickerScroll (
|
|
&mBootPicker,
|
|
DrawContext,
|
|
BootPickerX,
|
|
BootPickerY,
|
|
(BOOT_ENTRY_WIDTH + BOOT_ENTRY_SPACE) * Context->Scale
|
|
);
|
|
//
|
|
// If the selected entry is pushed off-screen by scrolling, select the
|
|
// appropriate neighbour entry.
|
|
//
|
|
SelectedEntry = InternalGetVolumeEntry (mBootPicker.SelectedIndex);
|
|
if (mBootPicker.Hdr.Obj.OffsetX + SelectedEntry->Hdr.Obj.OffsetX + SelectedEntry->Hdr.Obj.Width > mBootPickerContainer.Obj.Width) {
|
|
//
|
|
// The internal design ensures a selected entry cannot be off-screen,
|
|
// scrolling offsets it by at most one spot.
|
|
//
|
|
InternalBootPickerSelectEntry (
|
|
&mBootPicker,
|
|
DrawContext,
|
|
mBootPicker.SelectedIndex > 0
|
|
? mBootPicker.SelectedIndex - 1
|
|
: mBootPicker.Hdr.Obj.NumChildren - 1
|
|
);
|
|
|
|
SelectedEntry = InternalGetVolumeEntry (mBootPicker.SelectedIndex);
|
|
ASSERT (!(mBootPicker.Hdr.Obj.OffsetX + SelectedEntry->Hdr.Obj.OffsetX + SelectedEntry->Hdr.Obj.Width > mBootPickerContainer.Obj.Width));
|
|
}
|
|
|
|
//
|
|
// Falthrough to 'hit' case.
|
|
//
|
|
}
|
|
|
|
case CommonPtrHit:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return This;
|
|
}
|
|
|
|
GUI_OBJ *
|
|
InternalBootPickerRightScrollPtrEvent (
|
|
IN OUT GUI_OBJ *This,
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN BOOT_PICKER_GUI_CONTEXT *Context,
|
|
IN INT64 BaseX,
|
|
IN INT64 BaseY,
|
|
IN CONST GUI_PTR_EVENT *Event
|
|
)
|
|
{
|
|
UINT8 Result;
|
|
|
|
INT64 BootPickerX;
|
|
INT64 BootPickerY;
|
|
GUI_VOLUME_ENTRY *SelectedEntry;
|
|
|
|
if (This->Opacity == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
Result = InternalCommonSimpleButtonPtrEvent (
|
|
This,
|
|
DrawContext,
|
|
Context,
|
|
BaseX,
|
|
BaseY,
|
|
Event
|
|
);
|
|
switch (Result) {
|
|
case CommonPtrNotHit:
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
case CommonPtrAction:
|
|
{
|
|
//
|
|
// The view can only be scrolled when there are off-screen entries.
|
|
//
|
|
GuiGetBaseCoords (
|
|
&mBootPicker.Hdr.Obj,
|
|
DrawContext,
|
|
&BootPickerX,
|
|
&BootPickerY
|
|
);
|
|
//
|
|
// Scroll the boot entry view by one spot.
|
|
//
|
|
InternalBootPickerScroll (
|
|
&mBootPicker,
|
|
DrawContext,
|
|
BootPickerX,
|
|
BootPickerY,
|
|
-(INT64)(BOOT_ENTRY_WIDTH + BOOT_ENTRY_SPACE) * Context->Scale
|
|
);
|
|
//
|
|
// If the selected entry is pushed off-screen by scrolling, select the
|
|
// appropriate neighbour entry.
|
|
//
|
|
SelectedEntry = InternalGetVolumeEntry (mBootPicker.SelectedIndex);
|
|
if (mBootPicker.Hdr.Obj.OffsetX + SelectedEntry->Hdr.Obj.OffsetX < 0) {
|
|
//
|
|
// The internal design ensures a selected entry cannot be off-screen,
|
|
// scrolling offsets it by at most one spot.
|
|
//
|
|
InternalBootPickerSelectEntry (
|
|
&mBootPicker,
|
|
DrawContext,
|
|
mBootPicker.SelectedIndex + 1 < mBootPicker.Hdr.Obj.NumChildren
|
|
? mBootPicker.SelectedIndex + 1
|
|
: 0
|
|
);
|
|
|
|
SelectedEntry = InternalGetVolumeEntry (mBootPicker.SelectedIndex);
|
|
ASSERT (!(mBootPicker.Hdr.Obj.OffsetX + SelectedEntry->Hdr.Obj.OffsetX < 0));
|
|
}
|
|
|
|
//
|
|
// Falthrough to 'hit' case.
|
|
//
|
|
}
|
|
|
|
case CommonPtrHit:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return This;
|
|
}
|
|
|
|
STATIC GUI_IMAGE mVersionLabelImage;
|
|
|
|
VOID
|
|
InternalVersionLabelDraw (
|
|
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 UINT8 Opacity
|
|
)
|
|
{
|
|
if (mVersionLabelImage.Buffer != NULL) {
|
|
GuiDrawToBuffer (
|
|
&mVersionLabelImage,
|
|
Opacity,
|
|
DrawContext,
|
|
BaseX,
|
|
BaseY,
|
|
OffsetX,
|
|
OffsetY,
|
|
Width,
|
|
Height
|
|
);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
InternalBootPickerViewKeyEvent (
|
|
IN OUT GUI_OBJ *This,
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN BOOT_PICKER_GUI_CONTEXT *Context,
|
|
IN CONST GUI_KEY_EVENT *KeyEvent
|
|
)
|
|
{
|
|
GUI_OBJ *FocusChangedObj;
|
|
GUI_VOLUME_ENTRY *Entry;
|
|
|
|
ASSERT (This != NULL);
|
|
ASSERT (DrawContext != NULL);
|
|
|
|
if (KeyEvent->OcKeyCode == OC_INPUT_VOICE_OVER) {
|
|
DrawContext->GuiContext->PickerContext->ToggleVoiceOver (
|
|
DrawContext->GuiContext->PickerContext,
|
|
NULL,
|
|
NULL
|
|
);
|
|
return;
|
|
}
|
|
|
|
Entry = InternalGetVolumeEntry (mBootPicker.SelectedIndex);
|
|
|
|
FocusChangedObj = InternalFocusKeyHandler (
|
|
DrawContext,
|
|
Context,
|
|
KeyEvent
|
|
);
|
|
if (FocusChangedObj == &mBootPicker.Hdr.Obj) {
|
|
InternalStartAnimateLabel (DrawContext, Entry);
|
|
} else if (FocusChangedObj != NULL) {
|
|
if (!IsListEmpty (&mBootPickerLabelAnimation.Link)) {
|
|
InternalStopAnimateLabel (DrawContext, Entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
InternalBootPickerFocus (
|
|
IN CONST GUI_OBJ *This,
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN BOOLEAN Focus
|
|
)
|
|
{
|
|
if (!Focus) {
|
|
mBootPickerSelectorContainer.Obj.Opacity = 0;
|
|
} else {
|
|
mBootPickerSelectorContainer.Obj.Opacity = 0xFF;
|
|
|
|
DrawContext->GuiContext->AudioPlaybackTimeout = 0;
|
|
DrawContext->GuiContext->VoAction = CanopyVoSelectedEntry;
|
|
}
|
|
|
|
GuiRequestDraw (
|
|
(UINT32)(mBootPickerContainer.Obj.OffsetX + mBootPickerSelectorContainer.Obj.OffsetX),
|
|
(UINT32)(mBootPickerContainer.Obj.OffsetY + mBootPickerSelectorContainer.Obj.OffsetY),
|
|
mBootPickerSelectorContainer.Obj.Width,
|
|
mBootPickerSelectorContainer.Obj.Height
|
|
);
|
|
}
|
|
|
|
BOOLEAN
|
|
InternalBootPickerExitLoop (
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN BOOT_PICKER_GUI_CONTEXT *Context
|
|
)
|
|
{
|
|
ASSERT (Context != NULL);
|
|
|
|
if (Context->ReadyToBoot) {
|
|
ASSERT (Context->BootEntry == InternalGetVolumeEntry (mBootPicker.SelectedIndex)->Context);
|
|
}
|
|
|
|
return Context->ReadyToBoot || Context->Refresh;
|
|
}
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED GUI_OBJ_CHILD mBootPickerSelectorBackground = {
|
|
{
|
|
0, 0, 0, 0, 0xFF,
|
|
InternalBootPickerSelectorBackgroundDraw,
|
|
NULL,
|
|
GuiObjDelegatePtrEvent,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
},
|
|
&mBootPickerSelectorContainer.Obj
|
|
};
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED GUI_OBJ_CLICKABLE mBootPickerSelectorButton = {
|
|
{
|
|
{
|
|
0, 0, 0, 0, 0xFF,
|
|
InternalCommonSimpleButtonDraw,
|
|
NULL,
|
|
InternalBootPickerSelectorPtrEvent,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
},
|
|
&mBootPickerSelectorContainer.Obj
|
|
},
|
|
0,
|
|
0
|
|
};
|
|
|
|
STATIC GUI_OBJ_CHILD *mBootPickerSelectorContainerChildren[] = {
|
|
&mBootPickerSelectorBackground,
|
|
&mBootPickerSelectorButton.Hdr
|
|
};
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED GUI_OBJ_CHILD mBootPickerSelectorContainer = {
|
|
{
|
|
0, 0, 0, 0, 0xFF,
|
|
GuiObjDrawDelegate,
|
|
NULL,
|
|
GuiObjDelegatePtrEvent,
|
|
NULL,
|
|
ARRAY_SIZE (mBootPickerSelectorContainerChildren),
|
|
mBootPickerSelectorContainerChildren
|
|
},
|
|
&mBootPickerContainer.Obj
|
|
};
|
|
|
|
STATIC GUI_OBJ_CHILD *mBootPickerContainerChildren[] = {
|
|
&mBootPickerSelectorContainer,
|
|
&mBootPicker.Hdr
|
|
};
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED GUI_OBJ_CHILD mBootPickerContainer = {
|
|
{
|
|
0, 0, 0, 0, 0xFF,
|
|
GuiObjDrawDelegate,
|
|
NULL,
|
|
GuiObjDelegatePtrEvent,
|
|
NULL,
|
|
ARRAY_SIZE (mBootPickerContainerChildren),
|
|
mBootPickerContainerChildren
|
|
},
|
|
NULL
|
|
};
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED GUI_VOLUME_PICKER mBootPicker = {
|
|
{
|
|
{
|
|
0, 0, 0, 0, 0xFF,
|
|
GuiObjDrawDelegate,
|
|
InternalBootPickerKeyEvent,
|
|
GuiObjDelegatePtrEvent,
|
|
InternalBootPickerFocus,
|
|
0,
|
|
NULL
|
|
},
|
|
&mBootPickerContainer.Obj
|
|
},
|
|
1
|
|
};
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED GUI_OBJ_CLICKABLE mBootPickerLeftScroll = {
|
|
{
|
|
{
|
|
0, 0, 0, 0, 0xFF,
|
|
InternalCommonSimpleButtonDraw,
|
|
NULL,
|
|
InternalBootPickerLeftScrollPtrEvent,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
},
|
|
NULL
|
|
},
|
|
0,
|
|
0
|
|
};
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED GUI_OBJ_CLICKABLE mBootPickerRightScroll = {
|
|
{
|
|
{
|
|
0, 0, 0, 0, 0xFF,
|
|
InternalCommonSimpleButtonDraw,
|
|
NULL,
|
|
InternalBootPickerRightScrollPtrEvent,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
},
|
|
NULL
|
|
},
|
|
0,
|
|
0
|
|
};
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED GUI_OBJ_CHILD mBootPickerVersionLabel = {
|
|
{
|
|
0, 0, 0, 0, 0xFF,
|
|
InternalVersionLabelDraw,
|
|
NULL,
|
|
GuiObjDelegatePtrEvent,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
},
|
|
NULL
|
|
};
|
|
|
|
STATIC GUI_OBJ_CHILD *mBootPickerViewChildren[] = {
|
|
&mBootPickerContainer,
|
|
&mCommonActionButtonsContainer,
|
|
&mBootPickerLeftScroll.Hdr,
|
|
&mBootPickerRightScroll.Hdr,
|
|
&mBootPickerVersionLabel
|
|
};
|
|
|
|
STATIC GUI_OBJ_CHILD *mBootPickerViewChildrenMinimal[] = {
|
|
&mBootPickerContainer,
|
|
&mBootPickerLeftScroll.Hdr,
|
|
&mBootPickerRightScroll.Hdr,
|
|
&mBootPickerVersionLabel
|
|
};
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED GUI_VIEW_CONTEXT mBootPickerViewContext = {
|
|
InternalCommonViewDraw,
|
|
InternalCommonViewPtrEvent,
|
|
ARRAY_SIZE (mBootPickerViewChildren),
|
|
mBootPickerViewChildren,
|
|
InternalBootPickerViewKeyEvent,
|
|
InternalGetCursorImage,
|
|
InternalBootPickerExitLoop,
|
|
mBootPickerFocusList,
|
|
ARRAY_SIZE (mBootPickerFocusList)
|
|
};
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED GUI_VIEW_CONTEXT mBootPickerViewContextMinimal = {
|
|
InternalCommonViewDraw,
|
|
InternalCommonViewPtrEvent,
|
|
ARRAY_SIZE (mBootPickerViewChildrenMinimal),
|
|
mBootPickerViewChildrenMinimal,
|
|
InternalBootPickerViewKeyEvent,
|
|
InternalGetCursorImage,
|
|
InternalBootPickerExitLoop,
|
|
mBootPickerFocusListMinimal,
|
|
ARRAY_SIZE (mBootPickerFocusListMinimal)
|
|
};
|
|
|
|
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
|
|
BootPickerEntriesSet (
|
|
IN OC_PICKER_CONTEXT *Context,
|
|
IN BOOT_PICKER_GUI_CONTEXT *GuiContext,
|
|
IN OC_BOOT_ENTRY *Entry,
|
|
IN UINT8 EntryIndex
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
GUI_VOLUME_ENTRY *VolumeEntry;
|
|
CONST GUI_IMAGE *SuggestedIcon;
|
|
CONST GUI_VOLUME_ENTRY *PrevEntry;
|
|
UINT32 IconFileSize;
|
|
UINT32 IconTypeIndex;
|
|
VOID *IconFileData;
|
|
BOOLEAN UseVolumeIcon;
|
|
BOOLEAN UseFlavourIcon;
|
|
BOOLEAN UseDiskLabel;
|
|
BOOLEAN UseGenericLabel;
|
|
BOOLEAN Result;
|
|
CHAR16 *EntryName;
|
|
UINTN EntryNameLength;
|
|
CHAR8 *FlavourNameStart;
|
|
CHAR8 *FlavourNameEnd;
|
|
|
|
ASSERT (GuiContext != NULL);
|
|
ASSERT (Entry != NULL);
|
|
ASSERT (EntryIndex < mBootPicker.Hdr.Obj.NumChildren);
|
|
|
|
DEBUG ((DEBUG_INFO, "OCUI: Console attributes: %d\n", Context->ConsoleAttributes));
|
|
|
|
UseVolumeIcon = (Context->PickerAttributes & OC_ATTR_USE_VOLUME_ICON) != 0;
|
|
UseFlavourIcon = (Context->PickerAttributes & OC_ATTR_USE_FLAVOUR_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 = Context->GetEntryLabelImage (
|
|
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;
|
|
//
|
|
// Use flavour-based labels for system entries (e.g. from boot entry protocol).
|
|
//
|
|
case OC_BOOT_SYSTEM:
|
|
case OC_BOOT_EXTERNAL_TOOL:
|
|
if (OcAsciiStriStr (Entry->Flavour, OC_FLAVOUR_ID_RESET_NVRAM) != NULL) {
|
|
Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_RESET_NVRAM]);
|
|
} else if (OcAsciiStriStr (Entry->Flavour, OC_FLAVOUR_ID_TOGGLE_SIP_ENABLED) != NULL) {
|
|
Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_SIP_IS_ENABLED]);
|
|
} else if (OcAsciiStriStr (Entry->Flavour, OC_FLAVOUR_ID_TOGGLE_SIP_DISABLED) != NULL) {
|
|
Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_SIP_IS_DISABLED]);
|
|
} else if (OcAsciiStriStr (Entry->Flavour, OC_FLAVOUR_ID_UEFI_SHELL) != NULL) {
|
|
Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_SHELL]);
|
|
} else if (Entry->Type == OC_BOOT_EXTERNAL_TOOL) {
|
|
Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_TOOL]);
|
|
} else {
|
|
DEBUG ((DEBUG_WARN, "OCUI: System entry flavour %a unsupported for label\n", Entry->Flavour));
|
|
Status = EFI_UNSUPPORTED;
|
|
}
|
|
|
|
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));
|
|
Status = EFI_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
EntryName = Entry->Name;
|
|
EntryNameLength = StrLen (Entry->Name);
|
|
if (Entry->IsFolder) {
|
|
EntryName = AllocatePool (EntryNameLength * sizeof (CHAR16) + L_STR_SIZE (OC_MENU_DISK_IMAGE));
|
|
if (EntryName != NULL) {
|
|
CopyMem (EntryName, Entry->Name, EntryNameLength * sizeof (CHAR16));
|
|
CopyMem (&EntryName[EntryNameLength], OC_MENU_DISK_IMAGE, L_STR_SIZE (OC_MENU_DISK_IMAGE));
|
|
EntryNameLength += L_STR_LEN (OC_MENU_DISK_IMAGE);
|
|
} else {
|
|
EntryName = Entry->Name;
|
|
}
|
|
}
|
|
|
|
ASSERT (StrLen (EntryName) == EntryNameLength);
|
|
|
|
Result = GuiGetLabel (
|
|
&VolumeEntry->Label,
|
|
&GuiContext->FontContext,
|
|
EntryName,
|
|
EntryNameLength,
|
|
GuiContext->LightBackground
|
|
);
|
|
|
|
if (EntryName != Entry->Name) {
|
|
FreePool (EntryName);
|
|
}
|
|
|
|
if (!Result) {
|
|
DEBUG ((DEBUG_WARN, "OCUI: label failed\n"));
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
VolumeEntry->Context = Entry;
|
|
|
|
//
|
|
// Load volume icons when allowed.
|
|
// Do not load volume icons for Time Machine entries unless explicitly enabled.
|
|
// This works around Time Machine icon style incompatibilities.
|
|
//
|
|
if ( UseVolumeIcon
|
|
&& ( (Entry->Type != OC_BOOT_APPLE_TIME_MACHINE)
|
|
|| ((Context->PickerAttributes & OC_ATTR_HIDE_THEMED_ICONS) == 0)))
|
|
{
|
|
Status = Context->GetEntryIcon (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;
|
|
}
|
|
|
|
//
|
|
// Flavour system is used internally for icon priorities even when
|
|
// user-specified flavours from .contentFlavour are not being read
|
|
//
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT (Entry->Flavour != NULL);
|
|
|
|
IconTypeIndex = Entry->IsExternal ? ICON_TYPE_EXTERNAL : ICON_TYPE_BASE;
|
|
|
|
FlavourNameEnd = Entry->Flavour - 1;
|
|
do {
|
|
for (FlavourNameStart = ++FlavourNameEnd; *FlavourNameEnd != '\0' && *FlavourNameEnd != ':'; ++FlavourNameEnd) {
|
|
}
|
|
|
|
Status = InternalGetFlavourIcon (
|
|
GuiContext,
|
|
Context->StorageContext,
|
|
FlavourNameStart,
|
|
FlavourNameEnd - FlavourNameStart,
|
|
IconTypeIndex,
|
|
UseFlavourIcon,
|
|
&VolumeEntry->EntryIcon,
|
|
&VolumeEntry->CustomIcon
|
|
);
|
|
} while (EFI_ERROR (Status) && *FlavourNameEnd != '\0');
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
SuggestedIcon = NULL;
|
|
|
|
if (Entry->Type == OC_BOOT_EXTERNAL_OS) {
|
|
SuggestedIcon = &GuiContext->Icons[ICON_OTHER][IconTypeIndex];
|
|
} else if ((Entry->Type & (OC_BOOT_EXTERNAL_TOOL | OC_BOOT_SYSTEM)) != 0) {
|
|
SuggestedIcon = &GuiContext->Icons[ICON_TOOL][IconTypeIndex];
|
|
}
|
|
|
|
if ((SuggestedIcon == NULL) || (SuggestedIcon->Buffer == NULL)) {
|
|
SuggestedIcon = &GuiContext->Icons[ICON_GENERIC_HDD][IconTypeIndex];
|
|
}
|
|
|
|
CopyMem (&VolumeEntry->EntryIcon, SuggestedIcon, sizeof (VolumeEntry->EntryIcon));
|
|
} else {
|
|
DEBUG ((DEBUG_INFO, "OCUI: Using flavour icon, custom: %u\n", VolumeEntry->CustomIcon));
|
|
}
|
|
}
|
|
|
|
VolumeEntry->Hdr.Parent = &mBootPicker.Hdr.Obj;
|
|
VolumeEntry->Hdr.Obj.Width = BOOT_ENTRY_ICON_DIMENSION * GuiContext->Scale;
|
|
VolumeEntry->Hdr.Obj.Height = (BOOT_ENTRY_HEIGHT - BOOT_ENTRY_ICON_SPACE) * GuiContext->Scale;
|
|
VolumeEntry->Hdr.Obj.Opacity = 0xFF;
|
|
VolumeEntry->Hdr.Obj.Draw = InternalBootPickerEntryDraw;
|
|
VolumeEntry->Hdr.Obj.PtrEvent = InternalBootPickerEntryPtrEvent;
|
|
VolumeEntry->Hdr.Obj.NumChildren = 0;
|
|
VolumeEntry->Hdr.Obj.Children = NULL;
|
|
if (VolumeEntry->Hdr.Obj.Width > VolumeEntry->Label.Width) {
|
|
VolumeEntry->LabelOffset = (INT16)((VolumeEntry->Hdr.Obj.Width - VolumeEntry->Label.Width) / 2);
|
|
}
|
|
|
|
if (EntryIndex > 0) {
|
|
PrevEntry = InternalGetVolumeEntry (EntryIndex - 1);
|
|
VolumeEntry->Hdr.Obj.OffsetX = PrevEntry->Hdr.Obj.OffsetX + (BOOT_ENTRY_DIMENSION + BOOT_ENTRY_SPACE) * GuiContext->Scale;
|
|
} else {
|
|
//
|
|
// Account for the selector background.
|
|
//
|
|
VolumeEntry->Hdr.Obj.OffsetX = BOOT_ENTRY_ICON_SPACE * GuiContext->Scale;
|
|
}
|
|
|
|
VolumeEntry->Hdr.Obj.OffsetY = BOOT_ENTRY_ICON_SPACE * GuiContext->Scale;
|
|
|
|
mBootPicker.Hdr.Obj.Children[EntryIndex] = &VolumeEntry->Hdr;
|
|
VolumeEntry->Index = EntryIndex;
|
|
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;
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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
|
|
);
|
|
|
|
if (mBootPickerImageIndex == mBpAnimInfoImageList.EndValue) {
|
|
return TRUE;
|
|
}
|
|
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
STATIC GUI_INTERPOLATION mBpAnimInfoSinMove = {
|
|
GuiInterpolTypeSmooth,
|
|
0,
|
|
25,
|
|
0,
|
|
0,
|
|
0
|
|
};
|
|
|
|
VOID
|
|
InitBpAnimIntro (
|
|
IN CONST GUI_DRAWING_CONTEXT *DrawContext
|
|
)
|
|
{
|
|
mBpAnimInfoSinMove.EndValue = 35 * DrawContext->Scale;
|
|
//
|
|
// FIXME: This assumes that only relative changes of X are performed on
|
|
// mBootPickerContainer between animation initialisation and start.
|
|
//
|
|
mBootPickerContainer.Obj.OffsetX += 35 * DrawContext->Scale;
|
|
}
|
|
|
|
BOOLEAN
|
|
InternalBootPickerAnimateIntro (
|
|
IN BOOT_PICKER_GUI_CONTEXT *Context,
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN UINT64 CurrentTime
|
|
)
|
|
{
|
|
STATIC UINT32 PrevSine = 0;
|
|
|
|
UINT8 Opacity;
|
|
UINT32 InterpolVal;
|
|
UINT32 DeltaSine;
|
|
|
|
ASSERT (DrawContext != NULL);
|
|
|
|
Opacity = (UINT8)GuiGetInterpolatedValue (
|
|
&mCommonIntroOpacityInterpol,
|
|
CurrentTime
|
|
);
|
|
mBootPickerContainer.Obj.Opacity = Opacity;
|
|
//
|
|
// Animate the scroll buttons based on their active state.
|
|
//
|
|
if (mBootPicker.Hdr.Obj.OffsetX < 0) {
|
|
mBootPickerLeftScroll.Hdr.Obj.Opacity = Opacity;
|
|
}
|
|
|
|
if (mBootPicker.Hdr.Obj.OffsetX + mBootPicker.Hdr.Obj.Width > mBootPickerContainer.Obj.Width) {
|
|
mBootPickerRightScroll.Hdr.Obj.Opacity = Opacity;
|
|
}
|
|
|
|
//
|
|
// If PasswordView already faded-in the action buttons, skip them.
|
|
//
|
|
if (mCommonActionButtonsContainer.Obj.Opacity != 0xFF) {
|
|
mCommonActionButtonsContainer.Obj.Opacity = Opacity;
|
|
//
|
|
// The view is constructed such that the action buttons are always fully
|
|
// visible.
|
|
//
|
|
ASSERT (mCommonActionButtonsContainer.Obj.OffsetX >= 0);
|
|
ASSERT (mCommonActionButtonsContainer.Obj.OffsetY >= 0);
|
|
ASSERT (mCommonActionButtonsContainer.Obj.OffsetX + mCommonActionButtonsContainer.Obj.Width <= DrawContext->Screen.Width);
|
|
ASSERT (mCommonActionButtonsContainer.Obj.OffsetY + mCommonActionButtonsContainer.Obj.Height <= DrawContext->Screen.Height);
|
|
GuiRequestDraw (
|
|
(UINT32)mCommonActionButtonsContainer.Obj.OffsetX,
|
|
(UINT32)mCommonActionButtonsContainer.Obj.OffsetY,
|
|
mCommonActionButtonsContainer.Obj.Width,
|
|
mCommonActionButtonsContainer.Obj.Height
|
|
);
|
|
}
|
|
|
|
InterpolVal = GuiGetInterpolatedValue (&mBpAnimInfoSinMove, CurrentTime);
|
|
DeltaSine = InterpolVal - PrevSine;
|
|
mBootPickerContainer.Obj.OffsetX -= DeltaSine;
|
|
PrevSine = InterpolVal;
|
|
//
|
|
// Draw the full dimension of the inner container to implicitly cover the
|
|
// scroll buttons with the off-screen entries.
|
|
//
|
|
GuiRequestDrawCrop (
|
|
DrawContext,
|
|
mBootPickerContainer.Obj.OffsetX + mBootPicker.Hdr.Obj.OffsetX,
|
|
mBootPickerContainer.Obj.OffsetY + mBootPicker.Hdr.Obj.OffsetY,
|
|
mBootPicker.Hdr.Obj.Width + DeltaSine,
|
|
mBootPicker.Hdr.Obj.Height
|
|
);
|
|
|
|
ASSERT (mBpAnimInfoSinMove.Duration == mCommonIntroOpacityInterpol.Duration);
|
|
return CurrentTime - mBpAnimInfoSinMove.StartTime >= mBpAnimInfoSinMove.Duration;
|
|
}
|
|
|
|
STATIC GUI_INTERPOLATION mBootPickerTimeoutOpacityInterpolDown = {
|
|
GuiInterpolTypeLinear,
|
|
0,
|
|
125,
|
|
0xFF,
|
|
0x50,
|
|
0
|
|
};
|
|
|
|
STATIC GUI_INTERPOLATION mBootPickerTimeoutOpacityInterpolUp = {
|
|
GuiInterpolTypeLinear,
|
|
0,
|
|
125,
|
|
0x50,
|
|
0xFF,
|
|
20
|
|
};
|
|
|
|
BOOLEAN
|
|
InternalBootPickerAnimateTimeout (
|
|
IN BOOT_PICKER_GUI_CONTEXT *Context,
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN UINT64 CurrentTime
|
|
)
|
|
{
|
|
STATIC GUI_INTERPOLATION *CurInterpol = &mBootPickerTimeoutOpacityInterpolDown;
|
|
STATIC GUI_INTERPOLATION *NextInterpol = &mBootPickerTimeoutOpacityInterpolUp;
|
|
|
|
GUI_INTERPOLATION *Temp;
|
|
|
|
if (DrawContext->TimeOutSeconds > 0) {
|
|
mBootPickerSelectorButton.Hdr.Obj.Opacity = (UINT8)GuiGetInterpolatedValue (
|
|
CurInterpol,
|
|
CurrentTime
|
|
);
|
|
} else {
|
|
mBootPickerSelectorButton.Hdr.Obj.Opacity = 0xFF;
|
|
}
|
|
|
|
//
|
|
// The view is constructed such that the action buttons are always fully
|
|
// visible.
|
|
//
|
|
GuiRequestDraw (
|
|
(UINT32)(mBootPickerContainer.Obj.OffsetX + mBootPickerSelectorContainer.Obj.OffsetX + mBootPickerSelectorButton.Hdr.Obj.OffsetX),
|
|
(UINT32)(mBootPickerContainer.Obj.OffsetY + mBootPickerSelectorContainer.Obj.OffsetY + mBootPickerSelectorButton.Hdr.Obj.OffsetY),
|
|
mBootPickerSelectorButton.Hdr.Obj.Width,
|
|
mBootPickerSelectorButton.Hdr.Obj.Height
|
|
);
|
|
|
|
if (DrawContext->TimeOutSeconds == 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (CurrentTime - CurInterpol->StartTime >= CurInterpol->Duration + CurInterpol->HoldTime) {
|
|
Temp = CurInterpol;
|
|
CurInterpol = NextInterpol;
|
|
NextInterpol = Temp;
|
|
CurInterpol->StartTime = CurrentTime + 1;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
STATIC GUI_ANIMATION mBootPickerIntroAnimation = {
|
|
{ NULL, NULL },
|
|
NULL,
|
|
InternalBootPickerAnimateIntro
|
|
};
|
|
|
|
EFI_STATUS
|
|
BootPickerViewInitialize (
|
|
OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN BOOT_PICKER_GUI_CONTEXT *GuiContext,
|
|
IN GUI_CURSOR_GET_IMAGE GetCursorImage,
|
|
IN UINT8 NumBootEntries
|
|
)
|
|
{
|
|
CONST GUI_IMAGE *SelectorBackgroundImage;
|
|
CONST GUI_IMAGE *SelectorButtonImage;
|
|
UINT32 ContainerMaxWidth;
|
|
UINT32 ContainerWidthDelta;
|
|
UINTN DestLen;
|
|
CHAR16 *UString;
|
|
BOOLEAN Result;
|
|
|
|
ASSERT (DrawContext != NULL);
|
|
ASSERT (GuiContext != NULL);
|
|
ASSERT (GetCursorImage != NULL);
|
|
|
|
mKeyContext->KeyFilter = OC_PICKER_KEYS_FOR_PICKER;
|
|
|
|
CommonViewInitialize (
|
|
DrawContext,
|
|
GuiContext,
|
|
(GuiContext->PickerContext->PickerAttributes & OC_ATTR_USE_MINIMAL_UI) == 0
|
|
? &mBootPickerViewContext
|
|
: &mBootPickerViewContextMinimal
|
|
);
|
|
|
|
mBackgroundImageOffsetX = DivS64x64Remainder (
|
|
(INT64)DrawContext->Screen.Width - DrawContext->GuiContext->Background.Width,
|
|
2,
|
|
NULL
|
|
);
|
|
mBackgroundImageOffsetY = DivS64x64Remainder (
|
|
(INT64)DrawContext->Screen.Height - DrawContext->GuiContext->Background.Height,
|
|
2,
|
|
NULL
|
|
);
|
|
|
|
SelectorBackgroundImage = &GuiContext->Icons[ICON_SELECTED][ICON_TYPE_BASE];
|
|
SelectorButtonImage = &GuiContext->Icons[ICON_SELECTOR][ICON_TYPE_BASE];
|
|
|
|
mBootPickerSelectorBackground.Obj.OffsetX = 0;
|
|
mBootPickerSelectorBackground.Obj.OffsetY = 0;
|
|
mBootPickerSelectorBackground.Obj.Width = SelectorBackgroundImage->Width;
|
|
mBootPickerSelectorBackground.Obj.Height = SelectorBackgroundImage->Height;
|
|
|
|
ASSERT (SelectorBackgroundImage->Width >= SelectorButtonImage->Width);
|
|
mBootPickerSelectorButton.Hdr.Obj.OffsetX = (SelectorBackgroundImage->Width - SelectorButtonImage->Width) / 2;
|
|
mBootPickerSelectorButton.Hdr.Obj.OffsetY = SelectorBackgroundImage->Height + BOOT_SELECTOR_BUTTON_SPACE * DrawContext->Scale;
|
|
mBootPickerSelectorButton.Hdr.Obj.Width = SelectorButtonImage->Width;
|
|
mBootPickerSelectorButton.Hdr.Obj.Height = SelectorButtonImage->Height;
|
|
mBootPickerSelectorButton.ImageId = ICON_SELECTOR;
|
|
mBootPickerSelectorButton.ImageState = ICON_TYPE_BASE;
|
|
|
|
mBootPickerSelectorContainer.Obj.OffsetX = 0;
|
|
mBootPickerSelectorContainer.Obj.OffsetY = 0;
|
|
mBootPickerSelectorContainer.Obj.Width = SelectorBackgroundImage->Width;
|
|
mBootPickerSelectorContainer.Obj.Height = (UINT32)(mBootPickerSelectorButton.Hdr.Obj.OffsetY + mBootPickerSelectorButton.Hdr.Obj.Height);
|
|
|
|
mBootPickerLeftScroll.Hdr.Obj.Height = BOOT_SCROLL_BUTTON_DIMENSION * GuiContext->Scale;
|
|
mBootPickerLeftScroll.Hdr.Obj.Width = BOOT_SCROLL_BUTTON_DIMENSION * GuiContext->Scale;
|
|
mBootPickerLeftScroll.Hdr.Obj.OffsetX = BOOT_SCROLL_BUTTON_SPACE;
|
|
mBootPickerLeftScroll.Hdr.Obj.OffsetY = (DrawContext->Screen.Height - mBootPickerLeftScroll.Hdr.Obj.Height) / 2;
|
|
mBootPickerLeftScroll.ImageId = ICON_LEFT;
|
|
mBootPickerLeftScroll.ImageState = ICON_TYPE_BASE;
|
|
|
|
mBootPickerRightScroll.Hdr.Obj.Height = BOOT_SCROLL_BUTTON_DIMENSION * GuiContext->Scale;
|
|
mBootPickerRightScroll.Hdr.Obj.Width = BOOT_SCROLL_BUTTON_DIMENSION * GuiContext->Scale;
|
|
mBootPickerRightScroll.Hdr.Obj.OffsetX = DrawContext->Screen.Width - mBootPickerRightScroll.Hdr.Obj.Width - BOOT_SCROLL_BUTTON_SPACE;
|
|
mBootPickerRightScroll.Hdr.Obj.OffsetY = (DrawContext->Screen.Height - mBootPickerRightScroll.Hdr.Obj.Height) / 2;
|
|
mBootPickerRightScroll.ImageId = ICON_RIGHT;
|
|
mBootPickerRightScroll.ImageState = ICON_TYPE_BASE;
|
|
//
|
|
// The boot entry container must precisely show a set of boot entries, i.e.
|
|
// there may not be partial entries or extra padding.
|
|
//
|
|
ContainerMaxWidth = DrawContext->Screen.Width - mBootPickerLeftScroll.Hdr.Obj.Width - 2 * BOOT_SCROLL_BUTTON_SPACE - mBootPickerRightScroll.Hdr.Obj.Width - 2 * BOOT_SCROLL_BUTTON_SPACE;
|
|
ContainerWidthDelta = (ContainerMaxWidth + BOOT_ENTRY_SPACE * GuiContext->Scale) % ((BOOT_ENTRY_WIDTH + BOOT_ENTRY_SPACE) * GuiContext->Scale);
|
|
|
|
mBootPickerContainer.Obj.Height = BOOT_SELECTOR_HEIGHT * GuiContext->Scale;
|
|
mBootPickerContainer.Obj.Width = ContainerMaxWidth - ContainerWidthDelta;
|
|
mBootPickerContainer.Obj.OffsetX = (DrawContext->Screen.Width - mBootPickerContainer.Obj.Width) / 2;
|
|
//
|
|
// Center the icons and labels excluding the selector images vertically.
|
|
//
|
|
ASSERT ((DrawContext->Screen.Height - (BOOT_ENTRY_HEIGHT - BOOT_ENTRY_ICON_SPACE) * GuiContext->Scale) / 2 - (BOOT_ENTRY_ICON_SPACE * GuiContext->Scale) == (DrawContext->Screen.Height - (BOOT_ENTRY_HEIGHT + BOOT_ENTRY_ICON_SPACE) * GuiContext->Scale) / 2);
|
|
mBootPickerContainer.Obj.OffsetY = (DrawContext->Screen.Height - (BOOT_ENTRY_HEIGHT + BOOT_ENTRY_ICON_SPACE) * GuiContext->Scale) / 2;
|
|
|
|
mBootPicker.Hdr.Obj.Height = BOOT_SELECTOR_HEIGHT * GuiContext->Scale;
|
|
//
|
|
// Adding an entry will wrap around Width such that the first entry has no
|
|
// padding.
|
|
//
|
|
mBootPicker.Hdr.Obj.Width = 0U - (UINT32)(BOOT_ENTRY_SPACE * GuiContext->Scale);
|
|
//
|
|
// Adding an entry will also shift OffsetX considering the added boot entry
|
|
// space. This is not needed for the first, so initialise accordingly.
|
|
//
|
|
mBootPicker.Hdr.Obj.OffsetX = mBootPickerContainer.Obj.Width / 2 + (UINT32)(BOOT_ENTRY_SPACE * GuiContext->Scale) / 2;
|
|
mBootPicker.Hdr.Obj.OffsetY = 0;
|
|
|
|
mBootPicker.SelectedIndex = 0;
|
|
|
|
mBootPicker.Hdr.Obj.Children = AllocateZeroPool (NumBootEntries * sizeof (*mBootPicker.Hdr.Obj.Children));
|
|
if (mBootPicker.Hdr.Obj.Children == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
mBootPicker.Hdr.Obj.NumChildren = NumBootEntries;
|
|
|
|
if (GuiContext->PickerContext->TitleSuffix == NULL) {
|
|
mVersionLabelImage.Buffer = NULL;
|
|
|
|
mBootPickerVersionLabel.Obj.Width = 0;
|
|
mBootPickerVersionLabel.Obj.Height = 0;
|
|
mBootPickerVersionLabel.Obj.OffsetX = 0;
|
|
mBootPickerVersionLabel.Obj.OffsetY = 0;
|
|
} else {
|
|
DestLen = AsciiStrLen (GuiContext->PickerContext->TitleSuffix);
|
|
UString = AsciiStrCopyToUnicode (GuiContext->PickerContext->TitleSuffix, DestLen);
|
|
|
|
Result = GuiGetLabel (
|
|
&mVersionLabelImage,
|
|
&GuiContext->FontContext,
|
|
UString,
|
|
DestLen,
|
|
GuiContext->LightBackground
|
|
);
|
|
|
|
FreePool (UString);
|
|
|
|
if (!Result) {
|
|
DEBUG ((DEBUG_WARN, "OCUI: version label failed\n"));
|
|
return Result;
|
|
}
|
|
|
|
mBootPickerVersionLabel.Obj.Width = mVersionLabelImage.Width;
|
|
mBootPickerVersionLabel.Obj.Height = mVersionLabelImage.Height;
|
|
mBootPickerVersionLabel.Obj.OffsetX = DrawContext->Screen.Width - ((3 * mBootPickerVersionLabel.Obj.Width) / 2);
|
|
mBootPickerVersionLabel.Obj.OffsetY = DrawContext->Screen.Height - ((5 * mBootPickerVersionLabel.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:
|
|
//
|
|
|
|
InitializeListHead (&mBootPickerLabelAnimation.Link);
|
|
|
|
if (!GuiContext->DoneIntroAnimation) {
|
|
InitBpAnimIntro (DrawContext);
|
|
InsertHeadList (&DrawContext->Animations, &mBootPickerIntroAnimation.Link);
|
|
//
|
|
// Fade-in picker, scroll buttons, and action buttons.
|
|
//
|
|
mBootPickerContainer.Obj.Opacity = 0;
|
|
mBootPickerLeftScroll.Hdr.Obj.Opacity = 0;
|
|
mBootPickerRightScroll.Hdr.Obj.Opacity = 0;
|
|
|
|
GuiContext->DoneIntroAnimation = TRUE;
|
|
} else {
|
|
//
|
|
// The late code assumes the scroll buttons are visible by default.
|
|
//
|
|
mBootPickerLeftScroll.Hdr.Obj.Opacity = 0xFF;
|
|
mBootPickerRightScroll.Hdr.Obj.Opacity = 0xFF;
|
|
//
|
|
// Unhide action buttons immediately if they are not animated.
|
|
//
|
|
mCommonActionButtonsContainer.Obj.Opacity = 0xFF;
|
|
}
|
|
|
|
if (DrawContext->TimeOutSeconds > 0) {
|
|
STATIC GUI_ANIMATION PickerAnim2;
|
|
PickerAnim2.Context = NULL;
|
|
PickerAnim2.Animate = InternalBootPickerAnimateTimeout;
|
|
InsertHeadList (&DrawContext->Animations, &PickerAnim2.Link);
|
|
}
|
|
|
|
/*
|
|
InitBpAnimImageList(GuiInterpolTypeLinear, 25, 25);
|
|
STATIC GUI_ANIMATION PoofAnim;
|
|
PoofAnim.Context = GuiContext->Poof;
|
|
PoofAnim.Animate = InternalBootPickerAnimateImageList;
|
|
InsertHeadList(&DrawContext->Animations, &PoofAnim.Link);
|
|
*/
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
BootPickerViewLateInitialize (
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN BOOT_PICKER_GUI_CONTEXT *GuiContext,
|
|
IN UINT8 DefaultIndex
|
|
)
|
|
{
|
|
CONST GUI_VOLUME_ENTRY *BootEntry;
|
|
|
|
ASSERT (DefaultIndex < mBootPicker.Hdr.Obj.NumChildren);
|
|
|
|
mBootPicker.SelectedIndex = DefaultIndex;
|
|
|
|
//
|
|
// If more entries than screen width, firstly align left-most entry
|
|
// then scroll from there as needed to bring default entry on screen
|
|
//
|
|
if (mBootPicker.Hdr.Obj.OffsetX < 0) {
|
|
mBootPicker.Hdr.Obj.OffsetX = 0;
|
|
mBootPicker.Hdr.Obj.OffsetX = InternelBootPickerScrollSelected ();
|
|
}
|
|
|
|
//
|
|
// If the scroll buttons are hidden, the intro animation will update them
|
|
// implicitly.
|
|
//
|
|
if (mBootPickerLeftScroll.Hdr.Obj.Opacity == 0xFF) {
|
|
InternalUpdateScrollButtons ();
|
|
}
|
|
|
|
InternalBootPickerSelectEntry (&mBootPicker, NULL, DefaultIndex);
|
|
BootEntry = InternalGetVolumeEntry (DefaultIndex);
|
|
InternalStartAnimateLabel (DrawContext, BootEntry);
|
|
GuiContext->BootEntry = BootEntry->Context;
|
|
}
|
|
|
|
VOID
|
|
BootPickerViewDeinitialize (
|
|
IN OUT GUI_DRAWING_CONTEXT *DrawContext,
|
|
IN OUT BOOT_PICKER_GUI_CONTEXT *GuiContext
|
|
)
|
|
{
|
|
UINT32 Index;
|
|
|
|
if (mVersionLabelImage.Buffer != NULL) {
|
|
FreePool (mVersionLabelImage.Buffer);
|
|
mVersionLabelImage.Buffer = NULL;
|
|
}
|
|
|
|
for (Index = 0; Index < mBootPicker.Hdr.Obj.NumChildren; ++Index) {
|
|
InternalBootPickerEntryDestruct (InternalGetVolumeEntry (Index));
|
|
}
|
|
|
|
if (mBootPicker.Hdr.Obj.Children != NULL) {
|
|
FreePool (mBootPicker.Hdr.Obj.Children);
|
|
}
|
|
|
|
mBootPicker.Hdr.Obj.NumChildren = 0;
|
|
|
|
GuiViewDeinitialize (DrawContext, GuiContext);
|
|
}
|