diff --git a/xtldr2/textui.c b/xtldr2/textui.c index a1aa660..f55ed93 100644 --- a/xtldr2/textui.c +++ b/xtldr2/textui.c @@ -960,3 +960,192 @@ BlDisplayInputDialog(IN PWCHAR Caption, BlSetConsoleAttributes(EFI_TEXT_BGCOLOR_BLACK | EFI_TEXT_FGCOLOR_LIGHTGRAY); BlClearConsoleScreen(); } + + + + + + +XTCDECL +VOID +BlDisplayBootMenu() +{ + XTBL_DIALOG_HANDLE Handle; + PXTBL_BOOTMENU_ITEM MenuEntries = NULL; + ULONG Index, NumberOfEntries, HighligtedEntryId; + UINT_PTR EventIndex; + EFI_EVENT Events[2]; + EFI_INPUT_KEY Key; + EFI_EVENT TimerEvent; + EFI_STATUS Status; + LONG TimeOut; + PWCHAR TimeOutString; + + /* Initialize boot menu list */ + BlInitializeBootMenuList(MenuEntries, &NumberOfEntries, &HighligtedEntryId); + + /* Get timeout from the configuration */ + TimeOutString = NULL; //BlGetConfigValue(L"TIMEOUT"); + TimeOut = -1; + + /* Check if timeout is specified */ + if(TimeOutString != NULL) + { + /* Convert timeout string to number */ + TimeOut = 0; + while(*TimeOutString >= '0' && *TimeOutString <= '9') + { + TimeOut *= 10; + TimeOut += *TimeOutString - '0'; + TimeOutString++; + } + } + + /* Infinite boot menu loop */ + while(TRUE) + { + /* Draw boot menu */ + BlpDrawBootMenu(&Handle); + + /* Check if there is anything to show in the boot menu */ + if(NumberOfEntries > 0) { + /* Iterate through all menu entries */ + for(Index = 0; Index < NumberOfEntries; Index++) + { + /* Draw menu entry */ + BlpDrawBootMenuEntry(&Handle, MenuEntries[Index].EntryName, Index, Index == HighligtedEntryId); + } + } + else + { + /* No menu entries found, show error message */ + BlDisplayErrorDialog(L"XTLDR", L"No boot menu entries found in the configuration. Falling back to shell."); + + /* Exit into XTLDR shell */ + return; + } + + /* Create a timer event for controlling the timeout of the boot menu */ + Status = EfiSystemTable->BootServices->CreateEvent(EFI_EVENT_TIMER, EFI_TPL_CALLBACK, NULL, NULL, &TimerEvent); + if(Status == STATUS_EFI_SUCCESS) + { + /* Setup new EFI timer */ + Status = EfiSystemTable->BootServices->SetTimer(TimerEvent, TimerPeriodic, 10000000); + } + + /* Check is EFI timer was successfully created */ + if(Status != STATUS_EFI_SUCCESS) + { + /* Timer creation failed, disable the timer */ + TimeOut = -1; + } + + /* Initialize EFI events */ + Events[0] = EfiSystemTable->ConIn->WaitForKey; + Events[1] = TimerEvent; + + /* Infinite boot menu event loop */ + while(TRUE) + { + /* Wait for EFI event */ + BlWaitForEfiEvent(2, Events, &EventIndex); + + /* Check which event was received */ + if(EventIndex == 0) + { + /* Key pressed, check if timer is still active */ + if(TimeOut != -1) + { + /* Disable the timer */ + TimeOut = -1; + + /* Cancel timer event */ + EfiSystemTable->BootServices->SetTimer(TimerEvent, TimerCancel, 0); + + /* Remove the timer message */ + BlSetCursorPosition(4, Handle.PosY + Handle.Height + 4); + BlConsolePrint(L" ", TimeOut); + } + + /* Read key stroke */ + BlReadKeyStroke(&Key); + + if(Key.ScanCode == 0x03 || Key.UnicodeChar == 0x0D) + { + /* ENTER or RightArrow key pressed, boot the highlighted OS */ + BlDisplayInfoDialog(L"XTLDR", L"Booting highlighted OS ..."); + + /* Break from boot menu event loop to redraw whole boot menu */ + break; + } + else if(Key.ScanCode == 0x01) + { + /* UpArrow key pressed, go to previous entry if possible */ + if(HighligtedEntryId > 0) + { + /* Highlight previous entry */ + HighligtedEntryId--; + BlpDrawBootMenuEntry(&Handle, MenuEntries[HighligtedEntryId + 1].EntryName, HighligtedEntryId + 1, FALSE); + BlpDrawBootMenuEntry(&Handle, MenuEntries[HighligtedEntryId].EntryName, HighligtedEntryId, TRUE); + } + } + else if(Key.ScanCode == 0x02) + { + /* DownArrow key pressed, go to next entry if possible */ + if(HighligtedEntryId < NumberOfEntries - 1) + { + /* Highlight next entry */ + HighligtedEntryId++; + BlpDrawBootMenuEntry(&Handle, MenuEntries[HighligtedEntryId - 1].EntryName, HighligtedEntryId - 1, FALSE); + BlpDrawBootMenuEntry(&Handle, MenuEntries[HighligtedEntryId].EntryName, HighligtedEntryId, TRUE); + } + } + else if(Key.ScanCode == 0x0B) + { + /* F1 key pressed, show help */ + BlDisplayInfoDialog(L"XTLDR", L"XTLDR Boot Loader Help"); + + /* Break from boot menu event loop to redraw whole boot menu */ + break; + } + else if(Key.UnicodeChar == 0x65) + { + /* 'e' key pressed, edit the highlighted entry */ + + /* Break from boot menu event loop to redraw whole boot menu */ + break; + } + else if(Key.UnicodeChar == 0x73) + { + /* 's' key pressed, exit into XTLDR shell */ + return; + } + } + else + { + /* Timer tick, check if time out expired */ + if(TimeOut > 0) + { + /* Update a message and decrease timeout value */ + BlSetCursorPosition(4, Handle.PosY + Handle.Height + 4); + BlConsolePrint(L"The highlighted position will be booted automatically in %ld seconds. ", TimeOut); + TimeOut--; + } + else if(TimeOut == 0) + { + /* Time out expired, update a message */ + BlSetCursorPosition(4, Handle.PosY + Handle.Height + 4); + BlConsolePrint(L"Booting '%S' now... ", + MenuEntries[HighligtedEntryId].EntryName); + + /* Disable the timer just in case booting OS fails */ + TimeOut = -1; + + /* Boot the highlighted (default) OS */ + BlDisplayInfoDialog(L"XTLDR", L"Booting highlighted OS ..."); + break; + } + } + } + } +} diff --git a/xtldr2/xtldr.c b/xtldr2/xtldr.c index 3bb9bf6..0be8f71 100644 --- a/xtldr2/xtldr.c +++ b/xtldr2/xtldr.c @@ -9,6 +9,10 @@ #include +XTCDECL +VOID +BlDisplayBootMenu(); + /** * Initializes a list of operating systems for XTLDR boot menu. * @@ -186,6 +190,12 @@ BlStartXtLoader(IN EFI_HANDLE ImageHandle, BlDebugPrint(L"ERROR: Failed to discover and enumerate block devices\n"); } + for(;;) + { + /* Display boot menu */ + BlDisplayBootMenu(); + } + /* This point should be never reached, if this happen return error code */ return STATUS_EFI_LOAD_ERROR; }