[PATCH v2 00/36] expo: Support editing lines of text

So far expo only supports menus. These are quite flexible for various kinds of settings, but cannot deal with free-form input, such as a serial number or a machine name.
This series adds support for a textline object, which is a single line of text. It has a maximum length and its value is stored within the expo structure.
U-Boot already has a command-line editor which provides most of the features needed by expo. But the code runs in its own loop and only returns when the line is finished. This is not suitable for expo, which must handle a keypress at a time, returning to its caller after each one.
In order to use the CLI code, some significant refactoring is included here. This mostly involves moving the internal loop of the CLI to a separate function and recording its state in a struct, just as was done for single keypresses some time back. A minor addition is support for Ctrl-W to delete a word, since strangely this is currently only present in the simple version.
The video-console system provides most of the features needed by testline, but a few things are missing. This series provides:
- primitive cursor support so the user can see where he is typing - saving and restoring of the text-entry context, so that expo can allow the user to continue where he left off, including deleting previously entered characters correctly (for Truetype) - obtaining the nominal width of a string of n characters, so that a suitable width can be chosen for the textline object
Note that no support is provided for clearing the cursor. This was addressed in a previous series[1] which could perhaps be rebased. For this implementation, the cursor is therefore not enabled for the normal command line, only for expo.
Reading and writing textline objects is supported for FDT and environment, but not for CMOS RAM, since it would likely use too much RAM to store a string.
In terms of code size, the overall size increase is 180 bytes for Thumb02 boards, 160 of whcih is the addition of Ctrl-W to delete a word.
[1] https://patchwork.ozlabs.org/project/uboot/list/?series=280178&state=*
Changes in v2: - Drop blank line at end of help - Drop trailing ; - Correct variable declaration in switch() to make clang happy
Simon Glass (36): cli: Move simple readline into a function cli: Add a command to show cmdline history cli: Drop some #ifdefs in cli_readline cli: Drop #ifdefs for CONFIG_AUTO_COMPLETE in cli_readline cli: Implement delete-word in cread_line() cli: Use unsigned int instead of unsigned long cli: Convert cread_line() to use a struct for the main vars cli: Unindent some code in cread_line() cli: Create a function to process characters cli: Terminate the string in cread_line_process_ch() cli: Allow history to be disabled cli: Allow command completion to be disabled cli: Add a function to set up a new cread video: Allow obtaining the nominal size of a string size video: Allow saving and restoring text-entry state video: Export vidconsole_entry_start() video: Support showing a cursor expo: Add better error reporting expo: Fix up comments for get_cur_menuitem_text() et al expo: Use switch statements more for object types expo: Correct some swallowed errors in scene expo: Correct the logic for duplicate-ID detection expo: Allow highlighting other scene-object types expo: Add a function to write a property to a devicetree expo: Make calculation of an object bounding box generic expo: Allow rendering the background of any object expo: Add some scene fields needed for text entry expo: Add basic support for textline objects expo: Support opening a textline expo: Plumb in textlines to a scene video: Mark truetype_measure() static expo: Support handling any key in cedit expo: Plumb in textline to cedit expo: Support building an expo with a textline expo: Update tests to include textline expo: Update documentation to include textline
arch/sandbox/dts/cedit.dtsi | 8 + boot/Makefile | 3 +- boot/cedit.c | 198 ++++++++--- boot/expo_build.c | 102 +++++- boot/scene.c | 237 +++++++++++-- boot/scene_internal.h | 128 ++++++- boot/scene_menu.c | 68 +--- boot/scene_textline.c | 229 +++++++++++++ cmd/Kconfig | 7 + cmd/Makefile | 1 + cmd/history.c | 23 ++ common/cli_readline.c | 498 ++++++++++++++++------------ doc/develop/cedit.rst | 3 +- doc/develop/expo.rst | 48 ++- doc/usage/cmd/history.rst | 67 ++++ doc/usage/index.rst | 1 + drivers/video/console_core.c | 31 ++ drivers/video/console_normal.c | 29 ++ drivers/video/console_truetype.c | 191 ++++++++++- drivers/video/vidconsole-uclass.c | 71 +++- drivers/video/vidconsole_internal.h | 24 ++ include/cli.h | 51 +++ include/command.h | 6 + include/expo.h | 60 +++- include/menu.h | 7 +- include/test/cedit-test.h | 5 +- include/video_console.h | 123 +++++++ test/boot/cedit.c | 30 +- test/boot/expo.c | 2 +- test/boot/files/expo_ids.h | 3 + test/boot/files/expo_layout.dts | 8 + test/cmd/Makefile | 1 + test/cmd/history.c | 49 +++ 33 files changed, 1935 insertions(+), 377 deletions(-) create mode 100644 boot/scene_textline.c create mode 100644 cmd/history.c create mode 100644 doc/usage/cmd/history.rst create mode 100644 test/cmd/history.c

Move this code into its own function since it is a separate implementation from the full version.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
common/cli_readline.c | 79 +++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 33 deletions(-)
diff --git a/common/cli_readline.c b/common/cli_readline.c index e83743e90c92..9a7c268719da 100644 --- a/common/cli_readline.c +++ b/common/cli_readline.c @@ -440,41 +440,22 @@ int cli_readline(const char *const prompt) return cli_readline_into_buffer(prompt, console_buffer, 0); }
- -int cli_readline_into_buffer(const char *const prompt, char *buffer, - int timeout) +/** + * cread_line_simple() - Simple (small) command-line reader + * + * This supports only basic editing, with no cursor movement + * + * @prompt: Prompt to display + * @p: Text buffer to edit + * Return: length of text buffer, or -1 if input was cannncelled (Ctrl-C) + */ +static int cread_line_simple(const char *const prompt, char *p) { - char *p = buffer; -#ifdef CONFIG_CMDLINE_EDITING - unsigned int len = CONFIG_SYS_CBSIZE; - int rc; - static int initted; - - /* - * History uses a global array which is not - * writable until after relocation to RAM. - * Revert to non-history version if still - * running from flash. - */ - if (gd->flags & GD_FLG_RELOC) { - if (!initted) { - hist_init(); - initted = 1; - } - - if (prompt) - puts(prompt); - - rc = cread_line(prompt, p, &len, timeout); - return rc < 0 ? rc : len; - - } else { -#endif /* CONFIG_CMDLINE_EDITING */ char *p_buf = p; - int n = 0; /* buffer index */ - int plen = 0; /* prompt length */ - int col; /* output column cnt */ - char c; + int n = 0; /* buffer index */ + int plen = 0; /* prompt length */ + int col; /* output column cnt */ + char c;
/* print prompt */ if (prompt) { @@ -567,6 +548,38 @@ int cli_readline_into_buffer(const char *const prompt, char *buffer, } } } +} + +int cli_readline_into_buffer(const char *const prompt, char *buffer, + int timeout) +{ + char *p = buffer; +#ifdef CONFIG_CMDLINE_EDITING + unsigned int len = CONFIG_SYS_CBSIZE; + int rc; + static int initted; + + /* + * History uses a global array which is not + * writable until after relocation to RAM. + * Revert to non-history version if still + * running from flash. + */ + if (gd->flags & GD_FLG_RELOC) { + if (!initted) { + hist_init(); + initted = 1; + } + + if (prompt) + puts(prompt); + + rc = cread_line(prompt, p, &len, timeout); + return rc < 0 ? rc : len; + + } else { +#endif /* CONFIG_CMDLINE_EDITING */ + return cread_line_simple(prompt, p); #ifdef CONFIG_CMDLINE_EDITING } #endif

There is a function for this but it is never used. Showing the history is a useful feature, so add a new 'history' command.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: - Drop blank line at end of help
cmd/Kconfig | 7 ++++ cmd/Makefile | 1 + cmd/history.c | 23 ++++++++++++++ common/cli_readline.c | 4 +-- doc/usage/cmd/history.rst | 67 +++++++++++++++++++++++++++++++++++++++ doc/usage/index.rst | 1 + include/cli.h | 3 ++ test/cmd/Makefile | 1 + test/cmd/history.c | 49 ++++++++++++++++++++++++++++ 9 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 cmd/history.c create mode 100644 doc/usage/cmd/history.rst create mode 100644 test/cmd/history.c
diff --git a/cmd/Kconfig b/cmd/Kconfig index 64d723bd483b..34e1c91a15a9 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -176,6 +176,13 @@ config CMD_FWU_METADATA help Command to read the metadata and dump it's contents
+config CMD_HISTORY + bool "history" + depends on CMDLINE_EDITING + help + Show the command-line history, i.e. a list of commands that are in + the history buffer. + config CMD_LICENSE bool "license" select BUILD_BIN2C diff --git a/cmd/Makefile b/cmd/Makefile index 9bebf321c397..971f78a7fbd4 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -91,6 +91,7 @@ obj-$(CONFIG_CMD_FUSE) += fuse.o obj-$(CONFIG_CMD_FWU_METADATA) += fwu_mdata.o obj-$(CONFIG_CMD_GETTIME) += gettime.o obj-$(CONFIG_CMD_GPIO) += gpio.o +obj-$(CONFIG_CMD_HISTORY) += history.o obj-$(CONFIG_CMD_HVC) += smccc.o obj-$(CONFIG_CMD_I2C) += i2c.o obj-$(CONFIG_CMD_IOTRACE) += iotrace.o diff --git a/cmd/history.c b/cmd/history.c new file mode 100644 index 000000000000..b6bf4670b1ce --- /dev/null +++ b/cmd/history.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2023 Google LLC + * Written by Simon Glass sjg@chromium.org + */ + +#include <common.h> +#include <command.h> +#include <cli.h> + +static int do_history(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + cread_print_hist_list(); + + return 0; +} + +U_BOOT_CMD( + history, CONFIG_SYS_MAXARGS, 1, do_history, + "print command history", + "" +); diff --git a/common/cli_readline.c b/common/cli_readline.c index 9a7c268719da..f3ba76a62ec8 100644 --- a/common/cli_readline.c +++ b/common/cli_readline.c @@ -160,8 +160,7 @@ static char *hist_next(void) return ret; }
-#ifndef CONFIG_CMDLINE_EDITING -static void cread_print_hist_list(void) +void cread_print_hist_list(void) { int i; unsigned long n; @@ -179,7 +178,6 @@ static void cread_print_hist_list(void) i++; } } -#endif /* CONFIG_CMDLINE_EDITING */
#define BEGINNING_OF_LINE() { \ while (num) { \ diff --git a/doc/usage/cmd/history.rst b/doc/usage/cmd/history.rst new file mode 100644 index 000000000000..33d3fcd6247f --- /dev/null +++ b/doc/usage/cmd/history.rst @@ -0,0 +1,67 @@ +.. SPDX-License-Identifier: GPL-2.0+: + +history command +=============== + +Synopis +------- + +:: + + history + +Description +----------- + +The *history* command shows a list of previously entered commands on the +command line. When U-Boot starts, this it is initially empty. Each new command +entered is added to the list. + +Normally these commands can be accessed by pressing the `up arrow` and +`down arrow` keys, which cycle through the list. The `history` command provides +a simple way to view the list. + +Example +------- + +This example shows entering three commands, then `history`. Note that `history` +itself is added to the list. + +:: + + => bootflow scan -l + Scanning for bootflows in all bootdevs + Seq Method State Uclass Part Name Filename + --- ----------- ------ -------- ---- ------------------------ ---------------- + Scanning global bootmeth 'firmware0': + Hunting with: simple_bus + Found 2 extension board(s). + Scanning bootdev 'mmc2.bootdev': + Scanning bootdev 'mmc1.bootdev': + 0 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf + No more bootdevs + --- ----------- ------ -------- ---- ------------------------ ---------------- + (1 bootflow, 1 valid) + => bootflow select 0 + => bootflow info + Name: mmc1.bootdev.part_1 + Device: mmc1.bootdev + Block dev: mmc1.blk + Method: extlinux + State: ready + Partition: 1 + Subdir: (none) + Filename: /extlinux/extlinux.conf + Buffer: aebdea0 + Size: 253 (595 bytes) + OS: Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl) + Cmdline: (none) + Logo: (none) + FDT: <NULL> + Error: 0 + => history + bootflow scan -l + bootflow select 0 + bootflow info + history + => diff --git a/doc/usage/index.rst b/doc/usage/index.rst index fa702920faa7..98b4719c4088 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -67,6 +67,7 @@ Shell commands cmd/fwu_mdata cmd/gpio cmd/gpt + cmd/history cmd/host cmd/imxtract cmd/load diff --git a/include/cli.h b/include/cli.h index 094a6602d70e..ac09c80c784c 100644 --- a/include/cli.h +++ b/include/cli.h @@ -229,4 +229,7 @@ void cli_ch_init(struct cli_ch_state *cch); */ int cli_ch_process(struct cli_ch_state *cch, int ichar);
+/** cread_print_hist_list() - Print the command-line history list */ +void cread_print_hist_list(void); + #endif diff --git a/test/cmd/Makefile b/test/cmd/Makefile index 6e3d7e919ef2..8d70ac510a53 100644 --- a/test/cmd/Makefile +++ b/test/cmd/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_BDI) += bdinfo.o obj-$(CONFIG_CMD_FDT) += fdt.o obj-$(CONFIG_CONSOLE_TRUETYPE) += font.o +obj-$(CONFIG_CMD_HISTORY) += history.o obj-$(CONFIG_CMD_LOADM) += loadm.o obj-$(CONFIG_CMD_MEM_SEARCH) += mem_search.o ifdef CONFIG_CMD_PCI diff --git a/test/cmd/history.c b/test/cmd/history.c new file mode 100644 index 000000000000..06517fcdbb51 --- /dev/null +++ b/test/cmd/history.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for history command + * + * Copyright 2023 Google LLC + * Written by Simon Glass sjg@chromium.org + */ + +#include <common.h> +#include <cli.h> +#include <command.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +static int lib_test_history(struct unit_test_state *uts) +{ + static const char cmd1[] = "setenv fred hello"; + static const char cmd2[] = "print fred"; + + /* running commands directly does not add to history */ + ut_assertok(run_command(cmd1, 0)); + ut_assert_console_end(); + ut_assertok(run_command("history", 0)); + ut_assert_console_end(); + + /* enter commands via the console */ + console_in_puts(cmd1); + console_in_puts("\n"); + ut_asserteq(strlen(cmd1), cli_readline("")); + ut_assert_nextline(cmd1); + + console_in_puts(cmd2); + console_in_puts("\n"); + ut_asserteq(strlen(cmd2), cli_readline("")); + ut_assert_nextline(cmd2); + + ut_assertok(run_command("print fred", 0)); + ut_assert_nextline("fred=hello"); + ut_assert_console_end(); + + ut_assertok(run_command("history", 0)); + ut_assert_nextline(cmd1); + ut_assert_nextline(cmd2); + ut_assert_console_end(); + + return 0; +} +LIB_TEST(lib_test_history, UT_TESTF_CONSOLE_REC);

Add a few static inlines to avoid the need for #ifdefs in cli_readline_into_buffer()
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
common/cli_readline.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/common/cli_readline.c b/common/cli_readline.c index f3ba76a62ec8..61f9ba99068a 100644 --- a/common/cli_readline.c +++ b/common/cli_readline.c @@ -423,6 +423,18 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, return 0; }
+#else /* !CONFIG_CMDLINE_EDITING */ + +static inline void hist_init(void) +{ +} + +static int cread_line(const char *const prompt, char *buf, unsigned int *len, + int timeout) +{ + return 0; +} + #endif /* CONFIG_CMDLINE_EDITING */
/****************************************************************************/ @@ -552,8 +564,7 @@ int cli_readline_into_buffer(const char *const prompt, char *buffer, int timeout) { char *p = buffer; -#ifdef CONFIG_CMDLINE_EDITING - unsigned int len = CONFIG_SYS_CBSIZE; + uint len = CONFIG_SYS_CBSIZE; int rc; static int initted;
@@ -563,7 +574,7 @@ int cli_readline_into_buffer(const char *const prompt, char *buffer, * Revert to non-history version if still * running from flash. */ - if (gd->flags & GD_FLG_RELOC) { + if (IS_ENABLED(CONFIG_CMDLINE_EDITING) && (gd->flags & GD_FLG_RELOC)) { if (!initted) { hist_init(); initted = 1; @@ -576,9 +587,6 @@ int cli_readline_into_buffer(const char *const prompt, char *buffer, return rc < 0 ? rc : len;
} else { -#endif /* CONFIG_CMDLINE_EDITING */ return cread_line_simple(prompt, p); -#ifdef CONFIG_CMDLINE_EDITING } -#endif }

Use a static inline and adjust the logic to avoid the need for #ifdefs in cli_readline_into_buffer()
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
common/cli_readline.c | 85 +++++++++++++++++++++---------------------- include/command.h | 6 +++ 2 files changed, 48 insertions(+), 43 deletions(-)
diff --git a/common/cli_readline.c b/common/cli_readline.c index 61f9ba99068a..458e927e4922 100644 --- a/common/cli_readline.c +++ b/common/cli_readline.c @@ -386,27 +386,27 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, REFRESH_TO_EOL(); continue; } -#ifdef CONFIG_AUTO_COMPLETE - case '\t': { - int num2, col; + case '\t': + if (IS_ENABLED(CONFIG_AUTO_COMPLETE)) { + int num2, col; + + /* do not autocomplete when in the middle */ + if (num < eol_num) { + getcmd_cbeep(); + break; + }
- /* do not autocomplete when in the middle */ - if (num < eol_num) { - getcmd_cbeep(); + buf[num] = '\0'; + col = strlen(prompt) + eol_num; + num2 = num; + if (cmd_auto_complete(prompt, buf, &num2, &col)) { + col = num2 - num; + num += col; + eol_num += col; + } break; } - - buf[num] = '\0'; - col = strlen(prompt) + eol_num; - num2 = num; - if (cmd_auto_complete(prompt, buf, &num2, &col)) { - col = num2 - num; - num += col; - eol_num += col; - } - break; - } -#endif + fallthrough; default: cread_add_char(ichar, insert, &num, &eol_num, buf, *len); @@ -519,14 +519,15 @@ static int cread_line_simple(const char *const prompt, char *p) continue;
default: - /* - * Must be a normal character then - */ - if (n < CONFIG_SYS_CBSIZE-2) { - if (c == '\t') { /* expand TABs */ -#ifdef CONFIG_AUTO_COMPLETE + /* Must be a normal character then */ + if (n >= CONFIG_SYS_CBSIZE - 2) { /* Buffer full */ + putc('\a'); + break; + } + if (c == '\t') { /* expand TABs */ + if (IS_ENABLED(CONFIG_AUTO_COMPLETE)) { /* - * if auto completion triggered just + * if auto-completion triggered just * continue */ *p = '\0'; @@ -536,26 +537,24 @@ static int cread_line_simple(const char *const prompt, char *p) p = p_buf + n; /* reset */ continue; } -#endif - puts(tab_seq + (col & 07)); - col += 8 - (col & 07); - } else { - char __maybe_unused buf[2]; - - /* - * Echo input using puts() to force an - * LCD flush if we are using an LCD - */ - ++col; - buf[0] = c; - buf[1] = '\0'; - puts(buf); } - *p++ = c; - ++n; - } else { /* Buffer full */ - putc('\a'); + puts(tab_seq + (col & 07)); + col += 8 - (col & 07); + } else { + char __maybe_unused buf[2]; + + /* + * Echo input using puts() to force an LCD + * flush if we are using an LCD + */ + ++col; + buf[0] = c; + buf[1] = '\0'; + puts(buf); } + *p++ = c; + ++n; + break; } } } diff --git a/include/command.h b/include/command.h index ae7bb4a30b05..44636fbfc562 100644 --- a/include/command.h +++ b/include/command.h @@ -91,6 +91,12 @@ int var_complete(int argc, char *const argv[], char last_char, int maxv, char *cmdv[]); int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp); +#else +static inline int cmd_auto_complete(const char *const prompt, char *buf, + int *np, int *colp) +{ + return 0; +} #endif
/**

The Ctrl-W option is implemented in the cread_line_simple() but not in the full version. This seems odd, so add an implementation.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
common/cli_readline.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+)
diff --git a/common/cli_readline.c b/common/cli_readline.c index 458e927e4922..687e239bfa77 100644 --- a/common/cli_readline.c +++ b/common/cli_readline.c @@ -89,6 +89,14 @@ static char hist_lines[HIST_MAX][HIST_SIZE + 1]; /* Save room for NULL */
#define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1)
+static void getcmd_putchars(int count, int ch) +{ + int i; + + for (i = 0; i < count; i++) + getcmd_putch(ch); +} + static void hist_init(void) { int i; @@ -337,6 +345,29 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, case CTL_CH('o'): insert = !insert; break; + case CTL_CH('w'): + if (num) { + uint base; + + for (base = num - 1; + base >= 0 && buf[base] == ' ';) + base--; + for (; base > 0 && buf[base - 1] != ' ';) + base--; + + /* now delete chars from base to num */ + wlen = num - base; + eol_num -= wlen; + memmove(&buf[base], &buf[num], + eol_num - base + 1); + num = base; + getcmd_putchars(wlen, CTL_BACKSPACE); + puts(buf + base); + getcmd_putchars(wlen, ' '); + getcmd_putchars(wlen + eol_num - num, + CTL_BACKSPACE); + } + break; case CTL_CH('x'): case CTL_CH('u'): BEGINNING_OF_LINE();

The index values are not very large so it makes no sense to use a long integer. Change these to uint instead.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
common/cli_readline.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/common/cli_readline.c b/common/cli_readline.c index 687e239bfa77..f863404110c3 100644 --- a/common/cli_readline.c +++ b/common/cli_readline.c @@ -171,7 +171,7 @@ static char *hist_next(void) void cread_print_hist_list(void) { int i; - unsigned long n; + uint n;
n = hist_num - hist_max;
@@ -211,10 +211,10 @@ void cread_print_hist_list(void) } \ }
-static void cread_add_char(char ichar, int insert, unsigned long *num, - unsigned long *eol_num, char *buf, unsigned long len) +static void cread_add_char(char ichar, int insert, uint *num, + uint *eol_num, char *buf, uint len) { - unsigned long wlen; + uint wlen;
/* room ??? */ if (insert || *num == *eol_num) { @@ -245,8 +245,7 @@ static void cread_add_char(char ichar, int insert, unsigned long *num, }
static void cread_add_str(char *str, int strsize, int insert, - unsigned long *num, unsigned long *eol_num, - char *buf, unsigned long len) + uint *num, uint *eol_num, char *buf, uint len) { while (strsize--) { cread_add_char(*str, insert, num, eol_num, buf, len); @@ -258,9 +257,9 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, int timeout) { struct cli_ch_state s_cch, *cch = &s_cch; - unsigned long num = 0; - unsigned long eol_num = 0; - unsigned long wlen; + uint num = 0; + uint eol_num = 0; + uint wlen; char ichar; int insert = 1; int init_len = strlen(buf);

We want to reuse the editing code elsewhere. As a first step, move the common variables into a struct. This will allow us to eventually put the contents of the inner loop in a function, so it can be called from elsewhere.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
common/cli_readline.c | 116 ++++++++++++++++++++++-------------------- include/cli.h | 14 +++++ 2 files changed, 75 insertions(+), 55 deletions(-)
diff --git a/common/cli_readline.c b/common/cli_readline.c index f863404110c3..03624ca18afe 100644 --- a/common/cli_readline.c +++ b/common/cli_readline.c @@ -188,27 +188,27 @@ void cread_print_hist_list(void) }
#define BEGINNING_OF_LINE() { \ - while (num) { \ + while (cls->num) { \ getcmd_putch(CTL_BACKSPACE); \ - num--; \ + cls->num--; \ } \ }
#define ERASE_TO_EOL() { \ - if (num < eol_num) { \ - printf("%*s", (int)(eol_num - num), ""); \ + if (cls->num < cls->eol_num) { \ + printf("%*s", (int)(cls->eol_num - cls->num), ""); \ do { \ getcmd_putch(CTL_BACKSPACE); \ - } while (--eol_num > num); \ + } while (--cls->eol_num > cls->num); \ } \ }
-#define REFRESH_TO_EOL() { \ - if (num < eol_num) { \ - wlen = eol_num - num; \ - putnstr(buf + num, wlen); \ - num = eol_num; \ - } \ +#define REFRESH_TO_EOL() { \ + if (cls->num < cls->eol_num) { \ + uint wlen = cls->eol_num - cls->num; \ + putnstr(buf + cls->num, wlen); \ + cls->num = cls->eol_num; \ + } \ }
static void cread_add_char(char ichar, int insert, uint *num, @@ -257,18 +257,18 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, int timeout) { struct cli_ch_state s_cch, *cch = &s_cch; - uint num = 0; - uint eol_num = 0; - uint wlen; + struct cli_line_state s_cls, *cls = &s_cls; char ichar; - int insert = 1; int init_len = strlen(buf); int first = 1;
cli_ch_init(cch); + memset(cls, '\0', sizeof(struct cli_line_state)); + cls->insert = true;
if (init_len) - cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len); + cread_add_str(buf, init_len, 1, &cls->num, &cls->eol_num, buf, + *len);
while (1) { /* Check for saved characters */ @@ -309,30 +309,33 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, *buf = '\0'; /* discard input */ return -1; case CTL_CH('f'): - if (num < eol_num) { - getcmd_putch(buf[num]); - num++; + if (cls->num < cls->eol_num) { + getcmd_putch(buf[cls->num]); + cls->num++; } break; case CTL_CH('b'): - if (num) { + if (cls->num) { getcmd_putch(CTL_BACKSPACE); - num--; + cls->num--; } break; case CTL_CH('d'): - if (num < eol_num) { - wlen = eol_num - num - 1; + if (cls->num < cls->eol_num) { + uint wlen; + + wlen = cls->eol_num - cls->num - 1; if (wlen) { - memmove(&buf[num], &buf[num+1], wlen); - putnstr(buf + num, wlen); + memmove(&buf[cls->num], + &buf[cls->num + 1], wlen); + putnstr(buf + cls->num, wlen); }
getcmd_putch(' '); do { getcmd_putch(CTL_BACKSPACE); } while (wlen--); - eol_num--; + cls->eol_num--; } break; case CTL_CH('k'): @@ -342,28 +345,28 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, REFRESH_TO_EOL(); break; case CTL_CH('o'): - insert = !insert; + cls->insert = !cls->insert; break; case CTL_CH('w'): - if (num) { - uint base; + if (cls->num) { + uint base, wlen;
- for (base = num - 1; + for (base = cls->num - 1; base >= 0 && buf[base] == ' ';) base--; for (; base > 0 && buf[base - 1] != ' ';) base--;
- /* now delete chars from base to num */ - wlen = num - base; - eol_num -= wlen; - memmove(&buf[base], &buf[num], - eol_num - base + 1); - num = base; + /* now delete chars from base to cls->num */ + wlen = cls->num - base; + cls->eol_num -= wlen; + memmove(&buf[base], &buf[cls->num], + cls->eol_num - base + 1); + cls->num = base; getcmd_putchars(wlen, CTL_BACKSPACE); puts(buf + base); getcmd_putchars(wlen, ' '); - getcmd_putchars(wlen + eol_num - num, + getcmd_putchars(wlen + cls->eol_num - cls->num, CTL_BACKSPACE); } break; @@ -375,17 +378,20 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, case DEL: case DEL7: case 8: - if (num) { - wlen = eol_num - num; - num--; - memmove(&buf[num], &buf[num+1], wlen); + if (cls->num) { + uint wlen; + + wlen = cls->eol_num - cls->num; + cls->num--; + memmove(&buf[cls->num], &buf[cls->num + 1], + wlen); getcmd_putch(CTL_BACKSPACE); - putnstr(buf + num, wlen); + putnstr(buf + cls->num, wlen); getcmd_putch(' '); do { getcmd_putch(CTL_BACKSPACE); } while (wlen--); - eol_num--; + cls->eol_num--; } break; case CTL_CH('p'): @@ -412,7 +418,7 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len,
/* copy new line into place and display */ strcpy(buf, hline); - eol_num = strlen(buf); + cls->eol_num = strlen(buf); REFRESH_TO_EOL(); continue; } @@ -421,30 +427,30 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, int num2, col;
/* do not autocomplete when in the middle */ - if (num < eol_num) { + if (cls->num < cls->eol_num) { getcmd_cbeep(); break; }
- buf[num] = '\0'; - col = strlen(prompt) + eol_num; - num2 = num; + buf[cls->num] = '\0'; + col = strlen(prompt) + cls->eol_num; + num2 = cls->num; if (cmd_auto_complete(prompt, buf, &num2, &col)) { - col = num2 - num; - num += col; - eol_num += col; + col = num2 - cls->num; + cls->num += col; + cls->eol_num += col; } break; } fallthrough; default: - cread_add_char(ichar, insert, &num, &eol_num, buf, - *len); + cread_add_char(ichar, cls->insert, &cls->num, + &cls->eol_num, buf, *len); break; } } - *len = eol_num; - buf[eol_num] = '\0'; /* lose the newline */ + *len = cls->eol_num; + buf[cls->eol_num] = '\0'; /* lose the newline */
if (buf[0] && buf[0] != CREAD_HIST_CHAR) cread_add_to_hist(buf); diff --git a/include/cli.h b/include/cli.h index ac09c80c784c..1bc1ab8035c8 100644 --- a/include/cli.h +++ b/include/cli.h @@ -8,6 +8,7 @@ #define __CLI_H
#include <stdbool.h> +#include <linux/types.h>
/** * struct cli_ch_state - state information for reading cmdline characters @@ -24,6 +25,19 @@ struct cli_ch_state { bool emitting; };
+/** + * struct cli_line_state - state of the line editor + * + * @num: Current cursor position, where 0 is the start + * @eol_num: Number of characters in the buffer + * @insert: true if in 'insert' mode + */ +struct cli_line_state { + uint num; + uint eol_num; + bool insert; +}; + /** * Go into the command loop *

Reduce the indentation level of this code so it is easier to review the next patch, which moves it into a function.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
common/cli_readline.c | 279 +++++++++++++++++++++--------------------- 1 file changed, 139 insertions(+), 140 deletions(-)
diff --git a/common/cli_readline.c b/common/cli_readline.c index 03624ca18afe..f5d1cb034338 100644 --- a/common/cli_readline.c +++ b/common/cli_readline.c @@ -292,162 +292,161 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, ichar = cli_ch_process(cch, ichar); }
- /* ichar=0x0 when error occurs in U-Boot getc */ - if (!ichar) - continue; - - if (ichar == '\n') { - putc('\n'); - break; - } + /* ichar=0x0 when error occurs in U-Boot getc */ + if (!ichar) + continue;
- switch (ichar) { - case CTL_CH('a'): - BEGINNING_OF_LINE(); - break; - case CTL_CH('c'): /* ^C - break */ - *buf = '\0'; /* discard input */ - return -1; - case CTL_CH('f'): - if (cls->num < cls->eol_num) { - getcmd_putch(buf[cls->num]); - cls->num++; - } - break; - case CTL_CH('b'): - if (cls->num) { - getcmd_putch(CTL_BACKSPACE); - cls->num--; - } - break; - case CTL_CH('d'): - if (cls->num < cls->eol_num) { - uint wlen; + if (ichar == '\n') { + putc('\n'); + break; + }
- wlen = cls->eol_num - cls->num - 1; - if (wlen) { - memmove(&buf[cls->num], - &buf[cls->num + 1], wlen); - putnstr(buf + cls->num, wlen); - } + switch (ichar) { + case CTL_CH('a'): + BEGINNING_OF_LINE(); + break; + case CTL_CH('c'): /* ^C - break */ + *buf = '\0'; /* discard input */ + return -1; + case CTL_CH('f'): + if (cls->num < cls->eol_num) { + getcmd_putch(buf[cls->num]); + cls->num++; + } + break; + case CTL_CH('b'): + if (cls->num) { + getcmd_putch(CTL_BACKSPACE); + cls->num--; + } + break; + case CTL_CH('d'): + if (cls->num < cls->eol_num) { + uint wlen;
- getcmd_putch(' '); - do { - getcmd_putch(CTL_BACKSPACE); - } while (wlen--); - cls->eol_num--; - } - break; - case CTL_CH('k'): - ERASE_TO_EOL(); - break; - case CTL_CH('e'): - REFRESH_TO_EOL(); - break; - case CTL_CH('o'): - cls->insert = !cls->insert; - break; - case CTL_CH('w'): - if (cls->num) { - uint base, wlen; - - for (base = cls->num - 1; - base >= 0 && buf[base] == ' ';) - base--; - for (; base > 0 && buf[base - 1] != ' ';) - base--; - - /* now delete chars from base to cls->num */ - wlen = cls->num - base; - cls->eol_num -= wlen; - memmove(&buf[base], &buf[cls->num], - cls->eol_num - base + 1); - cls->num = base; - getcmd_putchars(wlen, CTL_BACKSPACE); - puts(buf + base); - getcmd_putchars(wlen, ' '); - getcmd_putchars(wlen + cls->eol_num - cls->num, - CTL_BACKSPACE); - } - break; - case CTL_CH('x'): - case CTL_CH('u'): - BEGINNING_OF_LINE(); - ERASE_TO_EOL(); - break; - case DEL: - case DEL7: - case 8: - if (cls->num) { - uint wlen; - - wlen = cls->eol_num - cls->num; - cls->num--; + wlen = cls->eol_num - cls->num - 1; + if (wlen) { memmove(&buf[cls->num], &buf[cls->num + 1], wlen); - getcmd_putch(CTL_BACKSPACE); putnstr(buf + cls->num, wlen); - getcmd_putch(' '); - do { - getcmd_putch(CTL_BACKSPACE); - } while (wlen--); - cls->eol_num--; } - break; - case CTL_CH('p'): - case CTL_CH('n'): - { - char *hline;
- if (ichar == CTL_CH('p')) - hline = hist_prev(); - else - hline = hist_next(); - - if (!hline) { - getcmd_cbeep(); - continue; - } + getcmd_putch(' '); + do { + getcmd_putch(CTL_BACKSPACE); + } while (wlen--); + cls->eol_num--; + } + break; + case CTL_CH('k'): + ERASE_TO_EOL(); + break; + case CTL_CH('e'): + REFRESH_TO_EOL(); + break; + case CTL_CH('o'): + cls->insert = !cls->insert; + break; + case CTL_CH('w'): + if (cls->num) { + uint base, wlen; + + for (base = cls->num - 1; + base >= 0 && buf[base] == ' ';) + base--; + for (; base > 0 && buf[base - 1] != ' ';) + base--; + + /* now delete chars from base to cls->num */ + wlen = cls->num - base; + cls->eol_num -= wlen; + memmove(&buf[base], &buf[cls->num], + cls->eol_num - base + 1); + cls->num = base; + getcmd_putchars(wlen, CTL_BACKSPACE); + puts(buf + base); + getcmd_putchars(wlen, ' '); + getcmd_putchars(wlen + cls->eol_num - cls->num, + CTL_BACKSPACE); + } + break; + case CTL_CH('x'): + case CTL_CH('u'): + BEGINNING_OF_LINE(); + ERASE_TO_EOL(); + break; + case DEL: + case DEL7: + case 8: + if (cls->num) { + uint wlen; + + wlen = cls->eol_num - cls->num; + cls->num--; + memmove(&buf[cls->num], &buf[cls->num + 1], wlen); + getcmd_putch(CTL_BACKSPACE); + putnstr(buf + cls->num, wlen); + getcmd_putch(' '); + do { + getcmd_putch(CTL_BACKSPACE); + } while (wlen--); + cls->eol_num--; + } + break; + case CTL_CH('p'): + case CTL_CH('n'): + { + char *hline; + + if (ichar == CTL_CH('p')) + hline = hist_prev(); + else + hline = hist_next(); + + if (!hline) { + getcmd_cbeep(); + continue; + }
- /* nuke the current line */ - /* first, go home */ - BEGINNING_OF_LINE(); + /* nuke the current line */ + /* first, go home */ + BEGINNING_OF_LINE();
- /* erase to end of line */ - ERASE_TO_EOL(); + /* erase to end of line */ + ERASE_TO_EOL();
- /* copy new line into place and display */ - strcpy(buf, hline); - cls->eol_num = strlen(buf); - REFRESH_TO_EOL(); - continue; - } - case '\t': - if (IS_ENABLED(CONFIG_AUTO_COMPLETE)) { - int num2, col; - - /* do not autocomplete when in the middle */ - if (cls->num < cls->eol_num) { - getcmd_cbeep(); - break; - } + /* copy new line into place and display */ + strcpy(buf, hline); + cls->eol_num = strlen(buf); + REFRESH_TO_EOL(); + continue; + } + case '\t': + if (IS_ENABLED(CONFIG_AUTO_COMPLETE)) { + int num2, col;
- buf[cls->num] = '\0'; - col = strlen(prompt) + cls->eol_num; - num2 = cls->num; - if (cmd_auto_complete(prompt, buf, &num2, &col)) { - col = num2 - cls->num; - cls->num += col; - cls->eol_num += col; - } + /* do not autocomplete when in the middle */ + if (cls->num < cls->eol_num) { + getcmd_cbeep(); break; } - fallthrough; - default: - cread_add_char(ichar, cls->insert, &cls->num, - &cls->eol_num, buf, *len); + + buf[cls->num] = '\0'; + col = strlen(prompt) + cls->eol_num; + num2 = cls->num; + if (cmd_auto_complete(prompt, buf, &num2, &col)) { + col = num2 - cls->num; + cls->num += col; + cls->eol_num += col; + } break; } + fallthrough; + default: + cread_add_char(ichar, cls->insert, &cls->num, &cls->eol_num, + buf, *len); + break; + } } *len = cls->eol_num; buf[cls->eol_num] = '\0'; /* lose the newline */

Move most of the inner loop from cread_line() into a new function. This will allow using it from other code.
This involves adding a few more members to the state struct.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
common/cli_readline.c | 108 ++++++++++++++++++++++++------------------ include/cli.h | 15 ++++++ 2 files changed, 78 insertions(+), 45 deletions(-)
diff --git a/common/cli_readline.c b/common/cli_readline.c index f5d1cb034338..62d419bb36a7 100644 --- a/common/cli_readline.c +++ b/common/cli_readline.c @@ -253,52 +253,17 @@ static void cread_add_str(char *str, int strsize, int insert, } }
-static int cread_line(const char *const prompt, char *buf, unsigned int *len, - int timeout) +int cread_line_process_ch(struct cli_line_state *cls, char ichar) { - struct cli_ch_state s_cch, *cch = &s_cch; - struct cli_line_state s_cls, *cls = &s_cls; - char ichar; - int init_len = strlen(buf); - int first = 1; - - cli_ch_init(cch); - memset(cls, '\0', sizeof(struct cli_line_state)); - cls->insert = true; - - if (init_len) - cread_add_str(buf, init_len, 1, &cls->num, &cls->eol_num, buf, - *len); - - while (1) { - /* Check for saved characters */ - ichar = cli_ch_process(cch, 0); - - if (!ichar) { - if (bootretry_tstc_timeout()) - return -2; /* timed out */ - if (first && timeout) { - u64 etime = endtick(timeout); - - while (!tstc()) { /* while no incoming data */ - if (get_ticks() >= etime) - return -2; /* timed out */ - schedule(); - } - first = 0; - } - - ichar = getcmd_getch(); - ichar = cli_ch_process(cch, ichar); - } + char *buf = cls->buf;
/* ichar=0x0 when error occurs in U-Boot getc */ if (!ichar) - continue; + return -EAGAIN;
if (ichar == '\n') { putc('\n'); - break; + return 0; }
switch (ichar) { @@ -307,7 +272,7 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, break; case CTL_CH('c'): /* ^C - break */ *buf = '\0'; /* discard input */ - return -1; + return -EINTR; case CTL_CH('f'): if (cls->num < cls->eol_num) { getcmd_putch(buf[cls->num]); @@ -405,7 +370,7 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len,
if (!hline) { getcmd_cbeep(); - continue; + break; }
/* nuke the current line */ @@ -419,7 +384,7 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, strcpy(buf, hline); cls->eol_num = strlen(buf); REFRESH_TO_EOL(); - continue; + break; } case '\t': if (IS_ENABLED(CONFIG_AUTO_COMPLETE)) { @@ -432,9 +397,9 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, }
buf[cls->num] = '\0'; - col = strlen(prompt) + cls->eol_num; + col = strlen(cls->prompt) + cls->eol_num; num2 = cls->num; - if (cmd_auto_complete(prompt, buf, &num2, &col)) { + if (cmd_auto_complete(cls->prompt, buf, &num2, &col)) { col = num2 - cls->num; cls->num += col; cls->eol_num += col; @@ -444,9 +409,62 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, fallthrough; default: cread_add_char(ichar, cls->insert, &cls->num, &cls->eol_num, - buf, *len); + buf, cls->len); break; } + + return -EAGAIN; +} + +static int cread_line(const char *const prompt, char *buf, unsigned int *len, + int timeout) +{ + struct cli_ch_state s_cch, *cch = &s_cch; + struct cli_line_state s_cls, *cls = &s_cls; + char ichar; + int init_len = strlen(buf); + int first = 1; + + cli_ch_init(cch); + memset(cls, '\0', sizeof(struct cli_line_state)); + cls->insert = true; + cls->len = *len; + cls->prompt = prompt; + cls->buf = buf; + + if (init_len) + cread_add_str(buf, init_len, 1, &cls->num, &cls->eol_num, buf, + *len); + + while (1) { + int ret; + + /* Check for saved characters */ + ichar = cli_ch_process(cch, 0); + + if (!ichar) { + if (bootretry_tstc_timeout()) + return -2; /* timed out */ + if (first && timeout) { + u64 etime = endtick(timeout); + + while (!tstc()) { /* while no incoming data */ + if (get_ticks() >= etime) + return -2; /* timed out */ + schedule(); + } + first = 0; + } + + ichar = getcmd_getch(); + ichar = cli_ch_process(cch, ichar); + } + + ret = cread_line_process_ch(cls, ichar); + if (ret == -EINTR) + return -1; + else if (!ret) + break; } *len = cls->eol_num; buf[cls->eol_num] = '\0'; /* lose the newline */ diff --git a/include/cli.h b/include/cli.h index 1bc1ab8035c8..bbc532764355 100644 --- a/include/cli.h +++ b/include/cli.h @@ -31,11 +31,16 @@ struct cli_ch_state { * @num: Current cursor position, where 0 is the start * @eol_num: Number of characters in the buffer * @insert: true if in 'insert' mode + * @buf: Buffer containing line + * @prompt: Prompt for the line */ struct cli_line_state { uint num; uint eol_num; + uint len; bool insert; + char *buf; + const char *prompt; };
/** @@ -243,6 +248,16 @@ void cli_ch_init(struct cli_ch_state *cch); */ int cli_ch_process(struct cli_ch_state *cch, int ichar);
+/** + * cread_line_process_ch() - Process a character for line input + * + * @cls: CLI line state + * @ichar: Character to process + * Return: 0 if input is complete, with line in cls->buf, -EINTR if input was + * cancelled with Ctrl-C, -EAGAIN if more characters are needed + */ +int cread_line_process_ch(struct cli_line_state *cls, char ichar); + /** cread_print_hist_list() - Print the command-line history list */ void cread_print_hist_list(void);

Rather than relying on the caller, terminate the string inside this function. Do this each time we return, whether input is finished or not. It is not needed when the input is aborted, since the string will be discarded in that case.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
common/cli_readline.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/common/cli_readline.c b/common/cli_readline.c index 62d419bb36a7..fdb84d9204f6 100644 --- a/common/cli_readline.c +++ b/common/cli_readline.c @@ -263,6 +263,7 @@ int cread_line_process_ch(struct cli_line_state *cls, char ichar)
if (ichar == '\n') { putc('\n'); + buf[cls->eol_num] = '\0'; /* terminate the string */ return 0; }
@@ -413,6 +414,12 @@ int cread_line_process_ch(struct cli_line_state *cls, char ichar) break; }
+ /* + * keep the string terminated...if we added a char at the end then we + * want a \0 after it + */ + buf[cls->eol_num] = '\0'; + return -EAGAIN; }
@@ -467,7 +474,6 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, break; } *len = cls->eol_num; - buf[cls->eol_num] = '\0'; /* lose the newline */
if (buf[0] && buf[0] != CREAD_HIST_CHAR) cread_add_to_hist(buf);

When inputting text outside the command line we don't want history to be accessible. Add an option to control this.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
common/cli_readline.c | 42 ++++++++++++++++++++++-------------------- include/cli.h | 2 ++ 2 files changed, 24 insertions(+), 20 deletions(-)
diff --git a/common/cli_readline.c b/common/cli_readline.c index fdb84d9204f6..fa8f525d3a41 100644 --- a/common/cli_readline.c +++ b/common/cli_readline.c @@ -361,32 +361,33 @@ int cread_line_process_ch(struct cli_line_state *cls, char ichar) break; case CTL_CH('p'): case CTL_CH('n'): - { - char *hline; + if (cls->history) { + char *hline;
- if (ichar == CTL_CH('p')) - hline = hist_prev(); - else - hline = hist_next(); + if (ichar == CTL_CH('p')) + hline = hist_prev(); + else + hline = hist_next();
- if (!hline) { - getcmd_cbeep(); - break; - } + if (!hline) { + getcmd_cbeep(); + break; + }
- /* nuke the current line */ - /* first, go home */ - BEGINNING_OF_LINE(); + /* nuke the current line */ + /* first, go home */ + BEGINNING_OF_LINE();
- /* erase to end of line */ - ERASE_TO_EOL(); + /* erase to end of line */ + ERASE_TO_EOL();
- /* copy new line into place and display */ - strcpy(buf, hline); - cls->eol_num = strlen(buf); - REFRESH_TO_EOL(); + /* copy new line into place and display */ + strcpy(buf, hline); + cls->eol_num = strlen(buf); + REFRESH_TO_EOL(); + break; + } break; - } case '\t': if (IS_ENABLED(CONFIG_AUTO_COMPLETE)) { int num2, col; @@ -438,6 +439,7 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, cls->len = *len; cls->prompt = prompt; cls->buf = buf; + cls->history = true;
if (init_len) cread_add_str(buf, init_len, 1, &cls->num, &cls->eol_num, buf, diff --git a/include/cli.h b/include/cli.h index bbc532764355..252bdb70ab0d 100644 --- a/include/cli.h +++ b/include/cli.h @@ -31,6 +31,7 @@ struct cli_ch_state { * @num: Current cursor position, where 0 is the start * @eol_num: Number of characters in the buffer * @insert: true if in 'insert' mode + * @history: true if history should be accessible * @buf: Buffer containing line * @prompt: Prompt for the line */ @@ -39,6 +40,7 @@ struct cli_line_state { uint eol_num; uint len; bool insert; + bool history; char *buf; const char *prompt; };

When inputting text outside the command line we don't want to use tab for command completion. Add an option to control this.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
common/cli_readline.c | 3 ++- include/cli.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/common/cli_readline.c b/common/cli_readline.c index fa8f525d3a41..23913a857a99 100644 --- a/common/cli_readline.c +++ b/common/cli_readline.c @@ -389,7 +389,7 @@ int cread_line_process_ch(struct cli_line_state *cls, char ichar) } break; case '\t': - if (IS_ENABLED(CONFIG_AUTO_COMPLETE)) { + if (IS_ENABLED(CONFIG_AUTO_COMPLETE) && cls->cmd_complete) { int num2, col;
/* do not autocomplete when in the middle */ @@ -440,6 +440,7 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, cls->prompt = prompt; cls->buf = buf; cls->history = true; + cls->cmd_complete = true;
if (init_len) cread_add_str(buf, init_len, 1, &cls->num, &cls->eol_num, buf, diff --git a/include/cli.h b/include/cli.h index 252bdb70ab0d..ad3cb4499fe1 100644 --- a/include/cli.h +++ b/include/cli.h @@ -32,6 +32,7 @@ struct cli_ch_state { * @eol_num: Number of characters in the buffer * @insert: true if in 'insert' mode * @history: true if history should be accessible + * @cmd_complete: true if tab completion should be enabled * @buf: Buffer containing line * @prompt: Prompt for the line */ @@ -41,6 +42,7 @@ struct cli_line_state { uint len; bool insert; bool history; + bool cmd_complete; char *buf; const char *prompt; };

Create a init function so that it is easy to use command-line reading.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
common/cli_readline.c | 24 +++++++++++++++--------- include/cli.h | 17 ++++++++++++++++- 2 files changed, 31 insertions(+), 10 deletions(-)
diff --git a/common/cli_readline.c b/common/cli_readline.c index 23913a857a99..06b8d4650447 100644 --- a/common/cli_readline.c +++ b/common/cli_readline.c @@ -424,28 +424,34 @@ int cread_line_process_ch(struct cli_line_state *cls, char ichar) return -EAGAIN; }
+void cli_cread_init(struct cli_line_state *cls, char *buf, uint buf_size) +{ + int init_len = strlen(buf); + + memset(cls, '\0', sizeof(struct cli_line_state)); + cls->insert = true; + cls->buf = buf; + cls->len = buf_size; + + if (init_len) + cread_add_str(buf, init_len, 0, &cls->num, &cls->eol_num, buf, + buf_size); +} + static int cread_line(const char *const prompt, char *buf, unsigned int *len, int timeout) { struct cli_ch_state s_cch, *cch = &s_cch; struct cli_line_state s_cls, *cls = &s_cls; char ichar; - int init_len = strlen(buf); int first = 1;
cli_ch_init(cch); - memset(cls, '\0', sizeof(struct cli_line_state)); - cls->insert = true; - cls->len = *len; + cli_cread_init(cls, buf, *len); cls->prompt = prompt; - cls->buf = buf; cls->history = true; cls->cmd_complete = true;
- if (init_len) - cread_add_str(buf, init_len, 1, &cls->num, &cls->eol_num, buf, - *len); - while (1) { int ret;
diff --git a/include/cli.h b/include/cli.h index ad3cb4499fe1..e183d5613697 100644 --- a/include/cli.h +++ b/include/cli.h @@ -32,7 +32,8 @@ struct cli_ch_state { * @eol_num: Number of characters in the buffer * @insert: true if in 'insert' mode * @history: true if history should be accessible - * @cmd_complete: true if tab completion should be enabled + * @cmd_complete: true if tab completion should be enabled (requires @prompt to + * be set) * @buf: Buffer containing line * @prompt: Prompt for the line */ @@ -262,6 +263,20 @@ int cli_ch_process(struct cli_ch_state *cch, int ichar); */ int cread_line_process_ch(struct cli_line_state *cls, char ichar);
+/** + * cli_cread_init() - Set up a new cread struct + * + * Sets up a new cread state, with history and cmd_complete set to false + * + * After calling this, you can use cread_line_process_ch() to process characters + * received from the user. + * + * @cls: CLI line state + * @buf: Text buffer containing the initial text + * @buf_size: Buffer size, including nul terminator + */ +void cli_cread_init(struct cli_line_state *cls, char *buf, uint buf_size); + /** cread_print_hist_list() - Print the command-line history list */ void cread_print_hist_list(void);

At present there is a method for measuring text, but if the actual text string is not known, it cannot be used.
For text editor we want to set the size of the entry box to cover the expected text size. Add the concept of a 'norminal' size with a method to calculate that for the vidconsole.
If the method is not implemented, fall back to using the font size, which is sufficient for fixed-width fonts.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
drivers/video/console_truetype.c | 31 +++++++++++++++++++++++++++++++ drivers/video/vidconsole-uclass.c | 22 ++++++++++++++++++++++ include/video_console.h | 30 ++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+)
diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c index 0f9bb49e44f7..8ed79c37676c 100644 --- a/drivers/video/console_truetype.c +++ b/drivers/video/console_truetype.c @@ -750,6 +750,36 @@ int truetype_measure(struct udevice *dev, const char *name, uint size, return 0; }
+static int truetype_nominal(struct udevice *dev, const char *name, uint size, + uint num_chars, struct vidconsole_bbox *bbox) +{ + struct console_tt_metrics *met; + stbtt_fontinfo *font; + int lsb, advance; + int width; + int ret; + + ret = get_metrics(dev, name, size, &met); + if (ret) + return log_msg_ret("sel", ret); + + font = &met->font; + width = 0; + + /* First get some basic metrics about this character */ + stbtt_GetCodepointHMetrics(font, 'W', &advance, &lsb); + + width = advance; + + bbox->valid = true; + bbox->x0 = 0; + bbox->y0 = 0; + bbox->x1 = tt_ceil((double)width * num_chars * met->scale); + bbox->y1 = met->font_size; + + return 0; +} + const char *console_truetype_get_font_size(struct udevice *dev, uint *sizep) { struct console_tt_priv *priv = dev_get_priv(dev); @@ -802,6 +832,7 @@ struct vidconsole_ops console_truetype_ops = { .get_font_size = console_truetype_get_font_size, .select_font = truetype_select_font, .measure = truetype_measure, + .nominal = truetype_nominal, };
U_BOOT_DRIVER(vidconsole_truetype) = { diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c index b5b3b6625902..23b1b81731bc 100644 --- a/drivers/video/vidconsole-uclass.c +++ b/drivers/video/vidconsole-uclass.c @@ -618,6 +618,28 @@ int vidconsole_measure(struct udevice *dev, const char *name, uint size, return 0; }
+int vidconsole_nominal(struct udevice *dev, const char *name, uint size, + uint num_chars, struct vidconsole_bbox *bbox) +{ + struct vidconsole_priv *priv = dev_get_uclass_priv(dev); + struct vidconsole_ops *ops = vidconsole_get_ops(dev); + int ret; + + if (ops->measure) { + ret = ops->nominal(dev, name, size, num_chars, bbox); + if (ret != -ENOSYS) + return ret; + } + + bbox->valid = true; + bbox->x0 = 0; + bbox->y0 = 0; + bbox->x1 = priv->x_charsize * num_chars; + bbox->y1 = priv->y_charsize; + + return 0; +} + void vidconsole_push_colour(struct udevice *dev, enum colour_idx fg, enum colour_idx bg, struct vidconsole_colour *old) { diff --git a/include/video_console.h b/include/video_console.h index 2694e44f6ecf..5234c85efd6f 100644 --- a/include/video_console.h +++ b/include/video_console.h @@ -224,6 +224,21 @@ struct vidconsole_ops { */ int (*measure)(struct udevice *dev, const char *name, uint size, const char *text, struct vidconsole_bbox *bbox); + + /** + * nominal() - Measure the expected width of a line of text + * + * Uses an average font width and nominal height + * + * @dev: Console device to use + * @name: Font name, NULL for default + * @size: Font size, ignored if @name is NULL + * @num_chars: Number of characters to use + * @bbox: Returns nounding box of @num_chars characters + * Returns: 0 if OK, -ve on error + */ + int (*nominal)(struct udevice *dev, const char *name, uint size, + uint num_chars, struct vidconsole_bbox *bbox); };
/* Get a pointer to the driver operations for a video console device */ @@ -263,6 +278,21 @@ int vidconsole_select_font(struct udevice *dev, const char *name, uint size); int vidconsole_measure(struct udevice *dev, const char *name, uint size, const char *text, struct vidconsole_bbox *bbox);
+/** + * vidconsole_nominal() - Measure the expected width of a line of text + * + * Uses an average font width and nominal height + * + * @dev: Console device to use + * @name: Font name, NULL for default + * @size: Font size, ignored if @name is NULL + * @num_chars: Number of characters to use + * @bbox: Returns nounding box of @num_chars characters + * Returns: 0 if OK, -ve on error + */ +int vidconsole_nominal(struct udevice *dev, const char *name, uint size, + uint num_chars, struct vidconsole_bbox *bbox); + /** * vidconsole_push_colour() - Temporarily change the font colour *

Text entry operates within a context which includes quite a bit of information. For example, with Truetype fonts, each character in the text string has a position stored, so that it is possible to backspace to that character. This information is built up as strings are drawn on the display.
For the command line, there is just a single context. It is created when command-line entry starts and it is destroyed (or at least not needed anymore) when the user presses <enter> to enter the command.
By contrast, expo needs to be able to switch in and out of a text-entry context, since it is also displaying other objects in the scene.
Add a way to save and restore the entry context for a vidconsole. This is only needed for the truetype vidconsole, so add a method for that, storing the information in an abuf struct.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
drivers/video/console_truetype.c | 53 +++++++++++++++++++++++++++++++ drivers/video/vidconsole-uclass.c | 32 +++++++++++++++++++ include/video_console.h | 49 ++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+)
diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c index 8ed79c37676c..1ce01a96bb6c 100644 --- a/drivers/video/console_truetype.c +++ b/drivers/video/console_truetype.c @@ -4,6 +4,7 @@ */
#include <common.h> +#include <abuf.h> #include <dm.h> #include <log.h> #include <malloc.h> @@ -175,6 +176,17 @@ struct console_tt_priv { int pos_ptr; };
+/** + * struct console_tt_store - Format used for save/restore of entry information + * + * @priv: Private data + * @cur: Current cursor position + */ +struct console_tt_store { + struct console_tt_priv priv; + struct pos_info cur; +}; + static int console_truetype_set_row(struct udevice *dev, uint row, int clr) { struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent); @@ -780,6 +792,45 @@ static int truetype_nominal(struct udevice *dev, const char *name, uint size, return 0; }
+static int truetype_entry_save(struct udevice *dev, struct abuf *buf) +{ + struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev); + struct console_tt_priv *priv = dev_get_priv(dev); + struct console_tt_store store; + const uint size = sizeof(store); + + /* + * store the whole priv structure as it is simpler that picking out + * what we need + */ + if (!abuf_realloc(buf, size)) + return log_msg_ret("sav", -ENOMEM); + + store.priv = *priv; + store.cur.xpos_frac = vc_priv->xcur_frac; + store.cur.ypos = vc_priv->ycur; + memcpy(abuf_data(buf), &store, size); + + return 0; +} + +static int truetype_entry_restore(struct udevice *dev, struct abuf *buf) +{ + struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev); + struct console_tt_priv *priv = dev_get_priv(dev); + struct console_tt_store store; + + memcpy(&store, abuf_data(buf), sizeof(store)); + + vc_priv->xcur_frac = store.cur.xpos_frac; + vc_priv->ycur = store.cur.ypos; + priv->pos_ptr = store.priv.pos_ptr; + memcpy(priv->pos, store.priv.pos, + store.priv.pos_ptr * sizeof(struct pos_info)); + + return 0; +} + const char *console_truetype_get_font_size(struct udevice *dev, uint *sizep) { struct console_tt_priv *priv = dev_get_priv(dev); @@ -833,6 +884,8 @@ struct vidconsole_ops console_truetype_ops = { .select_font = truetype_select_font, .measure = truetype_measure, .nominal = truetype_nominal, + .entry_save = truetype_entry_save, + .entry_restore = truetype_entry_restore, };
U_BOOT_DRIVER(vidconsole_truetype) = { diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c index 23b1b81731bc..07427ba346b0 100644 --- a/drivers/video/vidconsole-uclass.c +++ b/drivers/video/vidconsole-uclass.c @@ -10,6 +10,7 @@ #define LOG_CATEGORY UCLASS_VIDEO_CONSOLE
#include <common.h> +#include <abuf.h> #include <command.h> #include <console.h> #include <log.h> @@ -640,6 +641,37 @@ int vidconsole_nominal(struct udevice *dev, const char *name, uint size, return 0; }
+int vidconsole_entry_save(struct udevice *dev, struct abuf *buf) +{ + struct vidconsole_ops *ops = vidconsole_get_ops(dev); + int ret; + + if (ops->measure) { + ret = ops->entry_save(dev, buf); + if (ret != -ENOSYS) + return ret; + } + + /* no data so make sure the buffer is empty */ + abuf_realloc(buf, 0); + + return 0; +} + +int vidconsole_entry_restore(struct udevice *dev, struct abuf *buf) +{ + struct vidconsole_ops *ops = vidconsole_get_ops(dev); + int ret; + + if (ops->measure) { + ret = ops->entry_restore(dev, buf); + if (ret != -ENOSYS) + return ret; + } + + return 0; +} + void vidconsole_push_colour(struct udevice *dev, enum colour_idx fg, enum colour_idx bg, struct vidconsole_colour *old) { diff --git a/include/video_console.h b/include/video_console.h index 5234c85efd6f..28d65451889a 100644 --- a/include/video_console.h +++ b/include/video_console.h @@ -8,6 +8,7 @@
#include <video.h>
+struct abuf; struct video_priv;
#define VID_FRAC_DIV 256 @@ -239,6 +240,30 @@ struct vidconsole_ops { */ int (*nominal)(struct udevice *dev, const char *name, uint size, uint num_chars, struct vidconsole_bbox *bbox); + + /** + * entry_save() - Save any text-entry information for later use + * + * Saves text-entry context such as a list of positions for each + * character in the string. + * + * @dev: Console device to use + * @buf: Buffer to hold saved data + * Return: 0 if OK, -ENOMEM if out of memory + */ + int (*entry_save)(struct udevice *dev, struct abuf *buf); + + /** + * entry_restore() - Restore text-entry information for current use + * + * Restores text-entry context such as a list of positions for each + * character in the string. + * + * @dev: Console device to use + * @buf: Buffer containing data to restore + * Return: 0 if OK, -ve on error + */ + int (*entry_restore)(struct udevice *dev, struct abuf *buf); };
/* Get a pointer to the driver operations for a video console device */ @@ -293,6 +318,30 @@ int vidconsole_measure(struct udevice *dev, const char *name, uint size, int vidconsole_nominal(struct udevice *dev, const char *name, uint size, uint num_chars, struct vidconsole_bbox *bbox);
+/** + * vidconsole_entry_save() - Save any text-entry information for later use + * + * Saves text-entry context such as a list of positions for each + * character in the string. + * + * @dev: Console device to use + * @buf: Buffer to hold saved data + * Return: 0 if OK, -ENOMEM if out of memory + */ +int vidconsole_entry_save(struct udevice *dev, struct abuf *buf); + +/** + * entry_restore() - Restore text-entry information for current use + * + * Restores text-entry context such as a list of positions for each + * character in the string. + * + * @dev: Console device to use + * @buf: Buffer containing data to restore + * Return: 0 if OK, -ve on error + */ +int vidconsole_entry_restore(struct udevice *dev, struct abuf *buf); + /** * vidconsole_push_colour() - Temporarily change the font colour *

At present this is called only when a newline is detected, since this indicates the start of a line of text being entered.
Export this function so it can be used by expo, which may start a new text line itself, without first writing out a newline.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
drivers/video/vidconsole-uclass.c | 2 +- include/video_console.h | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c index 07427ba346b0..2f3f685a55c9 100644 --- a/drivers/video/vidconsole-uclass.c +++ b/drivers/video/vidconsole-uclass.c @@ -48,7 +48,7 @@ int vidconsole_set_row(struct udevice *dev, uint row, int clr) return ops->set_row(dev, row, clr); }
-static int vidconsole_entry_start(struct udevice *dev) +int vidconsole_entry_start(struct udevice *dev) { struct vidconsole_ops *ops = vidconsole_get_ops(dev);
diff --git a/include/video_console.h b/include/video_console.h index 28d65451889a..87f9a588575d 100644 --- a/include/video_console.h +++ b/include/video_console.h @@ -399,6 +399,15 @@ int vidconsole_move_rows(struct udevice *dev, uint rowdst, uint rowsrc, */ int vidconsole_set_row(struct udevice *dev, uint row, int clr);
+/** + * vidconsole_entry_start() - Set the start position of a vidconsole line + * + * Marks the current cursor position as the start of a line + * + * @dev: Device to adjust + */ +int vidconsole_entry_start(struct udevice *dev); + /** * vidconsole_put_char() - Output a character to the current console position *

Add rudimentary support for displaying a cursor on a vidconsole. This helps the user to see where text is being entered.
The implementation so far is very simple: the cursor is just a vertical bar of fixed width and cannot be erased. To erase the cursor, the text must be redrawn over it.
This is good enough for expo but will need enhancement to be useful for the command-line console. For example, it could save and restore the area behind the cursor.
For now, enable this only for expo, to reduce code size.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
drivers/video/console_core.c | 31 +++++++++ drivers/video/console_normal.c | 29 ++++++++ drivers/video/console_truetype.c | 103 ++++++++++++++++++++++++++++ drivers/video/vidconsole-uclass.c | 15 ++++ drivers/video/vidconsole_internal.h | 24 +++++++ include/video_console.h | 35 ++++++++++ 6 files changed, 237 insertions(+)
diff --git a/drivers/video/console_core.c b/drivers/video/console_core.c index b5d0e3dceca3..d17764d0b086 100644 --- a/drivers/video/console_core.c +++ b/drivers/video/console_core.c @@ -176,6 +176,37 @@ int fill_char_horizontally(uchar *pfont, void **line, struct video_priv *vid_pri return ret; }
+int draw_cursor_vertically(void **line, struct video_priv *vid_priv, + uint height, bool direction) +{ + int step, line_step, pbytes, ret; + uint value; + void *dst; + + ret = check_bpix_support(vid_priv->bpix); + if (ret) + return ret; + + pbytes = VNBYTES(vid_priv->bpix); + if (direction) { + step = -pbytes; + line_step = -vid_priv->line_length; + } else { + step = pbytes; + line_step = vid_priv->line_length; + } + + value = vid_priv->colour_fg; + + for (int row = 0; row < height; row++) { + dst = *line; + for (int col = 0; col < VIDCONSOLE_CURSOR_WIDTH; col++) + fill_pixel_and_goto_next(&dst, value, pbytes, step); + *line += line_step; + } + return ret; +} + int console_probe(struct udevice *dev) { return console_set_font(dev, fonts); diff --git a/drivers/video/console_normal.c b/drivers/video/console_normal.c index 413c7abee9e1..a0231293f311 100644 --- a/drivers/video/console_normal.c +++ b/drivers/video/console_normal.c @@ -97,6 +97,34 @@ static int console_putc_xy(struct udevice *dev, uint x_frac, uint y, char ch) return VID_TO_POS(fontdata->width); }
+static int console_set_cursor_visible(struct udevice *dev, bool visible, + uint x, uint y, uint index) +{ + struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev); + struct udevice *vid = dev->parent; + struct video_priv *vid_priv = dev_get_uclass_priv(vid); + struct console_simple_priv *priv = dev_get_priv(dev); + struct video_fontdata *fontdata = priv->fontdata; + int pbytes = VNBYTES(vid_priv->bpix); + void *start, *line; + + /* for now, this is not used outside expo */ + if (!IS_ENABLED(CONFIG_EXPO)) + return -ENOSYS; + + x += index * fontdata->width; + start = vid_priv->fb + y * vid_priv->line_length + x * pbytes; + + /* place the cursor 1 pixel before the start of the next char */ + x -= 1; + + line = start; + draw_cursor_vertically(&line, vid_priv, vc_priv->y_charsize, + NORMAL_DIRECTION); + + return 0; +} + struct vidconsole_ops console_ops = { .putc_xy = console_putc_xy, .move_rows = console_move_rows, @@ -104,6 +132,7 @@ struct vidconsole_ops console_ops = { .get_font_size = console_simple_get_font_size, .get_font = console_simple_get_font, .select_font = console_simple_select_font, + .set_cursor_visible = console_set_cursor_visible, };
U_BOOT_DRIVER(vidconsole_normal) = { diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c index 1ce01a96bb6c..242b8b71342d 100644 --- a/drivers/video/console_truetype.c +++ b/drivers/video/console_truetype.c @@ -831,6 +831,108 @@ static int truetype_entry_restore(struct udevice *dev, struct abuf *buf) return 0; }
+static int truetype_set_cursor_visible(struct udevice *dev, bool visible, + uint x, uint y, uint index) +{ + struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev); + struct udevice *vid = dev->parent; + struct video_priv *vid_priv = dev_get_uclass_priv(vid); + struct console_tt_priv *priv = dev_get_priv(dev); + struct console_tt_metrics *met = priv->cur_met; + uint row, width, height, xoff; + void *start, *line; + uint out, val; + int ret; + + if (!visible) + return 0; + + /* + * figure out where to place the cursor. This driver ignores the + * passed-in values, since an entry_restore() must have been done before + * calling this function. + */ + if (index < priv->pos_ptr) + x = VID_TO_PIXEL(priv->pos[index].xpos_frac); + else + x = VID_TO_PIXEL(vc_priv->xcur_frac); + + y = vc_priv->ycur; + height = met->font_size; + xoff = 0; + + val = vid_priv->colour_bg ? 0 : 255; + width = VIDCONSOLE_CURSOR_WIDTH; + + /* Figure out where to write the cursor in the frame buffer */ + start = vid_priv->fb + y * vid_priv->line_length + + x * VNBYTES(vid_priv->bpix); + line = start; + + /* draw a vertical bar in the correct position */ + for (row = 0; row < height; row++) { + switch (vid_priv->bpix) { + case VIDEO_BPP8: + if (IS_ENABLED(CONFIG_VIDEO_BPP8)) { + u8 *dst = line + xoff; + int i; + + out = val; + for (i = 0; i < width; i++) { + if (vid_priv->colour_fg) + *dst++ |= out; + else + *dst++ &= out; + } + } + break; + case VIDEO_BPP16: { + u16 *dst = (u16 *)line + xoff; + int i; + + if (IS_ENABLED(CONFIG_VIDEO_BPP16)) { + for (i = 0; i < width; i++) { + out = val >> 3 | + (val >> 2) << 5 | + (val >> 3) << 11; + if (vid_priv->colour_fg) + *dst++ |= out; + else + *dst++ &= out; + } + } + break; + } + case VIDEO_BPP32: { + u32 *dst = (u32 *)line + xoff; + int i; + + if (IS_ENABLED(CONFIG_VIDEO_BPP32)) { + for (i = 0; i < width; i++) { + int out; + + out = val | val << 8 | val << 16; + if (vid_priv->colour_fg) + *dst++ |= out; + else + *dst++ &= out; + } + } + break; + } + default: + return -ENOSYS; + } + + line += vid_priv->line_length; + } + ret = vidconsole_sync_copy(dev, start, line); + if (ret) + return ret; + + return video_sync(vid, true); +} + const char *console_truetype_get_font_size(struct udevice *dev, uint *sizep) { struct console_tt_priv *priv = dev_get_priv(dev); @@ -886,6 +988,7 @@ struct vidconsole_ops console_truetype_ops = { .nominal = truetype_nominal, .entry_save = truetype_entry_save, .entry_restore = truetype_entry_restore, + .set_cursor_visible = truetype_set_cursor_visible };
U_BOOT_DRIVER(vidconsole_truetype) = { diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c index 2f3f685a55c9..22d55df71f63 100644 --- a/drivers/video/vidconsole-uclass.c +++ b/drivers/video/vidconsole-uclass.c @@ -672,6 +672,21 @@ int vidconsole_entry_restore(struct udevice *dev, struct abuf *buf) return 0; }
+int vidconsole_set_cursor_visible(struct udevice *dev, bool visible, + uint x, uint y, uint index) +{ + struct vidconsole_ops *ops = vidconsole_get_ops(dev); + int ret; + + if (ops->set_cursor_visible) { + ret = ops->set_cursor_visible(dev, visible, x, y, index); + if (ret != -ENOSYS) + return ret; + } + + return 0; +} + void vidconsole_push_colour(struct udevice *dev, enum colour_idx fg, enum colour_idx bg, struct vidconsole_colour *old) { diff --git a/drivers/video/vidconsole_internal.h b/drivers/video/vidconsole_internal.h index c41edd45249a..0ec581b26638 100644 --- a/drivers/video/vidconsole_internal.h +++ b/drivers/video/vidconsole_internal.h @@ -92,6 +92,30 @@ int fill_char_vertically(uchar *pfont, void **line, struct video_priv *vid_priv, int fill_char_horizontally(uchar *pfont, void **line, struct video_priv *vid_priv, struct video_fontdata *fontdata, bool direction);
+/** + * draw_cursor_vertically() - Draw a simple vertical cursor + * + * @line: pointer to framebuffer buffer: upper left cursor corner + * @vid_priv: driver private data + * @height: height of the cursor in pixels + * @param direction controls cursor orientation. Can be normal or flipped. + * When normal: When flipped: + *|-----------------------------------------------| + *| * | line stepping | + *| ^ * * * * * | | | + *| | * * | v * * | + *| | | * * * * * | + *| line stepping | * | + *| | | + *| stepping -> | <<- stepping | + *|---!!we're starting from upper left char corner| + *|-----------------------------------------------| + * + * Return: 0, if success, or else error code. + */ +int draw_cursor_vertically(void **line, struct video_priv *vid_priv, + uint height, bool direction); + /** * console probe function. * diff --git a/include/video_console.h b/include/video_console.h index 87f9a588575d..bde67fa9a5a9 100644 --- a/include/video_console.h +++ b/include/video_console.h @@ -16,6 +16,11 @@ struct video_priv; #define VID_TO_PIXEL(x) ((x) / VID_FRAC_DIV) #define VID_TO_POS(x) ((x) * VID_FRAC_DIV)
+enum { + /* cursor width in pixels */ + VIDCONSOLE_CURSOR_WIDTH = 2, +}; + /** * struct vidconsole_priv - uclass-private data about a console device * @@ -264,6 +269,21 @@ struct vidconsole_ops { * Return: 0 if OK, -ve on error */ int (*entry_restore)(struct udevice *dev, struct abuf *buf); + + /** + * set_cursor_visible() - Show or hide the cursor + * + * Shows or hides a cursor at the current position + * + * @dev: Console device to use + * @visible: true to show the cursor, false to hide it + * @x: X position in pixels + * @y: Y position in pixels + * @index: Character position (0 = at start) + * Return: 0 if OK, -ve on error + */ + int (*set_cursor_visible)(struct udevice *dev, bool visible, + uint x, uint y, uint index); };
/* Get a pointer to the driver operations for a video console device */ @@ -342,6 +362,21 @@ int vidconsole_entry_save(struct udevice *dev, struct abuf *buf); */ int vidconsole_entry_restore(struct udevice *dev, struct abuf *buf);
+/** + * vidconsole_set_cursor_visible() - Show or hide the cursor + * + * Shows or hides a cursor at the current position + * + * @dev: Console device to use + * @visible: true to show the cursor, false to hide it + * @x: X position in pixels + * @y: Y position in pixels + * @index: Character position (0 = at start) + * Return: 0 if OK, -ve on error + */ +int vidconsole_set_cursor_visible(struct udevice *dev, bool visible, + uint x, uint y, uint index); + /** * vidconsole_push_colour() - Temporarily change the font colour *

When building an expo fails, show some information about which node caused the problem. Use -ENOENT consistently when the ID is missing. This makes it easier for the user to debug things.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: - Drop trailing ;
boot/expo_build.c | 57 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 12 deletions(-)
diff --git a/boot/expo_build.c b/boot/expo_build.c index bb33cc2a33f4..66a4e5e67d6d 100644 --- a/boot/expo_build.c +++ b/boot/expo_build.c @@ -23,10 +23,14 @@ * if there is nothing for this ID. Since ID 0 is never used, the first * element of this array is always NULL * @str_count: Number of entries in @str_for_id + * @err_node: Node being processed (for error reporting) + * @err_prop: Property being processed (for error reporting) */ struct build_info { const char **str_for_id; int str_count; + ofnode err_node; + const char *err_prop; };
/** @@ -46,6 +50,7 @@ int add_txt_str(struct build_info *info, ofnode node, struct scene *scn, uint str_id; int ret;
+ info->err_prop = find_name; text = ofnode_read_string(node, find_name); if (!text) { char name[40]; @@ -54,7 +59,7 @@ int add_txt_str(struct build_info *info, ofnode node, struct scene *scn, snprintf(name, sizeof(name), "%s-id", find_name); ret = ofnode_read_u32(node, name, &id); if (ret) - return log_msg_ret("id", -EINVAL); + return log_msg_ret("id", -ENOENT);
if (id >= info->str_count) return log_msg_ret("id", -E2BIG); @@ -164,9 +169,10 @@ static int read_strings(struct build_info *info, ofnode root) int ret; u32 id;
+ info->err_node = node; ret = ofnode_read_u32(node, "id", &id); if (ret) - return log_msg_ret("id", -EINVAL); + return log_msg_ret("id", -ENOENT); val = ofnode_read_string(node, "value"); if (!val) return log_msg_ret("val", -EINVAL); @@ -241,6 +247,8 @@ static int menu_build(struct build_info *info, ofnode node, struct scene *scn, return log_msg_ret("tit", ret); title_id = ret; ret = scene_menu_set_title(scn, menu_id, title_id); + if (ret) + return log_msg_ret("set", ret);
item_ids = ofnode_read_prop(node, "item-id", &size); if (!item_ids) @@ -300,7 +308,7 @@ static int obj_build(struct build_info *info, ofnode node, struct scene *scn) log_debug("- object %s\n", ofnode_get_name(node)); ret = ofnode_read_u32(node, "id", &id); if (ret) - return log_msg_ret("id", -EINVAL); + return log_msg_ret("id", -ENOENT);
type = ofnode_read_string(node, "type"); if (!type) @@ -308,8 +316,8 @@ static int obj_build(struct build_info *info, ofnode node, struct scene *scn)
if (!strcmp("menu", type)) ret = menu_build(info, node, scn, id, &obj); - else - ret = -EINVAL; + else + ret = -EOPNOTSUPP; if (ret) return log_msg_ret("bld", ret);
@@ -341,11 +349,12 @@ static int scene_build(struct build_info *info, ofnode scn_node, ofnode node; int ret;
+ info->err_node = scn_node; name = ofnode_get_name(scn_node); log_debug("Building scene %s\n", name); ret = ofnode_read_u32(scn_node, "id", &id); if (ret) - return log_msg_ret("id", -EINVAL); + return log_msg_ret("id", -ENOENT);
ret = scene_new(exp, name, id, &scn); if (ret < 0) @@ -362,6 +371,7 @@ static int scene_build(struct build_info *info, ofnode scn_node, return log_msg_ret("pr", ret);
ofnode_for_each_subnode(node, scn_node) { + info->err_node = node; ret = obj_build(info, node, scn); if (ret < 0) return log_msg_ret("mit", ret); @@ -370,20 +380,19 @@ static int scene_build(struct build_info *info, ofnode scn_node, return 0; }
-int expo_build(ofnode root, struct expo **expp) +int build_it(struct build_info *info, ofnode root, struct expo **expp) { - struct build_info info; ofnode scenes, node; struct expo *exp; u32 dyn_start; int ret;
- memset(&info, '\0', sizeof(info)); - ret = read_strings(&info, root); + ret = read_strings(info, root); if (ret) return log_msg_ret("str", ret); if (_DEBUG) - list_strings(&info); + list_strings(info); + info->err_node = root;
ret = expo_new("name", NULL, &exp); if (ret) @@ -397,7 +406,7 @@ int expo_build(ofnode root, struct expo **expp) return log_msg_ret("sno", -EINVAL);
ofnode_for_each_subnode(node, scenes) { - ret = scene_build(&info, node, exp); + ret = scene_build(info, node, exp); if (ret < 0) return log_msg_ret("scn", ret); } @@ -405,3 +414,27 @@ int expo_build(ofnode root, struct expo **expp)
return 0; } + +int expo_build(ofnode root, struct expo **expp) +{ + struct build_info info; + struct expo *exp; + int ret; + + memset(&info, '\0', sizeof(info)); + ret = build_it(&info, root, &exp); + if (ret) { + char buf[120]; + int node_ret; + + node_ret = ofnode_get_path(info.err_node, buf, sizeof(buf)); + log_warning("Build failed at node %s, property %s\n", + node_ret ? ofnode_get_name(info.err_node) : buf, + info.err_prop); + + return log_msg_ret("bui", ret); + } + *expp = exp; + + return 0; +}

This internal function could use a comment. Add one.
Also tidy up a few other comments.
Signed-off-by: Simon Glass sjg@chromium.org
fixup: comments ---
(no changes since v1)
boot/cedit.c | 10 ++++++++++ boot/scene_internal.h | 2 +- include/expo.h | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/boot/cedit.c b/boot/cedit.c index 73645f70b6cb..1324a8619981 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -229,6 +229,16 @@ static int check_space(int ret, struct abuf *buf) return 0; }
+/** + * get_cur_menuitem_text() - Get the text of the currently selected item + * + * Looks up the object for the current item, finds text object for it and looks + * up the string for that text + * + * @menu: Menu to look at + * @strp: Returns a pointer to the next + * Return: 0 if OK, -ENOENT if something was not found + */ static int get_cur_menuitem_text(const struct scene_obj_menu *menu, const char **strp) { diff --git a/boot/scene_internal.h b/boot/scene_internal.h index 695a907dc6af..42efcee092f3 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -185,7 +185,7 @@ int scene_render_deps(struct scene *scn, uint id); * Renders the menu and all of its attached objects * * @scn: Scene to render - * @menu: Menu render + * @menu: Menu to render * Returns: 0 if OK, -ve on error */ int scene_menu_render_deps(struct scene *scn, struct scene_obj_menu *menu); diff --git a/include/expo.h b/include/expo.h index a2b3a71c1591..297d892de8e7 100644 --- a/include/expo.h +++ b/include/expo.h @@ -505,7 +505,7 @@ int scene_txt(struct scene *scn, const char *name, uint id, uint str_id, struct scene_obj_txt **txtp);
/** - * scene_txt_str() - add a new string to expr and text object to a scene + * scene_txt_str() - add a new string to expo and text object to a scene * * @scn: Scene to update * @name: Name to use (this is allocated by this call)

In a lot of cases menus are the only objects which are have their own behaviour in the cedit, e.g. to move between menus. With expo expanding to support text, this is no-longer true.
Use a switch() statement so that we can simply insert a new 'case' for the new object types.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
boot/cedit.c | 88 +++++++++++++++++++++++++++++++--------------------- boot/scene.c | 38 ++++++++++++++++++----- 2 files changed, 83 insertions(+), 43 deletions(-)
diff --git a/boot/cedit.c b/boot/cedit.c index 1324a8619981..b7329c3245ee 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -71,10 +71,16 @@ int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id)
y = 100; list_for_each_entry(obj, &scn->obj_head, sibling) { - if (obj->type == SCENEOBJT_MENU) { + switch (obj->type) { + case SCENEOBJT_NONE: + case SCENEOBJT_IMAGE: + case SCENEOBJT_TEXT: + break; + case SCENEOBJT_MENU: scene_obj_set_pos(scn, obj->id, 50, y); scene_menu_arrange(scn, (struct scene_obj_menu *)obj); y += 50; + break; } }
@@ -422,31 +428,37 @@ static int h_write_settings_env(struct scene_obj *obj, void *vpriv) const char *str; int val, ret;
- if (obj->type != SCENEOBJT_MENU) - return 0; - - menu = (struct scene_obj_menu *)obj; - val = menu->cur_item_id; snprintf(var, sizeof(var), "c.%s", obj->name);
- if (priv->verbose) - printf("%s=%d\n", var, val); + switch (obj->type) { + case SCENEOBJT_NONE: + case SCENEOBJT_IMAGE: + case SCENEOBJT_TEXT: + break; + case SCENEOBJT_MENU: + menu = (struct scene_obj_menu *)obj; + val = menu->cur_item_id;
- ret = env_set_ulong(var, val); - if (ret) - return log_msg_ret("set", ret); + if (priv->verbose) + printf("%s=%d\n", var, val);
- ret = get_cur_menuitem_text(menu, &str); - if (ret) - return log_msg_ret("mis", ret); + ret = env_set_ulong(var, val); + if (ret) + return log_msg_ret("set", ret);
- snprintf(name, sizeof(name), "c.%s-str", obj->name); - if (priv->verbose) - printf("%s=%s\n", name, str); + ret = get_cur_menuitem_text(menu, &str); + if (ret) + return log_msg_ret("mis", ret);
- ret = env_set(name, str); - if (ret) - return log_msg_ret("st2", ret); + snprintf(name, sizeof(name), "c.%s-str", obj->name); + if (priv->verbose) + printf("%s=%s\n", name, str); + + ret = env_set(name, str); + if (ret) + return log_msg_ret("st2", ret); + break; + }
return 0; } @@ -474,24 +486,28 @@ static int h_read_settings_env(struct scene_obj *obj, void *vpriv) char var[60]; int val;
- if (obj->type != SCENEOBJT_MENU) - return 0; - - menu = (struct scene_obj_menu *)obj; - val = menu->cur_item_id; snprintf(var, sizeof(var), "c.%s", obj->name);
- val = env_get_ulong(var, 10, 0); - if (priv->verbose) - printf("%s=%d\n", var, val); - if (!val) - return log_msg_ret("get", -ENOENT); - - /* - * note that no validation is done here, to make sure the ID is valid - * and actually points to a menu item - */ - menu->cur_item_id = val; + switch (obj->type) { + case SCENEOBJT_NONE: + case SCENEOBJT_IMAGE: + case SCENEOBJT_TEXT: + break; + case SCENEOBJT_MENU: + menu = (struct scene_obj_menu *)obj; + val = env_get_ulong(var, 10, 0); + if (priv->verbose) + printf("%s=%d\n", var, val); + if (!val) + return log_msg_ret("get", -ENOENT); + + /* + * note that no validation is done here, to make sure the ID is + * valid * and actually points to a menu item + */ + menu->cur_item_id = val; + break; + }
return 0; } diff --git a/boot/scene.c b/boot/scene.c index 6c52948eb69c..08ed85e9188c 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -423,13 +423,20 @@ int scene_arrange(struct scene *scn) int ret;
list_for_each_entry(obj, &scn->obj_head, sibling) { - if (obj->type == SCENEOBJT_MENU) { + switch (obj->type) { + case SCENEOBJT_NONE: + case SCENEOBJT_IMAGE: + case SCENEOBJT_TEXT: + break; + case SCENEOBJT_MENU: { struct scene_obj_menu *menu;
menu = (struct scene_obj_menu *)obj, ret = scene_menu_arrange(scn, menu); if (ret) return log_msg_ret("arr", ret); + break; + } } }
@@ -452,9 +459,16 @@ int scene_render_deps(struct scene *scn, uint id) if (ret && ret != -ENOTSUPP) return log_msg_ret("ren", ret);
- if (obj->type == SCENEOBJT_MENU) + switch (obj->type) { + case SCENEOBJT_NONE: + case SCENEOBJT_IMAGE: + case SCENEOBJT_TEXT: + break; + case SCENEOBJT_MENU: scene_menu_render_deps(scn, (struct scene_obj_menu *)obj); + break; + } }
return 0; @@ -537,7 +551,6 @@ static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
int scene_send_key(struct scene *scn, int key, struct expo_action *event) { - struct scene_obj_menu *menu; struct scene_obj *obj; int ret;
@@ -561,10 +574,21 @@ int scene_send_key(struct scene *scn, int key, struct expo_action *event) return 0; }
- menu = (struct scene_obj_menu *)obj, - ret = scene_menu_send_key(scn, menu, key, event); - if (ret) - return log_msg_ret("key", ret); + switch (obj->type) { + case SCENEOBJT_NONE: + case SCENEOBJT_IMAGE: + case SCENEOBJT_TEXT: + break; + case SCENEOBJT_MENU: { + struct scene_obj_menu *menu; + + menu = (struct scene_obj_menu *)obj, + ret = scene_menu_send_key(scn, menu, key, event); + if (ret) + return log_msg_ret("key", ret); + break; + } + } return 0; }

Return the reported error, rather than assuming it is -ENOMEM
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
boot/scene.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/boot/scene.c b/boot/scene.c index 08ed85e9188c..9c4466c1a47a 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -137,7 +137,7 @@ int scene_img(struct scene *scn, const char *name, uint id, char *data, sizeof(struct scene_obj_img), (struct scene_obj **)&img); if (ret < 0) - return log_msg_ret("obj", -ENOMEM); + return log_msg_ret("obj", ret);
img->data = data;
@@ -157,7 +157,7 @@ int scene_txt(struct scene *scn, const char *name, uint id, uint str_id, sizeof(struct scene_obj_txt), (struct scene_obj **)&txt); if (ret < 0) - return log_msg_ret("obj", -ENOMEM); + return log_msg_ret("obj", ret);
txt->str_id = str_id;
@@ -183,7 +183,7 @@ int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id, sizeof(struct scene_obj_txt), (struct scene_obj **)&txt); if (ret < 0) - return log_msg_ret("obj", -ENOMEM); + return log_msg_ret("obj", ret);
txt->str_id = str_id;

Update scene_txt_str() to account for the possibility that the passed-in str_id may be 0
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
boot/scene.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/boot/scene.c b/boot/scene.c index 9c4466c1a47a..8e5d3aa7dc05 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -176,8 +176,9 @@ int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id, ret = expo_str(scn->expo, name, str_id, str); if (ret < 0) return log_msg_ret("str", ret); - else if (ret != str_id) + if (str_id && ret != str_id) return log_msg_ret("id", -EEXIST); + str_id = ret;
ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT, sizeof(struct scene_obj_txt),

So far only menus can be highlighted. With the coming addition of text lines we need to be able to highlight other objects. Add a function to determine whether an object can be highlighted.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
boot/scene.c | 11 ++++------- include/expo.h | 8 ++++++++ 2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/boot/scene.c b/boot/scene.c index 8e5d3aa7dc05..4d7cabd75f4f 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -516,7 +516,7 @@ static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key, sibling)) { obj = list_entry(obj->sibling.prev, struct scene_obj, sibling); - if (obj->type == SCENEOBJT_MENU) { + if (scene_obj_can_highlight(obj)) { event->type = EXPOACT_POINT_OBJ; event->select.id = obj->id; log_debug("up to obj %d\n", event->select.id); @@ -528,7 +528,7 @@ static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key, while (!list_is_last(&obj->sibling, &scn->obj_head)) { obj = list_entry(obj->sibling.next, struct scene_obj, sibling); - if (obj->type == SCENEOBJT_MENU) { + if (scene_obj_can_highlight(obj)) { event->type = EXPOACT_POINT_OBJ; event->select.id = obj->id; log_debug("down to obj %d\n", event->select.id); @@ -537,7 +537,7 @@ static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key, } break; case BKEY_SELECT: - if (obj->type == SCENEOBJT_MENU) { + if (scene_obj_can_highlight(obj)) { event->type = EXPOACT_OPEN; event->select.id = obj->id; log_debug("open obj %d\n", event->select.id); @@ -685,12 +685,9 @@ void scene_highlight_first(struct scene *scn) struct scene_obj *obj;
list_for_each_entry(obj, &scn->obj_head, sibling) { - switch (obj->type) { - case SCENEOBJT_MENU: + if (scene_obj_can_highlight(obj)) { scene_set_highlight_id(scn, obj->id); return; - default: - break; } } } diff --git a/include/expo.h b/include/expo.h index 297d892de8e7..b192d55665ab 100644 --- a/include/expo.h +++ b/include/expo.h @@ -146,6 +146,8 @@ enum scene_obj_t { SCENEOBJT_NONE = 0, SCENEOBJT_IMAGE, SCENEOBJT_TEXT, + + /* types from here on can be highlighted */ SCENEOBJT_MENU, };
@@ -203,6 +205,12 @@ struct scene_obj { struct list_head sibling; };
+/* object can be highlighted when moving around expo */ +static inline bool scene_obj_can_highlight(const struct scene_obj *obj) +{ + return obj->type >= SCENEOBJT_MENU; +} + /** * struct scene_obj_img - information about an image object in a scene *

When the devicetree is too small for the property being written, we need to expand the devicetree and retry the write.
Put this logic into a function so it can be reused.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
boot/cedit.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-)
diff --git a/boot/cedit.c b/boot/cedit.c index b7329c3245ee..bb194af77bdc 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -269,6 +269,28 @@ static int get_cur_menuitem_text(const struct scene_obj_menu *menu, return 0; }
+static int write_dt_string(struct abuf *buf, const char *name, const char *str) +{ + int ret, i; + + /* write the text of the current item */ + ret = -EAGAIN; + for (i = 0; ret && i < 2; i++) { + ret = fdt_property_string(abuf_data(buf), name, str); + if (!i) { + ret = check_space(ret, buf); + if (ret) + return log_msg_ret("rs2", -ENOMEM); + } + } + + /* this should not happen */ + if (ret) + return log_msg_ret("str", -EFAULT); + + return 0; +} + static int h_write_settings(struct scene_obj *obj, void *vpriv) { struct cedit_iter_priv *priv = vpriv; @@ -285,6 +307,7 @@ static int h_write_settings(struct scene_obj *obj, void *vpriv) char name[80]; int ret, i;
+ /* write the ID of the current item */ menu = (struct scene_obj_menu *)obj; ret = -EAGAIN; for (i = 0; ret && i < 2; i++) { @@ -304,20 +327,11 @@ static int h_write_settings(struct scene_obj *obj, void *vpriv) if (ret) return log_msg_ret("mis", ret);
+ /* write the text of the current item */ snprintf(name, sizeof(name), "%s-str", obj->name); - ret = -EAGAIN; - for (i = 0; ret && i < 2; i++) { - ret = fdt_property_string(abuf_data(buf), name, str); - if (!i) { - ret = check_space(ret, buf); - if (ret) - return log_msg_ret("rs2", -ENOMEM); - } - } - - /* this should not happen */ + ret = write_dt_string(buf, name, str); if (ret) - return log_msg_ret("wr2", -EFAULT); + return log_msg_ret("wr2", ret);
break; }

We want to support this for any object, not just menus. Move the code around to allow this.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
boot/scene.c | 45 +++++++++++++++++++++++++++++++++++++++++++ boot/scene_internal.h | 39 +++++++++++++++++++++++++++++++++++++ boot/scene_menu.c | 39 +++---------------------------------- 3 files changed, 87 insertions(+), 36 deletions(-)
diff --git a/boot/scene.c b/boot/scene.c index 4d7cabd75f4f..e98676b9387a 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -608,6 +608,25 @@ int scene_send_key(struct scene *scn, int key, struct expo_action *event) return 0; }
+int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox *bbox, + struct vidconsole_bbox *label_bbox) +{ + switch (obj->type) { + case SCENEOBJT_NONE: + case SCENEOBJT_IMAGE: + case SCENEOBJT_TEXT: + return -ENOSYS; + case SCENEOBJT_MENU: { + struct scene_obj_menu *menu = (struct scene_obj_menu *)obj; + + scene_menu_calc_bbox(menu, bbox, label_bbox); + break; + } + } + + return 0; +} + int scene_calc_dims(struct scene *scn, bool do_menus) { struct scene_obj *obj; @@ -719,3 +738,29 @@ int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
return 0; } + +int scene_bbox_union(struct scene *scn, uint id, int inset, + struct vidconsole_bbox *bbox) +{ + struct scene_obj *obj; + + if (!id) + return 0; + obj = scene_obj_find(scn, id, SCENEOBJT_NONE); + if (!obj) + return log_msg_ret("obj", -ENOENT); + if (bbox->valid) { + bbox->x0 = min(bbox->x0, obj->dim.x - inset); + bbox->y0 = min(bbox->y0, obj->dim.y); + bbox->x1 = max(bbox->x1, obj->dim.x + obj->dim.w + inset); + bbox->y1 = max(bbox->y1, obj->dim.y + obj->dim.h); + } else { + bbox->x0 = obj->dim.x - inset; + bbox->y0 = obj->dim.y; + bbox->x1 = obj->dim.x + obj->dim.w + inset; + bbox->y1 = obj->dim.y + obj->dim.h; + bbox->valid = true; + } + + return 0; +} diff --git a/boot/scene_internal.h b/boot/scene_internal.h index 42efcee092f3..326508874e6a 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -9,6 +9,8 @@ #ifndef __SCENE_INTERNAL_H #define __SCENE_INTERNAL_H
+struct vidconsole_bbox; + typedef int (*expo_scene_obj_iterator)(struct scene_obj *obj, void *priv);
/** @@ -246,4 +248,41 @@ struct scene_menitem *scene_menuitem_find(const struct scene_obj_menu *menu, struct scene_menitem *scene_menuitem_find_seq(const struct scene_obj_menu *menu, uint seq);
+/** + * scene_bbox_union() - update bouding box with the demensions of an object + * + * Updates @bbox so that it encompasses the bounding box of object @id + * + * @snd: Scene containing object + * @id: Object id + * @inset: Amount of inset to use for width + * @bbox: Bounding box to update + * Return: 0 if OK, -ve on error + */ +int scene_bbox_union(struct scene *scn, uint id, int inset, + struct vidconsole_bbox *bbox); + +/** + * scene_menu_calc_bbox() - Calculate bounding boxes for the menu + * + * @menu: Menu to process + * @bbox: Returns bounding box of menu including prompts + * @label_bbox: Returns bounding box of labels + * Return: 0 if OK, -ve on error + */ +void scene_menu_calc_bbox(struct scene_obj_menu *menu, + struct vidconsole_bbox *bbox, + struct vidconsole_bbox *label_bbox); + +/** + * scene_obj_calc_bbox() - Calculate bounding boxes for an object + * + * @obj: Object to process + * @bbox: Returns bounding box of object including prompts + * @label_bbox: Returns bounding box of labels (active area) + * Return: 0 if OK, -ve on error + */ +int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox *bbox, + struct vidconsole_bbox *label_bbox); + #endif /* __SCENE_INTERNAL_H */ diff --git a/boot/scene_menu.c b/boot/scene_menu.c index e0dcd0a4e041..95c283af4b08 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -114,42 +114,9 @@ static void menu_point_to_item(struct scene_obj_menu *menu, uint item_id) update_pointers(menu, item_id, true); }
-static int scene_bbox_union(struct scene *scn, uint id, int inset, - struct vidconsole_bbox *bbox) -{ - struct scene_obj *obj; - - if (!id) - return 0; - obj = scene_obj_find(scn, id, SCENEOBJT_NONE); - if (!obj) - return log_msg_ret("obj", -ENOENT); - if (bbox->valid) { - bbox->x0 = min(bbox->x0, obj->dim.x - inset); - bbox->y0 = min(bbox->y0, obj->dim.y); - bbox->x1 = max(bbox->x1, obj->dim.x + obj->dim.w + inset); - bbox->y1 = max(bbox->y1, obj->dim.y + obj->dim.h); - } else { - bbox->x0 = obj->dim.x - inset; - bbox->y0 = obj->dim.y; - bbox->x1 = obj->dim.x + obj->dim.w + inset; - bbox->y1 = obj->dim.y + obj->dim.h; - bbox->valid = true; - } - - return 0; -} - -/** - * scene_menu_calc_bbox() - Calculate bounding boxes for the menu - * - * @menu: Menu to process - * @bbox: Returns bounding box of menu including prompts - * @label_bbox: Returns bounding box of labels - */ -static void scene_menu_calc_bbox(struct scene_obj_menu *menu, - struct vidconsole_bbox *bbox, - struct vidconsole_bbox *label_bbox) +void scene_menu_calc_bbox(struct scene_obj_menu *menu, + struct vidconsole_bbox *bbox, + struct vidconsole_bbox *label_bbox) { const struct expo_theme *theme = &menu->obj.scene->expo->theme; const struct scene_menitem *item;

So far only menus have a background. When other object types are rendered, they may have a background too. Make this code more generic so it will be usable by new object types.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
boot/scene.c | 40 +++++++++++++++++++++++++++++++++++++++- boot/scene_internal.h | 7 ------- boot/scene_menu.c | 29 ----------------------------- 3 files changed, 39 insertions(+), 37 deletions(-)
diff --git a/boot/scene.c b/boot/scene.c index e98676b9387a..314dd7c6e849 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -314,6 +314,44 @@ int scene_obj_get_hw(struct scene *scn, uint id, int *widthp) return 0; }
+/** + * scene_render_background() - Render the background for an object + * + * @obj: Object to render + */ +static void scene_render_background(struct scene_obj *obj) +{ + struct expo *exp = obj->scene->expo; + const struct expo_theme *theme = &exp->theme; + struct vidconsole_bbox bbox, label_bbox; + struct udevice *dev = exp->display; + struct video_priv *vid_priv; + struct udevice *cons = exp->cons; + struct vidconsole_colour old; + enum colour_idx fore, back; + uint inset = theme->menu_inset; + + /* draw a background for the object */ + if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) { + fore = VID_BLACK; + back = VID_WHITE; + } else { + fore = VID_LIGHT_GRAY; + back = VID_BLACK; + } + + /* see if this object wants to render a background */ + if (scene_obj_calc_bbox(obj, &bbox, &label_bbox)) + return; + + vidconsole_push_colour(cons, fore, back, &old); + vid_priv = dev_get_uclass_priv(dev); + video_fill_part(dev, label_bbox.x0 - inset, label_bbox.y0 - inset, + label_bbox.x1 + inset, label_bbox.y1 + inset, + vid_priv->colour_fg); + vidconsole_pop_colour(cons, &old); +} + /** * scene_obj_render() - Render an object * @@ -397,7 +435,7 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode) return -ENOTSUPP;
/* draw a background behind the menu items */ - scene_menu_render(menu); + scene_render_background(obj); } /* * With a vidconsole, the text and item pointer are rendered as diff --git a/boot/scene_internal.h b/boot/scene_internal.h index 326508874e6a..1c2bfeadcd38 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -165,13 +165,6 @@ int scene_render(struct scene *scn); */ int scene_send_key(struct scene *scn, int key, struct expo_action *event);
-/** - * scene_menu_render() - Render the background behind a menu - * - * @menu: Menu to render - */ -void scene_menu_render(struct scene_obj_menu *menu); - /** * scene_render_deps() - Render an object and its dependencies * diff --git a/boot/scene_menu.c b/boot/scene_menu.c index 95c283af4b08..63994165efba 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -516,35 +516,6 @@ int scene_menu_display(struct scene_obj_menu *menu) return -ENOTSUPP; }
-void scene_menu_render(struct scene_obj_menu *menu) -{ - struct expo *exp = menu->obj.scene->expo; - const struct expo_theme *theme = &exp->theme; - struct vidconsole_bbox bbox, label_bbox; - struct udevice *dev = exp->display; - struct video_priv *vid_priv; - struct udevice *cons = exp->cons; - struct vidconsole_colour old; - enum colour_idx fore, back; - - if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) { - fore = VID_BLACK; - back = VID_WHITE; - } else { - fore = VID_LIGHT_GRAY; - back = VID_BLACK; - } - - scene_menu_calc_bbox(menu, &bbox, &label_bbox); - vidconsole_push_colour(cons, fore, back, &old); - vid_priv = dev_get_uclass_priv(dev); - video_fill_part(dev, label_bbox.x0 - theme->menu_inset, - label_bbox.y0 - theme->menu_inset, - label_bbox.x1, label_bbox.y1 + theme->menu_inset, - vid_priv->colour_fg); - vidconsole_pop_colour(cons, &old); -} - int scene_menu_render_deps(struct scene *scn, struct scene_obj_menu *menu) { struct scene_menitem *item;

Add the CLI state, a buffer to hold the old value of the text being edited and a place to save vidconsole entry context. These will be use by the textline object.
Set an upper limit on the maximum number of characters in a textline object supported by expo, at least for now.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
boot/scene.c | 10 ++++++++++ include/expo.h | 14 ++++++++++++++ 2 files changed, 24 insertions(+)
diff --git a/boot/scene.c b/boot/scene.c index 314dd7c6e849..0b44a13748ab 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -32,6 +32,14 @@ int scene_new(struct expo *exp, const char *name, uint id, struct scene **scnp) return log_msg_ret("name", -ENOMEM); }
+ abuf_init(&scn->buf); + if (!abuf_realloc(&scn->buf, EXPO_MAX_CHARS + 1)) { + free(scn->name); + free(scn); + return log_msg_ret("buf", -ENOMEM); + } + abuf_init(&scn->entry_save); + INIT_LIST_HEAD(&scn->obj_head); scn->id = resolve_id(exp, id); scn->expo = exp; @@ -57,6 +65,8 @@ void scene_destroy(struct scene *scn) list_for_each_entry_safe(obj, next, &scn->obj_head, sibling) scene_obj_destroy(obj);
+ abuf_uninit(&scn->entry_save); + abuf_uninit(&scn->buf); free(scn->name); free(scn); } diff --git a/include/expo.h b/include/expo.h index b192d55665ab..c470f5be34d3 100644 --- a/include/expo.h +++ b/include/expo.h @@ -7,11 +7,14 @@ #ifndef __EXPO_H #define __EXPO_H
+#include <abuf.h> #include <dm/ofnode_decl.h> #include <linux/list.h>
struct udevice;
+#include <cli.h> + /** * enum expoact_type - types of actions reported by the expo * @@ -121,6 +124,9 @@ struct expo_string { * @id: ID number of the scene * @title_id: String ID of title of the scene (allocated) * @highlight_id: ID of highlighted object, if any + * @cls: cread state to use for input + * @buf: Buffer for input + * @entry_save: Buffer to hold vidconsole text-entry information * @sibling: Node to link this scene to its siblings * @obj_head: List of objects in the scene */ @@ -130,6 +136,9 @@ struct scene { uint id; uint title_id; uint highlight_id; + struct cli_line_state cls; + struct abuf buf; + struct abuf entry_save; struct list_head sibling; struct list_head obj_head; }; @@ -180,6 +189,11 @@ enum scene_obj_flags_t { SCENEOF_OPEN = 1 << 2, };
+enum { + /* Maximum number of characters allowed in an line editor */ + EXPO_MAX_CHARS = 250, +}; + /** * struct scene_obj - information about an object in a scene *

A textline is a line of text which can be edited by the user. It has a maximum length (in chracters) but otherwise there are no restrictions.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
boot/Makefile | 3 +- boot/scene_textline.c | 234 ++++++++++++++++++++++++++++++++++++++++++ include/expo.h | 36 +++++++ 3 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 boot/scene_textline.c
diff --git a/boot/Makefile b/boot/Makefile index 6ce983b83fa4..ad608598d298 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -56,7 +56,8 @@ ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_LOAD_FIT) += common_fit.o endif
-obj-$(CONFIG_$(SPL_TPL_)EXPO) += expo.o scene.o scene_menu.o expo_build.o +obj-$(CONFIG_$(SPL_TPL_)EXPO) += expo.o scene.o expo_build.o +obj-$(CONFIG_$(SPL_TPL_)EXPO) += scene_menu.o scene_textline.o
obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE) += vbe.o obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_REQUEST) += vbe_request.o diff --git a/boot/scene_textline.c b/boot/scene_textline.c new file mode 100644 index 000000000000..2caa81ee1582 --- /dev/null +++ b/boot/scene_textline.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Implementation of a menu in a scene + * + * Copyright 2023 Google LLC + * Written by Simon Glass sjg@chromium.org + */ + +#define LOG_CATEGORY LOGC_EXPO + +#include <common.h> +#include <expo.h> +#include <menu.h> +#include <video_console.h> +#include "scene_internal.h" + +int scene_textline(struct scene *scn, const char *name, uint id, uint max_chars, + struct scene_obj_textline **tlinep) +{ + struct scene_obj_textline *tline; + char *buf; + int ret; + + if (max_chars >= EXPO_MAX_CHARS) + return log_msg_ret("chr", -E2BIG); + + ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXTLINE, + sizeof(struct scene_obj_textline), + (struct scene_obj **)&tline); + if (ret < 0) + return log_msg_ret("obj", -ENOMEM); + abuf_init(&tline->buf); + if (!abuf_realloc(&tline->buf, max_chars + 1)) + return log_msg_ret("buf", -ENOMEM); + buf = abuf_data(&tline->buf); + *buf = '\0'; + tline->pos = max_chars; + tline->max_chars = max_chars; + + if (tlinep) + *tlinep = tline; + + return tline->obj.id; +} + +void scene_textline_calc_bbox(struct scene_obj_textline *tline, + struct vidconsole_bbox *bbox, + struct vidconsole_bbox *edit_bbox) +{ + const struct expo_theme *theme = &tline->obj.scene->expo->theme; + + bbox->valid = false; + scene_bbox_union(tline->obj.scene, tline->label_id, 0, bbox); + scene_bbox_union(tline->obj.scene, tline->edit_id, 0, bbox); + + edit_bbox->valid = false; + scene_bbox_union(tline->obj.scene, tline->edit_id, theme->menu_inset, + edit_bbox); +} + +int scene_textline_calc_dims(struct scene_obj_textline *tline) +{ + struct scene *scn = tline->obj.scene; + struct vidconsole_bbox bbox; + struct scene_obj_txt *txt; + int ret; + + txt = scene_obj_find(scn, tline->edit_id, SCENEOBJT_NONE); + if (!txt) + return log_msg_ret("dim", -ENOENT); + + ret = vidconsole_nominal(scn->expo->cons, txt->font_name, + txt->font_size, tline->max_chars, &bbox); + if (ret) + return log_msg_ret("nom", ret); + + if (bbox.valid) { + tline->obj.dim.w = bbox.x1 - bbox.x0; + tline->obj.dim.h = bbox.y1 - bbox.y0; + + scene_obj_set_size(scn, tline->edit_id, tline->obj.dim.w, + tline->obj.dim.h); + } + + return 0; +} + +int scene_textline_arrange(struct scene *scn, struct scene_obj_textline *tline) +{ + const bool open = tline->obj.flags & SCENEOF_OPEN; + bool point; + int x, y; + int ret; + + x = tline->obj.dim.x; + y = tline->obj.dim.y; + if (tline->label_id) { + ret = scene_obj_set_pos(scn, tline->label_id, tline->obj.dim.x, + y); + if (ret < 0) + return log_msg_ret("tit", ret); + + ret = scene_obj_set_pos(scn, tline->edit_id, + tline->obj.dim.x + 200, y); + if (ret < 0) + return log_msg_ret("tit", ret); + + ret = scene_obj_get_hw(scn, tline->label_id, NULL); + if (ret < 0) + return log_msg_ret("hei", ret); + + y += ret * 2; + } + + point = scn->highlight_id == tline->obj.id; + point &= !open; + scene_obj_flag_clrset(scn, tline->edit_id, SCENEOF_POINT, + point ? SCENEOF_POINT : 0); + + return 0; +} + +int scene_textline_send_key(struct scene *scn, struct scene_obj_textline *tline, + int key, struct expo_action *event) +{ + const bool open = tline->obj.flags & SCENEOF_OPEN; + + log_debug("key=%d\n", key); + switch (key) { + case BKEY_QUIT: + if (open) { + event->type = EXPOACT_CLOSE; + event->select.id = tline->obj.id; + + /* Copy the backup text from the scene buffer */ + memcpy(abuf_data(&tline->buf), abuf_data(&scn->buf), + abuf_size(&scn->buf)); + } else { + event->type = EXPOACT_QUIT; + log_debug("menu quit\n"); + } + break; + case BKEY_SELECT: + if (!open) + break; + event->type = EXPOACT_CLOSE; + event->select.id = tline->obj.id; + key = '\n'; + fallthrough; + default: { + struct udevice *cons = scn->expo->cons; + int ret; + + ret = vidconsole_entry_restore(cons, &scn->entry_save); + if (ret) + return log_msg_ret("sav", ret); + ret = cread_line_process_ch(&scn->cls, key); + ret = vidconsole_entry_save(cons, &scn->entry_save); + if (ret) + return log_msg_ret("sav", ret); + break; + } + } + + return 0; +} + +int scene_textline_render_deps(struct scene *scn, + struct scene_obj_textline *tline) +{ + const bool open = tline->obj.flags & SCENEOF_OPEN; + struct udevice *cons = scn->expo->cons; + struct scene_obj_txt *txt; + int ret; + + scene_render_deps(scn, tline->label_id); + scene_render_deps(scn, tline->edit_id); + + /* show the vidconsole cursor if open */ + if (open) { + /* get the position within the field */ + txt = scene_obj_find(scn, tline->edit_id, SCENEOBJT_NONE); + if (!txt) + return log_msg_ret("cur", -ENOENT); + + if (txt->font_name || txt->font_size) { + ret = vidconsole_select_font(cons, + txt->font_name, + txt->font_size); + } else { + ret = vidconsole_select_font(cons, NULL, 0); + } + + ret = vidconsole_entry_restore(cons, &scn->entry_save); + if (ret) + return log_msg_ret("sav", ret); + + vidconsole_set_cursor_visible(cons, true, txt->obj.dim.x, + txt->obj.dim.y, scn->cls.num); + } + + return 0; +} + +int scene_textline_open(struct scene *scn, struct scene_obj_textline *tline) +{ + struct udevice *cons = scn->expo->cons; + struct scene_obj_txt *txt; + int ret; + + /* Copy the text into the scene buffer in case the edit is cancelled */ + memcpy(abuf_data(&scn->buf), abuf_data(&tline->buf), + abuf_size(&scn->buf)); + + /* get the position of the editable */ + txt = scene_obj_find(scn, tline->edit_id, SCENEOBJT_NONE); + if (!txt) + return log_msg_ret("cur", -ENOENT); + + vidconsole_set_cursor_pos(cons, txt->obj.dim.x, txt->obj.dim.y); + vidconsole_entry_start(cons); + cli_cread_init(&scn->cls, abuf_data(&tline->buf), tline->max_chars); + scn->cls.insert = true; + ret = vidconsole_entry_save(cons, &scn->entry_save); + if (ret) + return log_msg_ret("sav", ret); + + return 0; +} + +int scene_textline_close(struct scene *scn, struct scene_obj_textline *tline) +{ + return 0; +} diff --git a/include/expo.h b/include/expo.h index c470f5be34d3..3260703a7a01 100644 --- a/include/expo.h +++ b/include/expo.h @@ -150,6 +150,7 @@ struct scene { * @SCENEOBJT_IMAGE: Image data to render * @SCENEOBJT_TEXT: Text line to render * @SCENEOBJT_MENU: Menu containing items the user can select + * @SCENEOBJT_TEXTLINE: Line of text the user can edit */ enum scene_obj_t { SCENEOBJT_NONE = 0, @@ -158,6 +159,7 @@ enum scene_obj_t {
/* types from here on can be highlighted */ SCENEOBJT_MENU, + SCENEOBJT_TEXTLINE, };
/** @@ -318,6 +320,27 @@ struct scene_menitem { struct list_head sibling; };
+/** + * struct scene_obj_textline - information about a textline in a scene + * + * A textline has a prompt and a line of editable text + * + * @obj: Basic object information + * @label_id: ID of the label text, or 0 if none + * @edit_id: ID of the editable text + * @max_chars: Maximum number of characters allowed + * @buf: Text buffer containing current text + * @pos: Cursor position + */ +struct scene_obj_textline { + struct scene_obj obj; + uint label_id; + uint edit_id; + uint max_chars; + struct abuf buf; + uint pos; +}; + /** * expo_new() - create a new expo * @@ -552,6 +575,19 @@ int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id, int scene_menu(struct scene *scn, const char *name, uint id, struct scene_obj_menu **menup);
+/** + * scene_textline() - create a textline + * + * @scn: Scene to update + * @name: Name to use (this is allocated by this call) + * @id: ID to use for the new object (0 to allocate one) + * @max_chars: Maximum length of the textline in characters + * @tlinep: If non-NULL, returns the new object + * Returns: ID number for the object (typically @id), or -ve on error + */ +int scene_textline(struct scene *scn, const char *name, uint id, uint max_chars, + struct scene_obj_textline **tlinep); + /** * scene_txt_set_font() - Set the font for an object *

This object needs special handling when it is opened, to set up the CLI and the vidconsole context. Add special support for this.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
boot/scene.c | 32 ++++++++++++++++++++++++++++++++ boot/scene_internal.h | 22 ++++++++++++++++++++++ boot/scene_textline.c | 5 ----- 3 files changed, 54 insertions(+), 5 deletions(-)
diff --git a/boot/scene.c b/boot/scene.c index 0b44a13748ab..6c7c926f7c93 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -759,10 +759,42 @@ void scene_highlight_first(struct scene *scn) } }
+static int scene_obj_open(struct scene *scn, struct scene_obj *obj) +{ + int ret; + + switch (obj->type) { + case SCENEOBJT_NONE: + case SCENEOBJT_IMAGE: + case SCENEOBJT_MENU: + case SCENEOBJT_TEXT: + break; + case SCENEOBJT_TEXTLINE: + ret = scene_textline_open(scn, + (struct scene_obj_textline *)obj); + if (ret) + return log_msg_ret("op", ret); + break; + } + + return 0; +} + int scene_set_open(struct scene *scn, uint id, bool open) { + struct scene_obj *obj; int ret;
+ obj = scene_obj_find(scn, id, SCENEOBJT_NONE); + if (!obj) + return log_msg_ret("find", -ENOENT); + + if (open) { + ret = scene_obj_open(scn, obj); + if (ret) + return log_msg_ret("op", ret); + } + ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN, open ? SCENEOF_OPEN : 0); if (ret) diff --git a/boot/scene_internal.h b/boot/scene_internal.h index 1c2bfeadcd38..7a84977646a0 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -278,4 +278,26 @@ void scene_menu_calc_bbox(struct scene_obj_menu *menu, int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox *bbox, struct vidconsole_bbox *label_bbox);
+/** + * scene_textline_open() - Open a textline object + * + * Set up the text editor ready for use + * + * @scn: Scene containing the textline + * @tline: textline object + * Return: 0 if OK, -ve on error + */ +int scene_textline_open(struct scene *scn, struct scene_obj_textline *tline); + +/** + * scene_textline_close() - Close a textline object + * + * Close out the text editor after use + * + * @scn: Scene containing the textline + * @tline: textline object + * Return: 0 if OK, -ve on error + */ +int scene_textline_close(struct scene *scn, struct scene_obj_textline *tline); + #endif /* __SCENE_INTERNAL_H */ diff --git a/boot/scene_textline.c b/boot/scene_textline.c index 2caa81ee1582..6ea072a1c268 100644 --- a/boot/scene_textline.c +++ b/boot/scene_textline.c @@ -227,8 +227,3 @@ int scene_textline_open(struct scene *scn, struct scene_obj_textline *tline)
return 0; } - -int scene_textline_close(struct scene *scn, struct scene_obj_textline *tline) -{ - return 0; -}

Provide an implementation for textlines in the scene code, so that they are displayed correctly. Provide a way to have a border around the textline, with the internal part being the same colour as the background. This looks more natural.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
boot/scene.c | 56 +++++++++++++++++++++++++++++++++++++++-- boot/scene_internal.h | 58 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/boot/scene.c b/boot/scene.c index 6c7c926f7c93..d4dfb49ada15 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -280,6 +280,7 @@ int scene_obj_get_hw(struct scene *scn, uint id, int *widthp) switch (obj->type) { case SCENEOBJT_NONE: case SCENEOBJT_MENU: + case SCENEOBJT_TEXTLINE: break; case SCENEOBJT_IMAGE: { struct scene_obj_img *img = (struct scene_obj_img *)obj; @@ -328,8 +329,10 @@ int scene_obj_get_hw(struct scene *scn, uint id, int *widthp) * scene_render_background() - Render the background for an object * * @obj: Object to render + * @box_only: true to show a box around the object, but keep the normal + * background colour inside */ -static void scene_render_background(struct scene_obj *obj) +static void scene_render_background(struct scene_obj *obj, bool box_only) { struct expo *exp = obj->scene->expo; const struct expo_theme *theme = &exp->theme; @@ -360,6 +363,11 @@ static void scene_render_background(struct scene_obj *obj) label_bbox.x1 + inset, label_bbox.y1 + inset, vid_priv->colour_fg); vidconsole_pop_colour(cons, &old); + if (box_only) { + video_fill_part(dev, label_bbox.x0, label_bbox.y0, + label_bbox.x1, label_bbox.y1, + vid_priv->colour_bg); + } }
/** @@ -445,7 +453,7 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode) return -ENOTSUPP;
/* draw a background behind the menu items */ - scene_render_background(obj); + scene_render_background(obj, false); } /* * With a vidconsole, the text and item pointer are rendered as @@ -461,6 +469,10 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode)
break; } + case SCENEOBJT_TEXTLINE: + if (obj->flags & SCENEOF_OPEN) + scene_render_background(obj, true); + break; }
return 0; @@ -486,6 +498,15 @@ int scene_arrange(struct scene *scn) return log_msg_ret("arr", ret); break; } + case SCENEOBJT_TEXTLINE: { + struct scene_obj_textline *tline; + + tline = (struct scene_obj_textline *)obj, + ret = scene_textline_arrange(scn, tline); + if (ret) + return log_msg_ret("arr", ret); + break; + } } }
@@ -517,6 +538,10 @@ int scene_render_deps(struct scene *scn, uint id) scene_menu_render_deps(scn, (struct scene_obj_menu *)obj); break; + case SCENEOBJT_TEXTLINE: + scene_textline_render_deps(scn, + (struct scene_obj_textline *)obj); + break; } }
@@ -637,6 +662,15 @@ int scene_send_key(struct scene *scn, int key, struct expo_action *event) return log_msg_ret("key", ret); break; } + case SCENEOBJT_TEXTLINE: { + struct scene_obj_textline *tline; + + tline = (struct scene_obj_textline *)obj, + ret = scene_textline_send_key(scn, tline, key, event); + if (ret) + return log_msg_ret("key", ret); + break; + } } return 0; } @@ -670,6 +704,13 @@ int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox *bbox, scene_menu_calc_bbox(menu, bbox, label_bbox); break; } + case SCENEOBJT_TEXTLINE: { + struct scene_obj_textline *tline; + + tline = (struct scene_obj_textline *)obj; + scene_textline_calc_bbox(tline, bbox, label_bbox); + break; + } }
return 0; @@ -708,6 +749,16 @@ int scene_calc_dims(struct scene *scn, bool do_menus) } break; } + case SCENEOBJT_TEXTLINE: { + struct scene_obj_textline *tline; + + tline = (struct scene_obj_textline *)obj; + ret = scene_textline_calc_dims(tline); + if (ret) + return log_msg_ret("men", ret); + + break; + } } }
@@ -727,6 +778,7 @@ int scene_apply_theme(struct scene *scn, struct expo_theme *theme) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_MENU: + case SCENEOBJT_TEXTLINE: break; case SCENEOBJT_TEXT: scene_txt_set_font(scn, obj->id, NULL, diff --git a/boot/scene_internal.h b/boot/scene_internal.h index 7a84977646a0..e72202c98213 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -101,6 +101,18 @@ int scene_calc_dims(struct scene *scn, bool do_menus); */ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu);
+/** + * scene_textline_arrange() - Set the position of things in a textline + * + * This updates any items associated with a textline to make sure they are + * positioned correctly relative to the textline. + * + * @scn: Scene to update + * @tline: textline to process + * Returns: 0 if OK, -ve on error + */ +int scene_textline_arrange(struct scene *scn, struct scene_obj_textline *tline); + /** * scene_apply_theme() - Apply a theme to a scene * @@ -123,6 +135,18 @@ int scene_apply_theme(struct scene *scn, struct expo_theme *theme); int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key, struct expo_action *event);
+/** + * scene_textline_send_key() - Send a key to a textline for processing + * + * @scn: Scene to use + * @tline: textline to use + * @key: Key code to send (KEY_...) + * @event: Place to put any event which is generated by the key + * Returns: 0 if OK (always) + */ +int scene_textline_send_key(struct scene *scn, struct scene_obj_textline *tline, + int key, struct expo_action *event); + /** * scene_menu_destroy() - Destroy a menu in a scene * @@ -185,6 +209,18 @@ int scene_render_deps(struct scene *scn, uint id); */ int scene_menu_render_deps(struct scene *scn, struct scene_obj_menu *menu);
+/** + * scene_textline_render_deps() - Render a textline and its dependencies + * + * Renders the textline and all of its attached objects + * + * @scn: Scene to render + * @tline: textline to render + * Returns: 0 if OK, -ve on error + */ +int scene_textline_render_deps(struct scene *scn, + struct scene_obj_textline *tline); + /** * scene_menu_calc_dims() - Calculate the dimensions of a menu * @@ -255,6 +291,16 @@ struct scene_menitem *scene_menuitem_find_seq(const struct scene_obj_menu *menu, int scene_bbox_union(struct scene *scn, uint id, int inset, struct vidconsole_bbox *bbox);
+/** + * scene_textline_calc_dims() - Calculate the dimensions of a textline + * + * Updates the width and height of the textline based on its contents + * + * @tline: Textline to update + * Returns 0 if OK, -ENOTSUPP if there is no graphical console + */ +int scene_textline_calc_dims(struct scene_obj_textline *tline); + /** * scene_menu_calc_bbox() - Calculate bounding boxes for the menu * @@ -267,6 +313,18 @@ void scene_menu_calc_bbox(struct scene_obj_menu *menu, struct vidconsole_bbox *bbox, struct vidconsole_bbox *label_bbox);
+/** + * scene_textline_calc_bbox() - Calculate bounding box for the textline + * + * @textline: Menu to process + * @bbox: Returns bounding box of textline including prompt + * @edit_bbox: Returns bounding box of editable part + * Return: 0 if OK, -ve on error + */ +void scene_textline_calc_bbox(struct scene_obj_textline *menu, + struct vidconsole_bbox *bbox, + struct vidconsole_bbox *label_bbox); + /** * scene_obj_calc_bbox() - Calculate bounding boxes for an object *

This function is not used outside this file, so mark it static.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
drivers/video/console_truetype.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c index 242b8b71342d..14fb81e9563c 100644 --- a/drivers/video/console_truetype.c +++ b/drivers/video/console_truetype.c @@ -718,8 +718,8 @@ static int truetype_select_font(struct udevice *dev, const char *name, return 0; }
-int truetype_measure(struct udevice *dev, const char *name, uint size, - const char *text, struct vidconsole_bbox *bbox) +static int truetype_measure(struct udevice *dev, const char *name, uint size, + const char *text, struct vidconsole_bbox *bbox) { struct console_tt_metrics *met; stbtt_fontinfo *font;

At present cedit only supports menu keys. For textline objects we need to insert normal ASCII characters.
We also need to handle backspace, which is ASCII 9.
In fact, expo does not make use of all the menu keys, so partition them accordingly and update the logic to support normal ASCII characters, too.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
boot/cedit.c | 2 +- include/menu.h | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/boot/cedit.c b/boot/cedit.c index bb194af77bdc..407793aaddd4 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -176,7 +176,7 @@ int cedit_run(struct expo *exp) key = 0; if (ichar) { key = bootmenu_conv_key(ichar); - if (key == BKEY_NONE) + if (key == BKEY_NONE || key >= BKEY_FIRST_EXTRA) key = ichar; } if (!key) diff --git a/include/menu.h b/include/menu.h index 64ce89b7d263..6571c39b1430 100644 --- a/include/menu.h +++ b/include/menu.h @@ -50,12 +50,17 @@ enum bootmenu_key { BKEY_DOWN, BKEY_SELECT, BKEY_QUIT, + BKEY_SAVE, + + /* 'extra' keys, which are used by menus but not cedit */ BKEY_PLUS, BKEY_MINUS, BKEY_SPACE, - BKEY_SAVE,
BKEY_COUNT, + + /* Keys from here on are not used by cedit */ + BKEY_FIRST_EXTRA = BKEY_PLUS, };
/**

Support textlines in the configuration editor.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: - Correct variable declaration in switch() to make clang happy
boot/cedit.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-)
diff --git a/boot/cedit.c b/boot/cedit.c index 407793aaddd4..8c654dba6dc3 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -81,6 +81,12 @@ int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) scene_menu_arrange(scn, (struct scene_obj_menu *)obj); y += 50; break; + case SCENEOBJT_TEXTLINE: + scene_obj_set_pos(scn, obj->id, 50, y); + scene_textline_arrange(scn, + (struct scene_obj_textline *)obj); + y += 50; + break; } }
@@ -295,17 +301,27 @@ static int h_write_settings(struct scene_obj *obj, void *vpriv) { struct cedit_iter_priv *priv = vpriv; struct abuf *buf = priv->buf; + int ret;
switch (obj->type) { case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: break; + case SCENEOBJT_TEXTLINE: { + const struct scene_obj_textline *tline; + + tline = (struct scene_obj_textline *)obj; + ret = write_dt_string(buf, obj->name, abuf_data(&tline->buf)); + if (ret) + return log_msg_ret("wr2", ret); + break; + } case SCENEOBJT_MENU: { const struct scene_obj_menu *menu; const char *str; char name[80]; - int ret, i; + int i;
/* write the ID of the current item */ menu = (struct scene_obj_menu *)obj; @@ -394,6 +410,19 @@ static int h_read_settings(struct scene_obj *obj, void *vpriv) case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: break; + case SCENEOBJT_TEXTLINE: { + const struct scene_obj_textline *tline; + const char *val; + int len; + + tline = (struct scene_obj_textline *)obj; + + val = ofnode_read_prop(node, obj->name, &len); + if (len >= tline->max_chars) + return log_msg_ret("str", -ENOSPC); + strcpy(abuf_data(&tline->buf), val); + break; + } case SCENEOBJT_MENU: { struct scene_obj_menu *menu; uint val; @@ -472,6 +501,20 @@ static int h_write_settings_env(struct scene_obj *obj, void *vpriv) if (ret) return log_msg_ret("st2", ret); break; + case SCENEOBJT_TEXTLINE: { + const struct scene_obj_textline *tline; + + tline = (struct scene_obj_textline *)obj; + str = abuf_data(&tline->buf); + ret = env_set(var, str); + if (ret) + return log_msg_ret("set", ret); + + if (priv->verbose) + printf("%s=%s\n", var, str); + + break; + } }
return 0; @@ -521,6 +564,21 @@ static int h_read_settings_env(struct scene_obj *obj, void *vpriv) */ menu->cur_item_id = val; break; + case SCENEOBJT_TEXTLINE: { + const struct scene_obj_textline *tline; + const char *value; + + tline = (struct scene_obj_textline *)obj; + value = env_get(var); + if (value && strlen(value) >= tline->max_chars) + return log_msg_ret("str", -ENOSPC); + if (!value) + value = ""; + if (priv->verbose) + printf("%s=%s\n", var, value); + strcpy(abuf_data(&tline->buf), value); + break; + } }
return 0;

Add textline to the list of objects which tthe expo builder can build. This allows them to be provided in the description.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
boot/expo_build.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)
diff --git a/boot/expo_build.c b/boot/expo_build.c index 66a4e5e67d6d..d66d619911df 100644 --- a/boot/expo_build.c +++ b/boot/expo_build.c @@ -287,6 +287,49 @@ static int menu_build(struct build_info *info, ofnode node, struct scene *scn, return 0; }
+static int textline_build(struct build_info *info, ofnode node, + struct scene *scn, uint id, struct scene_obj **objp) +{ + struct scene_obj_textline *ted; + uint ted_id, edit_id; + const char *name; + u32 max_chars; + int ret; + + name = ofnode_get_name(node); + + info->err_prop = "max-chars"; + ret = ofnode_read_u32(node, "max-chars", &max_chars); + if (ret) + return log_msg_ret("max", -ENOENT); + + ret = scene_textline(scn, name, id, max_chars, &ted); + if (ret < 0) + return log_msg_ret("ted", ret); + ted_id = ret; + + /* Set the title */ + ret = add_txt_str(info, node, scn, "title", 0); + if (ret < 0) + return log_msg_ret("tit", ret); + ted->label_id = ret; + + /* Setup the editor */ + info->err_prop = "edit-id"; + ret = ofnode_read_u32(node, "edit-id", &id); + if (ret) + return log_msg_ret("id", -ENOENT); + edit_id = ret; + + ret = scene_txt_str(scn, "edit", edit_id, 0, abuf_data(&ted->buf), + NULL); + if (ret < 0) + return log_msg_ret("add", ret); + ted->edit_id = ret; + + return 0; +} + /** * obj_build() - Build an expo object and add it to a scene * @@ -316,6 +359,8 @@ static int obj_build(struct build_info *info, ofnode node, struct scene *scn)
if (!strcmp("menu", type)) ret = menu_build(info, node, scn, id, &obj); + else if (!strcmp("textline", type)) + ret = textline_build(info, node, scn, id, &obj); else ret = -EOPNOTSUPP; if (ret)

Provide test coverage for the new expo object type, including building and reading/writing settings.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
arch/sandbox/dts/cedit.dtsi | 8 ++++++++ include/test/cedit-test.h | 5 ++++- test/boot/cedit.c | 30 +++++++++++++++++++++++++++--- test/boot/expo.c | 2 +- test/boot/files/expo_ids.h | 3 +++ test/boot/files/expo_layout.dts | 8 ++++++++ 6 files changed, 51 insertions(+), 5 deletions(-)
diff --git a/arch/sandbox/dts/cedit.dtsi b/arch/sandbox/dts/cedit.dtsi index a9eb4c2d594a..9bd84e629367 100644 --- a/arch/sandbox/dts/cedit.dtsi +++ b/arch/sandbox/dts/cedit.dtsi @@ -51,6 +51,14 @@
item-id = <ID_AC_OFF ID_AC_ON ID_AC_MEMORY>; }; + + machine-name { + id = <ID_MACHINE_NAME>; + type = "textline"; + max-chars = <20>; + title = "Machine name"; + edit-id = <ID_MACHINE_NAME_EDIT>; + }; }; };
diff --git a/include/test/cedit-test.h b/include/test/cedit-test.h index 349df75b16d4..475ecc9c2dc2 100644 --- a/include/test/cedit-test.h +++ b/include/test/cedit-test.h @@ -24,6 +24,9 @@ #define ID_AC_ON 11 #define ID_AC_MEMORY 12
-#define ID_DYNAMIC_START 13 +#define ID_MACHINE_NAME 13 +#define ID_MACHINE_NAME_EDIT 14 + +#define ID_DYNAMIC_START 15
#endif diff --git a/test/boot/cedit.c b/test/boot/cedit.c index ab2b8a1f9ffa..aa4171904864 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -58,6 +58,7 @@ BOOTSTD_TEST(cedit_base, 0); /* Check the cedit write_fdt and read_fdt commands */ static int cedit_fdt(struct unit_test_state *uts) { + struct scene_obj_textline *tline; struct video_priv *vid_priv; extern struct expo *cur_exp; struct scene_obj_menu *menu; @@ -66,6 +67,7 @@ static int cedit_fdt(struct unit_test_state *uts) struct scene *scn; oftree tree; ofnode node; + char *str; void *fdt; int i;
@@ -79,6 +81,12 @@ static int cedit_fdt(struct unit_test_state *uts) ut_assertnonnull(menu); menu->cur_item_id = ID_CPU_SPEED_2;
+ /* get a textline to fiddle with too */ + tline = scene_obj_find(scn, ID_MACHINE_NAME, SCENEOBJT_TEXTLINE); + ut_assertnonnull(tline); + str = abuf_data(&tline->buf); + strcpy(str, "my-machine"); + ut_assertok(run_command("cedit write_fdt hostfs - settings.dtb", 0)); ut_assertok(run_commandf("load hostfs - %lx settings.dtb", addr)); ut_assert_nextlinen("1024 bytes read"); @@ -86,26 +94,29 @@ static int cedit_fdt(struct unit_test_state *uts) fdt = map_sysmem(addr, 1024); tree = oftree_from_fdt(fdt); node = ofnode_find_subnode(oftree_root(tree), CEDIT_NODE_NAME); + ut_assert(ofnode_valid(node));
ut_asserteq(ID_CPU_SPEED_2, ofnode_read_u32_default(node, "cpu-speed", 0)); ut_asserteq_str("2.5 GHz", ofnode_read_string(node, "cpu-speed-str")); - ut_assert(ofnode_valid(node)); + ut_asserteq_str("my-machine", ofnode_read_string(node, "machine-name"));
- /* There should only be 4 properties */ + /* There should only be 5 properties */ for (i = 0, ofnode_first_property(node, &prop); ofprop_valid(&prop); i++, ofnode_next_property(&prop)) ; - ut_asserteq(4, i); + ut_asserteq(5, i);
ut_assert_console_end();
/* reset the expo */ menu->cur_item_id = ID_CPU_SPEED_1; + *str = '\0';
/* load in the settings and make sure they update */ ut_assertok(run_command("cedit read_fdt hostfs - settings.dtb", 0)); ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id); + ut_asserteq_str("my-machine", ofnode_read_string(node, "machine-name"));
ut_assertnonnull(menu); ut_assert_console_end(); @@ -117,10 +128,12 @@ BOOTSTD_TEST(cedit_fdt, 0); /* Check the cedit write_env and read_env commands */ static int cedit_env(struct unit_test_state *uts) { + struct scene_obj_textline *tline; struct video_priv *vid_priv; extern struct expo *cur_exp; struct scene_obj_menu *menu; struct scene *scn; + char *str;
console_record_reset_enable(); ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0)); @@ -132,25 +145,36 @@ static int cedit_env(struct unit_test_state *uts) ut_assertnonnull(menu); menu->cur_item_id = ID_CPU_SPEED_2;
+ /* get a textline to fiddle with too */ + tline = scene_obj_find(scn, ID_MACHINE_NAME, SCENEOBJT_TEXTLINE); + ut_assertnonnull(tline); + str = abuf_data(&tline->buf); + strcpy(str, "my-machine"); + ut_assertok(run_command("cedit write_env -v", 0)); ut_assert_nextlinen("c.cpu-speed=7"); ut_assert_nextlinen("c.cpu-speed-str=2.5 GHz"); ut_assert_nextlinen("c.power-loss=10"); ut_assert_nextlinen("c.power-loss-str=Always Off"); + ut_assert_nextlinen("c.machine-name=my-machine"); ut_assert_console_end();
ut_asserteq(7, env_get_ulong("c.cpu-speed", 10, 0)); ut_asserteq_str("2.5 GHz", env_get("c.cpu-speed-str")); + ut_asserteq_str("my-machine", env_get("c.machine-name"));
/* reset the expo */ menu->cur_item_id = ID_CPU_SPEED_1; + *str = '\0';
ut_assertok(run_command("cedit read_env -v", 0)); ut_assert_nextlinen("c.cpu-speed=7"); ut_assert_nextlinen("c.power-loss=10"); + ut_assert_nextlinen("c.machine-name=my-machine"); ut_assert_console_end();
ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id); + ut_asserteq_str("my-machine", env_get("c.machine-name"));
return 0; } diff --git a/test/boot/expo.c b/test/boot/expo.c index 90027409c817..714fdfa415d1 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -654,7 +654,7 @@ static int expo_test_build(struct unit_test_state *uts)
ut_asserteq_str("name", exp->name); ut_asserteq(0, exp->scene_id); - ut_asserteq(ID_DYNAMIC_START + 20, exp->next_id); + ut_asserteq(ID_DYNAMIC_START + 24, exp->next_id); ut_asserteq(false, exp->popup);
/* check the scene */ diff --git a/test/boot/files/expo_ids.h b/test/boot/files/expo_ids.h index 027d44bf38c5..a86e0d06f6b5 100644 --- a/test/boot/files/expo_ids.h +++ b/test/boot/files/expo_ids.h @@ -21,5 +21,8 @@ enum { ID_AC_ON, ID_AC_MEMORY,
+ ID_MACHINE_NAME, + ID_MACHINE_NAME_EDIT, + ID_DYNAMIC_START, }; diff --git a/test/boot/files/expo_layout.dts b/test/boot/files/expo_layout.dts index cb2a674d9d56..bed552288f4c 100644 --- a/test/boot/files/expo_layout.dts +++ b/test/boot/files/expo_layout.dts @@ -55,6 +55,14 @@ start-bit = <0x422>; bit-length = <2>; }; + + machine-name { + id = <ID_MACHINE_NAME>; + type = "textline"; + max-chars = <20>; + title = "Machine name"; + edit-id = <ID_MACHINE_NAME_EDIT>; + }; }; };

Update the expo documentation to include mention of this new object type.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
doc/develop/cedit.rst | 3 ++- doc/develop/expo.rst | 48 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 6 deletions(-)
diff --git a/doc/develop/cedit.rst b/doc/develop/cedit.rst index 63dff9d3f140..82305b921f0a 100644 --- a/doc/develop/cedit.rst +++ b/doc/develop/cedit.rst @@ -162,7 +162,8 @@ Cedit provides several options for persistent settings: - Writing an FDT file to a filesystem - Writing to U-Boot's environment variables, which are then typically stored in a persistent manner -- Writing to CMOS RAM registers (common on x86 machines) +- Writing to CMOS RAM registers (common on x86 machines). Note that textline + objects do not appear in CMOS RAM registers
For now, reading and writing settings is not automatic. See the :doc:`../usage/cmd/cedit` for how to do this on the command line or in a diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst index f13761995d3f..c87b6ec81285 100644 --- a/doc/develop/expo.rst +++ b/doc/develop/expo.rst @@ -63,9 +63,12 @@ select the item), label and description. All three are shown in a single line within the menu. Items can also have a preview image, which is shown when the item is highlighted.
-All components have a name. This is purely for debugging, so it is easy to see -what object is referred to. Of course the ID numbers can help as well, but they -are less easy to distinguish. +A `textline object` contains a label and an editable string. + +All components have a name. This is mostly for debugging, so it is easy to see +what object is referred to, although the name is also used for saving values. +Of course the ID numbers can help as well, but they are less easy to +distinguish.
While the expo implementation provides support for handling keypresses and rendering on the display or serial port, it does not actually deal with reading @@ -136,7 +139,9 @@ this is to use `cli_ch_process()`, since it handles conversion of escape sequences into keys. However, expo has some special menu-key codes for navigating the interface. These are defined in `enum bootmenu_key` and include `BKEY_UP` for moving up and `BKEY_SELECT` for selecting an item. You can use -`bootmenu_conv_key()` to convert an ASCII key into one of these. +`bootmenu_conv_key()` to convert an ASCII key into one of these, but if it +returns a value >= `BKEY_FIRST_EXTRA` then you should pass the unmodified ASCII +key to the expo, since it may be used by textline objects.
Once a keypress is decoded, call `expo_send_key()` to send it to the expo. This may cause an update to the expo state and may produce an action. @@ -312,6 +317,9 @@ type "menu" Menu containing items which can be selected by the user
+ "textline" + A line of text which can be edited + id type: u32, required
@@ -362,6 +370,26 @@ desc-label / desc-label-id Specifies the description for each item in the menu. These are currently only intended for use in simple mode.
+Textline nodes have the following additional properties: + +label / label-id + type: string / u32, required + + Specifies the label of the textline. This is shown to the left of the area + for this textline. + +edit-id + type: u32, required + + Specifies the ID of the of the editable text object. This can be used to + obtain the text from the textline + +max-chars: + type: u32, required + + Specifies the maximum number of characters permitted to be in the textline. + The user will be prevented from adding more. +
Expo layout ~~~~~~~~~~~ @@ -401,6 +429,9 @@ strings are provided inline in the nodes where they are used. ID_AC_ON, ID_AC_MEMORY,
+ ID_MACHINE_NAME, + ID_MACHINE_NAME_EDIT, + ID_DYNAMIC_START, */
@@ -447,6 +478,13 @@ strings are provided inline in the nodes where they are used.
item-id = <ID_AC_OFF ID_AC_ON ID_AC_MEMORY>; }; + + machine-name { + id = <ID_MACHINE_NAME>; + type = "textline"; + max-chars = <20>; + title = "Machine name"; + edit-id = <ID_MACHINE_NAME_EDIT>; }; };
@@ -474,7 +512,7 @@ Some ideas for future work: - Image formats other than BMP - Use of ANSI sequences to control a serial terminal - Colour selection -- Support for more widgets, e.g. text, numeric, radio/option +- Support for more widgets, e.g. numeric, radio/option - Mouse support - Integrate Nuklear, NxWidgets or some other library for a richer UI - Optimise rendering by only updating the display with changes since last render

On Sun, 01 Oct 2023 19:13:04 -0600, Simon Glass wrote:
So far expo only supports menus. These are quite flexible for various kinds of settings, but cannot deal with free-form input, such as a serial number or a machine name.
This series adds support for a textline object, which is a single line of text. It has a maximum length and its value is stored within the expo structure.
[...]
Applied to u-boot/master, thanks!
participants (2)
-
Simon Glass
-
Tom Rini