diff --git a/xtldr2/textui.c b/xtldr2/textui.c index 0ec3605..6a9d6ba 100644 --- a/xtldr2/textui.c +++ b/xtldr2/textui.c @@ -9,6 +9,253 @@ #include +/** + * Displays a simple TUI-based boot menu. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +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 = 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 */ + BlClearConsoleLine(Handle.PosY + Handle.Height + 4); + } + + /* Read key stroke */ + BlReadKeyStroke(&Key); + + if(Key.ScanCode == 0x03 || Key.UnicodeChar == 0x0D) + { + /* ENTER or RightArrow key pressed, boot the highlighted OS */ + BlSetConsoleAttributes(Handle.DialogColor | Handle.TextColor); + BlClearConsoleLine(Handle.PosY + Handle.Height + 4); + BlSetCursorPosition(4, Handle.PosY + Handle.Height + 4); + BlConsolePrint(L"Booting '%S' now...\n", MenuEntries[HighligtedEntryId].EntryName); + + /* Boot the highlighted (chosen) OS */ + Status = BlInvokeBootProtocol(MenuEntries[HighligtedEntryId].Options); + if(Status != STATUS_SUCCESS) + { + /* Failed to boot OS */ + BlDebugPrint(L"ERROR: Failed to boot OS '%S' (Status Code: 0x%lx)\n", + MenuEntries[HighligtedEntryId].EntryName, Status); + BlDisplayErrorDialog(L"XTLDR", L"Failed to startup the selected Operating System."); + } + + /* 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 == 0x09) + { + /* PageUp key pressed, go to top entry */ + if(HighligtedEntryId != 0) + { + /* Highlight first entry */ + BlpDrawBootMenuEntry(&Handle, MenuEntries[HighligtedEntryId].EntryName, + HighligtedEntryId, FALSE); + BlpDrawBootMenuEntry(&Handle, MenuEntries[0].EntryName, 0, TRUE); + + /* Update highlighted entry ID */ + HighligtedEntryId = 0; + } + } + else if(Key.ScanCode == 0x0A) + { + /* PageDown key pressed, go to bottom entry */ + if(HighligtedEntryId != NumberOfEntries - 1) + { + /* Highlight last entry */ + BlpDrawBootMenuEntry(&Handle, MenuEntries[HighligtedEntryId].EntryName, + HighligtedEntryId, FALSE); + BlpDrawBootMenuEntry(&Handle, MenuEntries[NumberOfEntries - 1].EntryName, + NumberOfEntries - 1, TRUE); + + /* Update highlighted entry ID */ + HighligtedEntryId = NumberOfEntries - 1; + } + } + 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 */ + BlDisplayErrorDialog(L"XTLDR", L"Editing boot menu entries is not implemented yet!"); + + /* 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 */ + BlSetConsoleAttributes(Handle.DialogColor | Handle.TextColor); + BlClearConsoleLine(Handle.PosY + Handle.Height + 4); + 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 */ + BlSetConsoleAttributes(Handle.DialogColor | Handle.TextColor); + BlClearConsoleLine(Handle.PosY + Handle.Height + 4); + BlSetCursorPosition(4, Handle.PosY + Handle.Height + 4); + BlConsolePrint(L"Booting '%S' now...\n", MenuEntries[HighligtedEntryId].EntryName); + + /* Disable the timer just in case booting OS fails */ + TimeOut = -1; + + /* Boot the highlighted (default) OS */ + Status = BlInvokeBootProtocol(MenuEntries[HighligtedEntryId].Options); + if(Status != STATUS_SUCCESS) + { + /* Failed to boot OS */ + BlDebugPrint(L"ERROR: Failed to boot OS '%S' (Status Code: 0x%lx)\n", + MenuEntries[HighligtedEntryId].EntryName, Status); + BlDisplayErrorDialog(L"XTLDR", L"Failed to startup the selected Operating System."); + } + break; + } + } + } + } +} + /** * Displays a red error dialog box with the specified caption and message. * @@ -960,251 +1207,3 @@ BlDisplayInputDialog(IN PWCHAR Caption, BlSetConsoleAttributes(EFI_TEXT_BGCOLOR_BLACK | EFI_TEXT_FGCOLOR_LIGHTGRAY); BlClearConsoleScreen(); } - - - -XTCDECL -EFI_STATUS -BlInvokeBootProtocol(IN PLIST_ENTRY Options); - - - -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 = 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 */ - BlClearConsoleLine(Handle.PosY + Handle.Height + 4); - } - - /* Read key stroke */ - BlReadKeyStroke(&Key); - - if(Key.ScanCode == 0x03 || Key.UnicodeChar == 0x0D) - { - /* ENTER or RightArrow key pressed, boot the highlighted OS */ - BlSetConsoleAttributes(Handle.DialogColor | Handle.TextColor); - BlClearConsoleLine(Handle.PosY + Handle.Height + 4); - BlSetCursorPosition(4, Handle.PosY + Handle.Height + 4); - BlConsolePrint(L"Booting '%S' now...\n", MenuEntries[HighligtedEntryId].EntryName); - - /* Boot the highlighted (chosen) OS */ - Status = BlInvokeBootProtocol(MenuEntries[HighligtedEntryId].Options); - if(Status != STATUS_SUCCESS) - { - /* Failed to boot OS */ - BlDebugPrint(L"ERROR: Failed to boot OS '%S' (Status Code: 0x%lx)\n", - MenuEntries[HighligtedEntryId].EntryName, Status); - BlDisplayErrorDialog(L"XTLDR", L"Failed to startup the selected Operating System."); - } - - /* 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 == 0x09) - { - /* PageUp key pressed, go to top entry */ - if(HighligtedEntryId != 0) - { - /* Highlight first entry */ - BlpDrawBootMenuEntry(&Handle, MenuEntries[HighligtedEntryId].EntryName, - HighligtedEntryId, FALSE); - BlpDrawBootMenuEntry(&Handle, MenuEntries[0].EntryName, 0, TRUE); - - /* Update highlighted entry ID */ - HighligtedEntryId = 0; - } - } - else if(Key.ScanCode == 0x0A) - { - /* PageDown key pressed, go to bottom entry */ - if(HighligtedEntryId != NumberOfEntries - 1) - { - /* Highlight last entry */ - BlpDrawBootMenuEntry(&Handle, MenuEntries[HighligtedEntryId].EntryName, - HighligtedEntryId, FALSE); - BlpDrawBootMenuEntry(&Handle, MenuEntries[NumberOfEntries - 1].EntryName, - NumberOfEntries - 1, TRUE); - - /* Update highlighted entry ID */ - HighligtedEntryId = NumberOfEntries - 1; - } - } - 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 */ - BlDisplayErrorDialog(L"XTLDR", L"Editing boot menu entries is not implemented yet!"); - - /* 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 */ - BlSetConsoleAttributes(Handle.DialogColor | Handle.TextColor); - BlClearConsoleLine(Handle.PosY + Handle.Height + 4); - 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 */ - BlSetConsoleAttributes(Handle.DialogColor | Handle.TextColor); - BlClearConsoleLine(Handle.PosY + Handle.Height + 4); - BlSetCursorPosition(4, Handle.PosY + Handle.Height + 4); - BlConsolePrint(L"Booting '%S' now...\n", MenuEntries[HighligtedEntryId].EntryName); - - /* Disable the timer just in case booting OS fails */ - TimeOut = -1; - - /* Boot the highlighted (default) OS */ - Status = BlInvokeBootProtocol(MenuEntries[HighligtedEntryId].Options); - if(Status != STATUS_SUCCESS) - { - /* Failed to boot OS */ - BlDebugPrint(L"ERROR: Failed to boot OS '%S' (Status Code: 0x%lx)\n", - MenuEntries[HighligtedEntryId].EntryName, Status); - BlDisplayErrorDialog(L"XTLDR", L"Failed to startup the selected Operating System."); - } - break; - } - } - } - } -}