From 33eb0b9eef3360396aa0f650f8e1e01baf6dc181 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 1 Oct 2023 19:13:06 -0600 Subject: cli: Add a command to show cmdline history 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 --- include/cli.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/cli.h b/include/cli.h index 094a6602d70..ac09c80c784 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 -- cgit v1.3.1 From 6321391ac3e310081768b6170554d4f49416b5c2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 1 Oct 2023 19:13:08 -0600 Subject: cli: Drop #ifdefs for CONFIG_AUTO_COMPLETE in cli_readline Use a static inline and adjust the logic to avoid the need for #ifdefs in cli_readline_into_buffer() Signed-off-by: Simon Glass --- common/cli_readline.c | 85 +++++++++++++++++++++++++-------------------------- include/command.h | 6 ++++ 2 files changed, 48 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/common/cli_readline.c b/common/cli_readline.c index 61f9ba99068..458e927e492 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 34ea989b39b..1c4ec4257a5 100644 --- a/include/command.h +++ b/include/command.h @@ -95,6 +95,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 /** -- cgit v1.3.1 From be5c2edd10cc0c5c1b938fc03bc7e7c35801a0bd Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 1 Oct 2023 19:13:11 -0600 Subject: cli: Convert cread_line() to use a struct for the main vars 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 --- common/cli_readline.c | 116 ++++++++++++++++++++++++++------------------------ include/cli.h | 14 ++++++ 2 files changed, 75 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/common/cli_readline.c b/common/cli_readline.c index f863404110c..03624ca18af 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 ac09c80c784..1bc1ab8035c 100644 --- a/include/cli.h +++ b/include/cli.h @@ -8,6 +8,7 @@ #define __CLI_H #include +#include /** * 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 * -- cgit v1.3.1 From e5509ce87b29f773b045200735bec2903b72eed4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 1 Oct 2023 19:13:13 -0600 Subject: cli: Create a function to process characters 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 --- common/cli_readline.c | 108 +++++++++++++++++++++++++++++--------------------- include/cli.h | 15 +++++++ 2 files changed, 78 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/common/cli_readline.c b/common/cli_readline.c index f5d1cb03433..62d419bb36a 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 1bc1ab8035c..bbc53276435 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); -- cgit v1.3.1 From 8fc041fe4c34bb6108444ee9970151f43add0ce9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 1 Oct 2023 19:13:15 -0600 Subject: cli: Allow history to be disabled 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 --- common/cli_readline.c | 42 ++++++++++++++++++++++-------------------- include/cli.h | 2 ++ 2 files changed, 24 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/common/cli_readline.c b/common/cli_readline.c index fdb84d9204f..fa8f525d3a4 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 bbc53276435..252bdb70ab0 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; }; -- cgit v1.3.1 From 3b487bf51115bfb9a0f85667a5608fc57788107c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 1 Oct 2023 19:13:16 -0600 Subject: cli: Allow command completion to be disabled 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 --- common/cli_readline.c | 3 ++- include/cli.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/common/cli_readline.c b/common/cli_readline.c index fa8f525d3a4..23913a857a9 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 252bdb70ab0..ad3cb4499fe 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; }; -- cgit v1.3.1 From 39ee32166f607d4e30a74d46f82adb39e4134ec4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 1 Oct 2023 19:13:17 -0600 Subject: cli: Add a function to set up a new cread Create a init function so that it is easy to use command-line reading. Signed-off-by: Simon Glass --- common/cli_readline.c | 24 +++++++++++++++--------- include/cli.h | 17 ++++++++++++++++- 2 files changed, 31 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/common/cli_readline.c b/common/cli_readline.c index 23913a857a9..06b8d465044 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 ad3cb4499fe..e183d561369 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); -- cgit v1.3.1 From 9e55d09596a5b6ec1f8cbfc3e97d29b9929dee86 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 1 Oct 2023 19:13:18 -0600 Subject: video: Allow obtaining the nominal size of a string size 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 --- drivers/video/console_truetype.c | 31 +++++++++++++++++++++++++++++++ drivers/video/vidconsole-uclass.c | 22 ++++++++++++++++++++++ include/video_console.h | 30 ++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) (limited to 'include') diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c index 0f9bb49e44f..8ed79c37676 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 b5b3b662590..23b1b81731b 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 2694e44f6ec..5234c85efd6 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 * -- cgit v1.3.1 From 9899eef2cb41a9cde1dca87f3ddb041e347b177a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 1 Oct 2023 19:13:19 -0600 Subject: video: Allow saving and restoring text-entry state 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 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 --- drivers/video/console_truetype.c | 53 +++++++++++++++++++++++++++++++++++++++ drivers/video/vidconsole-uclass.c | 32 +++++++++++++++++++++++ include/video_console.h | 49 ++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) (limited to 'include') diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c index 8ed79c37676..1ce01a96bb6 100644 --- a/drivers/video/console_truetype.c +++ b/drivers/video/console_truetype.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -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 23b1b81731b..07427ba346b 100644 --- a/drivers/video/vidconsole-uclass.c +++ b/drivers/video/vidconsole-uclass.c @@ -10,6 +10,7 @@ #define LOG_CATEGORY UCLASS_VIDEO_CONSOLE #include +#include #include #include #include @@ -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 5234c85efd6..28d65451889 100644 --- a/include/video_console.h +++ b/include/video_console.h @@ -8,6 +8,7 @@ #include +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 * -- cgit v1.3.1 From 617d7b545b6fa555f47944a10b1a1b261491e3b9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 1 Oct 2023 19:13:20 -0600 Subject: video: Export vidconsole_entry_start() 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 --- drivers/video/vidconsole-uclass.c | 2 +- include/video_console.h | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c index 07427ba346b..2f3f685a55c 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 28d65451889..87f9a588575 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 * -- cgit v1.3.1 From 37db20d0a64132a7ae3d0223de6b93167d60bea4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 1 Oct 2023 19:13:21 -0600 Subject: video: Support showing a cursor 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 --- 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(+) (limited to 'include') diff --git a/drivers/video/console_core.c b/drivers/video/console_core.c index b5d0e3dceca..d17764d0b08 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 413c7abee9e..a0231293f31 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 1ce01a96bb6..242b8b71342 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 2f3f685a55c..22d55df71f6 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 c41edd45249..0ec581b2663 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 87f9a588575..bde67fa9a5a 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 * -- cgit v1.3.1 From 909c486d7cc52efd0db0a1922778fe5c17e5be24 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 1 Oct 2023 19:13:23 -0600 Subject: expo: Fix up comments for get_cur_menuitem_text() et al This internal function could use a comment. Add one. Also tidy up a few other comments. Signed-off-by: Simon Glass fixup: comments --- boot/cedit.c | 10 ++++++++++ boot/scene_internal.h | 2 +- include/expo.h | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/boot/cedit.c b/boot/cedit.c index 73645f70b6c..1324a861998 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 695a907dc6a..42efcee092f 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 9d2e817eb97..5b21110cde3 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) -- cgit v1.3.1 From d88edd2bda2150b253fac702dd26ec4c01ccf988 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 1 Oct 2023 19:13:27 -0600 Subject: expo: Allow highlighting other scene-object types 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 --- boot/scene.c | 11 ++++------- include/expo.h | 8 ++++++++ 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/boot/scene.c b/boot/scene.c index 8e5d3aa7dc0..4d7cabd75f4 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 5b21110cde3..c16b3dd61b0 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 * -- cgit v1.3.1 From 93f99b35ec7d32b86a00cd066d2f8107cc9c23ed Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 1 Oct 2023 19:13:31 -0600 Subject: expo: Add some scene fields needed for text entry 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 --- boot/scene.c | 10 ++++++++++ include/expo.h | 14 ++++++++++++++ 2 files changed, 24 insertions(+) (limited to 'include') diff --git a/boot/scene.c b/boot/scene.c index 314dd7c6e84..0b44a13748a 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 c16b3dd61b0..a535bc1695d 100644 --- a/include/expo.h +++ b/include/expo.h @@ -7,11 +7,14 @@ #ifndef __EXPO_H #define __EXPO_H +#include #include #include struct udevice; +#include + /** * 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 * -- cgit v1.3.1 From 4db7519032f94bc769a7a32421b26b2ec58cbbe5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 1 Oct 2023 19:13:32 -0600 Subject: expo: Add basic support for textline objects 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 --- 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 (limited to 'include') diff --git a/boot/Makefile b/boot/Makefile index 6ce983b83fa..ad608598d29 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 00000000000..2caa81ee158 --- /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 + */ + +#define LOG_CATEGORY LOGC_EXPO + +#include +#include +#include +#include +#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 a535bc1695d..264745f7f01 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 * -- cgit v1.3.1 From 8579cb010d25a881b7ea082f8450d385b745f825 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 1 Oct 2023 19:13:36 -0600 Subject: expo: Support handling any key in cedit 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 --- boot/cedit.c | 2 +- include/menu.h | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/boot/cedit.c b/boot/cedit.c index bb194af77bd..407793aaddd 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 64ce89b7d26..6571c39b143 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, }; /** -- cgit v1.3.1 From c2bd2d33d92cf729dfa7bac6c4c754098f0a2dfb Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 1 Oct 2023 19:13:39 -0600 Subject: expo: Update tests to include textline Provide test coverage for the new expo object type, including building and reading/writing settings. Signed-off-by: Simon Glass --- 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(-) (limited to 'include') diff --git a/arch/sandbox/dts/cedit.dtsi b/arch/sandbox/dts/cedit.dtsi index a9eb4c2d594..9bd84e62936 100644 --- a/arch/sandbox/dts/cedit.dtsi +++ b/arch/sandbox/dts/cedit.dtsi @@ -51,6 +51,14 @@ item-id = ; }; + + machine-name { + id = ; + type = "textline"; + max-chars = <20>; + title = "Machine name"; + edit-id = ; + }; }; }; diff --git a/include/test/cedit-test.h b/include/test/cedit-test.h index 349df75b16d..475ecc9c2dc 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 ab2b8a1f9ff..aa417190486 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 90027409c81..714fdfa415d 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 027d44bf38c..a86e0d06f6b 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 cb2a674d9d5..bed552288f4 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 = ; + type = "textline"; + max-chars = <20>; + title = "Machine name"; + edit-id = ; + }; }; }; -- cgit v1.3.1