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 "line_edit.h"
66 #include "../include/refit_call_wrapper.h"
68 #include "../include/egemb_back_selected_small.h"
69 #include "../include/egemb_back_selected_big.h"
70 #include "../include/egemb_arrow_left.h"
71 #include "../include/egemb_arrow_right.h"
73 // other menu definitions
75 #define MENU_FUNCTION_INIT (0)
76 #define MENU_FUNCTION_CLEANUP (1)
77 #define MENU_FUNCTION_PAINT_ALL (2)
78 #define MENU_FUNCTION_PAINT_SELECTION (3)
79 #define MENU_FUNCTION_PAINT_TIMEOUT (4)
80 #define MENU_FUNCTION_PAINT_HINTS (5)
82 typedef VOID (*MENU_STYLE_FUNC
)(IN REFIT_MENU_SCREEN
*Screen
, IN SCROLL_STATE
*State
, IN UINTN Function
, IN CHAR16
*ParamText
);
84 static CHAR16 ArrowUp
[2] = { ARROW_UP
, 0 };
85 static CHAR16 ArrowDown
[2] = { ARROW_DOWN
, 0 };
86 static UINTN TileSizes
[2] = { 144, 64 };
88 // Text and icon spacing constants....
89 #define TEXT_YMARGIN (2)
90 #define TITLEICON_SPACING (16)
92 #define TILE_XSPACING (8)
93 #define TILE_YSPACING (16)
95 // Alignment values for PaintIcon()
99 static EG_IMAGE
*SelectionImages
[2] = { NULL
, NULL
};
100 static EG_PIXEL SelectionBackgroundPixel
= { 0xff, 0xff, 0xff, 0 };
103 // Graphics helper functions
106 static VOID
InitSelection(VOID
)
108 EG_IMAGE
*TempSmallImage
= NULL
, *TempBigImage
= NULL
;
109 BOOLEAN LoadedSmallImage
= FALSE
;
111 if (!AllowGraphicsMode
)
113 if (SelectionImages
[0] != NULL
)
116 // load small selection image
117 if (GlobalConfig
.SelectionSmallFileName
!= NULL
) {
118 TempSmallImage
= egLoadImage(SelfDir
, GlobalConfig
.SelectionSmallFileName
, TRUE
);
120 if (TempSmallImage
== NULL
)
121 TempSmallImage
= egPrepareEmbeddedImage(&egemb_back_selected_small
, TRUE
);
123 LoadedSmallImage
= TRUE
;
124 SelectionImages
[1] = egScaleImage(TempSmallImage
, TileSizes
[1], TileSizes
[1]);
126 // load big selection image
127 if (GlobalConfig
.SelectionBigFileName
!= NULL
) {
128 TempBigImage
= egLoadImage(SelfDir
, GlobalConfig
.SelectionBigFileName
, TRUE
);
130 if (TempBigImage
== NULL
) {
131 if (LoadedSmallImage
) {
132 // calculate big selection image from small one
133 TempBigImage
= egCopyImage(TempSmallImage
);
135 TempBigImage
= egPrepareEmbeddedImage(&egemb_back_selected_big
, TRUE
);
138 SelectionImages
[0] = egScaleImage(TempBigImage
, TileSizes
[0], TileSizes
[0]);
141 egFreeImage(TempSmallImage
);
143 egFreeImage(TempBigImage
);
144 } // VOID InitSelection()
147 // Scrolling functions
150 static VOID
InitScroll(OUT SCROLL_STATE
*State
, IN UINTN ItemCount
, IN UINTN VisibleSpace
)
152 State
->PreviousSelection
= State
->CurrentSelection
= 0;
153 State
->MaxIndex
= (INTN
)ItemCount
- 1;
154 State
->FirstVisible
= 0;
155 if (AllowGraphicsMode
) {
156 State
->MaxVisible
= UGAWidth
/ (TileSizes
[0] + TILE_XSPACING
) - 1;
158 State
->MaxVisible
= ConHeight
- 4;
159 if ((VisibleSpace
> 0) && (VisibleSpace
< State
->MaxVisible
))
160 State
->MaxVisible
= (INTN
)VisibleSpace
;
161 State
->PaintAll
= TRUE
;
162 State
->PaintSelection
= FALSE
;
164 State
->LastVisible
= State
->FirstVisible
+ State
->MaxVisible
- 1;
167 // Adjust variables relating to the scrolling of tags, for when a selected icon isn't
168 // visible given the current scrolling condition....
169 static VOID
AdjustScrollState(IN SCROLL_STATE
*State
) {
170 if (State
->CurrentSelection
> State
->LastVisible
) {
171 State
->LastVisible
= State
->CurrentSelection
;
172 State
->FirstVisible
= 1 + State
->CurrentSelection
- State
->MaxVisible
;
173 if (State
->FirstVisible
< 0) // shouldn't happen, but just in case....
174 State
->FirstVisible
= 0;
175 State
->PaintAll
= TRUE
;
177 if (State
->CurrentSelection
< State
->FirstVisible
) {
178 State
->FirstVisible
= State
->CurrentSelection
;
179 State
->LastVisible
= State
->CurrentSelection
+ State
->MaxVisible
- 1;
180 State
->PaintAll
= TRUE
;
182 } // static VOID AdjustScrollState
184 static VOID
UpdateScroll(IN OUT SCROLL_STATE
*State
, IN UINTN Movement
)
186 State
->PreviousSelection
= State
->CurrentSelection
;
189 case SCROLL_LINE_LEFT
:
190 if (State
->CurrentSelection
> 0) {
191 State
->CurrentSelection
--;
195 case SCROLL_LINE_RIGHT
:
196 if (State
->CurrentSelection
< State
->MaxIndex
) {
197 State
->CurrentSelection
++;
202 if (State
->ScrollMode
== SCROLL_MODE_ICONS
) {
203 if (State
->CurrentSelection
>= State
->InitialRow1
) {
204 if (State
->MaxIndex
> State
->InitialRow1
) { // avoid division by 0!
205 State
->CurrentSelection
= State
->FirstVisible
+ (State
->LastVisible
- State
->FirstVisible
) *
206 (State
->CurrentSelection
- State
->InitialRow1
) /
207 (State
->MaxIndex
- State
->InitialRow1
);
209 State
->CurrentSelection
= State
->FirstVisible
;
211 } // if in second row
213 if (State
->CurrentSelection
> 0)
214 State
->CurrentSelection
--;
218 case SCROLL_LINE_DOWN
:
219 if (State
->ScrollMode
== SCROLL_MODE_ICONS
) {
220 if (State
->CurrentSelection
<= State
->FinalRow0
) {
221 if (State
->LastVisible
> State
->FirstVisible
) { // avoid division by 0!
222 State
->CurrentSelection
= State
->InitialRow1
+ (State
->MaxIndex
- State
->InitialRow1
) *
223 (State
->CurrentSelection
- State
->FirstVisible
) /
224 (State
->LastVisible
- State
->FirstVisible
);
226 State
->CurrentSelection
= State
->InitialRow1
;
230 if (State
->CurrentSelection
< State
->MaxIndex
)
231 State
->CurrentSelection
++;
236 if (State
->CurrentSelection
<= State
->FinalRow0
)
237 State
->CurrentSelection
-= State
->MaxVisible
;
238 else if (State
->CurrentSelection
== State
->InitialRow1
)
239 State
->CurrentSelection
= State
->FinalRow0
;
241 State
->CurrentSelection
= State
->InitialRow1
;
242 if (State
->CurrentSelection
< 0)
243 State
->CurrentSelection
= 0;
247 if (State
->CurrentSelection
> 0) {
248 State
->PaintAll
= TRUE
;
249 State
->CurrentSelection
= 0;
253 case SCROLL_PAGE_DOWN
:
254 if (State
->CurrentSelection
< State
->FinalRow0
) {
255 State
->CurrentSelection
+= State
->MaxVisible
;
256 if (State
->CurrentSelection
> State
->FinalRow0
)
257 State
->CurrentSelection
= State
->FinalRow0
;
258 } else if (State
->CurrentSelection
== State
->FinalRow0
) {
259 State
->CurrentSelection
++;
261 State
->CurrentSelection
= State
->MaxIndex
;
263 if (State
->CurrentSelection
> State
->MaxIndex
)
264 State
->CurrentSelection
= State
->MaxIndex
;
268 if (State
->CurrentSelection
< State
->MaxIndex
) {
269 State
->PaintAll
= TRUE
;
270 State
->CurrentSelection
= State
->MaxIndex
;
278 if (State
->ScrollMode
== SCROLL_MODE_TEXT
)
279 AdjustScrollState(State
);
281 if (!State
->PaintAll
&& State
->CurrentSelection
!= State
->PreviousSelection
)
282 State
->PaintSelection
= TRUE
;
283 State
->LastVisible
= State
->FirstVisible
+ State
->MaxVisible
- 1;
284 } // static VOID UpdateScroll()
287 // menu helper functions
290 VOID
AddMenuInfoLine(IN REFIT_MENU_SCREEN
*Screen
, IN CHAR16
*InfoLine
)
292 AddListElement((VOID
***) &(Screen
->InfoLines
), &(Screen
->InfoLineCount
), InfoLine
);
295 VOID
AddMenuEntry(IN REFIT_MENU_SCREEN
*Screen
, IN REFIT_MENU_ENTRY
*Entry
)
297 AddListElement((VOID
***) &(Screen
->Entries
), &(Screen
->EntryCount
), Entry
);
301 static INTN
FindMenuShortcutEntry(IN REFIT_MENU_SCREEN
*Screen
, IN CHAR16
*Defaults
)
306 while ((Shortcut
= FindCommaDelimited(Defaults
, j
)) != NULL
) {
307 if (StrLen(Shortcut
) == 1) {
308 if (Shortcut
[0] >= 'a' && Shortcut
[0] <= 'z')
309 Shortcut
[0] -= ('a' - 'A');
311 for (i
= 0; i
< Screen
->EntryCount
; i
++) {
312 if (Screen
->Entries
[i
]->ShortcutDigit
== Shortcut
[0] || Screen
->Entries
[i
]->ShortcutLetter
== Shortcut
[0]) {
313 MyFreePool(Shortcut
);
318 } else if (StrLen(Shortcut
) > 1) {
319 for (i
= 0; i
< Screen
->EntryCount
; i
++) {
320 if (StriSubCmp(Shortcut
, Screen
->Entries
[i
]->Title
)) {
321 MyFreePool(Shortcut
);
326 MyFreePool(Shortcut
);
332 // Identify the end of row 0 and the beginning of row 1; store the results in the
333 // appropriate fields in State. Also reduce MaxVisible if that value is greater
334 // than the total number of row-0 tags and if we're in an icon-based screen
335 static VOID
IdentifyRows(IN SCROLL_STATE
*State
, IN REFIT_MENU_SCREEN
*Screen
) {
338 State
->FinalRow0
= 0;
339 State
->InitialRow1
= State
->MaxIndex
;
340 for (i
= 0; i
<= State
->MaxIndex
; i
++) {
341 if (Screen
->Entries
[i
]->Row
== 0) {
342 State
->FinalRow0
= i
;
343 } else if ((Screen
->Entries
[i
]->Row
== 1) && (State
->InitialRow1
> i
)) {
344 State
->InitialRow1
= i
;
347 if ((State
->ScrollMode
== SCROLL_MODE_ICONS
) && (State
->MaxVisible
> (State
->FinalRow0
+ 1)))
348 State
->MaxVisible
= State
->FinalRow0
+ 1;
349 } // static VOID IdentifyRows()
351 // Blank the screen, wait for a keypress, and restore banner/background.
352 // Screen may still require redrawing of text and icons on return.
353 // TODO: Support more sophisticated screen savers, such as power-saving
354 // mode and dynamic images.
355 static VOID
SaveScreen(VOID
) {
357 EG_PIXEL Black
= { 0x0, 0x0, 0x0, 0 };
359 egClearScreen(&Black
);
360 refit_call3_wrapper(BS
->WaitForEvent
, 1, &ST
->ConIn
->WaitForKey
, &index
);
361 if (AllowGraphicsMode
)
362 SwitchToGraphicsAndClear();
364 } // VOID SaveScreen()
367 // generic menu function
369 static UINTN
RunGenericMenu(IN REFIT_MENU_SCREEN
*Screen
, IN MENU_STYLE_FUNC StyleFunc
, IN OUT INTN
*DefaultEntryIndex
,
370 OUT REFIT_MENU_ENTRY
**ChosenEntry
)
377 BOOLEAN HaveTimeout
= FALSE
;
378 BOOLEAN WaitForRelease
= FALSE
;
379 UINTN TimeoutCountdown
= 0;
380 INTN PreviousTime
= -1, CurrentTime
, TimeSinceKeystroke
= 0;
381 CHAR16 TimeoutMessage
[256];
382 CHAR16 KeyAsString
[2];
385 if (Screen
->TimeoutSeconds
> 0) {
387 TimeoutCountdown
= Screen
->TimeoutSeconds
* 10;
391 StyleFunc(Screen
, &State
, MENU_FUNCTION_INIT
, NULL
);
392 IdentifyRows(&State
, Screen
);
393 // override the starting selection with the default index, if any
394 if (*DefaultEntryIndex
>= 0 && *DefaultEntryIndex
<= State
.MaxIndex
) {
395 State
.CurrentSelection
= *DefaultEntryIndex
;
396 if (GlobalConfig
.ScreensaverTime
!= -1)
397 UpdateScroll(&State
, SCROLL_NONE
);
400 if (Screen
->TimeoutSeconds
== -1) {
401 Status
= refit_call2_wrapper(ST
->ConIn
->ReadKeyStroke
, ST
->ConIn
, &key
);
402 if (Status
== EFI_NOT_READY
) {
403 MenuExit
= MENU_EXIT_TIMEOUT
;
405 KeyAsString
[0] = key
.UnicodeChar
;
407 ShortcutEntry
= FindMenuShortcutEntry(Screen
, KeyAsString
);
408 if (ShortcutEntry
>= 0) {
409 State
.CurrentSelection
= ShortcutEntry
;
410 MenuExit
= MENU_EXIT_ENTER
;
412 WaitForRelease
= TRUE
;
418 if (GlobalConfig
.ScreensaverTime
!= -1)
419 State
.PaintAll
= TRUE
;
423 if (State
.PaintAll
&& (GlobalConfig
.ScreensaverTime
!= -1)) {
424 StyleFunc(Screen
, &State
, MENU_FUNCTION_PAINT_ALL
, NULL
);
425 State
.PaintAll
= FALSE
;
426 } else if (State
.PaintSelection
) {
427 StyleFunc(Screen
, &State
, MENU_FUNCTION_PAINT_SELECTION
, NULL
);
428 State
.PaintSelection
= FALSE
;
431 if (WaitForRelease
) {
432 Status
= refit_call2_wrapper(ST
->ConIn
->ReadKeyStroke
, ST
->ConIn
, &key
);
433 if (Status
== EFI_SUCCESS
) {
434 // reset, because otherwise the buffer gets queued with keystrokes
435 refit_call2_wrapper(ST
->ConIn
->Reset
, ST
->ConIn
, FALSE
);
436 refit_call1_wrapper(BS
->Stall
, 100000);
438 WaitForRelease
= FALSE
;
439 refit_call2_wrapper(ST
->ConIn
->Reset
, ST
->ConIn
, TRUE
);
445 CurrentTime
= (TimeoutCountdown
+ 5) / 10;
446 if (CurrentTime
!= PreviousTime
) {
447 SPrint(TimeoutMessage
, 255, L
"%s in %d seconds", Screen
->TimeoutText
, CurrentTime
);
448 if (GlobalConfig
.ScreensaverTime
!= -1)
449 StyleFunc(Screen
, &State
, MENU_FUNCTION_PAINT_TIMEOUT
, TimeoutMessage
);
450 PreviousTime
= CurrentTime
;
454 // read key press (and wait for it if applicable)
455 Status
= refit_call2_wrapper(ST
->ConIn
->ReadKeyStroke
, ST
->ConIn
, &key
);
456 if (Status
!= EFI_SUCCESS
) {
457 if (HaveTimeout
&& TimeoutCountdown
== 0) {
459 MenuExit
= MENU_EXIT_TIMEOUT
;
461 } else if (HaveTimeout
|| GlobalConfig
.ScreensaverTime
> 0) {
462 EFI_EVENT TimerEvent
;
463 UINTN ElapsCount
= 1;
465 Status
= refit_call5_wrapper(BS
->CreateEvent
, EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
466 if (EFI_ERROR(Status
)) {
467 refit_call1_wrapper(BS
->Stall
, 100000); // Pause for 100 ms
469 EFI_EVENT WaitList
[2];
472 refit_call3_wrapper(BS
->SetTimer
, TimerEvent
, TimerRelative
, 10000000); // 1s Timeout
473 WaitList
[0] = ST
->ConIn
->WaitForKey
;
474 WaitList
[1] = TimerEvent
;
475 Status
= refit_call3_wrapper(BS
->WaitForEvent
, 2, WaitList
, &Index
);
476 refit_call1_wrapper(BS
->CloseEvent
, TimerEvent
);
477 if (EFI_ERROR(Status
))
478 refit_call1_wrapper(BS
->Stall
, 100000); // Pause for 100 ms
482 ElapsCount
= 10; // always counted as 1s to end of the timeout
484 TimeSinceKeystroke
+= ElapsCount
;
486 TimeoutCountdown
= TimeoutCountdown
<= ElapsCount
? 0 : TimeoutCountdown
- ElapsCount
;
487 } else if (GlobalConfig
.ScreensaverTime
> 0 &&
488 TimeSinceKeystroke
> (GlobalConfig
.ScreensaverTime
* 10))
491 State
.PaintAll
= TRUE
;
492 TimeSinceKeystroke
= 0;
495 refit_call3_wrapper(BS
->WaitForEvent
, 1, &ST
->ConIn
->WaitForKey
, &index
);
499 TimeSinceKeystroke
= 0;
500 } // if/else !read keystroke
503 // the user pressed a key, cancel the timeout
504 StyleFunc(Screen
, &State
, MENU_FUNCTION_PAINT_TIMEOUT
, L
"");
506 if (GlobalConfig
.ScreensaverTime
== -1) { // cancel start-with-blank-screen coding
507 GlobalConfig
.ScreensaverTime
= 0;
508 if (!GlobalConfig
.TextOnly
)
509 BltClearScreen(TRUE
);
513 // react to key press
514 switch (key
.ScanCode
) {
516 UpdateScroll(&State
, SCROLL_LINE_UP
);
519 UpdateScroll(&State
, SCROLL_LINE_LEFT
);
522 UpdateScroll(&State
, SCROLL_LINE_DOWN
);
525 UpdateScroll(&State
, SCROLL_LINE_RIGHT
);
528 UpdateScroll(&State
, SCROLL_FIRST
);
531 UpdateScroll(&State
, SCROLL_LAST
);
534 UpdateScroll(&State
, SCROLL_PAGE_UP
);
537 UpdateScroll(&State
, SCROLL_PAGE_DOWN
);
540 MenuExit
= MENU_EXIT_ESCAPE
;
544 MenuExit
= MENU_EXIT_DETAILS
;
551 MenuExit
= MENU_EXIT_ESCAPE
;
554 switch (key
.UnicodeChar
) {
556 case CHAR_CARRIAGE_RETURN
:
558 MenuExit
= MENU_EXIT_ENTER
;
561 MenuExit
= MENU_EXIT_DETAILS
;
564 KeyAsString
[0] = key
.UnicodeChar
;
566 ShortcutEntry
= FindMenuShortcutEntry(Screen
, KeyAsString
);
567 if (ShortcutEntry
>= 0) {
568 State
.CurrentSelection
= ShortcutEntry
;
569 MenuExit
= MENU_EXIT_ENTER
;
575 StyleFunc(Screen
, &State
, MENU_FUNCTION_CLEANUP
, NULL
);
578 *ChosenEntry
= Screen
->Entries
[State
.CurrentSelection
];
579 *DefaultEntryIndex
= State
.CurrentSelection
;
581 } /* static UINTN RunGenericMenu() */
584 // text-mode generic style
587 // Show information lines in text mode.
588 static VOID
ShowTextInfoLines(IN REFIT_MENU_SCREEN
*Screen
) {
591 BeginTextScreen(Screen
->Title
);
592 if (Screen
->InfoLineCount
> 0) {
593 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
594 for (i
= 0; i
< (INTN
)Screen
->InfoLineCount
; i
++) {
595 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 3, 4 + i
);
596 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, Screen
->InfoLines
[i
]);
599 } // VOID ShowTextInfoLines()
601 // Do most of the work for text-based menus....
602 static VOID
TextMenuStyle(IN REFIT_MENU_SCREEN
*Screen
, IN SCROLL_STATE
*State
, IN UINTN Function
, IN CHAR16
*ParamText
)
605 UINTN MenuWidth
, ItemWidth
, MenuHeight
;
606 static UINTN MenuPosY
;
607 static CHAR16
**DisplayStrings
;
608 CHAR16 TimeoutMessage
[256];
610 State
->ScrollMode
= SCROLL_MODE_TEXT
;
613 case MENU_FUNCTION_INIT
:
616 if (Screen
->InfoLineCount
> 0)
617 MenuPosY
+= Screen
->InfoLineCount
+ 1;
618 MenuHeight
= ConHeight
- MenuPosY
- 3;
619 if (Screen
->TimeoutSeconds
> 0)
621 InitScroll(State
, Screen
->EntryCount
, MenuHeight
);
623 // determine width of the menu
624 MenuWidth
= 20; // minimum
625 for (i
= 0; i
<= State
->MaxIndex
; i
++) {
626 ItemWidth
= StrLen(Screen
->Entries
[i
]->Title
);
627 if (MenuWidth
< ItemWidth
)
628 MenuWidth
= ItemWidth
;
631 if (MenuWidth
> ConWidth
- 3)
632 MenuWidth
= ConWidth
- 3;
634 // prepare strings for display
635 DisplayStrings
= AllocatePool(sizeof(CHAR16
*) * Screen
->EntryCount
);
636 for (i
= 0; i
<= State
->MaxIndex
; i
++) {
637 // Note: Theoretically, SPrint() is a cleaner way to do this; but the
638 // description of the StrSize parameter to SPrint implies it's measured
639 // in characters, but in practice both TianoCore and GNU-EFI seem to
640 // use bytes instead, resulting in truncated displays. I could just
641 // double the size of the StrSize parameter, but that seems unsafe in
642 // case a future library change starts treating this as characters, so
643 // I'm doing it the hard way in this instance.
644 // TODO: Review the above and possibly change other uses of SPrint()
645 DisplayStrings
[i
] = AllocateZeroPool(2 * sizeof(CHAR16
));
646 DisplayStrings
[i
][0] = L
' ';
647 MergeStrings(&DisplayStrings
[i
], Screen
->Entries
[i
]->Title
, 0);
648 if (StrLen(DisplayStrings
[i
]) > MenuWidth
)
649 DisplayStrings
[i
][MenuWidth
- 1] = 0;
650 // TODO: use more elaborate techniques for shortening too long strings (ellipses in the middle)
651 // TODO: account for double-width characters
656 case MENU_FUNCTION_CLEANUP
:
657 // release temporary memory
658 for (i
= 0; i
<= State
->MaxIndex
; i
++)
659 MyFreePool(DisplayStrings
[i
]);
660 MyFreePool(DisplayStrings
);
663 case MENU_FUNCTION_PAINT_ALL
:
664 // paint the whole screen (initially and after scrolling)
666 ShowTextInfoLines(Screen
);
667 for (i
= 0; i
<= State
->MaxIndex
; i
++) {
668 if (i
>= State
->FirstVisible
&& i
<= State
->LastVisible
) {
669 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 2, MenuPosY
+ (i
- State
->FirstVisible
));
670 if (i
== State
->CurrentSelection
)
671 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_CHOICE_CURRENT
);
673 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_CHOICE_BASIC
);
674 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, DisplayStrings
[i
]);
677 // scrolling indicators
678 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_SCROLLARROW
);
679 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 0, MenuPosY
);
680 if (State
->FirstVisible
> 0)
681 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, ArrowUp
);
683 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, L
" ");
684 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 0, MenuPosY
+ State
->MaxVisible
);
685 if (State
->LastVisible
< State
->MaxIndex
)
686 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, ArrowDown
);
688 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, L
" ");
689 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HINTS
)) {
690 if (Screen
->Hint1
!= NULL
) {
691 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 0, ConHeight
- 2);
692 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, Screen
->Hint1
);
694 if (Screen
->Hint2
!= NULL
) {
695 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 0, ConHeight
- 1);
696 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, Screen
->Hint2
);
701 case MENU_FUNCTION_PAINT_SELECTION
:
702 // redraw selection cursor
703 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 2,
704 MenuPosY
+ (State
->PreviousSelection
- State
->FirstVisible
));
705 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_CHOICE_BASIC
);
706 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, DisplayStrings
[State
->PreviousSelection
]);
707 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 2,
708 MenuPosY
+ (State
->CurrentSelection
- State
->FirstVisible
));
709 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_CHOICE_CURRENT
);
710 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, DisplayStrings
[State
->CurrentSelection
]);
713 case MENU_FUNCTION_PAINT_TIMEOUT
:
714 if (ParamText
[0] == 0) {
716 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
717 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 0, ConHeight
- 3);
718 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, BlankLine
+ 1);
720 // paint or update message
721 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
722 refit_call3_wrapper(ST
->ConOut
->SetCursorPosition
, ST
->ConOut
, 3, ConHeight
- 3);
723 SPrint(TimeoutMessage
, 255, L
"%s ", ParamText
);
724 refit_call2_wrapper(ST
->ConOut
->OutputString
, ST
->ConOut
, TimeoutMessage
);
732 // graphical generic style
735 inline static UINTN
TextLineHeight(VOID
) {
736 return egGetFontHeight() + TEXT_YMARGIN
* 2;
737 } // UINTN TextLineHeight()
743 // Display text with a solid background (MenuBackgroundPixel or SelectionBackgroundPixel).
744 // Indents text by one character and placed TEXT_YMARGIN pixels down from the
745 // specified XPos and YPos locations.
746 static VOID
DrawText(IN CHAR16
*Text
, IN BOOLEAN Selected
, IN UINTN FieldWidth
, IN UINTN XPos
, IN UINTN YPos
)
748 EG_IMAGE
*TextBuffer
;
751 TextBuffer
= egCreateImage(FieldWidth
, TextLineHeight(), FALSE
);
753 egFillImage(TextBuffer
, &MenuBackgroundPixel
);
754 Bg
= MenuBackgroundPixel
;
756 // draw selection bar background
757 egFillImageArea(TextBuffer
, 0, 0, FieldWidth
, TextBuffer
->Height
, &SelectionBackgroundPixel
);
758 Bg
= SelectionBackgroundPixel
;
762 egRenderText(Text
, TextBuffer
, egGetFontCellWidth(), TEXT_YMARGIN
, (Bg
.r
+ Bg
.g
+ Bg
.b
) / 3);
763 egDrawImageWithTransparency(TextBuffer
, NULL
, XPos
, YPos
, TextBuffer
->Width
, TextBuffer
->Height
);
764 // BltImage(TextBuffer, XPos, YPos);
767 // Finds the average brightness of the input Image.
768 // NOTE: Passing an Image that covers the whole screen can strain the
769 // capacity of a UINTN on a 32-bit system with a very large display.
770 // Using UINT64 instead is unworkable, since the code won't compile
771 // on a 32-bit system. As the intended use for this function is to handle
772 // a single text string's background, this shouldn't be a problem, but it
773 // may need addressing if it's applied more broadly....
774 static UINT8
AverageBrightness(EG_IMAGE
*Image
) {
778 if ((Image
!= NULL
) && ((Image
->Width
* Image
->Height
) != 0)) {
779 for (i
= 0; i
< (Image
->Width
* Image
->Height
); i
++) {
780 Sum
+= (Image
->PixelData
[i
].r
+ Image
->PixelData
[i
].g
+ Image
->PixelData
[i
].b
);
782 Sum
/= (Image
->Width
* Image
->Height
* 3);
785 } // UINT8 AverageBrightness()
787 // Display text against the screen's background image. Special case: If Text is NULL
788 // or 0-length, clear the line. Does NOT indent the text or reposition it relative
789 // to the specified XPos and YPos values.
790 static VOID
DrawTextWithTransparency(IN CHAR16
*Text
, IN UINTN XPos
, IN UINTN YPos
)
793 EG_IMAGE
*TextBuffer
= NULL
;
798 egMeasureText(Text
, &TextWidth
, NULL
);
799 if (TextWidth
== 0) {
800 TextWidth
= UGAWidth
;
804 TextBuffer
= egCropImage(GlobalConfig
.ScreenBackground
, XPos
, YPos
, TextWidth
, TextLineHeight());
805 if (TextBuffer
== NULL
)
809 egRenderText(Text
, TextBuffer
, 0, 0, AverageBrightness(TextBuffer
));
810 egDrawImageWithTransparency(TextBuffer
, NULL
, XPos
, YPos
, TextBuffer
->Width
, TextBuffer
->Height
);
811 egFreeImage(TextBuffer
);
814 // Compute the size & position of the window that will hold a subscreen's information.
815 static VOID
ComputeSubScreenWindowSize(REFIT_MENU_SCREEN
*Screen
, IN SCROLL_STATE
*State
, UINTN
*XPos
, UINTN
*YPos
,
816 UINTN
*Width
, UINTN
*Height
, UINTN
*LineWidth
) {
817 UINTN i
, ItemWidth
, HintTop
, BannerBottomEdge
, TitleWidth
;
818 UINTN FontCellWidth
= egGetFontCellWidth();
819 UINTN FontCellHeight
= egGetFontHeight();
823 TitleWidth
= egComputeTextWidth(Screen
->Title
);
825 for (i
= 0; i
< Screen
->InfoLineCount
; i
++) {
826 ItemWidth
= StrLen(Screen
->InfoLines
[i
]);
827 if (*Width
< ItemWidth
) {
832 for (i
= 0; i
<= State
->MaxIndex
; i
++) {
833 ItemWidth
= StrLen(Screen
->Entries
[i
]->Title
);
834 if (*Width
< ItemWidth
) {
839 *Width
= (*Width
+ 2) * FontCellWidth
;
841 if (Screen
->TitleImage
)
842 *Width
+= (Screen
->TitleImage
->Width
+ TITLEICON_SPACING
* 2 + FontCellWidth
);
844 *Width
+= FontCellWidth
;
846 if (*Width
< TitleWidth
)
847 *Width
= TitleWidth
+ 2 * FontCellWidth
;
849 // Keep it within the bounds of the screen, or 2/3 of the screen's width
850 // for screens over 800 pixels wide
851 if (*Width
> UGAWidth
)
854 *XPos
= (UGAWidth
- *Width
) / 2;
856 HintTop
= UGAHeight
- (FontCellHeight
* 3); // top of hint text
857 *Height
*= TextLineHeight();
858 if (Screen
->TitleImage
&& (*Height
< (Screen
->TitleImage
->Height
+ TextLineHeight() * 4)))
859 *Height
= Screen
->TitleImage
->Height
+ TextLineHeight() * 4;
861 if (GlobalConfig
.BannerBottomEdge
>= HintTop
) { // probably a full-screen image; treat it as an empty banner
862 BannerBottomEdge
= 0;
864 BannerBottomEdge
= GlobalConfig
.BannerBottomEdge
;
866 if (*Height
> (HintTop
- BannerBottomEdge
- FontCellHeight
* 2)) {
867 BannerBottomEdge
= 0;
869 if (*Height
> (HintTop
- BannerBottomEdge
- FontCellHeight
* 2)) {
870 // TODO: Implement scrolling in text screen.
871 *Height
= (HintTop
- BannerBottomEdge
- FontCellHeight
* 2);
874 *YPos
= ((UGAHeight
- *Height
) / 2);
875 if (*YPos
< BannerBottomEdge
)
876 *YPos
= BannerBottomEdge
+ FontCellHeight
+ (HintTop
- BannerBottomEdge
- *Height
) / 2;
877 } // VOID ComputeSubScreenWindowSize()
879 // Displays sub-menus
880 static VOID
GraphicsMenuStyle(IN REFIT_MENU_SCREEN
*Screen
, IN SCROLL_STATE
*State
, IN UINTN Function
, IN CHAR16
*ParamText
)
884 static UINTN LineWidth
, MenuWidth
, MenuHeight
, EntriesPosX
, TitlePosX
, EntriesPosY
, TimeoutPosY
, CharWidth
;
886 EG_PIXEL
*BackgroundPixel
= &(GlobalConfig
.ScreenBackground
->PixelData
[0]);
888 CharWidth
= egGetFontCellWidth();
889 State
->ScrollMode
= SCROLL_MODE_TEXT
;
892 case MENU_FUNCTION_INIT
:
893 InitScroll(State
, Screen
->EntryCount
, 0);
894 ComputeSubScreenWindowSize(Screen
, State
, &EntriesPosX
, &EntriesPosY
, &MenuWidth
, &MenuHeight
, &LineWidth
);
895 TimeoutPosY
= EntriesPosY
+ (Screen
->EntryCount
+ 1) * TextLineHeight();
898 SwitchToGraphicsAndClear();
899 Window
= egCreateFilledImage(MenuWidth
, MenuHeight
, FALSE
, BackgroundPixel
);
900 egDrawImage(Window
, EntriesPosX
, EntriesPosY
);
901 ItemWidth
= egComputeTextWidth(Screen
->Title
);
902 if (MenuWidth
> ItemWidth
) {
903 TitlePosX
= EntriesPosX
+ (MenuWidth
- ItemWidth
) / 2 - CharWidth
;
905 TitlePosX
= EntriesPosX
;
907 i
= MenuWidth
/ CharWidth
- 2;
909 Screen
->Title
[i
] = 0;
914 case MENU_FUNCTION_CLEANUP
:
918 case MENU_FUNCTION_PAINT_ALL
:
919 ComputeSubScreenWindowSize(Screen
, State
, &EntriesPosX
, &EntriesPosY
, &MenuWidth
, &MenuHeight
, &LineWidth
);
920 DrawText(Screen
->Title
, FALSE
, (StrLen(Screen
->Title
) + 2) * CharWidth
, TitlePosX
, EntriesPosY
+= TextLineHeight());
921 if (Screen
->TitleImage
) {
922 BltImageAlpha(Screen
->TitleImage
, EntriesPosX
+ TITLEICON_SPACING
, EntriesPosY
+ TextLineHeight() * 2,
924 EntriesPosX
+= (Screen
->TitleImage
->Width
+ TITLEICON_SPACING
* 2);
926 EntriesPosY
+= (TextLineHeight() * 2);
927 if (Screen
->InfoLineCount
> 0) {
928 for (i
= 0; i
< (INTN
)Screen
->InfoLineCount
; i
++) {
929 DrawText(Screen
->InfoLines
[i
], FALSE
, LineWidth
, EntriesPosX
, EntriesPosY
);
930 EntriesPosY
+= TextLineHeight();
932 EntriesPosY
+= TextLineHeight(); // also add a blank line
935 for (i
= 0; i
<= State
->MaxIndex
; i
++) {
936 DrawText(Screen
->Entries
[i
]->Title
, (i
== State
->CurrentSelection
), LineWidth
, EntriesPosX
,
937 EntriesPosY
+ i
* TextLineHeight());
939 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HINTS
)) {
940 if ((Screen
->Hint1
!= NULL
) && (StrLen(Screen
->Hint1
) > 0))
941 DrawTextWithTransparency(Screen
->Hint1
, (UGAWidth
- egComputeTextWidth(Screen
->Hint1
)) / 2,
942 UGAHeight
- (egGetFontHeight() * 3));
943 if ((Screen
->Hint2
!= NULL
) && (StrLen(Screen
->Hint2
) > 0))
944 DrawTextWithTransparency(Screen
->Hint2
, (UGAWidth
- egComputeTextWidth(Screen
->Hint2
)) / 2,
945 UGAHeight
- (egGetFontHeight() * 2));
949 case MENU_FUNCTION_PAINT_SELECTION
:
950 // redraw selection cursor
951 DrawText(Screen
->Entries
[State
->PreviousSelection
]->Title
, FALSE
, LineWidth
,
952 EntriesPosX
, EntriesPosY
+ State
->PreviousSelection
* TextLineHeight());
953 DrawText(Screen
->Entries
[State
->CurrentSelection
]->Title
, TRUE
, LineWidth
,
954 EntriesPosX
, EntriesPosY
+ State
->CurrentSelection
* TextLineHeight());
957 case MENU_FUNCTION_PAINT_TIMEOUT
:
958 DrawText(ParamText
, FALSE
, LineWidth
, EntriesPosX
, TimeoutPosY
);
962 } // static VOID GraphicsMenuStyle()
965 // graphical main menu style
968 static VOID
DrawMainMenuEntry(REFIT_MENU_ENTRY
*Entry
, BOOLEAN selected
, UINTN XPos
, UINTN YPos
)
970 EG_IMAGE
*Background
;
972 if (SelectionImages
!= NULL
) {
974 Background
= egCropImage(GlobalConfig
.ScreenBackground
, XPos
, YPos
,
975 SelectionImages
[Entry
->Row
]->Width
, SelectionImages
[Entry
->Row
]->Height
);
976 egComposeImage(Background
, SelectionImages
[Entry
->Row
], 0, 0);
977 BltImageCompositeBadge(Background
, Entry
->Image
, Entry
->BadgeImage
, XPos
, YPos
);
978 } else { // Image not selected; copy background
979 egDrawImageWithTransparency(Entry
->Image
, Entry
->BadgeImage
, XPos
, YPos
,
980 SelectionImages
[Entry
->Row
]->Width
, SelectionImages
[Entry
->Row
]->Height
);
983 } // VOID DrawMainMenuEntry()
985 static VOID
PaintAll(IN REFIT_MENU_SCREEN
*Screen
, IN SCROLL_STATE
*State
, UINTN
*itemPosX
,
986 UINTN row0PosY
, UINTN row1PosY
, UINTN textPosY
) {
989 if (Screen
->Entries
[State
->CurrentSelection
]->Row
== 0)
990 AdjustScrollState(State
);
991 for (i
= State
->FirstVisible
; i
<= State
->MaxIndex
; i
++) {
992 if (Screen
->Entries
[i
]->Row
== 0) {
993 if (i
<= State
->LastVisible
) {
994 DrawMainMenuEntry(Screen
->Entries
[i
], (i
== State
->CurrentSelection
) ? TRUE
: FALSE
,
995 itemPosX
[i
- State
->FirstVisible
], row0PosY
);
998 DrawMainMenuEntry(Screen
->Entries
[i
], (i
== State
->CurrentSelection
) ? TRUE
: FALSE
, itemPosX
[i
], row1PosY
);
1001 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_LABEL
)) {
1002 DrawTextWithTransparency(L
"", 0, textPosY
);
1003 DrawTextWithTransparency(Screen
->Entries
[State
->CurrentSelection
]->Title
,
1004 (UGAWidth
- egComputeTextWidth(Screen
->Entries
[State
->CurrentSelection
]->Title
)) >> 1,
1008 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HINTS
)) {
1009 DrawTextWithTransparency(Screen
->Hint1
, (UGAWidth
- egComputeTextWidth(Screen
->Hint1
)) / 2,
1010 UGAHeight
- (egGetFontHeight() * 3));
1011 DrawTextWithTransparency(Screen
->Hint2
, (UGAWidth
- egComputeTextWidth(Screen
->Hint2
)) / 2,
1012 UGAHeight
- (egGetFontHeight() * 2));
1014 } // static VOID PaintAll()
1016 // Move the selection to State->CurrentSelection, adjusting icon row if necessary...
1017 static VOID
PaintSelection(IN REFIT_MENU_SCREEN
*Screen
, IN SCROLL_STATE
*State
, UINTN
*itemPosX
,
1018 UINTN row0PosY
, UINTN row1PosY
, UINTN textPosY
) {
1019 UINTN XSelectPrev
, XSelectCur
, YPosPrev
, YPosCur
;
1021 if (((State
->CurrentSelection
<= State
->LastVisible
) && (State
->CurrentSelection
>= State
->FirstVisible
)) ||
1022 (State
->CurrentSelection
>= State
->InitialRow1
) ) {
1023 if (Screen
->Entries
[State
->PreviousSelection
]->Row
== 0) {
1024 XSelectPrev
= State
->PreviousSelection
- State
->FirstVisible
;
1025 YPosPrev
= row0PosY
;
1027 XSelectPrev
= State
->PreviousSelection
;
1028 YPosPrev
= row1PosY
;
1030 if (Screen
->Entries
[State
->CurrentSelection
]->Row
== 0) {
1031 XSelectCur
= State
->CurrentSelection
- State
->FirstVisible
;
1034 XSelectCur
= State
->CurrentSelection
;
1037 DrawMainMenuEntry(Screen
->Entries
[State
->PreviousSelection
], FALSE
, itemPosX
[XSelectPrev
], YPosPrev
);
1038 DrawMainMenuEntry(Screen
->Entries
[State
->CurrentSelection
], TRUE
, itemPosX
[XSelectCur
], YPosCur
);
1039 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_LABEL
)) {
1040 DrawTextWithTransparency(L
"", 0, textPosY
);
1041 DrawTextWithTransparency(Screen
->Entries
[State
->CurrentSelection
]->Title
,
1042 (UGAWidth
- egComputeTextWidth(Screen
->Entries
[State
->CurrentSelection
]->Title
)) >> 1,
1045 } else { // Current selection not visible; must redraw the menu....
1046 MainMenuStyle(Screen
, State
, MENU_FUNCTION_PAINT_ALL
, NULL
);
1048 } // static VOID MoveSelection(VOID)
1050 // Display a 48x48 icon at the specified location. Uses the image specified by
1051 // ExternalFilename if it's available, or BuiltInImage if it's not. The
1052 // Y position is specified as the center value, and so is adjusted by half
1053 // the icon's height. The X position is set along the icon's left
1054 // edge if Alignment == ALIGN_LEFT, and along the right edge if
1055 // Alignment == ALIGN_RIGHT
1056 static VOID
PaintIcon(IN EG_EMBEDDED_IMAGE
*BuiltInIcon
, IN CHAR16
*ExternalFilename
, UINTN PosX
, UINTN PosY
, UINTN Alignment
) {
1057 EG_IMAGE
*Icon
= NULL
;
1059 Icon
= egFindIcon(ExternalFilename
, GlobalConfig
.IconSizes
[ICON_SIZE_SMALL
]);
1061 Icon
= egPrepareEmbeddedImage(BuiltInIcon
, TRUE
);
1063 if (Alignment
== ALIGN_RIGHT
)
1064 PosX
-= Icon
->Width
;
1065 egDrawImageWithTransparency(Icon
, NULL
, PosX
, PosY
- (Icon
->Height
/ 2), Icon
->Width
, Icon
->Height
);
1069 UINTN
ComputeRow0PosY(VOID
) {
1070 return ((UGAHeight
/ 2) - TileSizes
[0] / 2);
1071 } // UINTN ComputeRow0PosY()
1073 // Display (or erase) the arrow icons to the left and right of an icon's row,
1075 static VOID
PaintArrows(SCROLL_STATE
*State
, UINTN PosX
, UINTN PosY
, UINTN row0Loaders
) {
1076 EG_IMAGE
*TempImage
;
1077 UINTN Width
, Height
, RightX
, AdjPosY
;
1079 // NOTE: Assume that left and right arrows are of the same size....
1080 Width
= egemb_arrow_left
.Width
;
1081 Height
= egemb_arrow_left
.Height
;
1082 RightX
= (UGAWidth
+ (TileSizes
[0] + TILE_XSPACING
) * State
->MaxVisible
) / 2 + TILE_XSPACING
;
1083 AdjPosY
= PosY
- (Height
/ 2);
1085 // For PaintIcon() calls, the starting Y position is moved to the midpoint
1086 // of the surrounding row; PaintIcon() adjusts this back up by half the
1087 // icon's height to properly center it.
1088 if ((State
->FirstVisible
> 0) && (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_ARROWS
))) {
1089 PaintIcon(&egemb_arrow_left
, L
"arrow_left", PosX
, PosY
, ALIGN_RIGHT
);
1091 TempImage
= egCropImage(GlobalConfig
.ScreenBackground
, PosX
- Width
, AdjPosY
, Width
, Height
);
1092 BltImage(TempImage
, PosX
- Width
, AdjPosY
);
1093 egFreeImage(TempImage
);
1096 if ((State
->LastVisible
< (row0Loaders
- 1)) && (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_ARROWS
))) {
1097 PaintIcon(&egemb_arrow_right
, L
"arrow_right", RightX
, PosY
, ALIGN_LEFT
);
1099 TempImage
= egCropImage(GlobalConfig
.ScreenBackground
, RightX
, AdjPosY
, Width
, Height
);
1100 BltImage(TempImage
, RightX
, AdjPosY
);
1101 egFreeImage(TempImage
);
1103 } // VOID PaintArrows()
1105 // Display main menu in graphics mode
1106 VOID
MainMenuStyle(IN REFIT_MENU_SCREEN
*Screen
, IN SCROLL_STATE
*State
, IN UINTN Function
, IN CHAR16
*ParamText
)
1109 static UINTN row0PosX
, row0PosXRunning
, row1PosY
, row0Loaders
;
1110 UINTN row0Count
, row1Count
, row1PosX
, row1PosXRunning
;
1111 static UINTN
*itemPosX
;
1112 static UINTN row0PosY
, textPosY
;
1114 State
->ScrollMode
= SCROLL_MODE_ICONS
;
1117 case MENU_FUNCTION_INIT
:
1118 InitScroll(State
, Screen
->EntryCount
, GlobalConfig
.MaxTags
);
1124 for (i
= 0; i
<= State
->MaxIndex
; i
++) {
1125 if (Screen
->Entries
[i
]->Row
== 1) {
1129 if (row0Count
< State
->MaxVisible
)
1133 row0PosX
= (UGAWidth
+ TILE_XSPACING
- (TileSizes
[0] + TILE_XSPACING
) * row0Count
) >> 1;
1134 row0PosY
= ComputeRow0PosY();
1135 row1PosX
= (UGAWidth
+ TILE_XSPACING
- (TileSizes
[1] + TILE_XSPACING
) * row1Count
) >> 1;
1136 row1PosY
= row0PosY
+ TileSizes
[0] + TILE_YSPACING
;
1138 textPosY
= row1PosY
+ TileSizes
[1] + TILE_YSPACING
;
1140 textPosY
= row1PosY
;
1142 itemPosX
= AllocatePool(sizeof(UINTN
) * Screen
->EntryCount
);
1143 row0PosXRunning
= row0PosX
;
1144 row1PosXRunning
= row1PosX
;
1145 for (i
= 0; i
<= State
->MaxIndex
; i
++) {
1146 if (Screen
->Entries
[i
]->Row
== 0) {
1147 itemPosX
[i
] = row0PosXRunning
;
1148 row0PosXRunning
+= TileSizes
[0] + TILE_XSPACING
;
1150 itemPosX
[i
] = row1PosXRunning
;
1151 row1PosXRunning
+= TileSizes
[1] + TILE_XSPACING
;
1156 SwitchToGraphicsAndClear();
1159 case MENU_FUNCTION_CLEANUP
:
1160 MyFreePool(itemPosX
);
1163 case MENU_FUNCTION_PAINT_ALL
:
1164 PaintAll(Screen
, State
, itemPosX
, row0PosY
, row1PosY
, textPosY
);
1165 // For PaintArrows(), the starting Y position is moved to the midpoint
1166 // of the surrounding row; PaintIcon() adjusts this back up by half the
1167 // icon's height to properly center it.
1168 PaintArrows(State
, row0PosX
- TILE_XSPACING
, row0PosY
+ (TileSizes
[0] / 2), row0Loaders
);
1171 case MENU_FUNCTION_PAINT_SELECTION
:
1172 PaintSelection(Screen
, State
, itemPosX
, row0PosY
, row1PosY
, textPosY
);
1175 case MENU_FUNCTION_PAINT_TIMEOUT
:
1176 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_LABEL
)) {
1177 DrawTextWithTransparency(L
"", 0, textPosY
+ TextLineHeight());
1178 DrawTextWithTransparency(ParamText
, (UGAWidth
- egComputeTextWidth(ParamText
)) >> 1, textPosY
+ TextLineHeight());
1183 } // VOID MainMenuStyle()
1185 // Enable the user to edit boot loader options.
1186 // Returns TRUE if the user exited with edited options; FALSE if the user
1187 // pressed Esc to terminate the edit.
1188 static BOOLEAN
EditOptions(LOADER_ENTRY
*MenuEntry
) {
1190 CHAR16
*EditedOptions
;
1191 BOOLEAN retval
= FALSE
;
1193 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1197 refit_call4_wrapper(ST
->ConOut
->QueryMode
, ST
->ConOut
, ST
->ConOut
->Mode
->Mode
, &x_max
, &y_max
);
1199 if (!GlobalConfig
.TextOnly
)
1202 if (line_edit(MenuEntry
->LoadOptions
, &EditedOptions
, x_max
)) {
1203 MyFreePool(MenuEntry
->LoadOptions
);
1204 MenuEntry
->LoadOptions
= EditedOptions
;
1207 if (!GlobalConfig
.TextOnly
)
1210 } // VOID EditOptions()
1213 // user-callable dispatcher functions
1216 UINTN
RunMenu(IN REFIT_MENU_SCREEN
*Screen
, OUT REFIT_MENU_ENTRY
**ChosenEntry
)
1218 INTN DefaultEntry
= -1;
1219 MENU_STYLE_FUNC Style
= TextMenuStyle
;
1221 if (AllowGraphicsMode
)
1222 Style
= GraphicsMenuStyle
;
1224 return RunGenericMenu(Screen
, Style
, &DefaultEntry
, ChosenEntry
);
1227 UINTN
RunMainMenu(REFIT_MENU_SCREEN
*Screen
, CHAR16
** DefaultSelection
, REFIT_MENU_ENTRY
**ChosenEntry
)
1229 MENU_STYLE_FUNC Style
= TextMenuStyle
;
1230 MENU_STYLE_FUNC MainStyle
= TextMenuStyle
;
1231 REFIT_MENU_ENTRY
*TempChosenEntry
;
1234 INTN DefaultEntryIndex
= -1;
1235 INTN DefaultSubmenuIndex
= -1;
1237 TileSizes
[0] = (GlobalConfig
.IconSizes
[ICON_SIZE_BIG
] * 9) / 8;
1238 TileSizes
[1] = (GlobalConfig
.IconSizes
[ICON_SIZE_SMALL
] * 4) / 3;
1240 if ((DefaultSelection
!= NULL
) && (*DefaultSelection
!= NULL
)) {
1241 // Find a menu entry that includes *DefaultSelection as a substring
1242 DefaultEntryIndex
= FindMenuShortcutEntry(Screen
, *DefaultSelection
);
1245 if (AllowGraphicsMode
) {
1246 Style
= GraphicsMenuStyle
;
1247 MainStyle
= MainMenuStyle
;
1251 MenuExit
= RunGenericMenu(Screen
, MainStyle
, &DefaultEntryIndex
, &TempChosenEntry
);
1252 Screen
->TimeoutSeconds
= 0;
1254 MenuTitle
= StrDuplicate(TempChosenEntry
->Title
);
1255 if (MenuExit
== MENU_EXIT_DETAILS
) {
1256 if (TempChosenEntry
->SubScreen
!= NULL
) {
1257 MenuExit
= RunGenericMenu(TempChosenEntry
->SubScreen
, Style
, &DefaultSubmenuIndex
, &TempChosenEntry
);
1258 if (MenuExit
== MENU_EXIT_ESCAPE
|| TempChosenEntry
->Tag
== TAG_RETURN
)
1260 if (MenuExit
== MENU_EXIT_DETAILS
) {
1261 if (!EditOptions((LOADER_ENTRY
*) TempChosenEntry
))
1264 } else { // no sub-screen; ignore keypress
1267 } // Enter sub-screen
1271 *ChosenEntry
= TempChosenEntry
;
1272 if (DefaultSelection
) {
1273 MyFreePool(*DefaultSelection
);
1274 *DefaultSelection
= MenuTitle
;
1277 } /* UINTN RunMainMenu() */