CloverBootloader/rEFIt_UEFI/gui/REFIT_MAINMENU_SCREEN.cpp

883 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());
}else{
if ( SelfVolume->VolName.notEmpty() ) {
Text = SWPrintf("%ls %ls%ls", gFirmwareRevision, SelfVolume->VolName.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;
}