rulimine/common/menu.c

975 lines
29 KiB
C
Raw Normal View History

2020-06-05 21:27:52 +03:00
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
2021-12-31 12:58:05 +03:00
#include <stdnoreturn.h>
#include <config.h>
2020-06-05 21:27:52 +03:00
#include <menu.h>
#include <lib/print.h>
#include <lib/blib.h>
#include <lib/libc.h>
#include <lib/config.h>
2020-09-02 10:55:56 +03:00
#include <lib/term.h>
#include <lib/gterm.h>
2020-10-27 10:09:27 +03:00
#include <lib/readline.h>
2020-11-02 11:20:34 +03:00
#include <lib/uri.h>
2020-09-20 13:03:44 +03:00
#include <mm/pmm.h>
#include <drivers/vbe.h>
2021-10-09 14:32:57 +03:00
#include <console.h>
#include <protos/stivale.h>
#include <protos/stivale2.h>
#include <protos/linux.h>
#include <protos/chainload.h>
#include <protos/multiboot1.h>
#include <protos/multiboot2.h>
2020-06-05 21:27:52 +03:00
static char *menu_branding = NULL;
static char *menu_branding_colour = NULL;
2020-11-18 02:13:54 +03:00
#define EDITOR_MAX_BUFFER_SIZE 4096
2021-07-07 02:27:07 +03:00
#define TOK_KEY 0
#define TOK_EQUALS 1
#define TOK_VALUE 2
#define TOK_BADKEY 3
#define TOK_COMMENT 4
2020-11-18 02:13:54 +03:00
static size_t get_line_offset(size_t *displacement, size_t index, const char *buffer) {
size_t offset = 0;
size_t _index = index;
for (size_t i = 0; buffer[i]; i++) {
if (!_index--)
break;
if (buffer[i] == '\n')
offset = i + 1;
}
if (displacement)
*displacement = index - offset;
return offset;
}
static size_t get_line_length(size_t index, const char *buffer) {
size_t i;
for (i = index; buffer[i] != '\n' && buffer[i] != 0; i++);
return i - index;
}
static size_t get_next_line(size_t index, const char *buffer) {
if (buffer[index] == 0)
return index;
size_t displacement;
get_line_offset(&displacement, index, buffer);
while (buffer[index] != '\n') {
if (buffer[index] == 0)
return index;
index++;
}
index++;
2020-11-18 02:13:54 +03:00
size_t next_line_length = get_line_length(index, buffer);
if (displacement > next_line_length)
displacement = next_line_length;
return index + displacement;
}
static size_t get_prev_line(size_t index, const char *buffer) {
size_t offset, displacement, prev_line_offset, prev_line_length;
offset = get_line_offset(&displacement, index, buffer);
if (offset) {
prev_line_offset = get_line_offset(NULL, offset - 1, buffer);
prev_line_length = get_line_length(prev_line_offset, buffer);
if (displacement > prev_line_length)
displacement = prev_line_length;
return prev_line_offset + displacement;
}
return offset;
}
2021-07-07 02:27:07 +03:00
static const char *VALID_KEYS[] = {
"TIMEOUT",
"QUIET",
2021-07-07 02:27:07 +03:00
"DEFAULT_ENTRY",
"GRAPHICS",
"MENU_RESOLUTION",
"MENU_BRANDING",
"MENU_BRANDING_COLOUR",
"MENU_BRANDING_COLOR",
2021-07-07 02:27:07 +03:00
"MENU_FONT",
2021-08-18 15:06:55 +03:00
"MENU_FONT_SIZE",
"MENU_FONT_SCALE",
"MENU_FONT_SPACING",
2021-07-07 02:27:07 +03:00
"TERMINAL_FONT",
"TERMINAL_FONT_SIZE",
"TERMINAL_FONT_SCALE",
"TERMINAL_FONT_SPACING",
2021-07-07 02:27:07 +03:00
"THEME_COLOURS",
"THEME_COLORS",
2021-08-17 01:58:02 +03:00
"THEME_BRIGHT_COLOURS",
"THEME_BRIGHT_COLORS",
2021-07-07 02:27:07 +03:00
"THEME_BACKGROUND",
"THEME_FOREGROUND",
"THEME_MARGIN",
"THEME_MARGIN_GRADIENT",
"BACKGROUND_PATH",
"BACKGROUND_STYLE",
"BACKDROP_COLOUR",
"BACKDROP_COLOR",
"EDITOR_ENABLED",
"EDITOR_HIGHLIGHTING",
"EDITOR_VALIDATION",
"VERBOSE",
2021-08-17 01:58:02 +03:00
"RANDOMISE_MEMORY",
"RANDOMIZE_MEMORY",
"COMMENT",
2021-07-07 02:27:07 +03:00
"PROTOCOL",
"CMDLINE",
"KERNEL_CMDLINE",
"KERNEL_PATH",
"INITRD_PATH",
2021-07-07 02:27:07 +03:00
"MODULE_PATH",
"MODULE_STRING",
"RESOLUTION",
2021-08-17 01:58:02 +03:00
"TEXTMODE",
2021-07-07 02:27:07 +03:00
"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;
if (buffer[0] == '#')
return TOK_COMMENT;
2021-07-07 02:27:07 +03:00
char keybuf[64];
size_t i;
2021-07-07 02:27:07 +03:00
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
2021-07-07 02:56:01 +03:00
invalid_syntax = true;
2021-07-07 02:27:07 +03:00
return TOK_BADKEY;
found_equals:
if (i < 64) keybuf[i] = 0;
for (i = 0; VALID_KEYS[i]; i++) {
if (!strcmp(keybuf, VALID_KEYS[i])) {
return TOK_KEY;
}
}
goto fail;
}
2021-09-25 05:52:18 +03:00
static void putchar_tokencol(int type, char c) {
switch (type) {
case TOK_KEY:
print("\e[36m%c\e[0m", c);
break;
case TOK_EQUALS:
print("\e[32m%c\e[0m", c);
break;
default:
case TOK_VALUE:
print("\e[39m%c\e[0m", c);
break;
case TOK_BADKEY:
print("\e[31m%c\e[0m", c);
break;
case TOK_COMMENT:
print("\e[33m%c\e[0m", c);
break;
}
}
static bool editor_no_term_reset = false;
2021-12-12 00:22:48 +03:00
char *config_entry_editor(const char *title, const char *orig_entry) {
term_autoflush = false;
enable_cursor();
print("\e[2J\e[H");
2021-12-12 00:22:48 +03:00
size_t cursor_offset = 0;
size_t entry_size = strlen(orig_entry);
2021-07-07 05:23:34 +03:00
size_t _window_size = term_rows - 8;
size_t window_offset = 0;
size_t line_size = term_cols - 2;
2020-11-18 02:13:54 +03:00
bool display_overflow_error = false;
2020-11-18 02:13:54 +03:00
// Skip leading newlines
while (*orig_entry == '\n') {
orig_entry++;
entry_size--;
}
2021-07-07 02:27:07 +03:00
if (entry_size >= EDITOR_MAX_BUFFER_SIZE) {
2021-12-11 21:58:00 +03:00
panic(true, "Entry is too big to be edited.");
2021-07-07 02:27:07 +03:00
}
2020-11-18 02:13:54 +03:00
2021-07-07 02:27:07 +03:00
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;
2021-07-07 02:27:07 +03:00
validation_enabled = true;
char *validation_enabled_config = config_get_value(NULL, 0, "EDITOR_VALIDATION");
if (!strcmp(validation_enabled_config, "no")) validation_enabled = false;
char *buffer = ext_mem_alloc(EDITOR_MAX_BUFFER_SIZE);
2020-11-18 02:13:54 +03:00
memcpy(buffer, orig_entry, entry_size);
buffer[entry_size] = 0;
refresh:
2021-07-07 02:56:01 +03:00
invalid_syntax = false;
print("\e[2J\e[H");
2020-11-18 02:13:54 +03:00
disable_cursor();
2021-07-07 05:23:34 +03:00
{
size_t x, y;
2021-07-07 05:23:34 +03:00
print("\n");
get_cursor_pos(&x, &y);
set_cursor_pos_helper(term_cols / 2 - DIV_ROUNDUP(strlen(menu_branding), 2), y);
print("\e[3%sm%s\e[37m", menu_branding_colour, menu_branding);
2021-07-07 05:23:34 +03:00
print("\n\n");
}
2021-07-07 02:27:07 +03:00
print(" \e[32mESC\e[0m Discard and Exit \e[32mF10\e[0m Boot\n\n");
2020-11-18 02:13:54 +03:00
print(serial ? "/" : "\xda");
for (size_t i = 0; i < term_cols - 2; i++) {
switch (i) {
case 1: case 2: case 3:
if (window_offset > 0) {
print(serial ? "^" : "\x18");
break;
}
// FALLTHRU
2021-07-07 02:56:01 +03:00
default: {
size_t title_length = strlen(title);
2021-07-15 13:51:46 +03:00
if (i == (term_cols / 2) - DIV_ROUNDUP(title_length, 2) - 1) {
2021-07-07 02:56:01 +03:00
print("%s", title);
i += title_length - 1;
2021-07-07 02:27:07 +03:00
} else {
print(serial ? "-" : "\xc4");
2021-07-07 02:27:07 +03:00
}
2021-07-07 02:56:01 +03:00
}
}
}
size_t tmpx, tmpy;
get_cursor_pos(&tmpx, &tmpy);
print(serial ? "\\" : "\xbf");
set_cursor_pos_helper(0, tmpy + 1);
print(serial ? "|" : "\xb3");
2020-11-18 02:13:54 +03:00
size_t cursor_x, cursor_y;
size_t current_line = 0, line_offset = 0, window_size = _window_size;
bool printed_cursor = false;
2021-09-25 05:52:18 +03:00
bool printed_early = false;
2021-07-07 02:27:07 +03:00
int token_type = validate_line(buffer);
2020-11-18 02:13:54 +03:00
for (size_t i = 0; ; i++) {
2021-07-07 02:27:07 +03:00
// newline
if (buffer[i] == '\n'
&& current_line < window_offset + window_size
&& current_line >= window_offset) {
size_t x, y;
get_cursor_pos(&x, &y);
if (i == cursor_offset) {
cursor_x = x;
cursor_y = y;
printed_cursor = true;
}
set_cursor_pos_helper(term_cols - 1, y);
if (current_line == window_offset + window_size - 1) {
get_cursor_pos(&tmpx, &tmpy);
print(serial ? "|" : "\xb3");
set_cursor_pos_helper(0, tmpy + 1);
print(serial ? "\\" : "\xc0");
} else {
get_cursor_pos(&tmpx, &tmpy);
print(serial ? "|" : "\xb3");
set_cursor_pos_helper(0, tmpy + 1);
print(serial ? "|" : "\xb3");
}
line_offset = 0;
2021-09-25 05:52:18 +03:00
token_type = validate_line(buffer + i + 1);
current_line++;
continue;
}
2021-09-25 05:52:18 +03:00
// switch to token type 1 if equals sign
if (token_type == TOK_KEY && buffer[i] == '=') token_type = TOK_EQUALS;
if (buffer[i] != 0 && line_offset % line_size == line_size - 1) {
if (current_line < window_offset + window_size
&& current_line >= window_offset) {
if (i == cursor_offset) {
get_cursor_pos(&cursor_x, &cursor_y);
printed_cursor = true;
}
2021-09-25 05:52:18 +03:00
if (syntax_highlighting_enabled) {
putchar_tokencol(token_type, buffer[i]);
} else {
print("%c", buffer[i]);
}
printed_early = true;
size_t x, y;
get_cursor_pos(&x, &y);
if (y == term_rows - 3) {
print(serial ? ">" : "\x1a");
set_cursor_pos_helper(0, y + 1);
print(serial ? "\\" : "\xc0");
} else {
print(serial ? ">" : "\x1a");
set_cursor_pos_helper(0, y + 1);
print(serial ? "<" : "\x1b\x1b");
}
2021-09-25 05:52:18 +03:00
}
window_size--;
}
if (i == cursor_offset
&& current_line < window_offset + window_size
&& current_line >= window_offset
&& !printed_cursor) {
2020-11-18 02:13:54 +03:00
get_cursor_pos(&cursor_x, &cursor_y);
printed_cursor = true;
}
if (buffer[i] == 0 || current_line >= window_offset + window_size) {
if (!printed_cursor) {
if (i <= cursor_offset) {
window_offset++;
goto refresh;
}
if (i > cursor_offset) {
window_offset--;
goto refresh;
}
}
2020-11-18 02:13:54 +03:00
break;
}
if (buffer[i] == '\n') {
line_offset = 0;
2021-09-25 05:52:18 +03:00
token_type = validate_line(buffer + i + 1);
current_line++;
continue;
}
if (current_line >= window_offset) {
line_offset++;
2021-07-07 02:27:07 +03:00
// syntax highlighting
2021-09-25 05:52:18 +03:00
if (!printed_early) {
if (syntax_highlighting_enabled) {
putchar_tokencol(token_type, buffer[i]);
} else {
print("%c", buffer[i]);
}
}
printed_early = false;
// switch to token type 2 after equals sign
if (token_type == TOK_EQUALS) token_type = TOK_VALUE;
}
}
2021-07-07 02:27:07 +03:00
// syntax error alert
if (validation_enabled) {
size_t x, y;
2021-07-07 02:27:07 +03:00
get_cursor_pos(&x, &y);
set_cursor_pos_helper(0, term_rows-1);
scroll_disable();
2021-07-07 02:27:07 +03:00
if (invalid_syntax) {
print("\e[31mConfiguration is INVALID.\e[0m");
} else {
print("\e[32mConfiguration is valid.\e[0m");
}
scroll_enable();
set_cursor_pos_helper(x, y);
2021-07-07 02:27:07 +03:00
}
2020-11-18 02:13:54 +03:00
if (current_line - window_offset < window_size) {
size_t x, y;
for (size_t i = 0; i < (window_size - (current_line - window_offset)) - 1; i++) {
get_cursor_pos(&x, &y);
set_cursor_pos_helper(term_cols - 1, y);
print(serial ? "|" : "\xb3");
set_cursor_pos_helper(0, y + 1);
print(serial ? "|" : "\xb3");
}
get_cursor_pos(&x, &y);
set_cursor_pos_helper(term_cols - 1, y);
print(serial ? "|" : "\xb3");
set_cursor_pos_helper(0, y + 1);
print(serial ? "\\" : "\xc0");
2020-11-18 02:13:54 +03:00
}
for (size_t i = 0; i < term_cols - 2; i++) {
switch (i) {
case 1: case 2: case 3:
if (current_line - window_offset >= window_size) {
print(serial ? "v" : "\x19");
break;
}
// FALLTHRU
default:
print(serial ? "-" : "\xc4");
}
}
get_cursor_pos(&tmpx, &tmpy);
print(serial ? "/" : "\xd9");
set_cursor_pos_helper(0, tmpy + 1);
2020-11-18 02:13:54 +03:00
if (display_overflow_error) {
scroll_disable();
2021-07-07 05:23:34 +03:00
print("\e[31mText buffer not big enough, delete something instead.");
scroll_enable();
display_overflow_error = false;
}
2020-11-18 02:13:54 +03:00
// Hack to redraw the cursor
set_cursor_pos_helper(cursor_x, cursor_y);
2020-11-18 02:13:54 +03:00
enable_cursor();
2020-11-20 21:55:18 +03:00
term_double_buffer_flush();
2020-11-18 02:13:54 +03:00
int c = getchar();
switch (c) {
case GETCHAR_CURSOR_DOWN:
cursor_offset = get_next_line(cursor_offset, buffer);
break;
case GETCHAR_CURSOR_UP:
cursor_offset = get_prev_line(cursor_offset, buffer);
break;
case GETCHAR_CURSOR_LEFT:
if (cursor_offset) {
cursor_offset--;
}
break;
case GETCHAR_CURSOR_RIGHT:
if (cursor_offset < strlen(buffer)) {
cursor_offset++;
}
break;
case GETCHAR_HOME: {
size_t displacement;
get_line_offset(&displacement, cursor_offset, buffer);
cursor_offset -= displacement;
break;
}
case GETCHAR_END: {
cursor_offset += get_line_length(cursor_offset, buffer);
break;
}
2020-11-18 02:13:54 +03:00
case '\b':
if (cursor_offset) {
cursor_offset--;
case GETCHAR_DELETE:
for (size_t i = cursor_offset; ; i++) {
buffer[i] = buffer[i+1];
if (!buffer[i])
break;
}
}
break;
case GETCHAR_F10:
editor_no_term_reset ? editor_no_term_reset = false : reset_term();
2020-11-18 02:13:54 +03:00
return buffer;
case GETCHAR_ESCAPE:
pmm_free(buffer, EDITOR_MAX_BUFFER_SIZE);
editor_no_term_reset ? editor_no_term_reset = false : reset_term();
return NULL;
2020-11-18 02:13:54 +03:00
default:
if (strlen(buffer) < EDITOR_MAX_BUFFER_SIZE - 1) {
if (isprint(c) || c == '\n') {
for (size_t i = strlen(buffer); ; i--) {
buffer[i+1] = buffer[i];
if (i == cursor_offset)
break;
}
buffer[cursor_offset++] = c;
2020-11-18 02:13:54 +03:00
}
} else {
display_overflow_error = true;
2020-11-18 02:13:54 +03:00
}
break;
}
goto refresh;
}
static size_t print_tree(const char *shift, size_t level, size_t base_index, size_t selected_entry,
struct menu_entry *current_entry,
struct menu_entry **selected_menu_entry) {
size_t max_entries = 0;
bool no_print = false;
if (shift == NULL) {
no_print = true;
}
for (;;) {
if (current_entry == NULL)
break;
if (!no_print) print("%s", shift);
2020-11-17 02:50:26 +03:00
if (level) {
2021-08-17 01:00:23 +03:00
for (size_t i = level - 1; i > 0; i--) {
2020-11-17 02:50:26 +03:00
struct menu_entry *actual_parent = current_entry;
for (size_t j = 0; j < i; j++)
2020-11-17 02:50:26 +03:00
actual_parent = actual_parent->parent;
if (actual_parent->next != NULL) {
if (!no_print) print(serial ? " |" : " \xb3");
} else {
if (!no_print) print(" ");
}
}
if (current_entry->next == NULL) {
if (!no_print) print(serial ? " `" : " \xc0");
} else {
if (!no_print) print(serial ? " |" : " \xc3");
2020-11-17 02:50:26 +03:00
}
}
if (current_entry->sub) {
if (!no_print) print(current_entry->expanded ? "[-]" : "[+]");
} else if (level) {
if (!no_print) print(serial ? "-> " : "\xc4> ");
} else {
if (!no_print) print(" ");
}
if (base_index + max_entries == selected_entry) {
*selected_menu_entry = current_entry;
if (!no_print) print("\e[47m\e[30m");
}
if (!no_print) print(" %s \e[0m\n", current_entry->name);
if (current_entry->sub && current_entry->expanded) {
2021-07-07 05:23:34 +03:00
max_entries += print_tree(shift, level + 1, base_index + max_entries + 1,
selected_entry,
current_entry->sub,
selected_menu_entry);
}
max_entries++;
current_entry = current_entry->next;
}
return max_entries;
}
2020-06-05 21:27:52 +03:00
#if defined (__x86_64__)
__attribute__((used))
static uintptr_t stack_at_first_entry = 0;
#endif
2021-12-31 12:58:05 +03:00
__attribute__((naked))
noreturn void menu(__attribute__((unused)) bool timeout_enabled) {
#if defined (__i386__)
asm volatile (
2021-12-11 21:58:00 +03:00
"pop %eax\n\t"
"call 1f\n\t"
"1:\n\t"
"pop %eax\n\t"
"add $(2f - 1b), %eax\n\t"
"cmpl $0, (%eax)\n\t"
"jne 1f\n\t"
"mov %esp, (%eax)\n\t"
2021-12-11 21:58:00 +03:00
"jmp 3f\n\t"
"1:\n\t"
"mov (%esp), %edi\n\t"
"mov (%eax), %esp\n\t"
"push %edi\n\t"
2021-12-11 21:58:00 +03:00
"jmp 3f\n\t"
"2:\n\t"
2021-12-11 21:58:00 +03:00
".long 0\n\t"
"3:\n\t"
"push $0\n\t"
"jmp _menu"
);
#elif defined (__x86_64__)
asm volatile (
"xor %eax, %eax\n\t"
"cmp %rax, stack_at_first_entry(%rip)\n\t"
"jne 1f\n\t"
"mov %rsp, stack_at_first_entry(%rip)\n\t"
2021-12-11 21:58:00 +03:00
"jmp 2f\n\t"
"1:\n\t"
"mov stack_at_first_entry(%rip), %rsp\n\t"
2021-12-11 21:58:00 +03:00
"2:\n\t"
"push $0\n\t"
2021-12-12 20:30:41 +03:00
"push $0\n\t"
"jmp _menu"
);
#endif
}
2021-12-11 21:58:00 +03:00
static struct e820_entry_t *rewound_memmap = NULL;
static size_t rewound_memmap_entries = 0;
2021-12-12 20:40:27 +03:00
static uint8_t *rewound_data;
2021-12-19 03:08:34 +03:00
#if bios == 1
static uint8_t *rewound_s2_data;
#endif
2021-12-11 21:58:00 +03:00
2021-12-12 20:40:27 +03:00
extern symbol data_begin;
extern symbol data_end;
2021-12-19 03:08:34 +03:00
#if bios == 1
extern symbol s2_data_begin;
extern symbol s2_data_end;
#endif
2021-12-11 21:58:00 +03:00
2021-12-31 12:58:05 +03:00
__attribute__((used))
static noreturn void _menu(bool timeout_enabled) {
2021-12-12 20:40:27 +03:00
size_t data_size = (uintptr_t)data_end - (uintptr_t)data_begin;
2021-12-19 03:08:34 +03:00
#if bios == 1
size_t s2_data_size = (uintptr_t)s2_data_end - (uintptr_t)s2_data_begin;
#endif
2021-12-11 21:58:00 +03:00
if (rewound_memmap != NULL) {
2021-12-12 20:40:27 +03:00
memcpy(data_begin, rewound_data, data_size);
2021-12-19 03:08:34 +03:00
#if bios == 1
memcpy(s2_data_begin, rewound_s2_data, s2_data_size);
#endif
2021-12-11 21:58:00 +03:00
memcpy(memmap, rewound_memmap, rewound_memmap_entries * sizeof(struct e820_entry_t));
memmap_entries = rewound_memmap_entries;
} else {
2021-12-12 20:40:27 +03:00
rewound_data = ext_mem_alloc(data_size);
2021-12-19 03:08:34 +03:00
#if bios == 1
rewound_s2_data = ext_mem_alloc(s2_data_size);
#endif
2021-12-11 21:58:00 +03:00
rewound_memmap = ext_mem_alloc(256 * sizeof(struct e820_entry_t));
memcpy(rewound_memmap, memmap, memmap_entries * sizeof(struct e820_entry_t));
rewound_memmap_entries = memmap_entries;
2021-12-12 20:40:27 +03:00
memcpy(rewound_data, data_begin, data_size);
2021-12-19 03:08:34 +03:00
#if bios == 1
memcpy(rewound_s2_data, s2_data_begin, s2_data_size);
#endif
2021-12-11 21:58:00 +03:00
}
2021-12-12 20:48:36 +03:00
if (bad_config == false) {
2021-12-11 21:58:00 +03:00
volume_iterate_parts(boot_volume,
if (!init_config_disk(_PART)) {
boot_volume = _PART;
break;
}
);
}
char *quiet_str = config_get_value(NULL, 0, "QUIET");
quiet = quiet_str != NULL && strcmp(quiet_str, "yes") == 0;
char *verbose_str = config_get_value(NULL, 0, "VERBOSE");
verbose = verbose_str != NULL && strcmp(verbose_str, "yes") == 0;
char *serial_str = config_get_value(NULL, 0, "SERIAL");
serial = serial_str != NULL && strcmp(serial_str, "yes") == 0;
menu_branding = config_get_value(NULL, 0, "MENU_BRANDING");
if (menu_branding == NULL)
menu_branding = "Limine " LIMINE_VERSION;
menu_branding_colour = config_get_value(NULL, 0, "MENU_BRANDING_COLOUR");
if (menu_branding_colour == NULL)
menu_branding_colour = config_get_value(NULL, 0, "MENU_BRANDING_COLOR");
if (menu_branding_colour == NULL)
menu_branding_colour = "6";
bool skip_timeout = false;
struct menu_entry *selected_menu_entry = NULL;
size_t selected_entry = 0;
char *default_entry = config_get_value(NULL, 0, "DEFAULT_ENTRY");
if (default_entry != NULL) {
selected_entry = strtoui(default_entry, NULL, 10);
if (selected_entry)
selected_entry--;
}
size_t timeout = 5;
char *timeout_config = config_get_value(NULL, 0, "TIMEOUT");
if (timeout_config != NULL) {
if (!strcmp(timeout_config, "no"))
skip_timeout = true;
else
timeout = strtoui(timeout_config, NULL, 10);
}
if (!timeout_enabled) {
skip_timeout = true;
}
2021-07-07 02:27:07 +03:00
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 (!skip_timeout && !timeout) {
// Use print tree to load up selected_menu_entry and determine if the
// default entry is valid.
print_tree(NULL, 0, 0, selected_entry, menu_tree, &selected_menu_entry);
if (selected_menu_entry == NULL || selected_menu_entry->sub != NULL) {
print("Default entry is not valid or directory, booting to menu.\n");
skip_timeout = true;
} else {
goto autoboot;
}
}
// If there is GRAPHICS config key and the value is "yes", enable graphics
#if bios == 1
char *graphics = config_get_value(NULL, 0, "GRAPHICS");
#elif uefi == 1
char *graphics = "yes";
#endif
reterm:
if (graphics != NULL && !strcmp(graphics, "yes")) {
size_t req_width = 0, req_height = 0, req_bpp = 0;
2021-04-11 03:30:48 +03:00
char *menu_resolution = config_get_value(NULL, 0, "MENU_RESOLUTION");
if (menu_resolution != NULL)
parse_resolution(&req_width, &req_height, &req_bpp, menu_resolution);
2021-04-11 03:30:48 +03:00
term_vbe(req_width, req_height);
} else {
#if bios == 1
term_textmode();
#endif
2020-06-05 21:27:52 +03:00
}
2021-10-09 14:32:57 +03:00
refresh:
2021-09-26 19:16:55 +03:00
term_autoflush = false;
2020-11-20 21:55:18 +03:00
disable_cursor();
print("\e[2J\e[H");
2021-07-07 05:23:34 +03:00
{
size_t x, y;
2021-07-07 05:23:34 +03:00
print("\n");
get_cursor_pos(&x, &y);
set_cursor_pos_helper(term_cols / 2 - DIV_ROUNDUP(strlen(menu_branding), 2), y);
print("\e[3%sm%s\e[37m", menu_branding_colour, menu_branding);
2021-07-07 05:23:34 +03:00
print("\n\n\n\n");
}
2020-06-05 21:27:52 +03:00
2021-12-12 00:22:48 +03:00
while (menu_tree == NULL) {
print("Config file %s.\n\n", config_ready ? "contains no valid entries" : "not found");
print("For information on the format of Limine config entries, consult CONFIG.md in\n");
print("the root of the Limine source repository.\n\n");
2021-12-12 00:22:48 +03:00
print("Press a key to enter the Limine console...");
term_double_buffer_flush();
getchar();
2021-12-12 00:22:48 +03:00
reset_term();
console();
}
2021-07-08 17:07:33 +03:00
{ // Draw box around boot menu
size_t x, y;
2021-07-08 17:07:33 +03:00
get_cursor_pos(&x, &y);
print(serial ? "/" : "\xda");
for (size_t i = 0; i < term_cols - 2; i++) {
print(serial ? "-" : "\xc4");
2021-07-08 17:07:33 +03:00
}
print(serial ? "\\" : "\xbf");
2021-07-08 17:07:33 +03:00
for (size_t i = y + 1; i < term_rows - 2; i++) {
set_cursor_pos_helper(0, i);
print(serial ? "|" : "\xb3");
set_cursor_pos_helper(term_cols - 1, i);
print(serial ? "|" : "\xb3");
2021-07-08 17:07:33 +03:00
}
set_cursor_pos_helper(0, term_rows - 2);
2021-07-08 17:07:33 +03:00
print(serial ? "\\" : "\xc0");
for (size_t i = 0; i < term_cols - 2; i++) {
print(serial ? "-" : "\xc4");
2021-07-08 17:07:33 +03:00
}
print(serial ? "/" : "\xd9");
2021-07-08 17:07:33 +03:00
set_cursor_pos_helper(x, y + 2);
2021-07-08 17:07:33 +03:00
}
size_t max_entries = print_tree(serial ? "| " : "\xb3 ", 0, 0, selected_entry, menu_tree,
&selected_menu_entry);
2020-06-05 21:27:52 +03:00
2021-07-07 02:27:07 +03:00
{
size_t x, y;
2021-07-07 02:27:07 +03:00
get_cursor_pos(&x, &y);
set_cursor_pos_helper(0, 3);
2021-07-07 02:27:07 +03:00
if (editor_enabled && selected_menu_entry->sub == NULL) {
2021-07-07 05:23:34 +03:00
print(" \e[32mARROWS\e[0m Select \e[32mENTER\e[0m Boot \e[32mE\e[0m Edit");
2021-07-07 02:27:07 +03:00
} else {
print(" \e[32mARROWS\e[0m Select \e[32mENTER\e[0m %s",
selected_menu_entry->expanded ? "Collapse" : "Expand");
2021-07-07 02:27:07 +03:00
}
set_cursor_pos_helper(term_cols - 12, 3);
2021-10-09 14:32:57 +03:00
print("\e[32mC\e[0m Console");
set_cursor_pos_helper(x, y);
2021-07-07 02:27:07 +03:00
}
if (selected_menu_entry->sub != NULL)
skip_timeout = true;
int c;
2020-06-05 21:27:52 +03:00
if (skip_timeout == false) {
print("\n\n");
for (size_t i = timeout; i; i--) {
set_cursor_pos_helper(0, term_rows - 1);
scroll_disable();
print("\e[2K\e[32mBooting automatically in \e[92m%u\e[32m, press any key to stop the countdown...\e[0m", i);
scroll_enable();
2020-11-20 21:55:18 +03:00
term_double_buffer_flush();
2021-03-04 15:48:31 +03:00
if ((c = pit_sleep_and_quit_on_keypress(1))) {
2020-06-05 21:27:52 +03:00
skip_timeout = true;
if (quiet) {
quiet = false;
goto reterm;
} else {
print("\e[2K");
term_double_buffer_flush();
goto timeout_aborted;
}
2020-06-05 21:27:52 +03:00
}
}
goto autoboot;
}
set_cursor_pos_helper(0, term_rows - 1);
if (selected_menu_entry->comment != NULL) {
scroll_disable();
print("\e[36m%s\e[0m", selected_menu_entry->comment);
scroll_enable();
}
2021-07-08 17:57:40 +03:00
2020-11-20 21:55:18 +03:00
term_double_buffer_flush();
2020-06-05 21:27:52 +03:00
for (;;) {
c = getchar();
timeout_aborted:
2020-06-05 21:27:52 +03:00
switch (c) {
case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9': {
int ent = (c - '0') - 1;
if (ent < (int)max_entries) {
selected_entry = ent;
print_tree(NULL, 0, 0, selected_entry, menu_tree,
&selected_menu_entry);
goto autoboot;
}
goto refresh;
}
2022-02-05 00:30:53 +03:00
case GETCHAR_HOME:
selected_entry = 0;
goto refresh;
case GETCHAR_END:
selected_entry = max_entries - 1;
goto refresh;
2020-06-05 21:27:52 +03:00
case GETCHAR_CURSOR_UP:
if (selected_entry == 0)
2020-06-05 21:27:52 +03:00
selected_entry = max_entries - 1;
else
selected_entry--;
2020-06-05 21:27:52 +03:00
goto refresh;
case GETCHAR_CURSOR_DOWN:
if (++selected_entry == max_entries)
selected_entry = 0;
goto refresh;
case GETCHAR_CURSOR_RIGHT:
case '\n':
case ' ':
2020-06-05 21:27:52 +03:00
autoboot:
if (selected_menu_entry->sub != NULL) {
selected_menu_entry->expanded = !selected_menu_entry->expanded;
goto refresh;
}
if (term_backend == NOT_READY) {
#if bios == 1
term_textmode();
#elif uefi == 1
term_vbe(0, 0);
#endif
} else {
reset_term();
}
2021-12-12 00:22:48 +03:00
boot(selected_menu_entry->body);
case 'e':
case 'E': {
2021-07-07 02:27:07 +03:00
if (editor_enabled) {
if (selected_menu_entry->sub != NULL)
goto refresh;
editor_no_term_reset = true;
2021-07-07 02:27:07 +03:00
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;
}
2021-10-09 14:32:57 +03:00
break;
}
case 'c':
case 'C': {
2021-10-09 14:32:57 +03:00
reset_term();
console();
goto refresh;
2020-11-18 02:13:54 +03:00
}
2020-06-05 21:27:52 +03:00
}
}
2021-12-12 00:22:48 +03:00
}
2021-12-31 12:58:05 +03:00
noreturn void boot(char *config) {
2021-12-12 00:22:48 +03:00
char *cmdline = config_get_value(config, 0, "KERNEL_CMDLINE");
if (!cmdline) {
cmdline = config_get_value(config, 0, "CMDLINE");
}
if (!cmdline) {
cmdline = "";
}
char *proto = config_get_value(config, 0, "PROTOCOL");
if (proto == NULL) {
printv("PROTOCOL not specified, using autodetection...\n");
autodetect:
stivale2_load(config, cmdline);
stivale_load(config, cmdline);
multiboot2_load(config, cmdline);
multiboot1_load(config, cmdline);
linux_load(config, cmdline);
2021-12-11 21:58:00 +03:00
panic(true, "Kernel protocol autodetection failed");
}
bool ret = true;
if (!strcmp(proto, "stivale1") || !strcmp(proto, "stivale")) {
ret = stivale_load(config, cmdline);
} else if (!strcmp(proto, "stivale2")) {
ret = stivale2_load(config, cmdline);
} else if (!strcmp(proto, "linux")) {
ret = linux_load(config, cmdline);
} else if (!strcmp(proto, "multiboot1") || !strcmp(proto, "multiboot")) {
ret = multiboot1_load(config, cmdline);
} else if (!strcmp(proto, "multiboot2")) {
ret = multiboot2_load(config, cmdline);
} else if (!strcmp(proto, "chainload")) {
chainload(config);
}
if (ret) {
print("WARNING: Unsupported protocol specified: %s.\n", proto);
} else {
print("WARNING: Incorrect protocol specified for kernel.\n");
}
print(" Press A to attempt autodetection or any other key to return to menu.\n");
2021-12-12 00:22:48 +03:00
int c = getchar();
if (c == 'a' || c == 'A') {
goto autodetect;
} else {
menu(false);
}
2021-12-12 00:22:48 +03:00
__builtin_unreachable();
2020-06-05 21:27:52 +03:00
}