CloverBootloader/rEFIt_UEFI/gui/REFIT_MAINMENU_SCREEN.cpp
jief 42cece9885 Fix nanosvg leaks.
Move global variable textfaces in XTheme.
Move global variable fontsDB in XTheme.
Remove XTheme member SVGParser. SVGParser is deleted just after use.
Remove XTheme members ImageSVG and ImageSVGnight. All images are
rasterized at load, so no need to keep that.
Remove XIcon setFilled because XIcon knows if it's filled or not by
checking Image & ImageNight
2023-11-08 14:35:22 +01:00

879 lines
34 KiB
C++

/*
* refit/menu.c
* Menu functions
*
* Copyright (c) 2006 Christoph Pfisterer
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Christoph Pfisterer nor the names of the
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "./REFIT_MAINMENU_SCREEN.h"
#include <Platform.h>
#include "../Platform/BasicIO.h"
#include "../libeg/libegint.h" //this includes platform.h
//#include "../include/scroll_images.h"
//#include "colors.h"
#include "../libeg/nanosvg.h"
#include "../libeg/FloatLib.h"
#include "../Platform/HdaCodecDump.h"
#include "REFIT_MENU_SCREEN.h"
//#include "screen.h"
#include "../cpp_foundation/XString.h"
#include "../libeg/XTheme.h"
#include "../libeg/VectorGraphics.h" // for testSVG
#include "shared_with_menu.h"
#include "../refit/menu.h" // for DrawTextXY. Must disappear soon.
#include "../Platform/AcpiPatcher.h"
#include "../Platform/Nvram.h"
#include "../refit/screen.h"
#include "../Platform/Events.h"
#include "../Settings/Self.h"
#include "../Platform/Volumes.h"
#include "../include/OSFlags.h"
#include "../Platform/CloverVersion.h"
#ifndef DEBUG_ALL
#define DEBUG_MENU 1
#else
#define DEBUG_MENU DEBUG_ALL
#endif
#if DEBUG_MENU == 0
#define DBG(...)
#else
#define DBG(...) DebugLog(DEBUG_MENU, __VA_ARGS__)
#endif
INTN row0PosXRunning;
INTN row1PosXRunning;
INTN *itemPosX = NULL;
INTN *itemPosY = NULL;
INTN row1PosY, textPosY, FunctextPosY;
INTN OldTimeoutTextWidth = 0;
INTN EntriesWidth, EntriesHeight, EntriesGap;
INTN MaxItemOnScreen = -1;
REFIT_MAINMENU_SCREEN::REFIT_MAINMENU_SCREEN(UINTN ID, XStringW TTitle, XStringW TTimeoutText) : REFIT_MENU_SCREEN(ID, TTitle, TTimeoutText)
{
};
/**
* Draw entries for GUI.
*/
void REFIT_MAINMENU_SCREEN::DrawMainMenuLabel(IN CONST XStringW& Text, IN INTN XPos, IN INTN YPos)
{
INTN TextWidth = 0;
INTN BadgeDim = (INTN)(BADGE_DIMENSION * ThemeX->Scale);
ThemeX->MeasureText(Text, &TextWidth, NULL);
//Clear old text
ThemeX->FillRectAreaOfScreen(OldX, OldY, OldTextWidth, OldTextHeight);
if (!(ThemeX->BootCampStyle)
&& (ThemeX->HideBadges & HDBADGES_INLINE) && (!OldRow)
&& (OldTextWidth) && (OldTextWidth != TextWidth)
) {
//Clear badge
ThemeX->FillRectAreaOfScreen((OldX - (OldTextWidth >> 1) - (BadgeDim + 16)),
(OldY - ((BadgeDim - ThemeX->TextHeight) >> 1)), 128, 128);
}
DrawTextXY(Text, XPos, YPos, X_IS_CENTER);
//show inline badge
if (!(ThemeX->BootCampStyle) &&
(ThemeX->HideBadges & HDBADGES_INLINE) &&
(Entries[ScrollState.CurrentSelection].Row == 0)) {
// Display Inline Badge: small icon before the text
XImage Back(BadgeDim, BadgeDim);
INTN X = XPos - (TextWidth >> 1) - (BadgeDim + 16);
INTN Y = YPos - ((BadgeDim - ThemeX->TextHeight) >> 1);
Back.CopyRect(ThemeX->Background, X, Y);
const XImage& CurrSel = Entries[ScrollState.CurrentSelection].Image.GetBest(!Daylight);
Back.Compose(0, 0, CurrSel, false, BadgeDim/128.f);
Back.DrawOnBack(X, Y, Back);
}
OldX = XPos;
OldY = YPos;
OldTextWidth = TextWidth;
OldRow = Entries[ScrollState.CurrentSelection].Row;
}
void REFIT_MENU_SCREEN::CountItems()
{
row0PosX = 0;
row1PosX = Entries.size();
// layout
row0Count = 0; //Nr items in row0
row1Count = 0;
for (INTN i = 0; i < (INTN)Entries.size(); i++) {
if (Entries[i].Row == 0) {
row0Count++;
CONSTRAIN_MIN(row0PosX, i);
} else {
row1Count++;
CONSTRAIN_MAX(row1PosX, i);
}
}
}
void REFIT_MENU_SCREEN::DrawTextCorner(UINTN TextC, UINT8 Align)
{
INTN Xpos;
// CHAR16 *Text = NULL;
XStringW Text;
if (
// HIDEUI_ALL - included
((TextC == TEXT_CORNER_REVISION) && ((ThemeX->HideUIFlags & HIDEUI_FLAG_REVISION) != 0)) ||
((TextC == TEXT_CORNER_HELP) && ((ThemeX->HideUIFlags & HIDEUI_FLAG_HELP) != 0)) ||
((TextC == TEXT_CORNER_OPTIMUS) && (gSettings.GUI.ShowOptimus == false))
) {
return;
}
switch (TextC) {
case TEXT_CORNER_REVISION:
// Display Clover boot volume
if (SelfVolume->VolLabel.notEmpty() && SelfVolume->VolLabel[0] != L'#') {
Text = SWPrintf("%ls, booted from %ls %ls", gFirmwareRevision, SelfVolume->VolLabel.wc_str(), self.getCloverDirFullPath().wc_str());
}
if (Text.isEmpty()) {
Text = SWPrintf("%ls %ls", gFirmwareRevision, /*SelfVolume->VolName.wc_str(),*/ self.getCloverDirFullPath().wc_str());
}
break;
case TEXT_CORNER_HELP:
Text = L"F1:Help"_XSW;
break;
case TEXT_CORNER_OPTIMUS:
if (gConf.GfxPropertiesArray.size() > 0 && gConf.GfxPropertiesArray[0].Vendor != Intel) {
Text = L"Discrete"_XSW;
} else {
Text = L"Intel"_XSW;
}
// Text = (gConf.GfxPropertiesArray.size() == 2)?L"Intel":L"Discrete";
break;
default:
return;
}
switch (Align) {
case X_IS_LEFT:
Xpos = (INTN)(ThemeX->TextHeight * 0.75f);
break;
case X_IS_RIGHT:
Xpos = UGAWidth - (INTN)(ThemeX->TextHeight * 0.75f);//2
break;
case X_IS_CENTER:
Xpos = UGAWidth >> 1;
break;
default:
Text.setEmpty();
return;
}
// DBG("draw text %ls at (%d, %d)\n", Text, Xpos, UGAHeight - 5 - TextHeight),
// clovy DrawTextXY(Text, Xpos, UGAHeight - 5 - TextHeight, Align);
DrawTextXY(Text, Xpos, UGAHeight - (INTN)(ThemeX->TextHeight * 1.5f), Align);
}
void REFIT_MAINMENU_SCREEN::DrawMainMenuEntry(REFIT_ABSTRACT_MENU_ENTRY *Entry, XBool selected, INTN XPos, INTN YPos)
{
INTN MainSize = ThemeX->MainEntriesSize;
// XImage MainImage(MainSize, MainSize);
// XImage* BadgeImage;
XIcon MainIcon; //it can be changed here
XIcon* BadgeIcon = NULL;
if (Entry->Row == 0 && Entry->getDriveImage() && !(ThemeX->HideBadges & HDBADGES_SWAP)) {
MainIcon = *Entry->getDriveImage();
} else {
MainIcon = Entry->Image; // XIcon*
}
//this should be inited by the Theme
if (MainIcon.isEmpty()) {
// DBG(" why MainImage is empty? Report to devs\n");
if (!ThemeX->IsEmbeddedTheme()) {
MainIcon = ThemeX->GetIcon("os_mac"_XS8);
}
if (MainIcon.Image.isEmpty()) {
MainIcon.Image.DummyImage(MainSize);
}
}
// const XImage& MainImage = (!ThemeX->Daylight && !MainIcon.ImageNight.isEmpty())? MainIcon.ImageNight : MainIcon.Image;
const XImage& MainImage = MainIcon.GetBest(!Daylight);
INTN CompWidth = (Entry->Row == 0) ? ThemeX->row0TileSize : ThemeX->row1TileSize;
INTN CompHeight = CompWidth;
// float fScale;
// if (ThemeX->TypeSVG) {
// fScale = (selected ? 1.f : -1.f);
// } else {
// fScale = ((Entry->Row == 0) ? (ThemeX->MainEntriesSize/128.f * (selected ? 1.f : -1.f)): 1.f) ;
// }
if (Entry->Row == 0) {
BadgeIcon = Entry->getBadgeImage();
}
const XImage& TopImage = ThemeX->SelectionImages[((Entry->Row == 0) ? 0 : 2) + (selected ? 0 : 1)];
// DBG(" SelectionWidth=%lld\n", TopImage.GetWidth());
if (TopImage.GetWidth() > CompWidth) {
CompWidth = TopImage.GetWidth();
CompHeight = CompWidth;
}
XImage Back(CompWidth, CompHeight);
Back.CopyRect(ThemeX->Background, XPos, YPos);
INTN OffsetX = (CompWidth - MainImage.GetWidth()) / 2;
OffsetX = (OffsetX > 0) ? OffsetX: 0;
INTN OffsetY = (CompHeight - MainImage.GetHeight()) / 2;
OffsetY = (OffsetY > 0) ? OffsetY: 0;
INTN OffsetTX = (CompWidth - TopImage.GetWidth()) / 2;
OffsetTX = (OffsetTX > 0) ? OffsetTX: 0;
INTN OffsetTY = (CompHeight - TopImage.GetHeight()) / 2;
OffsetTY = (OffsetTY > 0) ? OffsetTY: 0;
// DBG(" Comp=[%lld,%lld], offset=[%lld,%lld]\n", CompWidth, CompHeight, OffsetX, OffsetY);
float composeScale = (ThemeX->NonSelectedGrey && !selected)? -1.f: 1.f;
if(ThemeX->SelectionOnTop) {
//place main image in centre. It may be OS or Drive
Back.Compose(OffsetX, OffsetY, MainImage, false, composeScale);
} else {
Back.Compose(OffsetTX, OffsetTY, TopImage, false); //selection first
Back.Compose(OffsetX, OffsetY, MainImage, false, composeScale);
}
Entry->Place.XPos = XPos;
Entry->Place.YPos = YPos;
Entry->Place.Width = MainImage.GetWidth();
Entry->Place.Height = MainImage.GetHeight();
// place the badge image
float fBadgeScale = ThemeX->BadgeScale/16.f;
if ((Entry->Row == 0) && BadgeIcon && !BadgeIcon->isEmpty()) {
// const XImage& BadgeImage = (!ThemeX->Daylight && !BadgeIcon->ImageNight.isEmpty()) ? &BadgeIcon->ImageNight : BadgeImage = &BadgeIcon->Image;
const XImage& BadgeImage = BadgeIcon->GetBest(!Daylight);
INTN BadgeWidth = (INTN)(BadgeImage.GetWidth() * fBadgeScale);
INTN BadgeHeight = (INTN)(BadgeImage.GetHeight() * fBadgeScale);
if ((BadgeWidth + 8) < CompWidth && (BadgeHeight + 8) < CompHeight) {
// Check for user badge x offset from theme.plist
if (ThemeX->BadgeOffsetX != 0xFFFF) {
OffsetX += ThemeX->BadgeOffsetX;
} else {
// Set default position
OffsetX += CompWidth - 8 - BadgeWidth;
}
// Check for user badge y offset from theme.plist
if (ThemeX->BadgeOffsetY != 0xFFFF) {
OffsetY += ThemeX->BadgeOffsetY;
} else {
// Set default position
OffsetY += CompHeight - 8 - BadgeHeight;
}
// DBG(" badge offset=[%lld,%lld]\n", OffsetX, OffsetY);
Back.Compose(OffsetX, OffsetY, BadgeImage, false, fBadgeScale);
}
}
if(ThemeX->SelectionOnTop) {
Back.Compose(OffsetTX, OffsetTY, TopImage, false); //selection at the top
}
Back.DrawWithoutCompose(XPos, YPos);
// draw BCS indicator
// Needy: if Labels (Titles) are hidden there is no point to draw the indicator
if (ThemeX->BootCampStyle && !(ThemeX->HideUIFlags & HIDEUI_FLAG_LABEL)) {
// indicator is for row 0, main entries, only
if (Entry->Row == 0) {
const XImage& SelImage = ThemeX->SelectionImages[4 + (selected ? 0 : 1)];
XPos = XPos + (ThemeX->row0TileSize / 2) - (INTN)(INDICATOR_SIZE * 0.5f * ThemeX->Scale);
YPos = row0PosY + ThemeX->row0TileSize + ThemeX->TextHeight + (INTN)((BCSMargin * 2) * ThemeX->Scale);
CompWidth = (INTN)(INDICATOR_SIZE * ThemeX->Scale);
CompHeight = (INTN)(INDICATOR_SIZE * ThemeX->Scale);
Back = XImage(CompWidth, CompHeight);
Back.CopyRect(ThemeX->Background, XPos, YPos);
Back.Compose(0, 0, SelImage, false);
Back.DrawWithoutCompose(XPos, YPos);
}
}
}
/**
* Main screen text.
*/
void REFIT_MAINMENU_SCREEN::MainMenuStyle(IN UINTN Function, IN CONST CHAR16 *ParamText)
{
EFI_STATUS Status = EFI_SUCCESS;
// INTN i = 0;
INTN MessageHeight = 0;
// clovy
if (ThemeX->TypeSVG && ThemeX->getTextFace(1).valid) {
MessageHeight = (INTN)(ThemeX->getTextFace(1).size * RowHeightFromTextHeight * ThemeX->Scale);
} else {
MessageHeight = (INTN)(ThemeX->TextHeight * RowHeightFromTextHeight * ThemeX->Scale);
}
switch (Function) {
case MENU_FUNCTION_INIT:
egGetScreenSize(&UGAWidth, &UGAHeight);
InitAnime();
SwitchToGraphicsAndClear();
//BltClearScreen(false);
EntriesGap = (int)(ThemeX->TileXSpace * ThemeX->Scale);
EntriesWidth = ThemeX->row0TileSize;
EntriesHeight = ThemeX->MainEntriesSize + (int)(16.f * ThemeX->Scale);
MaxItemOnScreen = (UGAWidth - (int)((ROW0_SCROLLSIZE * 2)* ThemeX->Scale)) / (EntriesWidth + EntriesGap); //8
CountItems();
InitScroll(row0Count, Entries.size(), MaxItemOnScreen, 0);
row0PosX = EntriesWidth + EntriesGap;
row0PosX = row0PosX * ((MaxItemOnScreen < row0Count)?MaxItemOnScreen:row0Count);
row0PosX = row0PosX - EntriesGap;
row0PosX = UGAWidth - row0PosX;
row0PosX = row0PosX >> 1;
row0PosY = (int)(((float)UGAHeight - ThemeX->LayoutHeight * ThemeX->Scale) * 0.5f +
ThemeX->LayoutBannerOffset * ThemeX->Scale);
row1PosX = (UGAWidth + 8 - (ThemeX->row1TileSize + (INTN)(8.0f * ThemeX->Scale)) * row1Count) >> 1;
if (ThemeX->BootCampStyle && !(ThemeX->HideUIFlags & HIDEUI_FLAG_LABEL)) {
row1PosY = row0PosY + ThemeX->row0TileSize + (INTN)((BCSMargin * 2) * ThemeX->Scale) + ThemeX->TextHeight +
(INTN)(INDICATOR_SIZE * ThemeX->Scale) +
(INTN)((ThemeX->LayoutButtonOffset + ThemeX->TileYSpace) * ThemeX->Scale);
} else {
row1PosY = row0PosY + EntriesHeight +
(INTN)((ThemeX->TileYSpace + ThemeX->LayoutButtonOffset) * ThemeX->Scale);
}
if (row1Count > 0) {
textPosY = row1PosY + MAX(ThemeX->row1TileSize, MessageHeight) + (INTN)((ThemeX->TileYSpace + ThemeX->LayoutTextOffset) * ThemeX->Scale);
} else {
textPosY = row1PosY;
}
if (ThemeX->BootCampStyle) {
textPosY = row0PosY + ThemeX->row0TileSize + (INTN)((TEXT_YMARGIN + BCSMargin) * ThemeX->Scale);
}
FunctextPosY = row1PosY + ThemeX->row1TileSize + (INTN)((ThemeX->TileYSpace + ThemeX->LayoutTextOffset) * ThemeX->Scale);
if (!itemPosX) {
itemPosX = (__typeof__(itemPosX))AllocatePool(sizeof(UINT64) * Entries.size());
}
row0PosXRunning = row0PosX;
row1PosXRunning = row1PosX;
//DBG("EntryCount =%d\n", Entries.size());
for (INTN i = 0; i < (INTN)Entries.size(); i++) {
if (Entries[i].Row == 0) {
itemPosX[i] = row0PosXRunning;
row0PosXRunning += EntriesWidth + EntriesGap;
} else {
itemPosX[i] = row1PosXRunning;
row1PosXRunning += ThemeX->row1TileSize + (INTN)(TILE1_XSPACING * ThemeX->Scale);
//DBG("next item in row1 at x=%d\n", row1PosXRunning);
}
}
// initial painting
// ThemeX->InitSelection(); //not needed to do here
// Update FilmPlace only if not set by InitAnime
if (FilmC->FilmPlace.Width == 0 || FilmC->FilmPlace.Height == 0) {
// CopyMem(&FilmPlace, &BannerPlace, sizeof(BannerPlace));
FilmC->FilmPlace = ThemeX->BannerPlace;
}
//DBG("main menu inited\n");
break;
case MENU_FUNCTION_CLEANUP:
FreePool(itemPosX);
itemPosX = NULL;
HidePointer();
break;
case MENU_FUNCTION_PAINT_ALL:
for (INTN i = 0; i <= ScrollState.MaxIndex; i++) {
if (Entries[i].Row == 0) {
if ((i >= ScrollState.FirstVisible) && (i <= ScrollState.LastVisible)) {
DrawMainMenuEntry(&Entries[i], (i == ScrollState.CurrentSelection)?true:false,
itemPosX[i - ScrollState.FirstVisible], row0PosY);
// draw static text for the boot options, BootCampStyle
if (ThemeX->BootCampStyle && !(ThemeX->HideUIFlags & HIDEUI_FLAG_LABEL)) {
INTN textPosX = itemPosX[i - ScrollState.FirstVisible] + (ThemeX->row0TileSize / 2);
// clear the screen
ThemeX->FillRectAreaOfScreen(textPosX, textPosY, EntriesWidth + ThemeX->TileXSpace,
MessageHeight);
DrawBCSText(Entries[i].Title.wc_str(), textPosX, textPosY, X_IS_CENTER);
}
}
} else {
DrawMainMenuEntry(&Entries[i], (i == ScrollState.CurrentSelection)?true:false,
itemPosX[i], row1PosY);
}
}
// clear the text from the second row, required by the BootCampStyle
if ((ThemeX->BootCampStyle) && (Entries[ScrollState.LastSelection].Row == 1)
&& (Entries[ScrollState.CurrentSelection].Row == 0) && !(ThemeX->HideUIFlags & HIDEUI_FLAG_LABEL)) {
ThemeX->FillRectAreaOfScreen((UGAWidth >> 1), FunctextPosY,
OldTextWidth, MessageHeight);
}
if ((Entries[ScrollState.LastSelection].Row == 0) && (Entries[ScrollState.CurrentSelection].Row == 1)
&& ThemeX->BootCampStyle && !(ThemeX->HideUIFlags & HIDEUI_FLAG_LABEL)) {
DrawMainMenuLabel(Entries[ScrollState.CurrentSelection].Title,
(UGAWidth >> 1), FunctextPosY);
}
if (!(ThemeX->BootCampStyle) && !(ThemeX->HideUIFlags & HIDEUI_FLAG_LABEL)) {
DrawMainMenuLabel(Entries[ScrollState.CurrentSelection].Title,
(UGAWidth >> 1), textPosY);
}
// DBG("draw TEXT_CORNER_HELP\n");
DrawTextCorner(TEXT_CORNER_HELP, X_IS_LEFT);
// DBG("draw TEXT_CORNER_OPTIMUS\n");
DrawTextCorner(TEXT_CORNER_OPTIMUS, X_IS_CENTER);
// DBG("draw TEXT_CORNER_REVISION\n");
DrawTextCorner(TEXT_CORNER_REVISION, X_IS_RIGHT);
// DBG("MouseBirth\n");
Status = MouseBirth();
if(EFI_ERROR(Status)) {
DBG("can't bear mouse at all! Status=%s\n", efiStrError(Status));
}
break;
case MENU_FUNCTION_PAINT_SELECTION:
HidePointer();
if (Entries[ScrollState.LastSelection].Row == 0) {
DrawMainMenuEntry(&Entries[ScrollState.LastSelection], false,
itemPosX[ScrollState.LastSelection - ScrollState.FirstVisible], row0PosY);
} else {
DrawMainMenuEntry(&Entries[ScrollState.LastSelection], false,
itemPosX[ScrollState.LastSelection], row1PosY);
}
if (Entries[ScrollState.CurrentSelection].Row == 0) {
DrawMainMenuEntry(&Entries[ScrollState.CurrentSelection], true,
itemPosX[ScrollState.CurrentSelection - ScrollState.FirstVisible], row0PosY);
} else {
DrawMainMenuEntry(&Entries[ScrollState.CurrentSelection], true,
itemPosX[ScrollState.CurrentSelection], row1PosY);
}
if ((ThemeX->BootCampStyle) && (!(ThemeX->HideUIFlags & HIDEUI_FLAG_LABEL))
&& Entries[ScrollState.CurrentSelection].Row == 1) {
DrawMainMenuLabel(Entries[ScrollState.CurrentSelection].Title,
(UGAWidth >> 1), FunctextPosY);
}
if ((!(ThemeX->BootCampStyle)) && (!(ThemeX->HideUIFlags & HIDEUI_FLAG_LABEL))) {
DrawMainMenuLabel(Entries[ScrollState.CurrentSelection].Title,
(UGAWidth >> 1), textPosY);
}
DrawTextCorner(TEXT_CORNER_HELP, X_IS_LEFT);
DrawTextCorner(TEXT_CORNER_OPTIMUS, X_IS_CENTER);
DrawTextCorner(TEXT_CORNER_REVISION, X_IS_RIGHT);
Status = MouseBirth();
if(EFI_ERROR(Status)) {
DBG("can't bear mouse at sel! Status=%s\n", efiStrError(Status));
}
break;
case MENU_FUNCTION_PAINT_TIMEOUT:
INTN hi = MessageHeight * ((ThemeX->HideBadges & HDBADGES_INLINE)?3:1);
HidePointer();
if (!(ThemeX->HideUIFlags & HIDEUI_FLAG_LABEL)){
ThemeX->FillRectAreaOfScreen((UGAWidth >> 1), FunctextPosY + hi,
OldTimeoutTextWidth, MessageHeight);
XStringW TextX;
TextX.takeValueFrom(ParamText);
OldTimeoutTextWidth = DrawTextXY(TextX, (UGAWidth >> 1), FunctextPosY + hi, X_IS_CENTER);
}
DrawTextCorner(TEXT_CORNER_HELP, X_IS_LEFT);
DrawTextCorner(TEXT_CORNER_OPTIMUS, X_IS_CENTER);
DrawTextCorner(TEXT_CORNER_REVISION, X_IS_RIGHT);
Status = MouseBirth();
if(EFI_ERROR(Status)) {
DBG("can't bear mouse at timeout! Status=%s\n", efiStrError(Status));
}
break;
}
}
void REFIT_MAINMENU_SCREEN::MainMenuVerticalStyle(IN UINTN Function, IN CONST CHAR16 *ParamText)
{
// INTN i;
// INTN row0PosYRunning;
// INTN VisibleHeight = 0; //assume vertical layout
switch (Function) {
case MENU_FUNCTION_INIT:
{
egGetScreenSize(&UGAWidth, &UGAHeight); //do this when needed
InitAnime();
SwitchToGraphicsAndClear();
//BltClearScreen(false);
//adjustable by theme.plist?
EntriesPosY = (int)(LAYOUT_Y_EDGE * ThemeX->Scale);
EntriesGap = (int)(ThemeX->TileYSpace * ThemeX->Scale);
EntriesWidth = ThemeX->MainEntriesSize + (int)(16 * ThemeX->Scale);
EntriesHeight = ThemeX->MainEntriesSize + (int)(16 * ThemeX->Scale);
//
INTN VisibleHeight = (UGAHeight - EntriesPosY - (int)(LAYOUT_Y_EDGE * ThemeX->Scale) + EntriesGap) / (EntriesHeight + EntriesGap);
EntriesPosX = UGAWidth - EntriesWidth - (int)((BAR_WIDTH + LAYOUT_X_EDGE) * ThemeX->Scale);
INTN MessageHeight = 20;
if (ThemeX->TypeSVG && ThemeX->getTextFace(1).valid) {
MessageHeight = (INTN)(ThemeX->getTextFace(1).size * RowHeightFromTextHeight * ThemeX->Scale);
} else {
MessageHeight = (INTN)(ThemeX->TextHeight * RowHeightFromTextHeight * ThemeX->Scale);
}
TimeoutPosY = UGAHeight - (int)(LAYOUT_Y_EDGE * ThemeX->Scale) - MessageHeight * 2; //optimus + timeout texts
CountItems();
InitScroll(row0Count, Entries.size(), VisibleHeight, 0);
row0PosX = EntriesPosX;
row0PosY = EntriesPosY;
row1PosX = (UGAWidth + EntriesGap - (ThemeX->row1TileSize + (int)(TILE1_XSPACING * ThemeX->Scale)) * row1Count) >> 1;
textPosY = TimeoutPosY - (int)(ThemeX->TileYSpace * ThemeX->Scale) - MessageHeight; //message text
row1PosY = textPosY - ThemeX->row1TileSize - (int)(ThemeX->TileYSpace * ThemeX->Scale) - ThemeX->LayoutTextOffset;
if (!itemPosX) {
itemPosX = (__typeof__(itemPosX))AllocatePool(sizeof(UINT64) * Entries.size());
itemPosY = (__typeof__(itemPosY))AllocatePool(sizeof(UINT64) * Entries.size());
}
INTN row0PosYRunning = row0PosY;
row1PosXRunning = row1PosX;
// DBG("EntryCount =%d\n", Entries.size());
for (INTN i = 0; i < (INTN)Entries.size(); i++) {
if (Entries[i].Row == 0) {
itemPosX[i] = row0PosX;
itemPosY[i] = row0PosYRunning;
row0PosYRunning += EntriesHeight + EntriesGap;
} else {
itemPosX[i] = row1PosXRunning;
itemPosY[i] = row1PosY;
row1PosXRunning += ThemeX->row1TileSize + (int)(ThemeX->TileXSpace * ThemeX->Scale);
// DBG("next item in row1 at x=%d\n", row1PosXRunning);
}
}
// Update FilmPlace only if not set by InitAnime
if (FilmC->FilmPlace.Width == 0 || FilmC->FilmPlace.Height == 0) {
FilmC->FilmPlace = ThemeX->BannerPlace;
}
ThemeX->InitBar(); //not sure
break;
}
case MENU_FUNCTION_CLEANUP:
FreePool(itemPosX);
itemPosX = NULL;
FreePool(itemPosY);
itemPosY = NULL;
HidePointer();
break;
case MENU_FUNCTION_PAINT_ALL:
SetBar(EntriesPosX + EntriesWidth + (int)(10 * ThemeX->Scale),
EntriesPosY, UGAHeight - (int)(LAYOUT_Y_EDGE * ThemeX->Scale), &ScrollState);
for (INTN i = 0; i <= ScrollState.MaxIndex; i++) {
if (Entries[i].Row == 0) {
if ((i >= ScrollState.FirstVisible) && (i <= ScrollState.LastVisible)) {
DrawMainMenuEntry(&Entries[i], (i == ScrollState.CurrentSelection)?true:false,
itemPosX[i - ScrollState.FirstVisible], itemPosY[i - ScrollState.FirstVisible]);
}
} else { //row1
DrawMainMenuEntry(&Entries[i], (i == ScrollState.CurrentSelection)?true:false,
itemPosX[i], itemPosY[i]);
}
}
if (!(ThemeX->HideUIFlags & HIDEUI_FLAG_LABEL)){
DrawMainMenuLabel(Entries[ScrollState.CurrentSelection].Title,
(UGAWidth >> 1), textPosY);
}
ScrollingBar(); //&ScrollState);
DrawTextCorner(TEXT_CORNER_REVISION, X_IS_LEFT);
DrawTextCorner(TEXT_CORNER_OPTIMUS, X_IS_CENTER);
MouseBirth();
break;
case MENU_FUNCTION_PAINT_SELECTION:
HidePointer();
if (Entries[ScrollState.LastSelection].Row == 0) {
DrawMainMenuEntry(&Entries[ScrollState.LastSelection], false,
itemPosX[ScrollState.LastSelection - ScrollState.FirstVisible],
itemPosY[ScrollState.LastSelection - ScrollState.FirstVisible]);
} else {
DrawMainMenuEntry(&Entries[ScrollState.LastSelection], false,
itemPosX[ScrollState.LastSelection],
itemPosY[ScrollState.LastSelection]);
}
if (Entries[ScrollState.CurrentSelection].Row == 0) {
DrawMainMenuEntry(&Entries[ScrollState.CurrentSelection], true,
itemPosX[ScrollState.CurrentSelection - ScrollState.FirstVisible],
itemPosY[ScrollState.CurrentSelection - ScrollState.FirstVisible]);
} else {
DrawMainMenuEntry(&Entries[ScrollState.CurrentSelection], true,
itemPosX[ScrollState.CurrentSelection],
itemPosY[ScrollState.CurrentSelection]);
}
if (!(ThemeX->HideUIFlags & HIDEUI_FLAG_LABEL)) {
DrawMainMenuLabel(Entries[ScrollState.CurrentSelection].Title,
(UGAWidth >> 1), textPosY);
}
ScrollingBar(); //&ScrollState);
DrawTextCorner(TEXT_CORNER_REVISION, X_IS_LEFT);
DrawTextCorner(TEXT_CORNER_OPTIMUS, X_IS_CENTER);
MouseBirth();
break;
case MENU_FUNCTION_PAINT_TIMEOUT:
INTN MessageHeight = 20;
if (ThemeX->TypeSVG && ThemeX->getTextFace(1).valid) {
MessageHeight = (INTN)(ThemeX->getTextFace(1).size * RowHeightFromTextHeight * ThemeX->Scale);
} else {
MessageHeight = (INTN)(ThemeX->TextHeight * RowHeightFromTextHeight * ThemeX->Scale);
}
INTN hi = MessageHeight * ((ThemeX->HideBadges & HDBADGES_INLINE)?3:1);
HidePointer();
if (!(ThemeX->HideUIFlags & HIDEUI_FLAG_LABEL)) {
ThemeX->FillRectAreaOfScreen((UGAWidth >> 1), textPosY + hi,
OldTimeoutTextWidth, ThemeX->TextHeight);
XStringW TextX;
TextX.takeValueFrom(ParamText);
OldTimeoutTextWidth = DrawTextXY(TextX, (UGAWidth >> 1), textPosY + hi, X_IS_CENTER);
}
DrawTextCorner(TEXT_CORNER_REVISION, X_IS_LEFT);
break;
}
}
UINTN REFIT_MAINMENU_SCREEN::RunMainMenu(IN INTN DefaultSelection, OUT REFIT_ABSTRACT_MENU_ENTRY **ChosenEntry)
{
// MENU_STYLE_FUNC Style = &REFIT_MENU_SCREEN::TextMenuStyle;
// MENU_STYLE_FUNC MainStyle = &REFIT_MENU_SCREEN::TextMenuStyle;
REFIT_ABSTRACT_MENU_ENTRY *TempChosenEntry = 0;
REFIT_ABSTRACT_MENU_ENTRY *MainChosenEntry = 0;
REFIT_ABSTRACT_MENU_ENTRY *NextChosenEntry = NULL;
UINTN MenuExit = 0, SubMenuExit = 0;
INTN DefaultEntryIndex = DefaultSelection;
INTN SubMenuIndex;
// initialize static variables when menu runs so that values from previos sessions won't be used
OldX = 0;
OldY = 0;
OldTextWidth = 0;
OldTextHeight = 0;
OldRow = 0;
OldTimeoutTextWidth = 0;
// if (AllowGraphicsMode) {
//// Style = &REFIT_MENU_SCREEN::GraphicsMenuStyle;
// if (ThemeX->VerticalLayout) {
// m_MainStyle = &REFIT_MAINMENU_SCREEN::MainMenuVerticalStyle;
// } else {
// m_MainStyle = &REFIT_MAINMENU_SCREEN::MainMenuStyle;
// }
// }else{
// m_MainStyle = &REFIT_MAINMENU_SCREEN::TextMenuStyle;
// }
while (!MenuExit) {
GetAnime();
// DBG("AnimeRun=%d\n", (FilmC && FilmC->AnimeRun)?1:0);
MenuExit = RunGenericMenu(&DefaultEntryIndex, &MainChosenEntry);
TimeoutSeconds = 0;
if (MenuExit == MENU_EXIT_DETAILS && MainChosenEntry->SubScreen != NULL) {
if ( MainChosenEntry->SubScreen->Entries.size() > 0 ) { // if MainChosenEntry->SubScreen->Entries.size() == 0, we got a crash in GraphicsMenuStyle
XString8Array TmpArgs;
if ( gSettings.Boot.BootArgs.length() > 0) {
TmpArgs = Split<XString8Array>(gSettings.Boot.BootArgs, " ");
}
SubMenuIndex = -1;
GlobalConfig.OptionsBits = EncodeOptions(TmpArgs);
// DBG("main OptionsBits = 0x%X\n", GlobalConfig.OptionsBits);
if (MainChosenEntry->getLOADER_ENTRY()) {
GlobalConfig.OptionsBits |= EncodeOptions(MainChosenEntry->getLOADER_ENTRY()->LoadOptions);
// DBG("add OptionsBits = 0x%X\n", GlobalConfig.OptionsBits);
}
if (MainChosenEntry->getREFIT_MENU_ITEM_BOOTNUM()) {
DecodeOptions(MainChosenEntry->getREFIT_MENU_ITEM_BOOTNUM());
}
// DBG(" enter menu with LoadOptions: %ls\n", ((LOADER_ENTRY*)MainChosenEntry)->LoadOptions);
if (MainChosenEntry->getLOADER_ENTRY()) {
// Only for non-legacy entries, as LEGACY_ENTRY doesn't have Flags
GlobalConfig.FlagsBits = MainChosenEntry->getLOADER_ENTRY()->Flags;
}
// DBG(" MainChosenEntry with FlagsBits = 0x%X\n", GlobalConfig.FlagsBits);
SubMenuExit = 0;
while (!SubMenuExit) {
//
//running details menu
//
SubMenuExit = MainChosenEntry->SubScreen->RunGenericMenu(&SubMenuIndex, &TempChosenEntry);
if (SubMenuExit == MENU_EXIT_ESCAPE || TempChosenEntry->getREFIT_MENU_ITEM_RETURN() ) {
SubMenuExit = MENU_EXIT_ENTER;
MenuExit = 0;
break;
}
if (MainChosenEntry->getREFIT_MENU_ENTRY_CLOVER()) {
DBG("menu entry Clover\n");
MainChosenEntry->getREFIT_MENU_ENTRY_CLOVER()->LoadOptions = (((REFIT_MENU_ENTRY_CLOVER*)TempChosenEntry)->LoadOptions);
break;
}
if (SubMenuExit == MENU_EXIT_DETAILS) {
SubMenuExit = 0;
continue;
}
// DBG(" exit menu with LoadOptions: %ls\n", ((LOADER_ENTRY*)MainChosenEntry)->LoadOptions);
if (SubMenuExit == MENU_EXIT_ENTER && MainChosenEntry->getLOADER_ENTRY() && TempChosenEntry->getLOADER_ENTRY()) {
// Only for non-legacy entries, as LEGACY_ENTRY doesn't have Flags/Options
MainChosenEntry->getLOADER_ENTRY()->Flags = TempChosenEntry->getLOADER_ENTRY()->Flags;
// DBG(" get MainChosenEntry FlagsBits = 0x%X\n", ((LOADER_ENTRY*)MainChosenEntry)->Flags);
if (OSFLAG_ISUNSET(TempChosenEntry->getLOADER_ENTRY()->Flags, OSFLAG_NODEFAULTARGS)) {
DecodeOptions(TempChosenEntry->getLOADER_ENTRY());
// DBG("get OptionsBits = 0x%X\n", GlobalConfig.OptionsBits);
// DBG(" TempChosenEntry FlagsBits = 0x%X\n", ((LOADER_ENTRY*)TempChosenEntry)->Flags);
}
// copy also loadoptions from subentry to mainentry
MainChosenEntry->getLOADER_ENTRY()->LoadOptions = TempChosenEntry->getLOADER_ENTRY()->LoadOptions;
}
if (/*MenuExit == MENU_EXIT_ENTER &&*/ TempChosenEntry->getLOADER_ENTRY()) {
if (TempChosenEntry->getLOADER_ENTRY()->LoadOptions.notEmpty()) {
gSettings.Boot.BootArgs = TempChosenEntry->getLOADER_ENTRY()->LoadOptions.ConcatAll(" "_XS8);
} else {
gSettings.Boot.BootArgs.setEmpty();
}
// DBG(" boot with args: %s\n", gSettings.Boot.BootArgs.c_str());
}
//---- Details submenu (kexts disabling etc)
if (SubMenuExit == MENU_EXIT_ENTER /*|| MenuExit == MENU_EXIT_DETAILS*/) {
if (TempChosenEntry->SubScreen != NULL) {
UINTN NextMenuExit = 0;
INTN NextEntryIndex = -1;
while (!NextMenuExit) {
//
// running submenu
//
NextMenuExit = TempChosenEntry->SubScreen->RunGenericMenu(&NextEntryIndex, &NextChosenEntry);
if (NextMenuExit == MENU_EXIT_ESCAPE || NextChosenEntry->getREFIT_MENU_ITEM_RETURN() ) {
SubMenuExit = 0;
NextMenuExit = MENU_EXIT_ENTER;
break;
}
// DBG(" get NextChosenEntry FlagsBits = 0x%X\n", ((LOADER_ENTRY*)NextChosenEntry)->Flags);
//---- Details submenu (kexts disabling etc) second level
if (NextMenuExit == MENU_EXIT_ENTER /*|| MenuExit == MENU_EXIT_DETAILS*/) {
if (NextChosenEntry->SubScreen != NULL) {
UINTN DeepMenuExit = 0;
INTN DeepEntryIndex = -1;
REFIT_ABSTRACT_MENU_ENTRY *DeepChosenEntry = NULL;
while (!DeepMenuExit) {
//
// run deep submenu
//
DeepMenuExit = NextChosenEntry->SubScreen->RunGenericMenu(&DeepEntryIndex, &DeepChosenEntry);
if (DeepMenuExit == MENU_EXIT_ESCAPE || DeepChosenEntry->getREFIT_MENU_ITEM_RETURN() ) {
DeepMenuExit = MENU_EXIT_ENTER;
NextMenuExit = 0;
break;
}
// DBG(" get DeepChosenEntry FlagsBits = 0x%X\n", ((LOADER_ENTRY*)DeepChosenEntry)->Flags);
} //while(!DeepMenuExit)
}
}
} //while(!NextMenuExit)
}
}
//---------
}
}else{
// Here, it means MainChosenEntry->SubScreen != null, but MainChosenEntry->SubScreen->Entries.size() == 0.
// This is a technical bug. GraphicsMenuStyle would crash.
#ifdef JIEF_DEBUG
panic("A sub menu doesn't have any entries");
#else
MenuExit = 0; // loop on main menu
#endif
}
}
}
if (ChosenEntry) {
*ChosenEntry = MainChosenEntry;
}
return MenuExit;
}