efi_loader: efi_console: support editable input fields
authorCasey Connolly <casey.connolly@linaro.org>
Thu, 23 Oct 2025 14:26:46 +0000 (16:26 +0200)
committerHeinrich Schuchardt <heinrich.schuchardt@canonical.com>
Sun, 26 Oct 2025 10:15:21 +0000 (10:15 +0000)
When editing eficonfig "optional data" (typically cmdline arguments)
it's useful to be able to edit the string rather than having to re-type
the entire thing. Implement support for editing buffers to make this a
whole lot nicer. Specifically, add support for moving the cursor with
the arrow keys and End key as well as deleting backwards with the delete
key.

Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
Acked-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Tested-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
cmd/eficonfig.c
lib/efi_loader/efi_console.c

index 8ac0fb9..d8d946c 100644 (file)
@@ -974,6 +974,8 @@ static efi_status_t handle_user_input(u16 *buf, int buf_size,
        if (!tmp)
                return EFI_OUT_OF_RESOURCES;
 
+       /* Populate tmp so user can edit existing string */
+       u16_strcpy(tmp, buf);
        ret = efi_console_get_u16_string(cin, tmp, buf_size, NULL, 4, cursor_col);
        if (ret == EFI_SUCCESS)
                u16_strcpy(buf, tmp);
index 953f683..068d1a0 100644 (file)
@@ -1384,7 +1384,9 @@ efi_status_t efi_console_get_u16_string(struct efi_simple_text_input_protocol *c
                                        int row, int col)
 {
        efi_status_t ret;
-       efi_uintn_t len = 0;
+       efi_uintn_t len;
+       efi_uintn_t cursor;
+       efi_uintn_t i;
        struct efi_input_key key;
 
        printf(ANSI_CURSOR_POSITION
@@ -1393,25 +1395,51 @@ efi_status_t efi_console_get_u16_string(struct efi_simple_text_input_protocol *c
 
        efi_cin_empty_buffer();
 
+       len = u16_strlen(buf);
+       cursor = len;
        for (;;) {
+               printf(ANSI_CURSOR_POSITION "%ls"
+                      ANSI_CLEAR_LINE_TO_END ANSI_CURSOR_POSITION,
+                      row, col, buf, row, col + (int)cursor);
                do {
                        ret = EFI_CALL(cin->read_key_stroke(cin, &key));
                        mdelay(10);
                } while (ret == EFI_NOT_READY);
 
                if (key.unicode_char == u'\b') {
-                       if (len > 0)
-                               buf[--len] = u'\0';
-
-                       printf(ANSI_CURSOR_POSITION
-                              "%ls"
-                              ANSI_CLEAR_LINE_TO_END, row, col, buf);
+                       if (cursor > 0) {
+                               if (cursor == len) {
+                                       buf[--cursor] = u'\0';
+                               } else {
+                                       for (i = cursor - 1; i < len; i++)
+                                               buf[i] = buf[i + 1];
+                                       cursor--;
+                               }
+                               len--;
+                       }
+                       continue;
+               } else if (key.scan_code == 8) { /* delete */
+                       for (i = cursor; i <= len; i++)
+                               buf[i] = buf[i + 1];
+                       len--;
                        continue;
                } else if (key.unicode_char == u'\r') {
                        buf[len] = u'\0';
                        return EFI_SUCCESS;
                } else if (key.unicode_char == 0x3 || key.scan_code == 23) {
                        return EFI_ABORTED;
+               } else if (key.scan_code == 3) { /* Right arrow */
+                       cursor += (cursor < len) ? 1 : 0;
+                       continue;
+               } else if (key.scan_code == 4) { /* Left arrow */
+                       cursor -= (cursor > 0) ? 1 : 0;
+                       continue;
+               } else if (key.scan_code == 5) { /* Home */
+                       cursor = 0;
+                       continue;
+               } else if (key.scan_code == 6) { /* End */
+                       cursor = len;
+                       continue;
                } else if (key.unicode_char < 0x20) {
                        /* ignore control codes other than Ctrl+C, '\r' and '\b' */
                        continue;
@@ -1428,8 +1456,17 @@ efi_status_t efi_console_get_u16_string(struct efi_simple_text_input_protocol *c
                if (len >= (count - 1))
                        continue;
 
-               buf[len] = key.unicode_char;
+               /*
+                * Insert the character into the middle of the buffer, shift the
+                * characters after the cursor along. The check above ensures we
+                * will never overflow the buffer.
+                * If the cursor is at the end of the string then this will
+                * do nothing.
+                */
+               for (i = len + 1; i > cursor; i--)
+                       buf[i] = buf[i - 1];
+               buf[cursor] = key.unicode_char;
+               cursor++;
                len++;
-               printf(ANSI_CURSOR_POSITION "%ls", row, col, buf);
        }
 }