Mike Beaton c8b93357b8 OcConsoleLib: Simplify use of builtin renderer uncontrolled mode
The renderer already (esp. since recent changes incl. reset to controlled on cls)
manages its own transitions to controlled, so we just need to indicate uncontrolled
in cases where something else has been rendering graphics
2023-05-04 09:59:22 +01:00

372 lines
10 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;
}
mPreviousMode = OcConsoleControlSetMode (EfiConsoleControlScreenGraphics);
return EFI_SUCCESS;
}
STATIC
VOID
OcShowMenuByOcLeave (
VOID
)
{
GuiLibDestruct ();
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);
//
// There are reasons to prefer not to perform this screen clear:
// - If starting macOS, boot.efi performs the same screen clear to the same UI theme colour
// immediately afterwards anyway
// - The native Apple picker does not clear its graphics before exit (i.e. does not do this)
// - Most OS booters in most circumstance perform their own screen clear
// - Each screen clear on a slow GOP (such as direct GOP rendering) is a noticeable slowdown
// However:
// - Windows without ACPI->Quirks->ResetLogoStatus does not clear any pre-existing graphics
// - Ref: https://github.com/acidanthera/bugtracker/issues/2231
// - Peforming this screen clear gives a sense of progress (i.e. something happens immediately
// rather than nothing) if the selected entry will be very slow to start (e.g. Recovery, in
/// some circumstances)
//
if (!mGuiContext.Refresh) {
//
// Clear the screen only when we exit.
//
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.
//
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);
//
// This screen clear is useful, even though the boot entry started will in general
// perform its own screen clear eventually anyway, since this gives a better sense
// of progress between (intentionally slow - computationally intensive) password
// verification and (can be slow) start of Recovery.
//
if (Context->PickerCommand != OcPickerShowPicker) {
//
// Clear the screen only if we will not show BootPicker afterwards.
//
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;
}