]> code.delx.au - refind/blob - refind/menu.c
Added transparency support for built-in selection icon.
[refind] / refind / menu.c
1 /*
2 * refit/menu.c
3 * Menu functions
4 *
5 * Copyright (c) 2006 Christoph Pfisterer
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
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
18 * distribution.
19 *
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.
23 *
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.
35 */
36 /*
37 * Modifications copyright (c) 2012 Roderick W. Smith
38 *
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), a copy of which must be distributed
41 * with this source code or binaries made from it.
42 *
43 */
44
45 #include "global.h"
46 #include "screen.h"
47 #include "lib.h"
48 #include "menu.h"
49 #include "config.h"
50 #include "libeg.h"
51 #include "libegint.h"
52 #include "../include/refit_call_wrapper.h"
53
54 #include "../include/egemb_back_selected_small.h"
55 #include "../include/egemb_arrow_left.h"
56 #include "../include/egemb_arrow_right.h"
57
58 // other menu definitions
59
60 #define MENU_FUNCTION_INIT (0)
61 #define MENU_FUNCTION_CLEANUP (1)
62 #define MENU_FUNCTION_PAINT_ALL (2)
63 #define MENU_FUNCTION_PAINT_SELECTION (3)
64 #define MENU_FUNCTION_PAINT_TIMEOUT (4)
65 #define MENU_FUNCTION_PAINT_HINTS (5)
66
67 typedef VOID (*MENU_STYLE_FUNC)(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, IN UINTN Function, IN CHAR16 *ParamText);
68
69 static CHAR16 ArrowUp[2] = { ARROW_UP, 0 };
70 static CHAR16 ArrowDown[2] = { ARROW_DOWN, 0 };
71
72 // Text and icon spacing constants....
73 #define TEXT_YMARGIN (2)
74 #define TEXT_XMARGIN (8)
75 #define TEXT_LINE_HEIGHT (FONT_CELL_HEIGHT + TEXT_YMARGIN * 2)
76 #define TITLEICON_SPACING (16)
77
78 #define ROW0_TILESIZE (144)
79 #define ROW1_TILESIZE (64)
80 #define TILE_XSPACING (8)
81 #define TILE_YSPACING (16)
82
83 // Alignment values for PaintIcon()
84 #define ALIGN_RIGHT 1
85 #define ALIGN_LEFT 0
86
87 static EG_IMAGE *SelectionImages[2] = { NULL, NULL };
88 static EG_PIXEL SelectionBackgroundPixel = { 0xff, 0xff, 0xff, 0 };
89 static EG_IMAGE *TextBuffer = NULL;
90
91 //
92 // Graphics helper functions
93 //
94
95 static VOID InitSelection(VOID)
96 {
97 UINTN x, y, src_x, src_y;
98 EG_PIXEL *DestPtr, *SrcPtr;
99
100 if (!AllowGraphicsMode)
101 return;
102 if (SelectionImages[0] != NULL)
103 return;
104
105 // load small selection image
106 if (GlobalConfig.SelectionSmallFileName != NULL) {
107 SelectionImages[1] = egLoadImage(SelfDir, GlobalConfig.SelectionSmallFileName, TRUE);
108 }
109 if (SelectionImages[1] == NULL)
110 SelectionImages[1] = egPrepareEmbeddedImage(&egemb_back_selected_small, TRUE);
111 SelectionImages[1] = egEnsureImageSize(SelectionImages[1],
112 ROW1_TILESIZE, ROW1_TILESIZE, &MenuBackgroundPixel);
113 if (SelectionImages[1] == NULL)
114 return;
115
116 // load big selection image
117 if (GlobalConfig.SelectionBigFileName != NULL) {
118 SelectionImages[0] = egLoadImage(SelfDir, GlobalConfig.SelectionBigFileName, TRUE);
119 SelectionImages[0] = egEnsureImageSize(SelectionImages[0],
120 ROW0_TILESIZE, ROW0_TILESIZE, &MenuBackgroundPixel);
121 }
122 if (SelectionImages[0] == NULL) {
123 // calculate big selection image from small one
124
125 SelectionImages[0] = egCreateImage(ROW0_TILESIZE, ROW0_TILESIZE, TRUE);
126 if (SelectionImages[0] == NULL) {
127 egFreeImage(SelectionImages[1]);
128 SelectionImages[1] = NULL;
129 return;
130 }
131
132 DestPtr = SelectionImages[0]->PixelData;
133 SrcPtr = SelectionImages[1]->PixelData;
134 for (y = 0; y < ROW0_TILESIZE; y++) {
135 if (y < (ROW1_TILESIZE >> 1))
136 src_y = y;
137 else if (y < (ROW0_TILESIZE - (ROW1_TILESIZE >> 1)))
138 src_y = (ROW1_TILESIZE >> 1);
139 else
140 src_y = y - (ROW0_TILESIZE - ROW1_TILESIZE);
141
142 for (x = 0; x < ROW0_TILESIZE; x++) {
143 if (x < (ROW1_TILESIZE >> 1))
144 src_x = x;
145 else if (x < (ROW0_TILESIZE - (ROW1_TILESIZE >> 1)))
146 src_x = (ROW1_TILESIZE >> 1);
147 else
148 src_x = x - (ROW0_TILESIZE - ROW1_TILESIZE);
149
150 *DestPtr++ = SrcPtr[src_y * ROW1_TILESIZE + src_x];
151 }
152 }
153 }
154 } // VOID InitSelection()
155
156 //
157 // Scrolling functions
158 //
159
160 static VOID InitScroll(OUT SCROLL_STATE *State, IN UINTN ItemCount, IN UINTN VisibleSpace)
161 {
162 State->PreviousSelection = State->CurrentSelection = 0;
163 State->MaxIndex = (INTN)ItemCount - 1;
164 State->FirstVisible = 0;
165 if (AllowGraphicsMode) {
166 State->MaxVisible = UGAWidth / (ROW0_TILESIZE + TILE_XSPACING) - 1;
167 } else
168 State->MaxVisible = ConHeight - 4;
169 if ((VisibleSpace > 0) && (VisibleSpace < State->MaxVisible))
170 State->MaxVisible = (INTN)VisibleSpace;
171 State->PaintAll = TRUE;
172 State->PaintSelection = FALSE;
173
174 State->LastVisible = State->FirstVisible + State->MaxVisible - 1;
175 }
176
177 // Adjust variables relating to the scrolling of tags, for when a selected icon isn't
178 // visible given the current scrolling condition....
179 static VOID AdjustScrollState(IN SCROLL_STATE *State) {
180 if (State->CurrentSelection > State->LastVisible) {
181 State->LastVisible = State->CurrentSelection;
182 State->FirstVisible = 1 + State->CurrentSelection - State->MaxVisible;
183 if (State->FirstVisible < 0) // shouldn't happen, but just in case....
184 State->FirstVisible = 0;
185 State->PaintAll = TRUE;
186 } // Scroll forward
187 if (State->CurrentSelection < State->FirstVisible) {
188 State->FirstVisible = State->CurrentSelection;
189 State->LastVisible = State->CurrentSelection + State->MaxVisible - 1;
190 State->PaintAll = TRUE;
191 } // Scroll backward
192 } // static VOID AdjustScrollState
193
194 static VOID UpdateScroll(IN OUT SCROLL_STATE *State, IN UINTN Movement)
195 {
196 State->PreviousSelection = State->CurrentSelection;
197
198 switch (Movement) {
199 case SCROLL_LINE_LEFT:
200 if (State->CurrentSelection > 0) {
201 State->CurrentSelection --;
202 }
203 break;
204
205 case SCROLL_LINE_RIGHT:
206 if (State->CurrentSelection < State->MaxIndex) {
207 State->CurrentSelection ++;
208 }
209 break;
210
211 case SCROLL_LINE_UP:
212 if (State->ScrollMode == SCROLL_MODE_ICONS) {
213 if (State->CurrentSelection >= State->InitialRow1) {
214 if (State->MaxIndex > State->InitialRow1) { // avoid division by 0!
215 State->CurrentSelection = State->FirstVisible + (State->LastVisible - State->FirstVisible) *
216 (State->CurrentSelection - State->InitialRow1) /
217 (State->MaxIndex - State->InitialRow1);
218 } else {
219 State->CurrentSelection = State->FirstVisible;
220 } // if/else
221 } // if in second row
222 } else {
223 if (State->CurrentSelection > 0)
224 State->CurrentSelection--;
225 } // if/else
226 break;
227
228 case SCROLL_LINE_DOWN:
229 if (State->ScrollMode == SCROLL_MODE_ICONS) {
230 if (State->CurrentSelection <= State->FinalRow0) {
231 if (State->LastVisible > State->FirstVisible) { // avoid division by 0!
232 State->CurrentSelection = State->InitialRow1 + (State->MaxIndex - State->InitialRow1) *
233 (State->CurrentSelection - State->FirstVisible) /
234 (State->LastVisible - State->FirstVisible);
235 } else {
236 State->CurrentSelection = State->InitialRow1;
237 } // if/else
238 } // if in first row
239 } else {
240 if (State->CurrentSelection < State->MaxIndex)
241 State->CurrentSelection++;
242 } // if/else
243 break;
244
245 case SCROLL_PAGE_UP:
246 if (State->CurrentSelection <= State->FinalRow0)
247 State->CurrentSelection -= State->MaxVisible;
248 else if (State->CurrentSelection == State->InitialRow1)
249 State->CurrentSelection = State->FinalRow0;
250 else
251 State->CurrentSelection = State->InitialRow1;
252 if (State->CurrentSelection < 0)
253 State->CurrentSelection = 0;
254 break;
255
256 case SCROLL_FIRST:
257 if (State->CurrentSelection > 0) {
258 State->PaintAll = TRUE;
259 State->CurrentSelection = 0;
260 }
261 break;
262
263 case SCROLL_PAGE_DOWN:
264 if (State->CurrentSelection < State->FinalRow0) {
265 State->CurrentSelection += State->MaxVisible;
266 if (State->CurrentSelection > State->FinalRow0)
267 State->CurrentSelection = State->FinalRow0;
268 } else if (State->CurrentSelection == State->FinalRow0) {
269 State->CurrentSelection++;
270 } else {
271 State->CurrentSelection = State->MaxIndex;
272 }
273 if (State->CurrentSelection > State->MaxIndex)
274 State->CurrentSelection = State->MaxIndex;
275 break;
276
277 case SCROLL_LAST:
278 if (State->CurrentSelection < State->MaxIndex) {
279 State->PaintAll = TRUE;
280 State->CurrentSelection = State->MaxIndex;
281 }
282 break;
283
284 case SCROLL_NONE:
285 break;
286
287 }
288 if (State->ScrollMode == SCROLL_MODE_TEXT)
289 AdjustScrollState(State);
290
291 if (!State->PaintAll && State->CurrentSelection != State->PreviousSelection)
292 State->PaintSelection = TRUE;
293 State->LastVisible = State->FirstVisible + State->MaxVisible - 1;
294 } // static VOID UpdateScroll()
295
296 //
297 // menu helper functions
298 //
299
300 VOID AddMenuInfoLine(IN REFIT_MENU_SCREEN *Screen, IN CHAR16 *InfoLine)
301 {
302 AddListElement((VOID ***) &(Screen->InfoLines), &(Screen->InfoLineCount), InfoLine);
303 }
304
305 VOID AddMenuEntry(IN REFIT_MENU_SCREEN *Screen, IN REFIT_MENU_ENTRY *Entry)
306 {
307 AddListElement((VOID ***) &(Screen->Entries), &(Screen->EntryCount), Entry);
308 }
309
310
311 static INTN FindMenuShortcutEntry(IN REFIT_MENU_SCREEN *Screen, IN CHAR16 *Shortcut)
312 {
313 UINTN i;
314
315 if (Shortcut == NULL)
316 return (-1);
317
318 if (StrLen(Shortcut) == 1) {
319 if (Shortcut[0] >= 'a' && Shortcut[0] <= 'z')
320 Shortcut[0] -= ('a' - 'A');
321 if (Shortcut[0]) {
322 for (i = 0; i < Screen->EntryCount; i++) {
323 if (Screen->Entries[i]->ShortcutDigit == Shortcut[0] || Screen->Entries[i]->ShortcutLetter == Shortcut[0]) {
324 return i;
325 } // if
326 } // for
327 } // if
328 } else if (StrLen(Shortcut) > 1) {
329 for (i = 0; i < Screen->EntryCount; i++) {
330 if (StriSubCmp(Shortcut, Screen->Entries[i]->Title))
331 return i;
332 } // for
333 }
334 return -1;
335 }
336
337 // Identify the end of row 0 and the beginning of row 1; store the results in the
338 // appropriate fields in State. Also reduce MaxVisible if that value is greater
339 // than the total number of row-0 tags and if we're in an icon-based screen
340 static VOID IdentifyRows(IN SCROLL_STATE *State, IN REFIT_MENU_SCREEN *Screen) {
341 UINTN i;
342
343 State->FinalRow0 = 0;
344 State->InitialRow1 = State->MaxIndex;
345 for (i = 0; i < State->MaxIndex; i++) {
346 if (Screen->Entries[i]->Row == 0) {
347 State->FinalRow0 = i;
348 } else if ((Screen->Entries[i]->Row == 1) && (State->InitialRow1 > i)) {
349 State->InitialRow1 = i;
350 } // if/else
351 } // for
352 if ((State->ScrollMode == SCROLL_MODE_ICONS) && (State->MaxVisible > (State->FinalRow0 + 1)))
353 State->MaxVisible = State->FinalRow0 + 1;
354 } // static VOID IdentifyRows()
355
356 //
357 // generic menu function
358 //
359 static UINTN RunGenericMenu(IN REFIT_MENU_SCREEN *Screen, IN MENU_STYLE_FUNC StyleFunc, IN OUT INTN *DefaultEntryIndex,
360 OUT REFIT_MENU_ENTRY **ChosenEntry)
361 {
362 SCROLL_STATE State;
363 EFI_STATUS Status;
364 EFI_INPUT_KEY key;
365 UINTN index;
366 INTN ShortcutEntry;
367 BOOLEAN HaveTimeout = FALSE;
368 UINTN TimeoutCountdown = 0;
369 CHAR16 TimeoutMessage[256];
370 CHAR16 KeyAsString[2];
371 UINTN MenuExit;
372
373 if (Screen->TimeoutSeconds > 0) {
374 HaveTimeout = TRUE;
375 TimeoutCountdown = Screen->TimeoutSeconds * 10;
376 }
377 MenuExit = 0;
378
379 StyleFunc(Screen, &State, MENU_FUNCTION_INIT, NULL);
380 IdentifyRows(&State, Screen);
381 // override the starting selection with the default index, if any
382 if (*DefaultEntryIndex >= 0 && *DefaultEntryIndex <= State.MaxIndex) {
383 State.CurrentSelection = *DefaultEntryIndex;
384 UpdateScroll(&State, SCROLL_NONE);
385 }
386 State.PaintAll = TRUE;
387
388 while (!MenuExit) {
389 // update the screen
390 if (State.PaintAll) {
391 StyleFunc(Screen, &State, MENU_FUNCTION_PAINT_ALL, NULL);
392 State.PaintAll = FALSE;
393 } else if (State.PaintSelection) {
394 StyleFunc(Screen, &State, MENU_FUNCTION_PAINT_SELECTION, NULL);
395 State.PaintSelection = FALSE;
396 }
397
398 if (HaveTimeout) {
399 SPrint(TimeoutMessage, 255, L"%s in %d seconds", Screen->TimeoutText, (TimeoutCountdown + 5) / 10);
400 StyleFunc(Screen, &State, MENU_FUNCTION_PAINT_TIMEOUT, TimeoutMessage);
401 }
402
403 // read key press (and wait for it if applicable)
404 Status = refit_call2_wrapper(ST->ConIn->ReadKeyStroke, ST->ConIn, &key);
405 if (Status == EFI_NOT_READY) {
406 if (HaveTimeout && TimeoutCountdown == 0) {
407 // timeout expired
408 MenuExit = MENU_EXIT_TIMEOUT;
409 break;
410 } else if (HaveTimeout) {
411 refit_call1_wrapper(BS->Stall, 100000);
412 TimeoutCountdown--;
413 } else
414 refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &index);
415 continue;
416 }
417 if (HaveTimeout) {
418 // the user pressed a key, cancel the timeout
419 StyleFunc(Screen, &State, MENU_FUNCTION_PAINT_TIMEOUT, L"");
420 HaveTimeout = FALSE;
421 }
422
423 // react to key press
424 switch (key.ScanCode) {
425 case SCAN_UP:
426 UpdateScroll(&State, SCROLL_LINE_UP);
427 break;
428 case SCAN_LEFT:
429 UpdateScroll(&State, SCROLL_LINE_LEFT);
430 break;
431 case SCAN_DOWN:
432 UpdateScroll(&State, SCROLL_LINE_DOWN);
433 break;
434 case SCAN_RIGHT:
435 UpdateScroll(&State, SCROLL_LINE_RIGHT);
436 break;
437 case SCAN_HOME:
438 UpdateScroll(&State, SCROLL_FIRST);
439 break;
440 case SCAN_END:
441 UpdateScroll(&State, SCROLL_LAST);
442 break;
443 case SCAN_PAGE_UP:
444 UpdateScroll(&State, SCROLL_PAGE_UP);
445 break;
446 case SCAN_PAGE_DOWN:
447 UpdateScroll(&State, SCROLL_PAGE_DOWN);
448 break;
449 case SCAN_ESC:
450 MenuExit = MENU_EXIT_ESCAPE;
451 break;
452 case SCAN_INSERT:
453 case SCAN_F2:
454 MenuExit = MENU_EXIT_DETAILS;
455 break;
456 case SCAN_F10:
457 egScreenShot();
458 break;
459 case 0x0016: // F12
460 if (EjectMedia())
461 MenuExit = MENU_EXIT_ESCAPE;
462 break;
463 }
464 switch (key.UnicodeChar) {
465 case CHAR_LINEFEED:
466 case CHAR_CARRIAGE_RETURN:
467 case ' ':
468 MenuExit = MENU_EXIT_ENTER;
469 break;
470 case '+':
471 MenuExit = MENU_EXIT_DETAILS;
472 break;
473 default:
474 KeyAsString[0] = key.UnicodeChar;
475 KeyAsString[1] = 0;
476 ShortcutEntry = FindMenuShortcutEntry(Screen, KeyAsString);
477 if (ShortcutEntry >= 0) {
478 State.CurrentSelection = ShortcutEntry;
479 MenuExit = MENU_EXIT_ENTER;
480 }
481 break;
482 }
483 }
484
485 StyleFunc(Screen, &State, MENU_FUNCTION_CLEANUP, NULL);
486
487 if (ChosenEntry)
488 *ChosenEntry = Screen->Entries[State.CurrentSelection];
489 *DefaultEntryIndex = State.CurrentSelection;
490 return MenuExit;
491 } /* static UINTN RunGenericMenu() */
492
493 //
494 // text-mode generic style
495 //
496
497 static VOID TextMenuStyle(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, IN UINTN Function, IN CHAR16 *ParamText)
498 {
499 INTN i;
500 UINTN MenuWidth, ItemWidth, MenuHeight;
501 static UINTN MenuPosY;
502 static CHAR16 **DisplayStrings;
503 CHAR16 TimeoutMessage[256];
504
505 State->ScrollMode = SCROLL_MODE_TEXT;
506 switch (Function) {
507
508 case MENU_FUNCTION_INIT:
509 // vertical layout
510 MenuPosY = 4;
511 if (Screen->InfoLineCount > 0)
512 MenuPosY += Screen->InfoLineCount + 1;
513 MenuHeight = ConHeight - MenuPosY - 3;
514 if (Screen->TimeoutSeconds > 0)
515 MenuHeight -= 2;
516 InitScroll(State, Screen->EntryCount, MenuHeight);
517
518 // determine width of the menu
519 MenuWidth = 20; // minimum
520 for (i = 0; i <= State->MaxIndex; i++) {
521 ItemWidth = StrLen(Screen->Entries[i]->Title);
522 if (MenuWidth < ItemWidth)
523 MenuWidth = ItemWidth;
524 }
525 MenuWidth += 2;
526 if (MenuWidth > ConWidth - 3)
527 MenuWidth = ConWidth - 3;
528
529 // prepare strings for display
530 DisplayStrings = AllocatePool(sizeof(CHAR16 *) * Screen->EntryCount);
531 for (i = 0; i <= State->MaxIndex; i++) {
532 // Note: Theoretically, SPrint() is a cleaner way to do this; but the
533 // description of the StrSize parameter to SPrint implies it's measured
534 // in characters, but in practice both TianoCore and GNU-EFI seem to
535 // use bytes instead, resulting in truncated displays. I could just
536 // double the size of the StrSize parameter, but that seems unsafe in
537 // case a future library change starts treating this as characters, so
538 // I'm doing it the hard way in this instance.
539 // TODO: Review the above and possibly change other uses of SPrint()
540 DisplayStrings[i] = AllocateZeroPool(2 * sizeof(CHAR16));
541 DisplayStrings[i][0] = L' ';
542 MergeStrings(&DisplayStrings[i], Screen->Entries[i]->Title, 0);
543 if (StrLen(DisplayStrings[i]) > MenuWidth)
544 DisplayStrings[i][MenuWidth - 1] = 0;
545 // TODO: use more elaborate techniques for shortening too long strings (ellipses in the middle)
546 // TODO: account for double-width characters
547 } // for
548
549 // initial painting
550 BeginTextScreen(Screen->Title);
551 if (Screen->InfoLineCount > 0) {
552 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
553 for (i = 0; i < (INTN)Screen->InfoLineCount; i++) {
554 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 3, 4 + i);
555 refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, Screen->InfoLines[i]);
556 }
557 }
558
559 break;
560
561 case MENU_FUNCTION_CLEANUP:
562 // release temporary memory
563 for (i = 0; i <= State->MaxIndex; i++)
564 MyFreePool(DisplayStrings[i]);
565 MyFreePool(DisplayStrings);
566 break;
567
568 case MENU_FUNCTION_PAINT_ALL:
569 // paint the whole screen (initially and after scrolling)
570 for (i = 0; i <= State->MaxIndex; i++) {
571 if (i >= State->FirstVisible && i <= State->LastVisible) {
572 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 2, MenuPosY + (i - State->FirstVisible));
573 if (i == State->CurrentSelection)
574 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_CHOICE_CURRENT);
575 else
576 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_CHOICE_BASIC);
577 refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, DisplayStrings[i]);
578 }
579 }
580 // scrolling indicators
581 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_SCROLLARROW);
582 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, MenuPosY);
583 if (State->FirstVisible > 0)
584 refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, ArrowUp);
585 else
586 refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, L" ");
587 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, MenuPosY + State->MaxVisible);
588 if (State->LastVisible < State->MaxIndex)
589 refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, ArrowDown);
590 else
591 refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, L" ");
592 if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_HINTS)) {
593 if (Screen->Hint1 != NULL) {
594 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, ConHeight - 2);
595 refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, Screen->Hint1);
596 }
597 if (Screen->Hint2 != NULL) {
598 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, ConHeight - 1);
599 refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, Screen->Hint2);
600 }
601 }
602 break;
603
604 case MENU_FUNCTION_PAINT_SELECTION:
605 // redraw selection cursor
606 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 2,
607 MenuPosY + (State->PreviousSelection - State->FirstVisible));
608 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_CHOICE_BASIC);
609 refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, DisplayStrings[State->PreviousSelection]);
610 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 2,
611 MenuPosY + (State->CurrentSelection - State->FirstVisible));
612 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_CHOICE_CURRENT);
613 refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, DisplayStrings[State->CurrentSelection]);
614 break;
615
616 case MENU_FUNCTION_PAINT_TIMEOUT:
617 if (ParamText[0] == 0) {
618 // clear message
619 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
620 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, ConHeight - 3);
621 refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, BlankLine + 1);
622 } else {
623 // paint or update message
624 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_ERROR);
625 refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 3, ConHeight - 3);
626 SPrint(TimeoutMessage, 255, L"%s ", ParamText);
627 refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, TimeoutMessage);
628 }
629 break;
630
631 }
632 }
633
634 //
635 // graphical generic style
636 //
637
638
639 // Display a submenu
640 static VOID DrawSubmenuText(IN CHAR16 *Text, IN UINTN SelectedWidth, IN UINTN XPos, IN UINTN YPos)
641 {
642 if (TextBuffer == NULL)
643 TextBuffer = egCreateImage(LAYOUT_TEXT_WIDTH, TEXT_LINE_HEIGHT, TRUE);
644
645 egFillImage(TextBuffer, &MenuBackgroundPixel);
646 if (SelectedWidth > 0) {
647 // draw selection bar background
648 egFillImageArea(TextBuffer, 0, 0, SelectedWidth, TextBuffer->Height,
649 &SelectionBackgroundPixel);
650 }
651
652 // render the text
653 egRenderText(Text, TextBuffer, TEXT_XMARGIN, TEXT_YMARGIN);
654 egDrawImageWithTransparency(TextBuffer, NULL, XPos, YPos, TextBuffer->Width, TextBuffer->Height);
655 // BltImage(TextBuffer, XPos, YPos);
656 }
657
658 // Displays sub-menus
659 static VOID GraphicsMenuStyle(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, IN UINTN Function, IN CHAR16 *ParamText)
660 {
661 INTN i;
662 UINTN ItemWidth;
663 static UINTN MenuWidth, EntriesPosX, EntriesPosY, TimeoutPosY;
664
665 State->ScrollMode = SCROLL_MODE_TEXT;
666 switch (Function) {
667
668 case MENU_FUNCTION_INIT:
669 InitScroll(State, Screen->EntryCount, 0);
670
671 // determine width of the menu
672 MenuWidth = 20; // minimum
673 for (i = 0; i < (INTN)Screen->InfoLineCount; i++) {
674 ItemWidth = StrLen(Screen->InfoLines[i]);
675 if (MenuWidth < ItemWidth)
676 MenuWidth = ItemWidth;
677 }
678 for (i = 0; i <= State->MaxIndex; i++) {
679 ItemWidth = StrLen(Screen->Entries[i]->Title);
680 if (MenuWidth < ItemWidth)
681 MenuWidth = ItemWidth;
682 }
683 MenuWidth = TEXT_XMARGIN * 2 + MenuWidth * FONT_CELL_WIDTH;
684 if (MenuWidth > LAYOUT_TEXT_WIDTH)
685 MenuWidth = LAYOUT_TEXT_WIDTH;
686
687 if (Screen->TitleImage)
688 EntriesPosX = (UGAWidth + (Screen->TitleImage->Width + TITLEICON_SPACING) - MenuWidth) >> 1;
689 else
690 EntriesPosX = (UGAWidth - MenuWidth) >> 1;
691 EntriesPosY = ComputeRow0PosX() + TEXT_LINE_HEIGHT * 2;
692 TimeoutPosY = EntriesPosY + (Screen->EntryCount + 1) * TEXT_LINE_HEIGHT;
693
694 // initial painting
695 SwitchToGraphicsAndClear();
696 egMeasureText(Screen->Title, &ItemWidth, NULL);
697 DrawSubmenuText(Screen->Title, 0, ((UGAWidth - ItemWidth) >> 1) - TEXT_XMARGIN, EntriesPosY - TEXT_LINE_HEIGHT * 2);
698 if (Screen->TitleImage)
699 BltImage(Screen->TitleImage, EntriesPosX - (Screen->TitleImage->Width + TITLEICON_SPACING), EntriesPosY);
700 if (Screen->InfoLineCount > 0) {
701 for (i = 0; i < (INTN)Screen->InfoLineCount; i++) {
702 DrawSubmenuText(Screen->InfoLines[i], 0, EntriesPosX, EntriesPosY);
703 EntriesPosY += TEXT_LINE_HEIGHT;
704 }
705 EntriesPosY += TEXT_LINE_HEIGHT; // also add a blank line
706 }
707
708 break;
709
710 case MENU_FUNCTION_CLEANUP:
711 // nothing to do
712 break;
713
714 case MENU_FUNCTION_PAINT_ALL:
715 for (i = 0; i <= State->MaxIndex; i++) {
716 DrawSubmenuText(Screen->Entries[i]->Title, (i == State->CurrentSelection) ? MenuWidth : 0,
717 EntriesPosX, EntriesPosY + i * TEXT_LINE_HEIGHT);
718 }
719 if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_HINTS)) {
720 if ((Screen->Hint1 != NULL) && (StrLen(Screen->Hint1) > 0)) {
721 DrawSubmenuText(Screen->Hint1, 0, (UGAWidth - (StrLen(Screen->Hint1) * FONT_CELL_WIDTH)) / 2,
722 UGAHeight - (FONT_CELL_HEIGHT * 3));
723 }
724 if ((Screen->Hint2 != NULL) && (StrLen(Screen->Hint2) > 0)) {
725 DrawSubmenuText(Screen->Hint2, 0, (UGAWidth - (StrLen(Screen->Hint2) * FONT_CELL_WIDTH)) / 2,
726 UGAHeight - (FONT_CELL_HEIGHT * 2));
727 } // if
728 } // if
729 break;
730
731 case MENU_FUNCTION_PAINT_SELECTION:
732 // redraw selection cursor
733 DrawSubmenuText(Screen->Entries[State->PreviousSelection]->Title, 0,
734 EntriesPosX, EntriesPosY + State->PreviousSelection * TEXT_LINE_HEIGHT);
735 DrawSubmenuText(Screen->Entries[State->CurrentSelection]->Title, MenuWidth,
736 EntriesPosX, EntriesPosY + State->CurrentSelection * TEXT_LINE_HEIGHT);
737 break;
738
739 case MENU_FUNCTION_PAINT_TIMEOUT:
740 DrawSubmenuText(ParamText, 0, EntriesPosX, TimeoutPosY);
741 break;
742
743 }
744 } // static VOID GraphicsMenuStyle()
745
746 //
747 // graphical main menu style
748 //
749
750 static VOID DrawMainMenuEntry(REFIT_MENU_ENTRY *Entry, BOOLEAN selected, UINTN XPos, UINTN YPos)
751 {
752 EG_IMAGE *Background;
753
754 if (SelectionImages != NULL) {
755 if (selected) {
756 Background = egCropImage(GlobalConfig.ScreenBackground, XPos, YPos,
757 SelectionImages[Entry->Row]->Width, SelectionImages[Entry->Row]->Height);
758 egComposeImage(Background, SelectionImages[Entry->Row], 0, 0);
759 BltImageCompositeBadge(Background, Entry->Image, Entry->BadgeImage, XPos, YPos);
760 } else { // Image not selected; copy background
761 egDrawImageWithTransparency(Entry->Image, Entry->BadgeImage, XPos, YPos,
762 SelectionImages[Entry->Row]->Width, SelectionImages[Entry->Row]->Height);
763 } // if/else
764 } // if
765 } // VOID DrawMainMenuEntry()
766
767 static VOID DrawMainMenuText(IN CHAR16 *Text, IN UINTN XPos, IN UINTN YPos)
768 {
769 UINTN TextWidth, TextPosX;
770
771 TextBuffer = egCropImage(GlobalConfig.ScreenBackground, XPos, YPos, LAYOUT_TEXT_WIDTH, TEXT_LINE_HEIGHT);
772
773 // render the text
774 egMeasureText(Text, &TextWidth, NULL);
775 if (TextWidth > TextBuffer->Width)
776 TextPosX = 0;
777 else
778 TextPosX = (TextBuffer->Width - TextWidth) / 2;
779 egRenderText(Text, TextBuffer, TextPosX, 0);
780 egDrawImageWithTransparency(TextBuffer, NULL, XPos, YPos, TextBuffer->Width, TextBuffer->Height);
781 }
782
783 static VOID PaintAll(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, UINTN *itemPosX,
784 UINTN row0PosY, UINTN row1PosY, UINTN textPosY) {
785 INTN i;
786
787 if (Screen->Entries[State->CurrentSelection]->Row == 0)
788 AdjustScrollState(State);
789 for (i = State->FirstVisible; i <= State->MaxIndex; i++) {
790 if (Screen->Entries[i]->Row == 0) {
791 if (i <= State->LastVisible) {
792 DrawMainMenuEntry(Screen->Entries[i], (i == State->CurrentSelection) ? TRUE : FALSE,
793 itemPosX[i - State->FirstVisible], row0PosY);
794 } // if
795 } else {
796 DrawMainMenuEntry(Screen->Entries[i], (i == State->CurrentSelection) ? TRUE : FALSE, itemPosX[i], row1PosY);
797 }
798 }
799 if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_LABEL))
800 DrawMainMenuText(Screen->Entries[State->CurrentSelection]->Title,
801 (UGAWidth - LAYOUT_TEXT_WIDTH) >> 1, textPosY);
802
803 if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_HINTS)) {
804 DrawMainMenuText(Screen->Hint1, (UGAWidth - LAYOUT_TEXT_WIDTH) / 2, UGAHeight - (FONT_CELL_HEIGHT * 3));
805 DrawMainMenuText(Screen->Hint2, (UGAWidth - LAYOUT_TEXT_WIDTH) / 2, UGAHeight - (FONT_CELL_HEIGHT * 2));
806 } // if
807 } // static VOID PaintAll()
808
809 // Move the selection to State->CurrentSelection, adjusting icon row if necessary...
810 static VOID PaintSelection(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, UINTN *itemPosX,
811 UINTN row0PosY, UINTN row1PosY, UINTN textPosY) {
812 UINTN XSelectPrev, XSelectCur, YPosPrev, YPosCur;
813
814 if (((State->CurrentSelection <= State->LastVisible) && (State->CurrentSelection >= State->FirstVisible)) ||
815 (State->CurrentSelection >= State->InitialRow1) ) {
816 if (Screen->Entries[State->PreviousSelection]->Row == 0) {
817 XSelectPrev = State->PreviousSelection - State->FirstVisible;
818 YPosPrev = row0PosY;
819 } else {
820 XSelectPrev = State->PreviousSelection;
821 YPosPrev = row1PosY;
822 } // if/else
823 if (Screen->Entries[State->CurrentSelection]->Row == 0) {
824 XSelectCur = State->CurrentSelection - State->FirstVisible;
825 YPosCur = row0PosY;
826 } else {
827 XSelectCur = State->CurrentSelection;
828 YPosCur = row1PosY;
829 } // if/else
830 DrawMainMenuEntry(Screen->Entries[State->PreviousSelection], FALSE, itemPosX[XSelectPrev], YPosPrev);
831 DrawMainMenuEntry(Screen->Entries[State->CurrentSelection], TRUE, itemPosX[XSelectCur], YPosCur);
832 if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_LABEL))
833 DrawMainMenuText(Screen->Entries[State->CurrentSelection]->Title,
834 (UGAWidth - LAYOUT_TEXT_WIDTH) >> 1, textPosY);
835 } else { // Current selection not visible; must redraw the menu....
836 MainMenuStyle(Screen, State, MENU_FUNCTION_PAINT_ALL, NULL);
837 }
838 } // static VOID MoveSelection(VOID)
839
840 // Display an icon at the specified location. Uses the image specified by
841 // ExternalFilename if it's available, or BuiltInImage if it's not. The
842 // Y position is specified as the center value, and so is adjusted by half
843 // the icon's height. The X position is set along the icon's left
844 // edge if Alignment == ALIGN_LEFT, and along the right edge if
845 // Alignment == ALIGN_RIGHT
846 static VOID PaintIcon(IN EG_EMBEDDED_IMAGE *BuiltInIcon, IN CHAR16 *ExternalFilename, UINTN PosX, UINTN PosY, UINTN Alignment) {
847 EG_IMAGE *Icon = NULL;
848
849 if (FileExists(SelfDir, ExternalFilename))
850 Icon = egLoadIcon(SelfDir, ExternalFilename, 48);
851 if (Icon == NULL)
852 Icon = egPrepareEmbeddedImage(BuiltInIcon, TRUE);
853 if (Icon != NULL) {
854 if (Alignment == ALIGN_RIGHT)
855 PosX -= Icon->Width;
856 egDrawImageWithTransparency(Icon, NULL, PosX, PosY - (Icon->Height / 2), Icon->Width, Icon->Height);
857 }
858 } // static VOID PaintIcon()
859
860 inline UINTN ComputeRow0PosX(VOID) {
861 // return ((UGAHeight / 2) - (5 * ROW0_TILESIZE / 6));
862 return ((UGAHeight / 2) - ROW0_TILESIZE / 2);
863 } // UINTN ComputeRow0PosX()
864
865 // Display (or erase) the arrow icons to the left and right of an icon's row,
866 // as appropriate.
867 static VOID PaintArrows(SCROLL_STATE *State, UINTN PosX, UINTN PosY, UINTN row0Loaders) {
868 CHAR16 FileName[256];
869 EG_IMAGE *TempImage;
870 UINTN Width, Height, RightX, AdjPosY;
871
872 // NOTE: Assume that left and right arrows are of the same size....
873 Width = egemb_arrow_left.Width;
874 Height = egemb_arrow_left.Height;
875 RightX = (UGAWidth + (ROW0_TILESIZE + TILE_XSPACING) * State->MaxVisible) / 2 + TILE_XSPACING;
876 AdjPosY = PosY - (Height / 2);
877
878 // For PaintIcon() calls, the starting Y position is moved to the midpoint
879 // of the surrounding row; PaintIcon() adjusts this back up by half the
880 // icon's height to properly center it.
881 if ((State->FirstVisible > 0) && (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_ARROWS))) {
882 SPrint(FileName, 255, L"%s\\arrow_left.icns", GlobalConfig.IconsDir ? GlobalConfig.IconsDir : DEFAULT_ICONS_DIR);
883 PaintIcon(&egemb_arrow_left, FileName, PosX, PosY, ALIGN_RIGHT);
884 } else {
885 TempImage = egCropImage(GlobalConfig.ScreenBackground, PosX - Width, AdjPosY, Width, Height);
886 BltImage(TempImage, PosX - Width, AdjPosY);
887 egFreeImage(TempImage);
888 } // if/else
889
890 if ((State->LastVisible < (row0Loaders - 1)) && (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_ARROWS))) {
891 SPrint(FileName, 255, L"%s\\arrow_right.icns", GlobalConfig.IconsDir ? GlobalConfig.IconsDir : DEFAULT_ICONS_DIR);
892 PaintIcon(&egemb_arrow_right, FileName, RightX, PosY, ALIGN_LEFT);
893 } else {
894 TempImage = egCropImage(GlobalConfig.ScreenBackground, RightX, AdjPosY, Width, Height);
895 BltImage(TempImage, RightX, AdjPosY);
896 egFreeImage(TempImage);
897 } // if/else
898 } // VOID PaintArrows()
899
900 // Display main menu in graphics mode
901 VOID MainMenuStyle(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, IN UINTN Function, IN CHAR16 *ParamText)
902 {
903 INTN i;
904 static UINTN row0PosX, row0PosXRunning, row1PosY, row0Loaders;
905 UINTN row0Count, row1Count, row1PosX, row1PosXRunning;
906 static UINTN *itemPosX;
907 static UINTN row0PosY, textPosY;
908
909 State->ScrollMode = SCROLL_MODE_ICONS;
910 switch (Function) {
911
912 case MENU_FUNCTION_INIT:
913 InitScroll(State, Screen->EntryCount, GlobalConfig.MaxTags);
914
915 // layout
916 row0Count = 0;
917 row1Count = 0;
918 row0Loaders = 0;
919 for (i = 0; i <= State->MaxIndex; i++) {
920 if (Screen->Entries[i]->Row == 1) {
921 row1Count++;
922 } else {
923 row0Loaders++;
924 if (row0Count < State->MaxVisible)
925 row0Count++;
926 }
927 }
928 row0PosX = (UGAWidth + TILE_XSPACING - (ROW0_TILESIZE + TILE_XSPACING) * row0Count) >> 1;
929 row0PosY = ComputeRow0PosX();
930 row1PosX = (UGAWidth + TILE_XSPACING - (ROW1_TILESIZE + TILE_XSPACING) * row1Count) >> 1;
931 row1PosY = row0PosY + ROW0_TILESIZE + TILE_YSPACING;
932 if (row1Count > 0)
933 textPosY = row1PosY + ROW1_TILESIZE + TILE_YSPACING;
934 else
935 textPosY = row1PosY;
936
937 itemPosX = AllocatePool(sizeof(UINTN) * Screen->EntryCount);
938 row0PosXRunning = row0PosX;
939 row1PosXRunning = row1PosX;
940 for (i = 0; i <= State->MaxIndex; i++) {
941 if (Screen->Entries[i]->Row == 0) {
942 itemPosX[i] = row0PosXRunning;
943 row0PosXRunning += ROW0_TILESIZE + TILE_XSPACING;
944 } else {
945 itemPosX[i] = row1PosXRunning;
946 row1PosXRunning += ROW1_TILESIZE + TILE_XSPACING;
947 }
948 }
949 // initial painting
950 InitSelection();
951 SwitchToGraphicsAndClear();
952 break;
953
954 case MENU_FUNCTION_CLEANUP:
955 MyFreePool(itemPosX);
956 break;
957
958 case MENU_FUNCTION_PAINT_ALL:
959 PaintAll(Screen, State, itemPosX, row0PosY, row1PosY, textPosY);
960 // For PaintArrows(), the starting Y position is moved to the midpoint
961 // of the surrounding row; PaintIcon() adjusts this back up by half the
962 // icon's height to properly center it.
963 PaintArrows(State, row0PosX - TILE_XSPACING, row0PosY + (ROW0_TILESIZE / 2), row0Loaders);
964 break;
965
966 case MENU_FUNCTION_PAINT_SELECTION:
967 PaintSelection(Screen, State, itemPosX, row0PosY, row1PosY, textPosY);
968 break;
969
970 case MENU_FUNCTION_PAINT_TIMEOUT:
971 if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_LABEL))
972 DrawMainMenuText(ParamText, (UGAWidth - LAYOUT_TEXT_WIDTH) >> 1, textPosY + TEXT_LINE_HEIGHT);
973 break;
974
975 }
976 } // VOID MainMenuStyle()
977
978 // Enable the user to edit boot loader options.
979 // Returns TRUE if the user exited with edited options; FALSE if the user
980 // pressed Esc to terminate the edit.
981 static BOOLEAN EditOptions(LOADER_ENTRY *MenuEntry) {
982 UINTN x_max, y_max;
983 CHAR16 *EditedOptions;
984 BOOLEAN retval = FALSE;
985
986 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
987 return FALSE;
988 }
989
990 refit_call4_wrapper(ST->ConOut->QueryMode, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max);
991
992 if (!GlobalConfig.TextOnly)
993 SwitchToText(TRUE);
994
995 if (line_edit(MenuEntry->LoadOptions, &EditedOptions, x_max)) {
996 MyFreePool(MenuEntry->LoadOptions);
997 MenuEntry->LoadOptions = EditedOptions;
998 retval = TRUE;
999 } // if
1000 if (!GlobalConfig.TextOnly)
1001 SwitchToGraphics();
1002 return retval;
1003 } // VOID EditOptions()
1004
1005 //
1006 // user-callable dispatcher functions
1007 //
1008
1009 UINTN RunMenu(IN REFIT_MENU_SCREEN *Screen, OUT REFIT_MENU_ENTRY **ChosenEntry)
1010 {
1011 INTN DefaultEntry = -1;
1012 MENU_STYLE_FUNC Style = TextMenuStyle;
1013
1014 if (AllowGraphicsMode)
1015 Style = GraphicsMenuStyle;
1016
1017 return RunGenericMenu(Screen, Style, &DefaultEntry, ChosenEntry);
1018 }
1019
1020 UINTN RunMainMenu(IN REFIT_MENU_SCREEN *Screen, IN CHAR16* DefaultSelection, OUT REFIT_MENU_ENTRY **ChosenEntry)
1021 {
1022 MENU_STYLE_FUNC Style = TextMenuStyle;
1023 MENU_STYLE_FUNC MainStyle = TextMenuStyle;
1024 REFIT_MENU_ENTRY *TempChosenEntry;
1025 UINTN MenuExit = 0;
1026 INTN DefaultEntryIndex = -1;
1027 INTN DefaultSubmenuIndex = -1;
1028
1029 if (DefaultSelection != NULL) {
1030 // Find a menu entry that includes *DefaultSelection as a substring
1031 DefaultEntryIndex = FindMenuShortcutEntry(Screen, DefaultSelection);
1032 }
1033
1034 if (AllowGraphicsMode) {
1035 Style = GraphicsMenuStyle;
1036 MainStyle = MainMenuStyle;
1037 }
1038
1039 while (!MenuExit) {
1040 MenuExit = RunGenericMenu(Screen, MainStyle, &DefaultEntryIndex, &TempChosenEntry);
1041 Screen->TimeoutSeconds = 0;
1042
1043 if (MenuExit == MENU_EXIT_DETAILS) {
1044 if (TempChosenEntry->SubScreen != NULL) {
1045 MenuExit = RunGenericMenu(TempChosenEntry->SubScreen, Style, &DefaultSubmenuIndex, &TempChosenEntry);
1046 if (MenuExit == MENU_EXIT_ESCAPE || TempChosenEntry->Tag == TAG_RETURN)
1047 MenuExit = 0;
1048 if (MenuExit == MENU_EXIT_DETAILS) {
1049 if (!EditOptions((LOADER_ENTRY *) TempChosenEntry))
1050 MenuExit = 0;
1051 } // if
1052 } else { // no sub-screen; ignore keypress
1053 MenuExit = 0;
1054 }
1055 } // Enter sub-screen
1056 }
1057
1058 if (ChosenEntry)
1059 *ChosenEntry = TempChosenEntry;
1060 return MenuExit;
1061 } /* UINTN RunMainMenu() */