Mike Beaton 2abe0760a0 OpenCanopy: Remove non-required clear screen on exit
The firmware standard (not just in Apple firmware) appears to be
for each step to leave its graphics up for the next step to clear
2023-01-30 23:48:23 +00:00

372 lines
9.9 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 <Library/OcMainLib.h>
#include <Uefi.h>
#include <Protocol/DevicePath.h>
#include <Protocol/LoadedImage.h>
#include <Protocol/OcBootstrap.h>
#include <Protocol/OcInterface.h>
#include <Library/UefiLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/OcBootManagementLib.h>
#include <Library/OcConsoleLib.h>
#include <Library/OcDebugLogLib.h>
#include <Library/DevicePathLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/OcDevicePathLib.h>
#include <Library/OcFileLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include "OpenCanopy.h"
#include "BmfLib.h"
#include "GuiApp.h"
//
// Add slight x offset of cursor by its position within its icon file,
// in order to match look of Apple picker initial position.
//
#define DEFAULT_CURSOR_OFFSET_X BOOT_CURSOR_OFFSET
#define DEFAULT_CURSOR_OFFSET_Y 112U
extern BOOT_PICKER_GUI_CONTEXT mGuiContext;
STATIC GUI_DRAWING_CONTEXT mDrawContext;
STATIC EFI_CONSOLE_CONTROL_SCREEN_MODE mPreviousMode;
STATIC
EFI_STATUS
OcShowMenuByOcEnter (
IN BOOT_PICKER_GUI_CONTEXT *GuiContext
)
{
EFI_STATUS Status;
Status = GuiLibConstruct (
GuiContext,
mGuiContext.CursorOffsetX,
mGuiContext.CursorOffsetY
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Extension for OpenCore builtin renderer to mark that we control text output here.
//
gST->ConOut->TestString (gST->ConOut, OC_CONSOLE_MARK_CONTROLLED);
mPreviousMode = OcConsoleControlSetMode (EfiConsoleControlScreenGraphics);
return EFI_SUCCESS;
}
STATIC
VOID
OcShowMenuByOcLeave (
VOID
)
{
GuiLibDestruct ();
//
// Extension for OpenCore builtin renderer to mark that we no longer control text output here.
//
gST->ConOut->TestString (gST->ConOut, OC_CONSOLE_MARK_UNCONTROLLED);
OcConsoleControlSetMode (mPreviousMode);
}
STATIC
VOID
OcSetInitialCursorOffset (
VOID
)
{
//
// The cursor position is updated on GUI exit, so don't overwrite it.
//
if (mGuiContext.PickerContext == NULL) {
mGuiContext.CursorOffsetX = DEFAULT_CURSOR_OFFSET_X * mGuiContext.Scale;
mGuiContext.CursorOffsetY = DEFAULT_CURSOR_OFFSET_Y * mGuiContext.Scale;
}
}
EFI_STATUS
EFIAPI
OcShowMenuByOc (
IN OC_BOOT_CONTEXT *BootContext,
IN OC_BOOT_ENTRY **BootEntries,
OUT OC_BOOT_ENTRY **ChosenBootEntry
)
{
EFI_STATUS Status;
UINTN Index;
*ChosenBootEntry = NULL;
OcSetInitialCursorOffset ();
mGuiContext.BootEntry = NULL;
mGuiContext.ReadyToBoot = FALSE;
mGuiContext.HideAuxiliary = BootContext->PickerContext->HideAuxiliary;
mGuiContext.Refresh = FALSE;
mGuiContext.PickerContext = BootContext->PickerContext;
mGuiContext.AudioPlaybackTimeout = -1;
Status = OcShowMenuByOcEnter (&mGuiContext);
if (EFI_ERROR (Status)) {
return Status;
}
mDrawContext.TimeOutSeconds = BootContext->PickerContext->TimeoutSeconds;
//
// Do not play intro animation for blind.
//
if (BootContext->PickerContext->PickerAudioAssist) {
mGuiContext.DoneIntroAnimation = TRUE;
}
Status = BootPickerViewInitialize (
&mDrawContext,
&mGuiContext,
InternalGetCursorImage,
(UINT8)BootContext->BootEntryCount
);
if (EFI_ERROR (Status)) {
OcShowMenuByOcLeave ();
return Status;
}
for (Index = 0; Index < BootContext->BootEntryCount; ++Index) {
Status = BootPickerEntriesSet (
BootContext->PickerContext,
&mGuiContext,
BootEntries[Index],
(UINT8)Index
);
if (EFI_ERROR (Status)) {
OcShowMenuByOcLeave ();
return Status;
}
}
BootPickerViewLateInitialize (
&mDrawContext,
&mGuiContext,
(UINT8)BootContext->DefaultEntry->EntryIndex - 1
);
GuiRedrawAndFlushScreen (&mDrawContext);
if (BootContext->PickerContext->PickerAudioAssist) {
BootContext->PickerContext->PlayAudioFile (
BootContext->PickerContext,
OC_VOICE_OVER_AUDIO_FILE_CHOOSE_OS,
OC_VOICE_OVER_AUDIO_BASE_TYPE_OPEN_CORE,
FALSE
);
for (Index = 0; Index < BootContext->BootEntryCount; ++Index) {
BootContext->PickerContext->PlayAudioEntry (
BootContext->PickerContext,
BootEntries[Index]
);
if ((BootContext->PickerContext->TimeoutSeconds > 0) && (BootContext->DefaultEntry->EntryIndex - 1 == Index)) {
BootContext->PickerContext->PlayAudioFile (
BootContext->PickerContext,
OC_VOICE_OVER_AUDIO_FILE_DEFAULT,
OC_VOICE_OVER_AUDIO_BASE_TYPE_OPEN_CORE,
FALSE
);
}
}
BootContext->PickerContext->PlayAudioBeep (
BootContext->PickerContext,
OC_VOICE_OVER_SIGNALS_NORMAL,
OC_VOICE_OVER_SIGNAL_NORMAL_MS,
OC_VOICE_OVER_SILENCE_NORMAL_MS
);
}
GuiDrawLoop (&mDrawContext);
ASSERT (mGuiContext.BootEntry != NULL || mGuiContext.Refresh);
//
// Do not use this clear screen, since the convention (not just in Apple firmware) seems to be
// for each UI step which finishes to leave its own graphics up for the next step to clear, so
// this matches that, and gives a slightly smoother looking, slightly faster boot.
//
#if 0
if (!mGuiContext.Refresh) {
//
// Clear the screen only when we exit.
//
GuiClearScreen (&mDrawContext, &mGuiContext.BackgroundColor.Pixel);
}
#endif
//
// Note, it is important to destruct GUI here, as we must ensure
// that keyboard/mouse polling does not conflict with FV2 ui.
//
BootPickerViewDeinitialize (&mDrawContext, &mGuiContext);
OcShowMenuByOcLeave ();
*ChosenBootEntry = mGuiContext.BootEntry;
BootContext->PickerContext->HideAuxiliary = mGuiContext.HideAuxiliary;
if (mGuiContext.Refresh) {
return EFI_ABORTED;
}
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
OcShowPasswordByOc (
IN OC_PICKER_CONTEXT *Context,
IN OC_PRIVILEGE_LEVEL Level
)
{
EFI_STATUS Status;
OcSetInitialCursorOffset ();
mGuiContext.BootEntry = NULL;
mGuiContext.ReadyToBoot = FALSE;
mGuiContext.HideAuxiliary = TRUE;
mGuiContext.Refresh = FALSE;
mGuiContext.PickerContext = Context;
mGuiContext.AudioPlaybackTimeout = -1;
Status = OcShowMenuByOcEnter (&mGuiContext);
if (EFI_ERROR (Status)) {
return Status;
}
mDrawContext.TimeOutSeconds = 0;
//
// Do not play intro animation for blind.
//
if (Context->PickerAudioAssist) {
mGuiContext.DoneIntroAnimation = TRUE;
}
Status = PasswordViewInitialize (
&mDrawContext,
&mGuiContext
);
if (EFI_ERROR (Status)) {
OcShowMenuByOcLeave ();
return Status;
}
GuiRedrawAndFlushScreen (&mDrawContext);
GuiDrawLoop (&mDrawContext);
//
// Keep this clear screen, since it seems to match Mac firmware behaviour here better,
// and it gives a better sense of progress between (intentionally slow - computationally
// intensive) password verification and (can be slow) start of Recovery.
//
// Clear the screen only if we will not show BootPicker afterwards.
//
if (Context->PickerCommand != OcPickerShowPicker) {
GuiClearScreen (&mDrawContext, &mGuiContext.BackgroundColor.Pixel);
}
//
// Note, it is important to destruct GUI here, as we must ensure
// that keyboard/mouse polling does not conflict with FV2 ui.
//
PasswordViewDeinitialize (&mDrawContext, &mGuiContext);
OcShowMenuByOcLeave ();
return EFI_SUCCESS;
}
EFI_STATUS
InternalContextConstruct (
OUT BOOT_PICKER_GUI_CONTEXT *Context,
IN OC_STORAGE_CONTEXT *Storage,
IN OC_PICKER_CONTEXT *Picker
);
STATIC
EFI_STATUS
EFIAPI
GuiOcInterfacePopulate (
IN OC_INTERFACE_PROTOCOL *This,
IN OC_STORAGE_CONTEXT *Storage,
IN OC_PICKER_CONTEXT *Context
)
{
EFI_STATUS Status;
Status = InternalContextConstruct (&mGuiContext, Storage, Context);
if (EFI_ERROR (Status)) {
return Status;
}
Context->ShowMenu = OcShowMenuByOc;
Context->RequestPrivilege = OcShowPasswordByOc;
return EFI_SUCCESS;
}
STATIC OC_INTERFACE_PROTOCOL mOcInterface = {
OC_INTERFACE_REVISION,
GuiOcInterfacePopulate
};
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
VOID *PrevInterface;
EFI_HANDLE NewHandle;
//
// Check for previous GUI protocols.
//
Status = gBS->LocateProtocol (
&gOcInterfaceProtocolGuid,
NULL,
&PrevInterface
);
if (!EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "OCUI: Another GUI is already present\n"));
return EFI_ALREADY_STARTED;
}
//
// Install new GUI protocol
//
NewHandle = NULL;
Status = gBS->InstallMultipleProtocolInterfaces (
&NewHandle,
&gOcInterfaceProtocolGuid,
&mOcInterface,
NULL
);
if (!EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "OCUI: Registered custom GUI protocol\n"));
} else {
DEBUG ((DEBUG_WARN, "OCUI: Failed to install GUI protocol - %r\n", Status));
}
return Status;
}