New menu system and editor

This commit is contained in:
Carver 2021-07-06 16:27:07 -07:00
parent ae2d924c14
commit 34825df41b
2 changed files with 160 additions and 16 deletions

View File

@ -67,6 +67,9 @@ Some keys take *URIs* as values; these are described in the next section.
* `BACKGROUND_STYLE` - The style which will be used to display the background image. Either `tiled` or `centered`. Default is `tiled`.
* `BACKDROP_COLOUR` - When the background style is `centered`, this specifies the colour of the backdrop for parts of the screen not covered by the background image, in RRGGBB format.
* `BACKDROP_COLOR` - Alias of `BACKDROP_COLOUR`.
* `EDITOR_ENABLED` - If set to `no`, the editor will not be accessible. Defaults to `yes`.
* `EDITOR_HIGHLIGHTING` - If set to `no`, syntax highlighting in the editor will be disabled. Defaults to `yes`.
* `EDITOR_VALIDATION` - If set to `no`, the editor will not alert you about invalid keys / syntax errors. Defaults to `yes`.
* `VERBOSE` - If set to `yes`, print additional information during boot. Defaults to not verbose.
*Locally assignable (non protocol specific)* keys are:

View File

@ -16,6 +16,10 @@
static char *menu_branding = NULL;
#define EDITOR_MAX_BUFFER_SIZE 4096
#define TOK_KEY 0
#define TOK_EQUALS 1
#define TOK_VALUE 2
#define TOK_BADKEY 3
static size_t get_line_offset(size_t *displacement, size_t index, const char *buffer) {
size_t offset = 0;
@ -67,7 +71,70 @@ static size_t get_prev_line(size_t index, const char *buffer) {
return offset;
}
static char *config_entry_editor(const char *orig_entry) {
static const char *VALID_KEYS[] = {
"TIMEOUT",
"DEFAULT_ENTRY",
"GRAPHICS",
"MENU_RESOLUTION",
"MENU_BRANDING",
"MENU_FONT",
"TERMINAL_FONT",
"THEME_COLOURS",
"THEME_COLORS",
"THEME_BACKGROUND",
"THEME_FOREGROUND",
"THEME_MARGIN",
"THEME_MARGIN_GRADIENT",
"BACKGROUND_PATH",
"BACKGROUND_STYLE",
"BACKDROP_COLOUR",
"BACKDROP_COLOR",
"EDITOR_ENABLED",
"EDITOR_HIGHLIGHTING",
"EDITOR_VALIDATION",
"VERBOSE",
"PROTOCOL",
"CMDLINE",
"KERNEL_CMDLINE",
"KERNEL_PATH",
"MODULE_PATH",
"MODULE_STRING",
"RESOLUTION",
"KASLR",
"DRIVE",
"PARTITION",
"IMAGE_PATH",
NULL
};
static bool validation_enabled = true;
static bool invalid_syntax = false;
static int validate_line(const char *buffer) {
if (!validation_enabled) return TOK_KEY;
char keybuf[64];
int i;
for (i = 0; buffer[i] && i < 64; i++) {
if (buffer[i] == '=') goto found_equals;
keybuf[i] = buffer[i];
}
fail:
if (i < 64) keybuf[i] = 0;
if (keybuf[0] == '\n' || (!keybuf[0] && buffer[0] != '=')) return TOK_KEY; // blank line is valid
invalid_syntax = 1;
return TOK_BADKEY;
found_equals:
if (i < 64) keybuf[i] = 0;
for (i = 0; VALID_KEYS[i]; i++) {
//print("'%s'", keybuf);
if (!strcmp(keybuf, VALID_KEYS[i])) {
return TOK_KEY;
}
}
goto fail;
}
static char *config_entry_editor(const char *title, const char *orig_entry) {
size_t cursor_offset = 0;
size_t entry_size = strlen(orig_entry);
size_t _window_size = term_rows - 11;
@ -82,9 +149,18 @@ static char *config_entry_editor(const char *orig_entry) {
entry_size--;
}
if (entry_size >= EDITOR_MAX_BUFFER_SIZE)
if (entry_size >= EDITOR_MAX_BUFFER_SIZE) {
panic("Entry is too big to be edited.");
}
bool syntax_highlighting_enabled = true;
char *syntax_highlighting_enabled_config = config_get_value(NULL, 0, "EDITOR_HIGHLIGHTING");
if (!strcmp(syntax_highlighting_enabled_config, "no")) syntax_highlighting_enabled = false;
validation_enabled = true;
char *validation_enabled_config = config_get_value(NULL, 0, "EDITOR_VALIDATION");
if (!strcmp(validation_enabled_config, "no")) validation_enabled = false;
static char *buffer = NULL;
if (buffer == NULL)
buffer = ext_mem_alloc(EDITOR_MAX_BUFFER_SIZE);
@ -92,12 +168,15 @@ static char *config_entry_editor(const char *orig_entry) {
buffer[entry_size] = 0;
refresh:
invalid_syntax = 0;
clear(true);
disable_cursor();
print("\n\n \e[36m %s \e[37m\n\n\n", menu_branding);
print("Editing entry.\n");
print("Press esc to return to main menu and discard changes, press F10 to boot.\n");
//print("Editing \"%s\"\n", title);
print(" \e[32mESC\e[0m Discard and Exit \e[32mF10\e[0m Boot\n");
print("\n\xda");
for (int i = 0; i < term_cols - 2; i++) {
@ -109,7 +188,12 @@ refresh:
}
// FALLTHRU
default:
print("\xc4");
if (i > term_cols / 2 - (int)strlen(title) && i < term_cols / 2) {
print(title);
i += strlen(title) - 1;
} else {
print("\xc4");
}
}
}
print("\xbf\xb3");
@ -117,7 +201,9 @@ refresh:
int cursor_x, cursor_y;
size_t current_line = 0, line_offset = 0, window_size = _window_size;
bool printed_cursor = false;
int token_type = validate_line(buffer);
for (size_t i = 0; ; i++) {
// newline
if (buffer[i] == '\n'
&& current_line < window_offset + window_size
&& current_line >= window_offset) {
@ -134,6 +220,7 @@ refresh:
else
print("\xb3\xb3");
line_offset = 0;
token_type = validate_line(buffer+i+1);
current_line++;
continue;
}
@ -175,9 +262,47 @@ refresh:
if (current_line >= window_offset) {
line_offset++;
print("%c", buffer[i]);
// switch to token type 1 if equals sign
if (token_type == TOK_KEY && buffer[i] == '=') token_type = TOK_EQUALS;
// syntax highlighting
if (syntax_highlighting_enabled) {
switch (token_type) {
case TOK_KEY:
print("\e[36m%c\e[0m", buffer[i]);
break;
case TOK_EQUALS:
print("\e[32m%c\e[0m", buffer[i]);
break;
case TOK_VALUE:
print("\e[39m%c\e[0m", buffer[i]);
break;
case TOK_BADKEY:
print("\e[31m%c\e[0m", buffer[i]);
break;
}
} else {
print("%c", buffer[i]);
}
// switch to token type 2 after equals sign
if (token_type == TOK_EQUALS) token_type = TOK_VALUE;
}
}
// syntax error alert
if (validation_enabled) {
int x, y;
get_cursor_pos(&x, &y);
set_cursor_pos(0, term_rows-2);
if (invalid_syntax) {
print("\e[31mConfiguration is INVALID.\e[0m");
} else {
print("\e[32mConfiguration is valid.\e[0m");
}
set_cursor_pos(x, y);
}
if (current_line - window_offset < window_size) {
int x, y;
@ -348,6 +473,10 @@ char *menu(char **cmdline) {
else
timeout = strtoui(timeout_config, NULL, 10);
}
bool editor_enabled = true;
char *editor_enabled_config = config_get_value(NULL, 0, "EDITOR_ENABLED");
if (!strcmp(editor_enabled_config, "no")) editor_enabled = false;
if (!timeout) {
// Use print tree to load up selected_menu_entry and determine if the
@ -394,7 +523,7 @@ refresh:
getchar();
char *new_body = NULL;
while (new_body == NULL)
new_body = config_entry_editor("");
new_body = config_entry_editor("New Entry", "");
selected_menu_entry = ext_mem_alloc(sizeof(struct menu_entry));
selected_menu_entry->body = new_body;
config_ready = true;
@ -406,7 +535,17 @@ refresh:
int max_entries = print_tree(0, 0, selected_entry, menu_tree,
&selected_menu_entry);
print("\nArrows to select, enter to boot, 'e' to edit selected entry.");
{
int x, y;
get_cursor_pos(&x, &y);
set_cursor_pos(0, 4);
if (editor_enabled && selected_menu_entry->sub == NULL) {
print("\n \e[32mARROWS\e[0m Select \e[32mENTER\e[0m Boot \e[32mE\e[0m Edit");
} else {
print("\n \e[32mARROWS\e[0m Select \e[32mENTER\e[0m Boot");
}
set_cursor_pos(x, y);
}
if (selected_menu_entry->sub != NULL)
skip_timeout = true;
@ -461,14 +600,16 @@ timeout_aborted:
term_double_buffer(false);
return selected_menu_entry->body;
case 'e': {
if (selected_menu_entry->sub != NULL)
goto refresh;
enable_cursor();
char *new_body = config_entry_editor(selected_menu_entry->body);
if (new_body == NULL)
goto refresh;
selected_menu_entry->body = new_body;
goto autoboot;
if (editor_enabled) {
if (selected_menu_entry->sub != NULL)
goto refresh;
enable_cursor();
char *new_body = config_entry_editor(selected_menu_entry->name, selected_menu_entry->body);
if (new_body == NULL)
goto refresh;
selected_menu_entry->body = new_body;
goto autoboot;
}
}
}
}