Implement boot entry editor
All checks were successful
Builds / ExectOS (amd64, release) (push) Successful in 34s
Builds / ExectOS (i686, release) (push) Successful in 32s
Builds / ExectOS (amd64, debug) (push) Successful in 47s
Builds / ExectOS (i686, debug) (push) Successful in 45s

This commit is contained in:
Aiken Harris 2025-08-27 19:15:38 +02:00
parent b68514b176
commit c6643125e1
Signed by: harraiken
GPG Key ID: C40F06CB7493C1F5
5 changed files with 696 additions and 33 deletions

View File

@ -4,11 +4,84 @@
* FILE: xtldr/config.c
* DESCRIPTION: XT Boot Loader Configuration
* DEVELOPERS: Rafal Kupiec <belliash@codingworkshop.eu.org>
* Aiken Harris <harraiken91@gmail.com>
*/
#include <xtldr.h>
/**
* @brief Retrieves the value of a specific OS boot option from a list.
*
* @param Options
* A pointer to the head of a list of XTBL_CONFIG_ENTRY structures.
*
* @param OptionName
* A pointer to wide string that contains the name of the boot option to retrieve.
*
* @param OptionValue
* A pointer to a variable that receives a pointer to the retrieved boot option's value.
*
* @return This routine returns a status code.
*
* @since XT 1.0
*/
XTCDECL
EFI_STATUS
BlGetBootOptionValue(IN PLIST_ENTRY Options,
IN CONST PWCHAR OptionName,
OUT PWCHAR *OptionValue)
{
PXTBL_CONFIG_ENTRY ConfigEntry;
PLIST_ENTRY ConfigList;
ULONG KeyLength, ValueLength;
EFI_STATUS Status;
/* Assume the option will not be found */
*OptionValue = NULL;
/* Get the length of the option name we are looking for */
KeyLength = RtlWideStringLength(OptionName, 0);
/* Start iterating from the first entry in the options list */
ConfigList = Options->Flink;
while(ConfigList != Options)
{
/* Get the container record for the current config entry */
ConfigEntry = CONTAIN_RECORD(ConfigList, XTBL_CONFIG_ENTRY, Flink);
/* Compare the current entry's name with the requested option name */
if(RtlCompareWideStringInsensitive(ConfigEntry->Name, OptionName, KeyLength) == 0)
{
/* Found the option, now prepare to copy its value */
ValueLength = RtlWideStringLength(ConfigEntry->Value, 0);
/* Allocate memory for the output value string */
Status = BlAllocateMemoryPool((ValueLength + 1) * sizeof(WCHAR), (PVOID *)OptionValue);
if(Status != STATUS_EFI_SUCCESS)
{
/* Memory allocation failure, print debug message and return status code */
BlDebugPrint(L"ERROR: Memory allocation failure (Status Code: 0x%zX)\n", Status);
*OptionValue = NULL;
return Status;
}
/* Copy the value and NULL-terminate the new string */
RtlCopyMemory(*OptionValue, ConfigEntry->Value, ValueLength * sizeof(WCHAR));
(*OptionValue)[ValueLength] = L'\0';
/* Successfully retrieved the option value, return success */
return STATUS_EFI_SUCCESS;
}
/* Move to the next entry in the list */
ConfigList = ConfigList->Flink;
}
/* Option not found */
return STATUS_EFI_NOT_FOUND;
}
/**
* Returns a boolean value of the specified configuration key.
*
@ -101,6 +174,156 @@ BlGetConfigValue(IN CONST PWCHAR ConfigName)
return NULL;
}
/**
* Retrieves the list of user-editable boot options.
*
* @param OptionsArray
* A pointer to a variable that will receive the pointer to the array of editable option names.
*
* @param OptionsCount
* A pointer to a variable that will be updated with the number of elements in the OptionsArray.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTCDECL
VOID
BlGetEditableOptions(OUT CONST PWCHAR **OptionsArray,
OUT PSIZE_T OptionsCount)
{
ULONG Count = 0;
/* Return a pointer to the global array of editable options */
*OptionsArray = BlpEditableConfigOptions;
/* Calculate the number of elements in the array */
while(BlpEditableConfigOptions[Count])
{
Count++;
}
/* Return the number of elements */
*OptionsCount = Count;
}
/**
* Sets the value of a specific OS boot option in a list, or adds it if it doesn't exist.
*
* @param Options
* A pointer to the head of a list of XTBL_CONFIG_ENTRY structures.
*
* @param OptionName
* A pointer to a wide string that contains the name of the boot option to set.
*
* @param OptionValue
* A pointer to a wide string that contains the new value for the boot option.
*
* @return This routine returns a status code.
*
* @since XT 1.0
*/
XTCDECL
EFI_STATUS
BlSetBootOptionValue(IN PLIST_ENTRY Options,
IN CONST PWCHAR OptionName,
IN CONST PWCHAR OptionValue)
{
PXTBL_CONFIG_ENTRY ConfigEntry;
PLIST_ENTRY ConfigList;
ULONG Length;
EFI_STATUS Status;
/* Get the length of the option name we are looking for */
Length = RtlWideStringLength(OptionName, 0);
/* Start iterating from the first entry in the options list */
ConfigList = Options->Flink;
while(ConfigList != Options)
{
/* Get the container record for the current config entry */
ConfigEntry = CONTAIN_RECORD(ConfigList, XTBL_CONFIG_ENTRY, Flink);
/* Compare the current entry's name with the requested option name */
if(RtlCompareWideStringInsensitive(ConfigEntry->Name, OptionName, Length) == 0)
{
/* Found the option, get its length */
Length = RtlWideStringLength(OptionValue, 0);
/* Reallocate memory for the new value */
Status = BlFreeMemoryPool(ConfigEntry->Value);
if(Status != STATUS_EFI_SUCCESS)
{
/* Failed to free memory, return status code */
return Status;
}
/* Allocate new memory for the updated value */
Status = BlAllocateMemoryPool((Length + 1) * sizeof(WCHAR), (PVOID *)&ConfigEntry->Value);
if(Status != STATUS_EFI_SUCCESS)
{
/* Memory allocation failure, print debug message and return status code */
BlDebugPrint(L"ERROR: Memory allocation failure (Status Code: 0x%zX)\\n", Status);
return Status;
}
/* Copy the value and NULL-terminate the new string */
RtlCopyMemory(ConfigEntry->Value, OptionValue, Length * sizeof(WCHAR));
ConfigEntry->Value[Length] = L'\0';
return STATUS_EFI_SUCCESS;
}
/* Move to the next entry in the list */
ConfigList = ConfigList->Flink;
}
/* Option not found, allocate memory for the new one */
Status = BlAllocateMemoryPool(sizeof(XTBL_CONFIG_ENTRY), (PVOID *)&ConfigEntry);
if(Status != STATUS_EFI_SUCCESS)
{
/* Memory allocation failure, print debug message and return status code */
BlDebugPrint(L"ERROR: Memory allocation failure (Status Code: 0x%zX)\\n", Status);
return Status;
}
/* Allocate memory for the option name */
Length = RtlWideStringLength(OptionName, 0);
Status = BlAllocateMemoryPool((Length + 1) * sizeof(WCHAR), (PVOID *)&ConfigEntry->Name);
if(Status != STATUS_EFI_SUCCESS)
{
/* Memory allocation failure, print debug message and return status code */
BlDebugPrint(L"ERROR: Memory allocation failure (Status Code: 0x%zX)\\n", Status);
BlFreeMemoryPool(ConfigEntry);
return Status;
}
/* Copy the option name and NULL-terminate the new string */
RtlCopyMemory(ConfigEntry->Name, OptionName, Length * sizeof(WCHAR));
ConfigEntry->Name[Length] = L'\0';
/* Allocate memory for the option value */
Length = RtlWideStringLength(OptionValue, 0);
Status = BlAllocateMemoryPool((Length + 1) * sizeof(WCHAR), (PVOID *)&ConfigEntry->Value);
if(Status != STATUS_EFI_SUCCESS)
{
/* Memory allocation failure, print debug message and return status code */
BlDebugPrint(L"ERROR: Memory allocation failure (Status Code: 0x%zX)\\n", Status);
BlFreeMemoryPool(ConfigEntry->Name);
BlFreeMemoryPool(ConfigEntry);
return Status;
}
/* Copy the value and NULL-terminate the new string */
RtlCopyMemory(ConfigEntry->Value, OptionValue, Length * sizeof(WCHAR));
ConfigEntry->Value[Length] = L'\0';
/* Insert the new config entry at the end of the options list */
RtlInsertTailList(Options, &ConfigEntry->Flink);
/* Return success */
return STATUS_EFI_SUCCESS;
}
/**
* Updates existing configuration value.
*
@ -110,7 +333,7 @@ BlGetConfigValue(IN CONST PWCHAR ConfigName)
* @param ConfigValue
* Specifies the new configuration value.
*
* @return This routine returns status code.
* @return This routine returns a status code.
*
* @since XT 1.0
*/
@ -174,7 +397,7 @@ BlSetConfigValue(IN CONST PWCHAR ConfigName,
/**
* Loads and parses XTLDR configuration file.
*
* @return This routine returns status code.
* @return This routine returns a status code.
*
* @since XT 1.0
*/
@ -568,7 +791,7 @@ BlpParseConfigFile(IN CONST PCHAR RawConfig,
* @param ConfigData
* Provides a buffer to store the data read from the configuration file.
*
* @return This routine returns status code.
* @return This routine returns a status code.
*
* @since XT 1.0
*/

View File

@ -21,6 +21,13 @@ LIST_ENTRY BlpConfig;
/* XT Boot Loader loaded configuration */
LIST_ENTRY BlpConfigSections;
/* List of user-editable boot options */
PWCHAR BlpEditableConfigOptions[] = {
L"BootModules", L"SystemType", L"SystemPath",
L"KernelFile", L"InitrdFile", L"HalFile",
L"Parameters", NULL
};
/* XT Boot Loader protocol */
XTBL_LOADER_PROTOCOL BlpLdrProtocol;
@ -41,3 +48,4 @@ EFI_HANDLE EfiImageHandle;
/* EFI System Table */
PEFI_SYSTEM_TABLE EfiSystemTable;

View File

@ -24,6 +24,9 @@ EXTERN LIST_ENTRY BlpConfig;
/* XT Boot Loader loaded configuration */
EXTERN LIST_ENTRY BlpConfigSections;
/* List of user-editable boot options */
EXTERN PWCHAR BlpEditableConfigOptions[];
/* XT Boot Loader protocol */
EXTERN XTBL_LOADER_PROTOCOL BlpLdrProtocol;

View File

@ -71,6 +71,10 @@ XTCDECL
VOID
BlDisplayBootMenu();
XTCDECL
VOID
BlDisplayEditMenu(IN PXTBL_BOOTMENU_ITEM MenuEntry);
XTCDECL
VOID
BlDisplayErrorDialog(IN PWCHAR Caption,
@ -134,6 +138,12 @@ BOOLEAN
BlGetBooleanParameter(IN CONST PWCHAR Parameters,
IN CONST PWCHAR Needle);
XTCDECL
EFI_STATUS
BlGetBootOptionValue(IN PLIST_ENTRY Options,
IN CONST PWCHAR OptionName,
OUT PWCHAR *OptionValue);
XTCDECL
BOOLEAN
BlGetConfigBooleanValue(IN CONST PWCHAR ConfigName);
@ -147,6 +157,11 @@ EFI_STATUS
BlGetConfigurationTable(IN PEFI_GUID TableGuid,
OUT PVOID *Table);
XTCDECL
VOID
BlGetEditableOptions(OUT CONST PWCHAR **OptionsArray,
OUT PSIZE_T OptionsCount);
XTCDECL
EFI_STATUS
BlGetEfiPath(IN PWCHAR SystemPath,
@ -332,6 +347,12 @@ XTCDECL
VOID
BlResetConsoleInputBuffer();
XTCDECL
EFI_STATUS
BlSetBootOptionValue(IN PLIST_ENTRY Options,
IN CONST PWCHAR OptionName,
IN CONST PWCHAR OptionValue);
XTCDECL
EFI_STATUS
BlSetConfigValue(IN CONST PWCHAR ConfigName,
@ -456,6 +477,18 @@ VOID
BlpDrawDialogProgressBar(IN PXTBL_DIALOG_HANDLE Handle,
IN UCHAR Percentage);
XTCDECL
VOID
BlpDrawEditMenu(OUT PXTBL_DIALOG_HANDLE Handle);
XTCDECL
EFI_STATUS
BlpDrawEditMenuEntry(IN PXTBL_DIALOG_HANDLE Handle,
IN PWCHAR OptionName,
IN PWCHAR OptionValue,
IN UINT Position,
IN BOOLEAN Highlighted);
XTCDECL
PEFI_DEVICE_PATH_PROTOCOL
BlpDuplicateDevicePath(IN PEFI_DEVICE_PATH_PROTOCOL DevicePath);

View File

@ -107,6 +107,8 @@ BlDisplayBootMenu()
BlpDrawBootMenuEntry(&Handle, MenuEntries[TopVisibleEntry + Index].EntryName,
Index, (TopVisibleEntry + Index) == HighligtedEntryId);
}
/* Clear redraw entries flag */
RedrawEntries = FALSE;
}
}
@ -198,6 +200,7 @@ BlDisplayBootMenu()
if(HighligtedEntryId > 0)
{
/* Highlight previous entry */
OldHighligtedEntryId = HighligtedEntryId;
HighligtedEntryId--;
/* Check if we need to scroll the view */
@ -214,9 +217,6 @@ BlDisplayBootMenu()
OldHighligtedEntryId - TopVisibleEntry, FALSE);
BlpDrawBootMenuEntry(&Handle, MenuEntries[HighligtedEntryId].EntryName,
HighligtedEntryId - TopVisibleEntry, TRUE);
/* Update old highlighted entry */
OldHighligtedEntryId = HighligtedEntryId;
}
}
else if(Key.ScanCode == 0x02)
@ -225,6 +225,7 @@ BlDisplayBootMenu()
if(HighligtedEntryId < NumberOfEntries - 1)
{
/* Highlight next entry */
OldHighligtedEntryId = HighligtedEntryId;
HighligtedEntryId++;
/* Check if we need to scroll the view */
@ -241,9 +242,6 @@ BlDisplayBootMenu()
OldHighligtedEntryId - TopVisibleEntry, FALSE);
BlpDrawBootMenuEntry(&Handle, MenuEntries[HighligtedEntryId].EntryName,
HighligtedEntryId - TopVisibleEntry, TRUE);
/* Update old highlighted entry */
OldHighligtedEntryId = HighligtedEntryId;
}
}
else if(Key.ScanCode == 0x09)
@ -326,7 +324,7 @@ BlDisplayBootMenu()
else if(Key.UnicodeChar == 0x65)
{
/* 'e' key pressed, edit the highlighted entry */
BlDisplayErrorDialog(L"XTLDR", L"Editing boot menu entries is not implemented yet!");
BlDisplayEditMenu(&MenuEntries[HighligtedEntryId]);
RedrawBootMenu = TRUE;
/* Break from boot menu event loop to redraw whole boot menu */
@ -379,6 +377,232 @@ BlDisplayBootMenu()
}
}
/**
* Displays a simple TUI-based edit menu.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTCDECL
VOID
BlDisplayEditMenu(IN PXTBL_BOOTMENU_ITEM MenuEntry)
{
ULONG HighligtedOptionId, Index, NumberOfOptions, OldHighligtedOptionId, TopVisibleEntry, VisibleEntries;
XTBL_DIALOG_HANDLE Handle;
BOOLEAN RedrawEditMenu, RedrawEntries;
EFI_INPUT_KEY Key;
UINT_PTR EventIndex;
PWCHAR NewValue, OptionName, OriginalValue, Value, ValueToEdit;
CONST PWCHAR *EditableOptions;
/* Draw edit menu */
BlpDrawEditMenu(&Handle);
/* Get the list of user editable options */
BlGetEditableOptions(&EditableOptions, &NumberOfOptions);
/* Calculate how many entries can be visible in the menu box */
VisibleEntries = Handle.Height - 2;
/* Assume the first option is highlighted by default */
HighligtedOptionId = 0;
OldHighligtedOptionId = 0;
TopVisibleEntry = 0;
/* Set redraw flags to not redraw the menu itself, but fill it with entries */
RedrawEditMenu = FALSE;
RedrawEntries = TRUE;
/* Infinite edit menu loop */
while(TRUE)
{
/* Redraw edit menu frame if requested */
if(RedrawEditMenu)
{
BlpDrawEditMenu(&Handle);
RedrawEditMenu = FALSE;
RedrawEntries = TRUE;
}
/* Sanity check to ensure we do not display more entries than possible */
if(VisibleEntries > NumberOfOptions)
{
VisibleEntries = NumberOfOptions;
}
/* Check if we need to redraw boot menu entries */
if(RedrawEntries)
{
/* Iterate through all menu entries */
for(Index = 0; Index < VisibleEntries; Index++)
{
/* Draw menu entry */
BlGetBootOptionValue(MenuEntry->Options, EditableOptions[TopVisibleEntry + Index], &Value);
BlpDrawEditMenuEntry(&Handle, EditableOptions[TopVisibleEntry + Index], Value, Index,
(TopVisibleEntry + Index) == HighligtedOptionId);
/* Free allocated value string if needed */
if(Value != NULL)
{
BlFreeMemoryPool(Value);
}
}
/* Clear redraw entries flag */
RedrawEntries = FALSE;
}
/* Wait for EFI event and read key stroke */
BlWaitForEfiEvent(1, &EfiSystemTable->ConIn->WaitForKey, &EventIndex);
BlReadKeyStroke(&Key);
/* Check key press scan code */
if(Key.UnicodeChar == 0x0D)
{
/* ENTER key pressed, edit the highlighted option */
OptionName = EditableOptions[HighligtedOptionId];
BlGetBootOptionValue(MenuEntry->Options, OptionName, &OriginalValue);
/* If the original value is NULL, use an empty string for editing */
if(OriginalValue == NULL)
{
ValueToEdit = L"";
}
else
{
ValueToEdit = OriginalValue;
}
/* Display input dialog to edit the option value */
NewValue = ValueToEdit;
BlDisplayInputDialog(OptionName, L"Enter new value:", &NewValue);
/* Check if the value was changed */
if(NewValue != ValueToEdit)
{
/* Update the boot option with the new value and free the old value */
BlSetBootOptionValue(MenuEntry->Options, OptionName, NewValue);
BlFreeMemoryPool(NewValue);
}
/* Free the original value if it was allocated */
if(OriginalValue != NULL)
{
BlFreeMemoryPool(OriginalValue);
}
/* Mark the edit menu for redraw */
RedrawEditMenu = TRUE;
}
else if(Key.ScanCode == 0x01)
{
/* UpArrow key pressed, go to previous entry if possible */
if(HighligtedOptionId > 0)
{
/* Highlight previous entry */
OldHighligtedOptionId = HighligtedOptionId;
HighligtedOptionId--;
/* Check if we need to scroll the view */
if(HighligtedOptionId < TopVisibleEntry)
{
/* Scroll the view */
TopVisibleEntry = HighligtedOptionId;
RedrawEntries = TRUE;
continue;
}
/* Redraw old highlighted entry */
BlGetBootOptionValue(MenuEntry->Options, EditableOptions[OldHighligtedOptionId], &Value);
BlpDrawEditMenuEntry(&Handle, EditableOptions[OldHighligtedOptionId], Value, OldHighligtedOptionId - TopVisibleEntry, FALSE);
/* Free allocated value string if needed */
if(Value != NULL)
{
BlFreeMemoryPool(Value);
}
/* Redraw new highlighted entry */
BlGetBootOptionValue(MenuEntry->Options, EditableOptions[HighligtedOptionId], &Value);
BlpDrawEditMenuEntry(&Handle, EditableOptions[HighligtedOptionId], Value, HighligtedOptionId - TopVisibleEntry, TRUE);
/* Free allocated value string if needed */
if(Value != NULL)
{
BlFreeMemoryPool(Value);
}
}
}
else if(Key.ScanCode == 0x02)
{
/* DownArrow key pressed, go to next entry if possible */
if(HighligtedOptionId < NumberOfOptions - 1)
{
/* Highlight next entry */
OldHighligtedOptionId = HighligtedOptionId;
HighligtedOptionId++;
/* Check if we need to scroll the view */
if(HighligtedOptionId >= TopVisibleEntry + VisibleEntries)
{
/* Scroll the view */
TopVisibleEntry = HighligtedOptionId - VisibleEntries + 1;
RedrawEntries = TRUE;
continue;
}
/* Redraw old highlighted entry */
BlGetBootOptionValue(MenuEntry->Options, EditableOptions[OldHighligtedOptionId], &Value);
BlpDrawEditMenuEntry(&Handle, EditableOptions[OldHighligtedOptionId], Value, OldHighligtedOptionId - TopVisibleEntry, FALSE);
/* Free allocated value string if needed */
if(Value != NULL)
{
BlFreeMemoryPool(Value);
}
/* Redraw new highlighted entry */
BlGetBootOptionValue(MenuEntry->Options, EditableOptions[HighligtedOptionId], &Value);
BlpDrawEditMenuEntry(&Handle, EditableOptions[HighligtedOptionId], Value, HighligtedOptionId - TopVisibleEntry, TRUE);
/* Free allocated value string if needed */
if(Value != NULL)
{
BlFreeMemoryPool(Value);
}
}
}
else if(Key.ScanCode == 0x09)
{
/* PageUp key pressed, go to top entry */
if(HighligtedOptionId != 0)
{
/* Highlight first entry */
HighligtedOptionId = 0;
TopVisibleEntry = 0;
RedrawEntries = TRUE;
}
}
else if(Key.ScanCode == 0x0A)
{
/* PageDown key pressed, go to bottom entry */
if(HighligtedOptionId != NumberOfOptions - 1)
{
/* Highlight last entry */
HighligtedOptionId = NumberOfOptions - 1;
TopVisibleEntry = (NumberOfOptions > VisibleEntries) ? (NumberOfOptions - VisibleEntries) : 0;
RedrawEntries = TRUE;
}
}
else if(Key.ScanCode == 0x17)
{
/* ESC key pressed, exit edit menu */
break;
}
}
}
/**
* Displays a red error dialog box with the specified caption and message.
*
@ -505,12 +729,11 @@ XTCDECL
VOID
BlDisplayInputDialog(IN PWCHAR Caption,
IN PWCHAR Message,
IN PWCHAR *InputFieldText)
IN OUT PWCHAR *InputFieldText)
{
SIZE_T InputFieldLength, TextCursorPosition, TextIndex, TextPosition;
XTBL_DIALOG_HANDLE Handle;
PWCHAR InputFieldBuffer;
SIZE_T BufferLength;
EFI_INPUT_KEY Key;
EFI_STATUS Status;
UINT_PTR Index;
@ -535,9 +758,11 @@ BlDisplayInputDialog(IN PWCHAR Caption,
Key.ScanCode = 0;
Key.UnicodeChar = 0;
/* Get initial input text length and allocate a buffer */
BufferLength = RtlWideStringLength(*InputFieldText, 0);
Status = BlAllocateMemoryPool(BufferLength * sizeof(WCHAR), (PVOID *)&InputFieldBuffer);
/* Determine input field length */
InputFieldLength = RtlWideStringLength(*InputFieldText, 0);
/* Allocate a buffer for storing the input field text */
Status = BlAllocateMemoryPool(2048 * sizeof(WCHAR), (PVOID *)&InputFieldBuffer);
if(Status != STATUS_EFI_SUCCESS)
{
/* Memory allocation failure, print error message and return */
@ -547,15 +772,8 @@ BlDisplayInputDialog(IN PWCHAR Caption,
}
/* Copy input text into edit buffer */
RtlCopyMemory(InputFieldBuffer, *InputFieldText, BufferLength * sizeof(WCHAR));
InputFieldBuffer[BufferLength] = L'\0';
/* Determine input field length */
InputFieldLength = BufferLength;
if(InputFieldLength > Handle.Width - 8)
{
InputFieldLength = Handle.Width - 8;
}
RtlCopyMemory(InputFieldBuffer, *InputFieldText, InputFieldLength * sizeof(WCHAR));
InputFieldBuffer[InputFieldLength] = L'\0';
/* Start at first character */
TextPosition = 0;
@ -618,12 +836,16 @@ BlDisplayInputDialog(IN PWCHAR Caption,
/* DELETE key pressed, delete character */
if(Handle.Attributes & XTBL_TUI_DIALOG_ACTIVE_INPUT)
{
/* Check if buffer is not empty */
if(InputFieldLength > 0 && TextPosition < InputFieldLength)
{
/* Delete character */
RtlMoveMemory(InputFieldBuffer + TextPosition, InputFieldBuffer + TextPosition + 1,
(InputFieldLength - TextPosition) * sizeof(WCHAR));
/* Decrement length and null terminate string */
InputFieldLength--;
InputFieldBuffer[InputFieldLength] = 0;
InputFieldBuffer[InputFieldLength] = L'\0';
}
}
}
@ -632,13 +854,17 @@ BlDisplayInputDialog(IN PWCHAR Caption,
/* BACKSPACE key pressed, delete character */
if(Handle.Attributes & XTBL_TUI_DIALOG_ACTIVE_INPUT)
{
/* Check if buffer is not empty */
if(InputFieldLength > 0 && TextPosition > 0 && TextPosition <= InputFieldLength)
{
TextPosition--;
/* Delete character */
RtlMoveMemory(InputFieldBuffer + TextPosition, InputFieldBuffer + TextPosition + 1,
(InputFieldLength - TextPosition) * sizeof(WCHAR));
/* Decrement length, position and null terminate string */
TextPosition--;
InputFieldLength--;
InputFieldBuffer[InputFieldLength] = 0;
InputFieldBuffer[InputFieldLength] = L'\0';
}
}
}
@ -653,15 +879,23 @@ BlDisplayInputDialog(IN PWCHAR Caption,
/* Other key pressed, add character to the buffer */
if(Handle.Attributes & XTBL_TUI_DIALOG_ACTIVE_INPUT && Key.UnicodeChar != 0)
{
RtlMoveMemory(InputFieldBuffer + TextPosition + 1, InputFieldBuffer + TextPosition,
(InputFieldLength - TextPosition) * sizeof(WCHAR));
InputFieldBuffer[TextPosition] = Key.UnicodeChar;
TextPosition++;
InputFieldLength++;
InputFieldBuffer[InputFieldLength] = 0;
/* Check if buffer is full */
if(InputFieldLength < 2047)
{
/* Insert character at current position */
RtlMoveMemory(InputFieldBuffer + TextPosition + 1, InputFieldBuffer + TextPosition,
(InputFieldLength - TextPosition) * sizeof(WCHAR));
InputFieldBuffer[TextPosition] = Key.UnicodeChar;
/* Increment length, position and null terminate string */
TextPosition++;
InputFieldLength++;
InputFieldBuffer[InputFieldLength] = L'\0';
}
}
}
/* Calculate text index and cursor position */
if(TextPosition > (Handle.Width - 9))
{
TextIndex = TextPosition - (Handle.Width - 9);
@ -677,6 +911,7 @@ BlDisplayInputDialog(IN PWCHAR Caption,
BlpDrawDialogButton(&Handle);
BlpDrawDialogInputField(&Handle, &InputFieldBuffer[TextIndex]);
/* Set cursor position if input field is active */
if(Handle.Attributes & XTBL_TUI_DIALOG_ACTIVE_INPUT)
{
BlSetCursorPosition(Handle.PosX + 4 + TextCursorPosition, Handle.PosY + Handle.Height - 4);
@ -1395,3 +1630,164 @@ BlpDrawDialogProgressBar(IN PXTBL_DIALOG_HANDLE Handle,
BlDisableConsoleCursor();
BlConsoleWrite(ProgressBar);
}
/**
* Draws a text-based boot edition menu.
*
* @param Handle
* Supplies a pointer to the edition menu handle.
*
* @return This function does not return any value.
*
* @since XT 1.0
*/
XTCDECL
VOID
BlpDrawEditMenu(OUT PXTBL_DIALOG_HANDLE Handle)
{
/* Query console screen resolution */
BlQueryConsoleMode(&Handle->ResX, &Handle->ResY);
/* Set boot menu parameters */
Handle->Attributes = 0;
Handle->DialogColor = EFI_TEXT_BGCOLOR_BLACK;
Handle->TextColor = EFI_TEXT_FGCOLOR_LIGHTGRAY;
Handle->PosX = 3;
Handle->PosY = 3;
Handle->Width = Handle->ResX - 6;
Handle->Height = Handle->ResY - 10;
/* Clear screen and disable cursor */
BlSetConsoleAttributes(Handle->DialogColor | Handle->TextColor);
BlClearConsoleScreen();
BlDisableConsoleCursor();
/* Check if debugging enabled */
if(DEBUG)
{
/* Print debug version of XTLDR banner */
BlSetCursorPosition((Handle->ResX - 44) / 2, 1);
BlConsolePrint(L"XTLDR Boot Loader v%d.%d (%s-%s)\n",
XTLDR_VERSION_MAJOR, XTLDR_VERSION_MINOR, XTOS_VERSION_DATE, XTOS_VERSION_HASH);
}
else
{
/* Print standard XTLDR banner */
BlSetCursorPosition((Handle->ResX - 22) / 2, 1);
BlConsolePrint(L"XTLDR Boot Loader v%d.%d\n", XTLDR_VERSION_MAJOR, XTLDR_VERSION_MINOR);
}
/* Draw empty dialog box for boot menu */
BlpDrawDialogBox(Handle, L"Edit Options", NULL);
/* Print help message below the edit menu */
BlSetCursorPosition(0, Handle->PosY + Handle->Height);
BlSetConsoleAttributes(EFI_TEXT_BGCOLOR_BLACK | EFI_TEXT_FGCOLOR_LIGHTGRAY);
BlConsolePrint(L" Use cursors to change the selection. Press ENTER key to edit the chosen\n"
L" option or ESC to return to the main boot menu.");
}
/**
* Draws edit menu entry at the specified position.
*
* @param Handle
* Supplies a pointer to the boot menu handle.
*
* @param OptionName
* Supplies a pointer to the buffer containing a part of the menu entry name (an option name).
*
* @param OptionValue
* Supplies a pointer to the buffer containing a part of the menu entry name (an option value).
*
* @param Position
* Specifies entry position on the list in the boot menu.
*
* @param Highlighted
* Specifies whether this entry should be highlighted or not.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTCDECL
EFI_STATUS
BlpDrawEditMenuEntry(IN PXTBL_DIALOG_HANDLE Handle,
IN PWCHAR OptionName,
IN PWCHAR OptionValue,
IN UINT Position,
IN BOOLEAN Highlighted)
{
BOOLEAN Allocation;
PWCHAR DisplayValue, ShortValue;
UINT Index;
ULONG OptionNameLength, OptionValueLength, OptionWidth;
EFI_STATUS Status;
/* Assume no allocation was made */
Allocation = FALSE;
/* Set display value depending on input */
DisplayValue = (OptionValue != NULL) ? OptionValue : L"";
/* Determine lengths */
OptionNameLength = RtlWideStringLength(OptionName, 0);
OptionValueLength = RtlWideStringLength(DisplayValue, 0);
OptionWidth = Handle->Width - 4 - (OptionNameLength + 2);
/* Check if value needs to be truncated */
if(OptionValueLength > OptionWidth)
{
/* Allocate buffer for new, shortened value */
Status = BlAllocateMemoryPool((OptionWidth + 1) * sizeof(WCHAR), (PVOID *)&ShortValue);
if(Status != STATUS_EFI_SUCCESS)
{
/* Memory allocation failure, print debug message and return */
BlDebugPrint(L"ERROR: Memory allocation failure (Status Code: 0x%zX)\n", Status);
return Status;
}
/* Copy a desired value length into the allocated buffer and append "..." */
RtlCopyMemory(ShortValue, DisplayValue, (OptionWidth - 3) * sizeof(WCHAR));
RtlCopyMemory(ShortValue + OptionWidth - 3, L"...", 3 * sizeof(WCHAR));
ShortValue[OptionWidth] = L'\0';
/* Mark that allocation was made and set new display value */
Allocation = TRUE;
DisplayValue = ShortValue;
}
/* Move cursor to the right position */
BlSetCursorPosition(5, 4 + Position);
/* Check whether this entry should be highlighted */
if(Highlighted)
{
/* Highlight this entry */
BlSetConsoleAttributes(EFI_TEXT_BGCOLOR_LIGHTGRAY | EFI_TEXT_FGCOLOR_BLACK);
}
else
{
/* Set default colors */
BlSetConsoleAttributes(EFI_TEXT_BGCOLOR_BLACK | EFI_TEXT_FGCOLOR_LIGHTGRAY);
}
/* Clear menu entry */
for(Index = 0; Index < Handle->Width - 4; Index++)
{
BlConsolePrint(L" ");
}
/* Print menu entry */
BlSetCursorPosition(5, 4 + Position);
BlConsolePrint(L"%S: %S", OptionName, DisplayValue);
/* Check if allocation was made */
if(Allocation)
{
/* Free allocated memory */
BlFreeMemoryPool(DisplayValue);
}
/* Return success */
return STATUS_EFI_SUCCESS;
}