5 * Copyright (c) 2006 Christoph Pfisterer
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 * Modifications copyright (c) 2012-2015 Roderick W. Smith
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), or (at your option) any later version.
44 * This program is free software: you can redistribute it and/or modify
45 * it under the terms of the GNU General Public License as published by
46 * the Free Software Foundation, either version 3 of the License, or
47 * (at your option) any later version.
49 * This program is distributed in the hope that it will be useful,
50 * but WITHOUT ANY WARRANTY; without even the implied warranty of
51 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
52 * GNU General Public License for more details.
54 * You should have received a copy of the GNU General Public License
55 * along with this program. If not, see <http://www.gnu.org/licenses/>.
65 #include "../include/refit_call_wrapper.h"
67 #include "../include/egemb_back_selected_small.h"
68 #include "../include/egemb_back_selected_big.h"
69 #include "../include/egemb_arrow_left.h"
70 #include "../include/egemb_arrow_right.h"
72 // other menu definitions
74 #define MENU_FUNCTION_INIT (0)
75 #define MENU_FUNCTION_CLEANUP (1)
76 #define MENU_FUNCTION_PAINT_ALL (2)
77 #define MENU_FUNCTION_PAINT_SELECTION (3)
78 #define MENU_FUNCTION_PAINT_TIMEOUT (4)
79 #define MENU_FUNCTION_PAINT_HINTS (5)
81 typedef VOID (*MENU_STYLE_FUNC
)(IN REFIT_MENU_SCREEN
*Screen
, IN SCROLL_STATE
*State
, IN UINTN Function
, IN CHAR16
*ParamText
);
83 static CHAR16 ArrowUp
[2] = { ARROW_UP
, 0 };
84 static CHAR16 ArrowDown
[2] = { ARROW_DOWN
, 0 };
85 static UINTN TileSizes
[2] = { 144, 64 };
87 // Text and icon spacing constants....
88 #define TEXT_YMARGIN (2)
89 #define TITLEICON_SPACING (16)
91 #define TILE_XSPACING (8)
92 #define TILE_YSPACING (16)
94 // Alignment values for PaintIcon()
98 static EG_IMAGE
*SelectionImages
[2] = { NULL
, NULL
};
99 static EG_PIXEL SelectionBackgroundPixel
= { 0xff, 0xff, 0xff, 0 };
102 // Graphics helper functions
105 static VOID
InitSelection(VOID
)
107 EG_IMAGE
*TempSmallImage
= NULL
, *TempBigImage
= NULL
;
108 BOOLEAN LoadedSmallImage
= FALSE
;
110 if (!AllowGraphicsMode
)
112 if (SelectionImages
[0] != NULL
)
115 // load small selection image
116 if (GlobalConfig
.SelectionSmallFileName
!= NULL
) {
117 TempSmallImage
= egLoadImage(SelfDir
, GlobalConfig
.SelectionSmallFileName
, TRUE
);
119 if (TempSmallImage
== NULL
)
120 TempSmallImage
= egPrepareEmbeddedImage(&egemb_back_selected_small
, TRUE
);
122 LoadedSmallImage
= TRUE
;
123 SelectionImages
[1] = egScaleImage(TempSmallImage
, TileSizes
[1], TileSizes
[1]);
125 // load big selection image
126 if (GlobalConfig
.SelectionBigFileName
!= NULL
) {
127 TempBigImage
= egLoadImage(SelfDir
, GlobalConfig
.SelectionBigFileName
, TRUE
);
129 if (TempBigImage
== NULL
) {
130 if (LoadedSmallImage
) {
131 // calculate big selection image from small one
132 TempBigImage
= egCopyImage(TempSmallImage
);
134 TempBigImage
= egPrepareEmbeddedImage(&egemb_back_selected_big
, TRUE
);
137 SelectionImages
[0] = egScaleImage(TempBigImage
, TileSizes
[0], TileSizes
[0]);
140 egFreeImage(TempSmallImage
);
142 egFreeImage(TempBigImage
);
143 } // VOID InitSelection()
146 // Scrolling functions
149 static VOID
InitScroll(OUT SCROLL_STATE
*State
, IN UINTN ItemCount
, IN UINTN VisibleSpace
)
151 State
->PreviousSelection
= State
->CurrentSelection
= 0;
152 State
->MaxIndex
= (INTN
)ItemCount
- 1;
153 State
->FirstVisible
= 0;
154 if (AllowGraphicsMode
) {
155 State
->MaxVisible
= UGAWidth
/ (TileSizes
[0] + TILE_XSPACING
) - 1;
157 State
->MaxVisible
= ConHeight
- 4;
158 if ((VisibleSpace
> 0) && (VisibleSpace
< State
->MaxVisible
))
159 State
->MaxVisible
= (INTN
)VisibleSpace
;
160 State
->PaintAll
= TRUE
;
161 State
->PaintSelection
= FALSE
;
163 State
->LastVisible
= State
->FirstVisible
+ State
->MaxVisible
- 1;
166 // Adjust variables relating to the scrolling of tags, for when a selected icon isn't
167 // visible given the current scrolling condition....
168 static VOID
AdjustScrollState(IN SCROLL_STATE
*State
) {
169 if (State
->CurrentSelection
> State
->LastVisible
) {
170 State
->LastVisible
= State
->CurrentSelection
;
171 State
->FirstVisible
= 1 + State
->CurrentSelection
- State
->MaxVisible
;
172 if (State
->FirstVisible
< 0) // shouldn't happen, but just in case....
173 State
->FirstVisible
= 0;
174 State
->PaintAll
= TRUE
;
176 if (State
->CurrentSelection
< State
->FirstVisible
) {
177 State
->FirstVisible
= State
->CurrentSelection
;
178 State
->LastVisible
= State
->CurrentSelection
+ State
->MaxVisible
- 1;
179 State
->PaintAll
= TRUE
;
181 } // static VOID AdjustScrollState
183 static VOID
UpdateScroll(IN OUT SCROLL_STATE
*State
, IN UINTN Movement
)
185 State
->PreviousSelection
= State
->CurrentSelection
;
188 case SCROLL_LINE_LEFT
:
189 if (State
->CurrentSelection
> 0) {
190 State
->CurrentSelection
--;
194 case SCROLL_LINE_RIGHT
:
195 if (State
->CurrentSelection
< State
->MaxIndex
) {
196 State
->CurrentSelection
++;
201 if (State
->ScrollMode
== SCROLL_MODE_ICONS
) {
202 if (State
->CurrentSelection
>= State
->InitialRow1
) {
203 if (State
->MaxIndex
> State
->InitialRow1
) { // avoid division by 0!
204 State
->CurrentSelection
= State
->FirstVisible
+ (State
->LastVisible
- State
->FirstVisible
) *
205 (State
->CurrentSelection
- State
->InitialRow1
) /
206 (State
->MaxIndex
- State
->InitialRow1
);
208 State
->CurrentSelection
= State
->FirstVisible
;
210 } // if in second row
212 if (State
->CurrentSelection
> 0)
213 State
->CurrentSelection
--;
217 case SCROLL_LINE_DOWN
:
218 if (State
->ScrollMode
== SCROLL_MODE_ICONS
) {
219 if (State
->CurrentSelection
<= State
->FinalRow0
) {
220 if (State
->LastVisible
> State
->FirstVisible
) { // avoid division by 0!
221 State
->CurrentSelection
= State
->InitialRow1
+ (State
->MaxIndex
- State
->InitialRow1
) *
222 (State
->CurrentSelection
- State
->FirstVisible
) /
223 (State
->LastVisible
- State
->FirstVisible
);
225 State
->CurrentSelection
= State
->InitialRow1
;
229 if (State
->CurrentSelection
< State
->MaxIndex
)
230 State
->CurrentSelection
++;
235 if (State
->CurrentSelection
<= State
->FinalRow0
)
236 State
->CurrentSelection
-= State
->MaxVisible
;
237 else if (State
->CurrentSelection
== State
->InitialRow1
)
238 State
->CurrentSelection
= State
->FinalRow0
;
240 State
->CurrentSelection
= State
->InitialRow1
;
241 if (State
->CurrentSelection
< 0)
242 State
->CurrentSelection
= 0;
246 if (State
->CurrentSelection
> 0) {
247 State
->PaintAll
= TRUE
;
248 State
->CurrentSelection
= 0;
252 case SCROLL_PAGE_DOWN
:
253 if (State
->CurrentSelection
< State
->FinalRow0
) {
254 State
->CurrentSelection
+= State
->MaxVisible
;
255 if (State
->CurrentSelection
> State
->FinalRow0
)
256 State
->CurrentSelection
= State
->FinalRow0
;
257 } else if (State
->CurrentSelection
== State
->FinalRow0
) {
258 State
->CurrentSelection
++;
260 State
->CurrentSelection
= State
->MaxIndex
;
262 if (State
->CurrentSelection
> State
->MaxIndex
)
263 State
->CurrentSelection
= State
->MaxIndex
;
267 if (State
->CurrentSelection
< State
->MaxIndex
) {
268 State
->PaintAll
= TRUE
;
269 State
->CurrentSelection
= State
->MaxIndex
;
277 if (State
->ScrollMode
== SCROLL_MODE_TEXT
)
278 AdjustScrollState(State
);
280 if (!State
->PaintAll
&& State
->CurrentSelection
!= State
->PreviousSelection
)
281 State
->PaintSelection
= TRUE
;
282 State
->LastVisible
= State
->FirstVisible
+ State
->MaxVisible
- 1;
283 } // static VOID UpdateScroll()
286 // menu helper functions
289 VOID
AddMenuInfoLine(IN REFIT_MENU_SCREEN
*Screen
, IN CHAR16
*InfoLine
)
291 AddListElement((VOID
***) &(Screen
->InfoLines
), &(Screen
->InfoLineCount
), InfoLine
);
294 VOID
AddMenuEntry(IN REFIT_MENU_SCREEN
*Screen
, IN REFIT_MENU_ENTRY
*Entry
)
296 AddListElement((VOID
***) &(Screen
->Entries
), &(Screen
->EntryCount
), Entry
);
300 static INTN
FindMenuShortcutEntry(IN REFIT_MENU_SCREEN
*Screen
, IN CHAR16
*Defaults
)
305 while ((Shortcut
= FindCommaDelimited(Defaults
, j
)) != NULL
) {
306 if (StrLen(Shortcut
) == 1) {
307 if (Shortcut
[0] >= 'a' && Shortcut
[0] <= 'z')
308 Shortcut
[0] -= ('a' - 'A');
310 for (i
= 0; i
< Screen
->EntryCount
; i
++) {
311 if (Screen
->Entries
[i
]->ShortcutDigit
== Shortcut
[0] || Screen
->Entries
[i
]->ShortcutLetter
== Shortcut
[0]) {
312 MyFreePool(Shortcut
);
317 } else if (StrLen(Shortcut
) > 1) {
318 for (i
= 0; i
< Screen
->EntryCount
; i
++) {
319 if (StriSubCmp(Shortcut
, Screen
->Entries
[i
]->Title
)) {
320 MyFreePool(Shortcut
);
325 MyFreePool(Shortcut
);
331 // Identify the end of row 0 and the beginning of row 1; store the results in the
332 // appropriate fields in State. Also reduce MaxVisible if that value is greater
333 // than the total number of row-0 tags and if we're in an icon-based screen
334 static VOID
IdentifyRows(IN SCROLL_STATE
*State
, IN REFIT_MENU_SCREEN
*Screen
) {
337 State
->FinalRow0
= 0;
338 State
->InitialRow1
= State
->MaxIndex
;
339 for (i
= 0; i
<= State
->MaxIndex
; i
++) {
340 if (Screen
->Entries
[i
]->Row
== 0) {
341 State
->FinalRow0
= i
;
342 } else if ((Screen
->Entries
[i
]->Row
== 1) && (State
->InitialRow1
> i
)) {
343 State
->InitialRow1
= i
;
346 if ((State
->ScrollMode
== SCROLL_MODE_ICONS
) && (State
->MaxVisible
> (State
->FinalRow0
+ 1)))
347 State
->MaxVisible
= State
->FinalRow0
+ 1;
348 } // static VOID IdentifyRows()
350 // Blank the screen, wait for a keypress, and restore banner/background.
351 // Screen may still require redrawing of text and icons on return.
352 // TODO: Support more sophisticated screen savers, such as power-saving
353 // mode and dynamic images.
354 static VOID
SaveScreen(VOID
) {
356 EG_PIXEL Black
= { 0x0, 0x0, 0x0, 0 };
358 egClearScreen(&Black
);
359 refit_call3_wrapper(BS
->WaitForEvent
, 1, &ST
->ConIn
->WaitForKey
, &index
);
360 if (AllowGraphicsMode
)
361 SwitchToGraphicsAndClear();
363 } // VOID SaveScreen()
366 // generic menu function
368 static UINTN
RunGenericMenu(IN REFIT_MENU_SCREEN
*Screen
, IN MENU_STYLE_FUNC StyleFunc
, IN OUT INTN
*DefaultEntryIndex
,
369 OUT REFIT_MENU_ENTRY
**ChosenEntry
)
376 BOOLEAN HaveTimeout
= FALSE
;
377 BOOLEAN WaitForRelease
= FALSE
;
378 UINTN TimeoutCountdown
= 0;
379 INTN PreviousTime
= -1, CurrentTime
, TimeSinceKeystroke
= 0;
380 CHAR16 TimeoutMessage
[256];
381 CHAR16 KeyAsString
[2];
384 if (Screen
->TimeoutSeconds
> 0) {
386 TimeoutCountdown
= Screen
->TimeoutSeconds
* 10;
390 StyleFunc(Screen
, &State
, MENU_FUNCTION_INIT
, NULL
);
391 IdentifyRows(&State
, Screen
);
392 // override the starting selection with the default index, if any
393 if (*DefaultEntryIndex
>= 0 && *DefaultEntryIndex
<= State
.MaxIndex
) {
394 State
.CurrentSelection
= *DefaultEntryIndex
;
395 if (GlobalConfig
.ScreensaverTime
!= -1)
396 UpdateScroll(&State
, SCROLL_NONE
);
399 if (Screen
->TimeoutSeconds
== -1) {
400 Status
= refit_call2_wrapper(ST
->ConIn
->ReadKeyStroke
, ST
->ConIn
, &key
);
401 if (Status
== EFI_NOT_READY
) {
402 MenuExit
= MENU_EXIT_TIMEOUT
;
404 KeyAsString
[0] = key
.UnicodeChar
;
406 ShortcutEntry
= FindMenuShortcutEntry(Screen
, KeyAsString
);
407 if (ShortcutEntry
>= 0) {
408 State
.CurrentSelection
= ShortcutEntry
;
409 MenuExit
= MENU_EXIT_ENTER
;
411 WaitForRelease
= TRUE
;
417 if (GlobalConfig
.ScreensaverTime
!= -1)
418 State
.PaintAll
= TRUE
;
422 if (State
.PaintAll
&& (GlobalConfig
.ScreensaverTime
!= -1)) {
423 StyleFunc(Screen
, &State
, MENU_FUNCTION_PAINT_ALL
, NULL
);
424 State
.PaintAll
= FALSE
;
425 } else if (State
.PaintSelection
) {
426 StyleFunc(Screen
, &State
, MENU_FUNCTION_PAINT_SELECTION
, NULL
);
427 State
.PaintSelection
= FALSE
;
430 if (WaitForRelease
) {
431 Status
= refit_call2_wrapper(ST
->ConIn
->ReadKeyStroke
, ST
->ConIn
, &key
);
432 if (Status
== EFI_SUCCESS
) {
433 // reset, because otherwise the buffer gets queued with keystrokes
434 refit_call2_wrapper(ST
->ConIn
->Reset
, ST
->ConIn
, FALSE
);
435 refit_call1_wrapper(BS
->Stall
, 100000);
437 WaitForRelease
= FALSE
;
438 refit_call2_wrapper(ST
->ConIn
->Reset
, ST
->ConIn
, TRUE
);
444 CurrentTime
= (TimeoutCountdown
+ 5) / 10;
445 if (CurrentTime
!= PreviousTime
) {
446 SPrint(TimeoutMessage
, 255, L
"%s in %d seconds", Screen
->TimeoutText
, CurrentTime
);
447 if (GlobalConfig
.ScreensaverTime
!= -1)
448 StyleFunc(Screen
, &State
, MENU_FUNCTION_PAINT_TIMEOUT
, TimeoutMessage
);
449 PreviousTime
= CurrentTime
;
453 // read key press (and wait for it if applicable)
454 Status
= refit_call2_wrapper(ST
->ConIn
->ReadKeyStroke
, ST
->ConIn
, &key
);
455 if (Status
!= EFI_SUCCESS
) {
456 if (HaveTimeout
&& TimeoutCountdown
== 0) {
458 MenuExit
= MENU_EXIT_TIMEOUT
;
460 } else if (HaveTimeout
|| GlobalConfig
.ScreensaverTime
> 0) {
461 EFI_EVENT TimerEvent
;
462 UINTN ElapsCount
= 1;
464 Status
= refit_call5_wrapper(BS
->CreateEvent
, EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
465 if (EFI_ERROR(Status
)) {
466 refit_call1_wrapper(BS
->Stall
, 100000); // Pause for 100 ms
468 EFI_EVENT WaitList
[2];
471 refit_call3_wrapper(BS
->SetTimer
, TimerEvent
, TimerRelative
, 10000000); // 1s Timeout
472 WaitList
[0] = ST
->ConIn
->WaitForKey
;
473 WaitList
[1] = TimerEvent
;
474 Status
= refit_call3_wrapper(BS
->WaitForEvent
, 2, WaitList
, &Index
);
475 refit_call1_wrapper(BS
->CloseEvent
, TimerEvent
);
476 if (EFI_ERROR(Status
))
477 refit_call1_wrapper(BS
->Stall
, 100000); // Pause for 100 ms
481 ElapsCount
= 10; // always counted as 1s to end of the timeout
483 TimeSinceKeystroke
+= ElapsCount
;
485 TimeoutCountdown
= TimeoutCountdown
<= ElapsCount
? 0 : TimeoutCountdown
- ElapsCount
;
486 } else if (GlobalConfig
.ScreensaverTime
> 0 &&
487 TimeSinceKeystroke
> (GlobalConfig
.ScreensaverTime
* 10))
490 State
.PaintAll
= TRUE
;
491 TimeSinceKeystroke
= 0;
494 refit_call3_wrapper(BS
->WaitForEvent
, 1, &ST
->ConIn
->WaitForKey
, &index
);
498 TimeSinceKeystroke
= 0;
499 } // if/else !read keystroke
502 // the user pressed a key, cancel the timeout
503 StyleFunc(Screen
, &State
, MENU_FUNCTION_PAINT_TIMEOUT
, L
"");
505 if (GlobalConfig
.ScreensaverTime
== -1) { // cancel start-with-blank-screen coding
506 GlobalConfig
.ScreensaverTime
= 0;
507 if (!GlobalConfig
.TextOnly
)
508 BltClearScreen(TRUE
);
512 // react to key press
513 switch (key
.ScanCode
) {
515 UpdateScroll(&State
, SCROLL_LINE_UP
);
518 UpdateScroll(&State
, SCROLL_LINE_LEFT
);
521 UpdateScroll(&State
, SCROLL_LINE_DOWN
);
524 UpdateScroll(&State
, SCROLL_LINE_RIGHT
);
527 UpdateScroll(&State
, SCROLL_FIRST
);
530 UpdateScroll(&State
, SCROLL_LAST
);
533 UpdateScroll(&State
, SCROLL_PAGE_UP
);
536 UpdateScroll(&State
, SCROLL_PAGE_DOWN
);
539 MenuExit
= MENU_EXIT_ESCAPE
;
543 MenuExit
= MENU_EXIT_DETAILS
;
550 MenuExit
= MENU_EXIT_ESCAPE
;
553 switch (key
.UnicodeChar
) {
555 case CHAR_CARRIAGE_RETURN
:
557 MenuExit
= MENU_EXIT_ENTER
;
560 MenuExit
= MENU_EXIT_DETAILS
;
563 KeyAsString
[0] = key
.UnicodeChar
;
565 ShortcutEntry
= FindMenuShortcutEntry(Screen
, KeyAsString
);
566 if (ShortcutEntry
>= 0) {
567 State
.CurrentSelection
= ShortcutEntry
;
568 MenuExit
= MENU_EXIT_ENTER
;
574 StyleFunc(Screen
, &State
, MENU_FUNCTION_CLEANUP
, NULL
);
577 *ChosenEntry
= Screen
->Entries
[State
.CurrentSelection
];
578 *DefaultEntryIndex
= State
.CurrentSelection
;
580 } /* static UINTN RunGenericMenu() */
583 // text-mode generic style
586 // Show information lines in text mode.
587 static VOID
ShowTextInfoLines(IN REFIT_MENU_SCREEN
*Screen
) {
590 BeginTextScreen(Screen
->Title
);
591 if (Screen
->InfoLineCount
> 0) {
592 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
593 for (i
= 0; i
< (INTN
)Screen
->InfoLineCount
; i
++) {
594 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 3, 4 + i
);
595 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, Screen
->InfoLines
[i
]);
598 } // VOID ShowTextInfoLines()
600 // Do most of the work for text-based menus....
601 static VOID
TextMenuStyle(IN REFIT_MENU_SCREEN
*Screen
, IN SCROLL_STATE
*State
, IN UINTN Function
, IN CHAR16
*ParamText
)
604 UINTN MenuWidth
, ItemWidth
, MenuHeight
;
605 static UINTN MenuPosY
;
606 static CHAR16
**DisplayStrings
;
607 CHAR16 TimeoutMessage
[256];
609 State
->ScrollMode
= SCROLL_MODE_TEXT
;
612 case MENU_FUNCTION_INIT
:
615 if (Screen
->InfoLineCount
> 0)
616 MenuPosY
+= Screen
->InfoLineCount
+ 1;
617 MenuHeight
= ConHeight
- MenuPosY
- 3;
618 if (Screen
->TimeoutSeconds
> 0)
620 InitScroll(State
, Screen
->EntryCount
, MenuHeight
);
622 // determine width of the menu
623 MenuWidth
= 20; // minimum
624 for (i
= 0; i
<= State
->MaxIndex
; i
++) {
625 ItemWidth
= StrLen(Screen
->Entries
[i
]->Title
);
626 if (MenuWidth
< ItemWidth
)
627 MenuWidth
= ItemWidth
;
630 if (MenuWidth
> ConWidth
- 3)
631 MenuWidth
= ConWidth
- 3;
633 // prepare strings for display
634 DisplayStrings
= AllocatePool(sizeof(CHAR16
*) * Screen
->EntryCount
);
635 for (i
= 0; i
<= State
->MaxIndex
; i
++) {
636 // Note: Theoretically, SPrint() is a cleaner way to do this; but the
637 // description of the StrSize parameter to SPrint implies it's measured
638 // in characters, but in practice both TianoCore and GNU-EFI seem to
639 // use bytes instead, resulting in truncated displays. I could just
640 // double the size of the StrSize parameter, but that seems unsafe in
641 // case a future library change starts treating this as characters, so
642 // I'm doing it the hard way in this instance.
643 // TODO: Review the above and possibly change other uses of SPrint()
644 DisplayStrings
[i
] = AllocateZeroPool(2 * sizeof(CHAR16
));
645 DisplayStrings
[i
][0] = L
' ';
646 MergeStrings(&DisplayStrings
[i
], Screen
->Entries
[i
]->Title
, 0);
647 if (StrLen(DisplayStrings
[i
]) > MenuWidth
)
648 DisplayStrings
[i
][MenuWidth
- 1] = 0;
649 // TODO: use more elaborate techniques for shortening too long strings (ellipses in the middle)
650 // TODO: account for double-width characters
655 case MENU_FUNCTION_CLEANUP
:
656 // release temporary memory
657 for (i
= 0; i
<= State
->MaxIndex
; i
++)
658 MyFreePool(DisplayStrings
[i
]);
659 MyFreePool(DisplayStrings
);
662 case MENU_FUNCTION_PAINT_ALL
:
663 // paint the whole screen (initially and after scrolling)
665 ShowTextInfoLines(Screen
);
666 for (i
= 0; i
<= State
->MaxIndex
; i
++) {
667 if (i
>= State
->FirstVisible
&& i
<= State
->LastVisible
) {
668 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 2, MenuPosY
+ (i
- State
->FirstVisible
));
669 if (i
== State
->CurrentSelection
)
670 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_CHOICE_CURRENT
);
672 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_CHOICE_BASIC
);
673 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, DisplayStrings
[i
]);
676 // scrolling indicators
677 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_SCROLLARROW
);
678 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 0, MenuPosY
);
679 if (State
->FirstVisible
> 0)
680 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, ArrowUp
);
682 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, L
" ");
683 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 0, MenuPosY
+ State
->MaxVisible
);
684 if (State
->LastVisible
< State
->MaxIndex
)
685 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, ArrowDown
);
687 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, L
" ");
688 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HINTS
)) {
689 if (Screen
->Hint1
!= NULL
) {
690 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 0, ConHeight
- 2);
691 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, Screen
->Hint1
);
693 if (Screen
->Hint2
!= NULL
) {
694 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 0, ConHeight
- 1);
695 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, Screen
->Hint2
);
700 case MENU_FUNCTION_PAINT_SELECTION
:
701 // redraw selection cursor
702 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 2,
703 MenuPosY
+ (State
->PreviousSelection
- State
->FirstVisible
));
704 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_CHOICE_BASIC
);
705 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, DisplayStrings
[State
->PreviousSelection
]);
706 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 2,
707 MenuPosY
+ (State
->CurrentSelection
- State
->FirstVisible
));
708 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_CHOICE_CURRENT
);
709 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, DisplayStrings
[State
->CurrentSelection
]);
712 case MENU_FUNCTION_PAINT_TIMEOUT
:
713 if (ParamText
[0] == 0) {
715 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
716 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 0, ConHeight
- 3);
717 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, BlankLine
+ 1);
719 // paint or update message
720 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
721 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 3, ConHeight
- 3);
722 SPrint(TimeoutMessage
, 255, L
"%s ", ParamText
);
723 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, TimeoutMessage
);
731 // graphical generic style
734 inline static UINTN
TextLineHeight(VOID
) {
735 return egGetFontHeight() + TEXT_YMARGIN
* 2;
736 } // UINTN TextLineHeight()
742 // Display text with a solid background (MenuBackgroundPixel or SelectionBackgroundPixel).
743 // Indents text by one character and placed TEXT_YMARGIN pixels down from the
744 // specified XPos and YPos locations.
745 static VOID
DrawText(IN CHAR16
*Text
, IN BOOLEAN Selected
, IN UINTN FieldWidth
, IN UINTN XPos
, IN UINTN YPos
)
747 EG_IMAGE
*TextBuffer
;
750 TextBuffer
= egCreateImage(FieldWidth
, TextLineHeight(), FALSE
);
752 egFillImage(TextBuffer
, &MenuBackgroundPixel
);
753 Bg
= MenuBackgroundPixel
;
755 // draw selection bar background
756 egFillImageArea(TextBuffer
, 0, 0, FieldWidth
, TextBuffer
->Height
, &SelectionBackgroundPixel
);
757 Bg
= SelectionBackgroundPixel
;
761 egRenderText(Text
, TextBuffer
, egGetFontCellWidth(), TEXT_YMARGIN
, (Bg
.r
+ Bg
.g
+ Bg
.b
) / 3);
762 egDrawImageWithTransparency(TextBuffer
, NULL
, XPos
, YPos
, TextBuffer
->Width
, TextBuffer
->Height
);
763 // BltImage(TextBuffer, XPos, YPos);
766 // Finds the average brightness of the input Image.
767 // NOTE: Passing an Image that covers the whole screen can strain the
768 // capacity of a UINTN on a 32-bit system with a very large display.
769 // Using UINT64 instead is unworkable, since the code won't compile
770 // on a 32-bit system. As the intended use for this function is to handle
771 // a single text string's background, this shouldn't be a problem, but it
772 // may need addressing if it's applied more broadly....
773 static UINT8
AverageBrightness(EG_IMAGE
*Image
) {
777 if ((Image
!= NULL
) && ((Image
->Width
* Image
->Height
) != 0)) {
778 for (i
= 0; i
< (Image
->Width
* Image
->Height
); i
++) {
779 Sum
+= (Image
->PixelData
[i
].r
+ Image
->PixelData
[i
].g
+ Image
->PixelData
[i
].b
);
781 Sum
/= (Image
->Width
* Image
->Height
* 3);
784 } // UINT8 AverageBrightness()
786 // Display text against the screen's background image. Special case: If Text is NULL
787 // or 0-length, clear the line. Does NOT indent the text or reposition it relative
788 // to the specified XPos and YPos values.
789 static VOID
DrawTextWithTransparency(IN CHAR16
*Text
, IN UINTN XPos
, IN UINTN YPos
)
792 EG_IMAGE
*TextBuffer
= NULL
;
797 egMeasureText(Text
, &TextWidth
, NULL
);
798 if (TextWidth
== 0) {
799 TextWidth
= UGAWidth
;
803 TextBuffer
= egCropImage(GlobalConfig
.ScreenBackground
, XPos
, YPos
, TextWidth
, TextLineHeight());
804 if (TextBuffer
== NULL
)
808 egRenderText(Text
, TextBuffer
, 0, 0, AverageBrightness(TextBuffer
));
809 egDrawImageWithTransparency(TextBuffer
, NULL
, XPos
, YPos
, TextBuffer
->Width
, TextBuffer
->Height
);
810 egFreeImage(TextBuffer
);
813 // Compute the size & position of the window that will hold a subscreen's information.
814 static VOID
ComputeSubScreenWindowSize(REFIT_MENU_SCREEN
*Screen
, IN SCROLL_STATE
*State
, UINTN
*XPos
, UINTN
*YPos
,
815 UINTN
*Width
, UINTN
*Height
, UINTN
*LineWidth
) {
816 UINTN i
, ItemWidth
, HintTop
, BannerBottomEdge
, TitleWidth
;
817 UINTN FontCellWidth
= egGetFontCellWidth();
818 UINTN FontCellHeight
= egGetFontHeight();
822 TitleWidth
= egComputeTextWidth(Screen
->Title
);
824 for (i
= 0; i
< Screen
->InfoLineCount
; i
++) {
825 ItemWidth
= StrLen(Screen
->InfoLines
[i
]);
826 if (*Width
< ItemWidth
) {
831 for (i
= 0; i
<= State
->MaxIndex
; i
++) {
832 ItemWidth
= StrLen(Screen
->Entries
[i
]->Title
);
833 if (*Width
< ItemWidth
) {
838 *Width
= (*Width
+ 2) * FontCellWidth
;
840 if (Screen
->TitleImage
)
841 *Width
+= (Screen
->TitleImage
->Width
+ TITLEICON_SPACING
* 2 + FontCellWidth
);
843 *Width
+= FontCellWidth
;
845 if (*Width
< TitleWidth
)
846 *Width
= TitleWidth
+ 2 * FontCellWidth
;
848 // Keep it within the bounds of the screen, or 2/3 of the screen's width
849 // for screens over 800 pixels wide
850 if (*Width
> UGAWidth
)
853 *XPos
= (UGAWidth
- *Width
) / 2;
855 HintTop
= UGAHeight
- (FontCellHeight
* 3); // top of hint text
856 *Height
*= TextLineHeight();
857 if (Screen
->TitleImage
&& (*Height
< (Screen
->TitleImage
->Height
+ TextLineHeight() * 4)))
858 *Height
= Screen
->TitleImage
->Height
+ TextLineHeight() * 4;
860 if (GlobalConfig
.BannerBottomEdge
>= HintTop
) { // probably a full-screen image; treat it as an empty banner
861 BannerBottomEdge
= 0;
863 BannerBottomEdge
= GlobalConfig
.BannerBottomEdge
;
865 if (*Height
> (HintTop
- BannerBottomEdge
- FontCellHeight
* 2)) {
866 BannerBottomEdge
= 0;
868 if (*Height
> (HintTop
- BannerBottomEdge
- FontCellHeight
* 2)) {
869 // TODO: Implement scrolling in text screen.
870 *Height
= (HintTop
- BannerBottomEdge
- FontCellHeight
* 2);
873 *YPos
= ((UGAHeight
- *Height
) / 2);
874 if (*YPos
< BannerBottomEdge
)
875 *YPos
= BannerBottomEdge
+ FontCellHeight
+ (HintTop
- BannerBottomEdge
- *Height
) / 2;
876 } // VOID ComputeSubScreenWindowSize()
878 // Displays sub-menus
879 static VOID
GraphicsMenuStyle(IN REFIT_MENU_SCREEN
*Screen
, IN SCROLL_STATE
*State
, IN UINTN Function
, IN CHAR16
*ParamText
)
883 static UINTN LineWidth
, MenuWidth
, MenuHeight
, EntriesPosX
, TitlePosX
, EntriesPosY
, TimeoutPosY
, CharWidth
;
885 EG_PIXEL
*BackgroundPixel
= &(GlobalConfig
.ScreenBackground
->PixelData
[0]);
887 CharWidth
= egGetFontCellWidth();
888 State
->ScrollMode
= SCROLL_MODE_TEXT
;
891 case MENU_FUNCTION_INIT
:
892 InitScroll(State
, Screen
->EntryCount
, 0);
893 ComputeSubScreenWindowSize(Screen
, State
, &EntriesPosX
, &EntriesPosY
, &MenuWidth
, &MenuHeight
, &LineWidth
);
894 TimeoutPosY
= EntriesPosY
+ (Screen
->EntryCount
+ 1) * TextLineHeight();
897 SwitchToGraphicsAndClear();
898 Window
= egCreateFilledImage(MenuWidth
, MenuHeight
, FALSE
, BackgroundPixel
);
899 egDrawImage(Window
, EntriesPosX
, EntriesPosY
);
900 ItemWidth
= egComputeTextWidth(Screen
->Title
);
901 if (MenuWidth
> ItemWidth
) {
902 TitlePosX
= EntriesPosX
+ (MenuWidth
- ItemWidth
) / 2 - CharWidth
;
904 TitlePosX
= EntriesPosX
;
906 i
= MenuWidth
/ CharWidth
- 2;
908 Screen
->Title
[i
] = 0;
913 case MENU_FUNCTION_CLEANUP
:
917 case MENU_FUNCTION_PAINT_ALL
:
918 ComputeSubScreenWindowSize(Screen
, State
, &EntriesPosX
, &EntriesPosY
, &MenuWidth
, &MenuHeight
, &LineWidth
);
919 DrawText(Screen
->Title
, FALSE
, (StrLen(Screen
->Title
) + 2) * CharWidth
, TitlePosX
, EntriesPosY
+= TextLineHeight());
920 if (Screen
->TitleImage
) {
921 BltImageAlpha(Screen
->TitleImage
, EntriesPosX
+ TITLEICON_SPACING
, EntriesPosY
+ TextLineHeight() * 2,
923 EntriesPosX
+= (Screen
->TitleImage
->Width
+ TITLEICON_SPACING
* 2);
925 EntriesPosY
+= (TextLineHeight() * 2);
926 if (Screen
->InfoLineCount
> 0) {
927 for (i
= 0; i
< (INTN
)Screen
->InfoLineCount
; i
++) {
928 DrawText(Screen
->InfoLines
[i
], FALSE
, LineWidth
, EntriesPosX
, EntriesPosY
);
929 EntriesPosY
+= TextLineHeight();
931 EntriesPosY
+= TextLineHeight(); // also add a blank line
934 for (i
= 0; i
<= State
->MaxIndex
; i
++) {
935 DrawText(Screen
->Entries
[i
]->Title
, (i
== State
->CurrentSelection
), LineWidth
, EntriesPosX
,
936 EntriesPosY
+ i
* TextLineHeight());
938 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HINTS
)) {
939 if ((Screen
->Hint1
!= NULL
) && (StrLen(Screen
->Hint1
) > 0))
940 DrawTextWithTransparency(Screen
->Hint1
, (UGAWidth
- egComputeTextWidth(Screen
->Hint1
)) / 2,
941 UGAHeight
- (egGetFontHeight() * 3));
942 if ((Screen
->Hint2
!= NULL
) && (StrLen(Screen
->Hint2
) > 0))
943 DrawTextWithTransparency(Screen
->Hint2
, (UGAWidth
- egComputeTextWidth(Screen
->Hint2
)) / 2,
944 UGAHeight
- (egGetFontHeight() * 2));
948 case MENU_FUNCTION_PAINT_SELECTION
:
949 // redraw selection cursor
950 DrawText(Screen
->Entries
[State
->PreviousSelection
]->Title
, FALSE
, LineWidth
,
951 EntriesPosX
, EntriesPosY
+ State
->PreviousSelection
* TextLineHeight());
952 DrawText(Screen
->Entries
[State
->CurrentSelection
]->Title
, TRUE
, LineWidth
,
953 EntriesPosX
, EntriesPosY
+ State
->CurrentSelection
* TextLineHeight());
956 case MENU_FUNCTION_PAINT_TIMEOUT
:
957 DrawText(ParamText
, FALSE
, LineWidth
, EntriesPosX
, TimeoutPosY
);
961 } // static VOID GraphicsMenuStyle()
964 // graphical main menu style
967 static VOID
DrawMainMenuEntry(REFIT_MENU_ENTRY
*Entry
, BOOLEAN selected
, UINTN XPos
, UINTN YPos
)
969 EG_IMAGE
*Background
;
971 if (SelectionImages
!= NULL
) {
973 Background
= egCropImage(GlobalConfig
.ScreenBackground
, XPos
, YPos
,
974 SelectionImages
[Entry
->Row
]->Width
, SelectionImages
[Entry
->Row
]->Height
);
975 egComposeImage(Background
, SelectionImages
[Entry
->Row
], 0, 0);
976 BltImageCompositeBadge(Background
, Entry
->Image
, Entry
->BadgeImage
, XPos
, YPos
);
977 } else { // Image not selected; copy background
978 egDrawImageWithTransparency(Entry
->Image
, Entry
->BadgeImage
, XPos
, YPos
,
979 SelectionImages
[Entry
->Row
]->Width
, SelectionImages
[Entry
->Row
]->Height
);
982 } // VOID DrawMainMenuEntry()
984 static VOID
PaintAll(IN REFIT_MENU_SCREEN
*Screen
, IN SCROLL_STATE
*State
, UINTN
*itemPosX
,
985 UINTN row0PosY
, UINTN row1PosY
, UINTN textPosY
) {
988 if (Screen
->Entries
[State
->CurrentSelection
]->Row
== 0)
989 AdjustScrollState(State
);
990 for (i
= State
->FirstVisible
; i
<= State
->MaxIndex
; i
++) {
991 if (Screen
->Entries
[i
]->Row
== 0) {
992 if (i
<= State
->LastVisible
) {
993 DrawMainMenuEntry(Screen
->Entries
[i
], (i
== State
->CurrentSelection
) ? TRUE
: FALSE
,
994 itemPosX
[i
- State
->FirstVisible
], row0PosY
);
997 DrawMainMenuEntry(Screen
->Entries
[i
], (i
== State
->CurrentSelection
) ? TRUE
: FALSE
, itemPosX
[i
], row1PosY
);
1000 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_LABEL
)) {
1001 DrawTextWithTransparency(L
"", 0, textPosY
);
1002 DrawTextWithTransparency(Screen
->Entries
[State
->CurrentSelection
]->Title
,
1003 (UGAWidth
- egComputeTextWidth(Screen
->Entries
[State
->CurrentSelection
]->Title
)) >> 1,
1007 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HINTS
)) {
1008 DrawTextWithTransparency(Screen
->Hint1
, (UGAWidth
- egComputeTextWidth(Screen
->Hint1
)) / 2,
1009 UGAHeight
- (egGetFontHeight() * 3));
1010 DrawTextWithTransparency(Screen
->Hint2
, (UGAWidth
- egComputeTextWidth(Screen
->Hint2
)) / 2,
1011 UGAHeight
- (egGetFontHeight() * 2));
1013 } // static VOID PaintAll()
1015 // Move the selection to State->CurrentSelection, adjusting icon row if necessary...
1016 static VOID
PaintSelection(IN REFIT_MENU_SCREEN
*Screen
, IN SCROLL_STATE
*State
, UINTN
*itemPosX
,
1017 UINTN row0PosY
, UINTN row1PosY
, UINTN textPosY
) {
1018 UINTN XSelectPrev
, XSelectCur
, YPosPrev
, YPosCur
;
1020 if (((State
->CurrentSelection
<= State
->LastVisible
) && (State
->CurrentSelection
>= State
->FirstVisible
)) ||
1021 (State
->CurrentSelection
>= State
->InitialRow1
) ) {
1022 if (Screen
->Entries
[State
->PreviousSelection
]->Row
== 0) {
1023 XSelectPrev
= State
->PreviousSelection
- State
->FirstVisible
;
1024 YPosPrev
= row0PosY
;
1026 XSelectPrev
= State
->PreviousSelection
;
1027 YPosPrev
= row1PosY
;
1029 if (Screen
->Entries
[State
->CurrentSelection
]->Row
== 0) {
1030 XSelectCur
= State
->CurrentSelection
- State
->FirstVisible
;
1033 XSelectCur
= State
->CurrentSelection
;
1036 DrawMainMenuEntry(Screen
->Entries
[State
->PreviousSelection
], FALSE
, itemPosX
[XSelectPrev
], YPosPrev
);
1037 DrawMainMenuEntry(Screen
->Entries
[State
->CurrentSelection
], TRUE
, itemPosX
[XSelectCur
], YPosCur
);
1038 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_LABEL
)) {
1039 DrawTextWithTransparency(L
"", 0, textPosY
);
1040 DrawTextWithTransparency(Screen
->Entries
[State
->CurrentSelection
]->Title
,
1041 (UGAWidth
- egComputeTextWidth(Screen
->Entries
[State
->CurrentSelection
]->Title
)) >> 1,
1044 } else { // Current selection not visible; must redraw the menu....
1045 MainMenuStyle(Screen
, State
, MENU_FUNCTION_PAINT_ALL
, NULL
);
1047 } // static VOID MoveSelection(VOID)
1049 // Display a 48x48 icon at the specified location. Uses the image specified by
1050 // ExternalFilename if it's available, or BuiltInImage if it's not. The
1051 // Y position is specified as the center value, and so is adjusted by half
1052 // the icon's height. The X position is set along the icon's left
1053 // edge if Alignment == ALIGN_LEFT, and along the right edge if
1054 // Alignment == ALIGN_RIGHT
1055 static VOID
PaintIcon(IN EG_EMBEDDED_IMAGE
*BuiltInIcon
, IN CHAR16
*ExternalFilename
, UINTN PosX
, UINTN PosY
, UINTN Alignment
) {
1056 EG_IMAGE
*Icon
= NULL
;
1058 Icon
= egFindIcon(ExternalFilename
, GlobalConfig
.IconSizes
[ICON_SIZE_SMALL
]);
1060 Icon
= egPrepareEmbeddedImage(BuiltInIcon
, TRUE
);
1062 if (Alignment
== ALIGN_RIGHT
)
1063 PosX
-= Icon
->Width
;
1064 egDrawImageWithTransparency(Icon
, NULL
, PosX
, PosY
- (Icon
->Height
/ 2), Icon
->Width
, Icon
->Height
);
1068 UINTN
ComputeRow0PosY(VOID
) {
1069 return ((UGAHeight
/ 2) - TileSizes
[0] / 2);
1070 } // UINTN ComputeRow0PosY()
1072 // Display (or erase) the arrow icons to the left and right of an icon's row,
1074 static VOID
PaintArrows(SCROLL_STATE
*State
, UINTN PosX
, UINTN PosY
, UINTN row0Loaders
) {
1075 EG_IMAGE
*TempImage
;
1076 UINTN Width
, Height
, RightX
, AdjPosY
;
1078 // NOTE: Assume that left and right arrows are of the same size....
1079 Width
= egemb_arrow_left
.Width
;
1080 Height
= egemb_arrow_left
.Height
;
1081 RightX
= (UGAWidth
+ (TileSizes
[0] + TILE_XSPACING
) * State
->MaxVisible
) / 2 + TILE_XSPACING
;
1082 AdjPosY
= PosY
- (Height
/ 2);
1084 // For PaintIcon() calls, the starting Y position is moved to the midpoint
1085 // of the surrounding row; PaintIcon() adjusts this back up by half the
1086 // icon's height to properly center it.
1087 if ((State
->FirstVisible
> 0) && (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_ARROWS
))) {
1088 PaintIcon(&egemb_arrow_left
, L
"arrow_left", PosX
, PosY
, ALIGN_RIGHT
);
1090 TempImage
= egCropImage(GlobalConfig
.ScreenBackground
, PosX
- Width
, AdjPosY
, Width
, Height
);
1091 BltImage(TempImage
, PosX
- Width
, AdjPosY
);
1092 egFreeImage(TempImage
);
1095 if ((State
->LastVisible
< (row0Loaders
- 1)) && (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_ARROWS
))) {
1096 PaintIcon(&egemb_arrow_right
, L
"arrow_right", RightX
, PosY
, ALIGN_LEFT
);
1098 TempImage
= egCropImage(GlobalConfig
.ScreenBackground
, RightX
, AdjPosY
, Width
, Height
);
1099 BltImage(TempImage
, RightX
, AdjPosY
);
1100 egFreeImage(TempImage
);
1102 } // VOID PaintArrows()
1104 // Display main menu in graphics mode
1105 VOID
MainMenuStyle(IN REFIT_MENU_SCREEN
*Screen
, IN SCROLL_STATE
*State
, IN UINTN Function
, IN CHAR16
*ParamText
)
1108 static UINTN row0PosX
, row0PosXRunning
, row1PosY
, row0Loaders
;
1109 UINTN row0Count
, row1Count
, row1PosX
, row1PosXRunning
;
1110 static UINTN
*itemPosX
;
1111 static UINTN row0PosY
, textPosY
;
1113 State
->ScrollMode
= SCROLL_MODE_ICONS
;
1116 case MENU_FUNCTION_INIT
:
1117 InitScroll(State
, Screen
->EntryCount
, GlobalConfig
.MaxTags
);
1123 for (i
= 0; i
<= State
->MaxIndex
; i
++) {
1124 if (Screen
->Entries
[i
]->Row
== 1) {
1128 if (row0Count
< State
->MaxVisible
)
1132 row0PosX
= (UGAWidth
+ TILE_XSPACING
- (TileSizes
[0] + TILE_XSPACING
) * row0Count
) >> 1;
1133 row0PosY
= ComputeRow0PosY();
1134 row1PosX
= (UGAWidth
+ TILE_XSPACING
- (TileSizes
[1] + TILE_XSPACING
) * row1Count
) >> 1;
1135 row1PosY
= row0PosY
+ TileSizes
[0] + TILE_YSPACING
;
1137 textPosY
= row1PosY
+ TileSizes
[1] + TILE_YSPACING
;
1139 textPosY
= row1PosY
;
1141 itemPosX
= AllocatePool(sizeof(UINTN
) * Screen
->EntryCount
);
1142 row0PosXRunning
= row0PosX
;
1143 row1PosXRunning
= row1PosX
;
1144 for (i
= 0; i
<= State
->MaxIndex
; i
++) {
1145 if (Screen
->Entries
[i
]->Row
== 0) {
1146 itemPosX
[i
] = row0PosXRunning
;
1147 row0PosXRunning
+= TileSizes
[0] + TILE_XSPACING
;
1149 itemPosX
[i
] = row1PosXRunning
;
1150 row1PosXRunning
+= TileSizes
[1] + TILE_XSPACING
;
1155 SwitchToGraphicsAndClear();
1158 case MENU_FUNCTION_CLEANUP
:
1159 MyFreePool(itemPosX
);
1162 case MENU_FUNCTION_PAINT_ALL
:
1163 PaintAll(Screen
, State
, itemPosX
, row0PosY
, row1PosY
, textPosY
);
1164 // For PaintArrows(), the starting Y position is moved to the midpoint
1165 // of the surrounding row; PaintIcon() adjusts this back up by half the
1166 // icon's height to properly center it.
1167 PaintArrows(State
, row0PosX
- TILE_XSPACING
, row0PosY
+ (TileSizes
[0] / 2), row0Loaders
);
1170 case MENU_FUNCTION_PAINT_SELECTION
:
1171 PaintSelection(Screen
, State
, itemPosX
, row0PosY
, row1PosY
, textPosY
);
1174 case MENU_FUNCTION_PAINT_TIMEOUT
:
1175 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_LABEL
)) {
1176 DrawTextWithTransparency(L
"", 0, textPosY
+ TextLineHeight());
1177 DrawTextWithTransparency(ParamText
, (UGAWidth
- egComputeTextWidth(ParamText
)) >> 1, textPosY
+ TextLineHeight());
1182 } // VOID MainMenuStyle()
1184 // Enable the user to edit boot loader options.
1185 // Returns TRUE if the user exited with edited options; FALSE if the user
1186 // pressed Esc to terminate the edit.
1187 static BOOLEAN
EditOptions(LOADER_ENTRY
*MenuEntry
) {
1189 CHAR16
*EditedOptions
;
1190 BOOLEAN retval
= FALSE
;
1192 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1196 refit_call4_wrapper(ST
->ConOut
->QueryMode
, ST
->ConOut
, ST
->ConOut
->Mode
->Mode
, &x_max
, &y_max
);
1198 if (!GlobalConfig
.TextOnly
)
1201 if (line_edit(MenuEntry
->LoadOptions
, &EditedOptions
, x_max
)) {
1202 MyFreePool(MenuEntry
->LoadOptions
);
1203 MenuEntry
->LoadOptions
= EditedOptions
;
1206 if (!GlobalConfig
.TextOnly
)
1209 } // VOID EditOptions()
1212 // user-callable dispatcher functions
1215 UINTN
RunMenu(IN REFIT_MENU_SCREEN
*Screen
, OUT REFIT_MENU_ENTRY
**ChosenEntry
)
1217 INTN DefaultEntry
= -1;
1218 MENU_STYLE_FUNC Style
= TextMenuStyle
;
1220 if (AllowGraphicsMode
)
1221 Style
= GraphicsMenuStyle
;
1223 return RunGenericMenu(Screen
, Style
, &DefaultEntry
, ChosenEntry
);
1226 UINTN
RunMainMenu(REFIT_MENU_SCREEN
*Screen
, CHAR16
** DefaultSelection
, REFIT_MENU_ENTRY
**ChosenEntry
)
1228 MENU_STYLE_FUNC Style
= TextMenuStyle
;
1229 MENU_STYLE_FUNC MainStyle
= TextMenuStyle
;
1230 REFIT_MENU_ENTRY
*TempChosenEntry
;
1233 INTN DefaultEntryIndex
= -1;
1234 INTN DefaultSubmenuIndex
= -1;
1236 TileSizes
[0] = (GlobalConfig
.IconSizes
[ICON_SIZE_BIG
] * 9) / 8;
1237 TileSizes
[1] = (GlobalConfig
.IconSizes
[ICON_SIZE_SMALL
] * 4) / 3;
1239 if ((DefaultSelection
!= NULL
) && (*DefaultSelection
!= NULL
)) {
1240 // Find a menu entry that includes *DefaultSelection as a substring
1241 DefaultEntryIndex
= FindMenuShortcutEntry(Screen
, *DefaultSelection
);
1244 if (AllowGraphicsMode
) {
1245 Style
= GraphicsMenuStyle
;
1246 MainStyle
= MainMenuStyle
;
1250 MenuExit
= RunGenericMenu(Screen
, MainStyle
, &DefaultEntryIndex
, &TempChosenEntry
);
1251 Screen
->TimeoutSeconds
= 0;
1253 MenuTitle
= StrDuplicate(TempChosenEntry
->Title
);
1254 if (MenuExit
== MENU_EXIT_DETAILS
) {
1255 if (TempChosenEntry
->SubScreen
!= NULL
) {
1256 MenuExit
= RunGenericMenu(TempChosenEntry
->SubScreen
, Style
, &DefaultSubmenuIndex
, &TempChosenEntry
);
1257 if (MenuExit
== MENU_EXIT_ESCAPE
|| TempChosenEntry
->Tag
== TAG_RETURN
)
1259 if (MenuExit
== MENU_EXIT_DETAILS
) {
1260 if (!EditOptions((LOADER_ENTRY
*) TempChosenEntry
))
1263 } else { // no sub-screen; ignore keypress
1266 } // Enter sub-screen
1270 *ChosenEntry
= TempChosenEntry
;
1271 if (DefaultSelection
) {
1272 MyFreePool(*DefaultSelection
);
1273 *DefaultSelection
= MenuTitle
;
1276 } /* UINTN RunMainMenu() */