#include "export-vars.h"
#include "graphics.h"
#include "initrd.h"
+#include "line-edit.h"
#include "linux.h"
#include "measure.h"
#include "memory-util-fundamental.h"
IDX_INVALID,
};
-static void cursor_left(size_t *cursor, size_t *first) {
- assert(cursor);
- assert(first);
-
- if ((*cursor) > 0)
- (*cursor)--;
- else if ((*first) > 0)
- (*first)--;
-}
-
-static void cursor_right(size_t *cursor, size_t *first, size_t x_max, size_t len) {
- assert(cursor);
- assert(first);
-
- if ((*cursor)+1 < x_max)
- (*cursor)++;
- else if ((*first) + (*cursor) < len)
- (*first)++;
-}
-
-static bool line_edit(char16_t **line_in, size_t x_max, size_t y_pos) {
- _cleanup_free_ char16_t *line = NULL, *print = NULL;
- size_t size, len, first = 0, cursor = 0, clear = 0;
-
- /* Edit the line and return true if it should be executed, false if not. */
-
- assert(line_in);
-
- len = strlen16(*line_in);
- size = len + 1024;
- line = xnew(char16_t, size);
- print = xnew(char16_t, x_max + 1);
- strcpy16(line, strempty(*line_in));
-
- for (;;) {
- EFI_STATUS err;
- uint64_t key;
- size_t j, cursor_color = EFI_TEXT_ATTR_SWAP(COLOR_EDIT);
-
- j = MIN(len - first, x_max);
- memcpy(print, line + first, j * sizeof(char16_t));
- while (clear > 0 && j < x_max) {
- clear--;
- print[j++] = ' ';
- }
- print[j] = '\0';
-
- /* See comment at edit_line() call site for why we start at 1. */
- print_at(1, y_pos, COLOR_EDIT, print);
-
- if (!print[cursor])
- print[cursor] = ' ';
- print[cursor+1] = '\0';
- do {
- print_at(cursor + 1, y_pos, cursor_color, print + cursor);
- cursor_color = EFI_TEXT_ATTR_SWAP(cursor_color);
-
- err = console_key_read(&key, 750 * 1000);
- if (!IN_SET(err, EFI_SUCCESS, EFI_TIMEOUT, EFI_NOT_READY))
- return false;
-
- print_at(cursor + 1, y_pos, COLOR_EDIT, print + cursor);
- } while (err != EFI_SUCCESS);
-
- switch (key) {
- case KEYPRESS(0, SCAN_ESC, 0):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'c'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'g'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('c')):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('g')):
- return false;
-
- case KEYPRESS(0, SCAN_HOME, 0):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'a'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('a')):
- /* beginning-of-line */
- cursor = 0;
- first = 0;
- continue;
-
- case KEYPRESS(0, SCAN_END, 0):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'e'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('e')):
- /* end-of-line */
- cursor = len - first;
- if (cursor+1 >= x_max) {
- cursor = x_max-1;
- first = len - (x_max-1);
- }
- continue;
-
- case KEYPRESS(0, SCAN_DOWN, 0):
- case KEYPRESS(EFI_ALT_PRESSED, 0, 'f'):
- case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_RIGHT, 0):
- /* forward-word */
- while (line[first + cursor] == ' ')
- cursor_right(&cursor, &first, x_max, len);
- while (line[first + cursor] && line[first + cursor] != ' ')
- cursor_right(&cursor, &first, x_max, len);
- continue;
-
- case KEYPRESS(0, SCAN_UP, 0):
- case KEYPRESS(EFI_ALT_PRESSED, 0, 'b'):
- case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_LEFT, 0):
- /* backward-word */
- if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
- cursor_left(&cursor, &first);
- while ((first + cursor) > 0 && line[first + cursor] == ' ')
- cursor_left(&cursor, &first);
- }
- while ((first + cursor) > 0 && line[first + cursor-1] != ' ')
- cursor_left(&cursor, &first);
- continue;
-
- case KEYPRESS(0, SCAN_RIGHT, 0):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'f'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('f')):
- /* forward-char */
- if (first + cursor == len)
- continue;
- cursor_right(&cursor, &first, x_max, len);
- continue;
-
- case KEYPRESS(0, SCAN_LEFT, 0):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'b'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('b')):
- /* backward-char */
- cursor_left(&cursor, &first);
- continue;
-
- case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_DELETE, 0):
- case KEYPRESS(EFI_ALT_PRESSED, 0, 'd'):
- /* kill-word */
- clear = 0;
-
- size_t k;
- for (k = first + cursor; k < len && line[k] == ' '; k++)
- clear++;
- for (; k < len && line[k] != ' '; k++)
- clear++;
-
- for (size_t i = first + cursor; i + clear < len; i++)
- line[i] = line[i + clear];
- len -= clear;
- line[len] = '\0';
- continue;
-
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'w'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('w')):
- case KEYPRESS(EFI_ALT_PRESSED, 0, '\b'):
- /* backward-kill-word */
- clear = 0;
- if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
- cursor_left(&cursor, &first);
- clear++;
- while ((first + cursor) > 0 && line[first + cursor] == ' ') {
- cursor_left(&cursor, &first);
- clear++;
- }
- }
- while ((first + cursor) > 0 && line[first + cursor-1] != ' ') {
- cursor_left(&cursor, &first);
- clear++;
- }
-
- for (size_t i = first + cursor; i + clear < len; i++)
- line[i] = line[i + clear];
- len -= clear;
- line[len] = '\0';
- continue;
-
- case KEYPRESS(0, SCAN_DELETE, 0):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'd'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('d')):
- if (len == 0)
- continue;
- if (first + cursor == len)
- continue;
- for (size_t i = first + cursor; i < len; i++)
- line[i] = line[i+1];
- clear = 1;
- len--;
- continue;
-
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'k'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('k')):
- /* kill-line */
- line[first + cursor] = '\0';
- clear = len - (first + cursor);
- len = first + cursor;
- continue;
-
- case KEYPRESS(0, 0, '\n'):
- case KEYPRESS(0, 0, '\r'):
- case KEYPRESS(0, SCAN_F3, 0): /* EZpad Mini 4s firmware sends malformed events */
- case KEYPRESS(0, SCAN_F3, '\r'): /* Teclast X98+ II firmware sends malformed events */
- if (!streq16(line, *line_in)) {
- free(*line_in);
- *line_in = TAKE_PTR(line);
- }
- return true;
-
- case KEYPRESS(0, 0, '\b'):
- if (len == 0)
- continue;
- if (first == 0 && cursor == 0)
- continue;
- for (size_t i = first + cursor-1; i < len; i++)
- line[i] = line[i+1];
- clear = 1;
- len--;
- if (cursor > 0)
- cursor--;
- if (cursor > 0 || first == 0)
- continue;
- /* show full line if it fits */
- if (len < x_max) {
- cursor = first;
- first = 0;
- continue;
- }
- /* jump left to see what we delete */
- if (first > 10) {
- first -= 10;
- cursor = 10;
- } else {
- cursor = first;
- first = 0;
- }
- continue;
-
- case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
- case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
- if (len+1 == size)
- continue;
- for (size_t i = len; i > first + cursor; i--)
- line[i] = line[i-1];
- line[first + cursor] = KEYCHAR(key);
- len++;
- line[len] = '\0';
- if (cursor+1 < x_max)
- cursor++;
- else if (first + cursor < len)
- first++;
- continue;
- }
- }
-}
static size_t entry_lookup_key(Config *config, size_t start, char16_t key) {
assert(config);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "console.h"
+#include "line-edit.h"
+#include "util.h"
+
+static void cursor_left(size_t *cursor, size_t *first) {
+ assert(cursor);
+ assert(first);
+
+ if ((*cursor) > 0)
+ (*cursor)--;
+ else if ((*first) > 0)
+ (*first)--;
+}
+
+static void cursor_right(size_t *cursor, size_t *first, size_t x_max, size_t len) {
+ assert(cursor);
+ assert(first);
+
+ if ((*cursor)+1 < x_max)
+ (*cursor)++;
+ else if ((*first) + (*cursor) < len)
+ (*first)++;
+}
+
+bool line_edit(char16_t **line_in, size_t x_max, size_t y_pos) {
+ _cleanup_free_ char16_t *line = NULL, *print = NULL;
+ size_t size, len, first = 0, cursor = 0, clear = 0;
+
+ /* Edit the line and return true if it should be executed, false if not. */
+
+ assert(line_in);
+
+ len = strlen16(*line_in);
+ size = len + 1024;
+ line = xnew(char16_t, size);
+ print = xnew(char16_t, x_max + 1);
+ strcpy16(line, strempty(*line_in));
+
+ for (;;) {
+ EFI_STATUS err;
+ uint64_t key;
+ size_t j, cursor_color = EFI_TEXT_ATTR_SWAP(COLOR_EDIT);
+
+ j = MIN(len - first, x_max);
+ memcpy(print, line + first, j * sizeof(char16_t));
+ while (clear > 0 && j < x_max) {
+ clear--;
+ print[j++] = ' ';
+ }
+ print[j] = '\0';
+
+ /* See comment at edit_line() call site for why we start at 1. */
+ print_at(1, y_pos, COLOR_EDIT, print);
+
+ if (!print[cursor])
+ print[cursor] = ' ';
+ print[cursor+1] = '\0';
+ do {
+ print_at(cursor + 1, y_pos, cursor_color, print + cursor);
+ cursor_color = EFI_TEXT_ATTR_SWAP(cursor_color);
+
+ err = console_key_read(&key, 750 * 1000);
+ if (!IN_SET(err, EFI_SUCCESS, EFI_TIMEOUT, EFI_NOT_READY))
+ return false;
+
+ print_at(cursor + 1, y_pos, COLOR_EDIT, print + cursor);
+ } while (err != EFI_SUCCESS);
+
+ switch (key) {
+ case KEYPRESS(0, SCAN_ESC, 0):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'c'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'g'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('c')):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('g')):
+ return false;
+
+ case KEYPRESS(0, SCAN_HOME, 0):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'a'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('a')):
+ /* beginning-of-line */
+ cursor = 0;
+ first = 0;
+ continue;
+
+ case KEYPRESS(0, SCAN_END, 0):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'e'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('e')):
+ /* end-of-line */
+ cursor = len - first;
+ if (cursor+1 >= x_max) {
+ cursor = x_max-1;
+ first = len - (x_max-1);
+ }
+ continue;
+
+ case KEYPRESS(0, SCAN_DOWN, 0):
+ case KEYPRESS(EFI_ALT_PRESSED, 0, 'f'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_RIGHT, 0):
+ /* forward-word */
+ while (line[first + cursor] == ' ')
+ cursor_right(&cursor, &first, x_max, len);
+ while (line[first + cursor] && line[first + cursor] != ' ')
+ cursor_right(&cursor, &first, x_max, len);
+ continue;
+
+ case KEYPRESS(0, SCAN_UP, 0):
+ case KEYPRESS(EFI_ALT_PRESSED, 0, 'b'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_LEFT, 0):
+ /* backward-word */
+ if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
+ cursor_left(&cursor, &first);
+ while ((first + cursor) > 0 && line[first + cursor] == ' ')
+ cursor_left(&cursor, &first);
+ }
+ while ((first + cursor) > 0 && line[first + cursor-1] != ' ')
+ cursor_left(&cursor, &first);
+ continue;
+
+ case KEYPRESS(0, SCAN_RIGHT, 0):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'f'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('f')):
+ /* forward-char */
+ if (first + cursor == len)
+ continue;
+ cursor_right(&cursor, &first, x_max, len);
+ continue;
+
+ case KEYPRESS(0, SCAN_LEFT, 0):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'b'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('b')):
+ /* backward-char */
+ cursor_left(&cursor, &first);
+ continue;
+
+ case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_DELETE, 0):
+ case KEYPRESS(EFI_ALT_PRESSED, 0, 'd'):
+ /* kill-word */
+ clear = 0;
+
+ size_t k;
+ for (k = first + cursor; k < len && line[k] == ' '; k++)
+ clear++;
+ for (; k < len && line[k] != ' '; k++)
+ clear++;
+
+ for (size_t i = first + cursor; i + clear < len; i++)
+ line[i] = line[i + clear];
+ len -= clear;
+ line[len] = '\0';
+ continue;
+
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'w'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('w')):
+ case KEYPRESS(EFI_ALT_PRESSED, 0, '\b'):
+ /* backward-kill-word */
+ clear = 0;
+ if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
+ cursor_left(&cursor, &first);
+ clear++;
+ while ((first + cursor) > 0 && line[first + cursor] == ' ') {
+ cursor_left(&cursor, &first);
+ clear++;
+ }
+ }
+ while ((first + cursor) > 0 && line[first + cursor-1] != ' ') {
+ cursor_left(&cursor, &first);
+ clear++;
+ }
+
+ for (size_t i = first + cursor; i + clear < len; i++)
+ line[i] = line[i + clear];
+ len -= clear;
+ line[len] = '\0';
+ continue;
+
+ case KEYPRESS(0, SCAN_DELETE, 0):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'd'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('d')):
+ if (len == 0)
+ continue;
+ if (first + cursor == len)
+ continue;
+ for (size_t i = first + cursor; i < len; i++)
+ line[i] = line[i+1];
+ clear = 1;
+ len--;
+ continue;
+
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'k'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('k')):
+ /* kill-line */
+ line[first + cursor] = '\0';
+ clear = len - (first + cursor);
+ len = first + cursor;
+ continue;
+
+ case KEYPRESS(0, 0, '\n'):
+ case KEYPRESS(0, 0, '\r'):
+ case KEYPRESS(0, SCAN_F3, 0): /* EZpad Mini 4s firmware sends malformed events */
+ case KEYPRESS(0, SCAN_F3, '\r'): /* Teclast X98+ II firmware sends malformed events */
+ if (!streq16(line, *line_in)) {
+ free(*line_in);
+ *line_in = TAKE_PTR(line);
+ }
+ return true;
+
+ case KEYPRESS(0, 0, '\b'):
+ if (len == 0)
+ continue;
+ if (first == 0 && cursor == 0)
+ continue;
+ for (size_t i = first + cursor-1; i < len; i++)
+ line[i] = line[i+1];
+ clear = 1;
+ len--;
+ if (cursor > 0)
+ cursor--;
+ if (cursor > 0 || first == 0)
+ continue;
+ /* show full line if it fits */
+ if (len < x_max) {
+ cursor = first;
+ first = 0;
+ continue;
+ }
+ /* jump left to see what we delete */
+ if (first > 10) {
+ first -= 10;
+ cursor = 10;
+ } else {
+ cursor = first;
+ first = 0;
+ }
+ continue;
+
+ case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
+ case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
+ if (len+1 == size)
+ continue;
+ for (size_t i = len; i > first + cursor; i--)
+ line[i] = line[i-1];
+ line[first + cursor] = KEYCHAR(key);
+ len++;
+ line[len] = '\0';
+ if (cursor+1 < x_max)
+ cursor++;
+ else if (first + cursor < len)
+ first++;
+ continue;
+ }
+ }
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "efi.h"
+
+bool line_edit(char16_t **line_in, size_t x_max, size_t y_pos);
systemd_boot_sources = files(
'boot.c',
+ 'line-edit.c',
)
stub_sources = files(