[Concept,02/16] cli: Add putch callback to struct cli_line_state

Message ID 20260118204303.1982533-3-sjg@u-boot.org
State New
Headers
Series expo: Continue preparations for textedit (part D) |

Commit Message

Simon Glass Jan. 18, 2026, 8:42 p.m. UTC
  From: Simon Glass <simon.glass@canonical.com>

Add a callback function pointer to struct cli_line_state that allows
redirection of character output during line editing. This enables expo
textlines to direct output to the correct vidconsole context.

Add a new Kconfig option CLI_READLINE_CALLBACK which is selected by
EXPO

When enabled, the callback is checked before outputting characters. When
disabled, the compiler optimises away the check.

Update all character-output functions in cli_readline.c to use the new
cls_putch() helper, which calls the callback if set or falls back to
putc()

Add a few comments while we are here.

Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---

 boot/Kconfig          |  1 +
 cmd/Kconfig           |  8 +++++
 common/cli_readline.c | 82 +++++++++++++++++++++++++++----------------
 include/cli.h         |  2 ++
 4 files changed, 63 insertions(+), 30 deletions(-)
  

Patch

diff --git a/boot/Kconfig b/boot/Kconfig
index cde4472ca57..7ff0dedb748 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -973,6 +973,7 @@  config EXPO
 	bool "Support for expos - groups of scenes displaying a UI"
 	depends on VIDEO
 	default y if BOOTMETH_VBE
+	select CLI_READLINE_CALLBACK if CMDLINE_EDITING
 	help
 	  An expo is a way of presenting and collecting information from the
 	  user. It consists of a collection of 'scenes' of which only one is
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 072ff879cd8..8a873b1d927 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -54,6 +54,14 @@  config CMDLINE_EDITING
 	  Enable editing and History functions for interactive command line
 	  input operations
 
+config CLI_READLINE_CALLBACK
+	bool "Support a callback for character output"
+	depends on CMDLINE_EDITING
+	help
+	  Enable a callback for character output during line editing. This
+	  allows redirection of output to a different destination, such as
+	  a vidconsole. This is used by expo to support textline editing.
+
 config CMDLINE_PS_SUPPORT
 	bool "Enable support for changing the command prompt string at run-time"
 	depends on HUSH_PARSER
diff --git a/common/cli_readline.c b/common/cli_readline.c
index 244a287b435..81b0688da38 100644
--- a/common/cli_readline.c
+++ b/common/cli_readline.c
@@ -71,9 +71,23 @@  static char *delete_char (char *buffer, char *p, int *colp, int *np, int plen)
 #define DEL7			((char)127)
 #define CREAD_HIST_CHAR		('!')
 
-#define getcmd_putch(ch)	putc(ch)
 #define getcmd_getch()		getchar()
-#define getcmd_cbeep()		getcmd_putch('\a')
+
+/**
+ * cls_putch() - Output a character, using callback if available
+ *
+ * @cls: CLI line state
+ * @ch: Character to output
+ */
+static void cls_putch(struct cli_line_state *cls, int ch)
+{
+	if (CONFIG_IS_ENABLED(CLI_READLINE_CALLBACK) && cls->putch)
+		cls->putch(cls, ch);
+	else
+		putc(ch);
+}
+
+#define getcmd_cbeep(cls)	cls_putch(cls, '\a')
 
 #ifdef CONFIG_XPL_BUILD
 #define HIST_MAX		3
@@ -95,12 +109,19 @@  static char *hist_list[HIST_MAX];
 
 #define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1)
 
-static void getcmd_putchars(int count, int ch)
+/**
+ * cls_putchars() - Output a character multiple times
+ *
+ * @cls: CLI line state
+ * @count: Number of times to output the character
+ * @ch: Character to output
+ */
+static void cls_putchars(struct cli_line_state *cls, int count, int ch)
 {
 	int i;
 
 	for (i = 0; i < count; i++)
-		getcmd_putch(ch);
+		cls_putch(cls, ch);
 }
 
 static int hist_init(void)
@@ -206,7 +227,7 @@  void cread_print_hist_list(void)
 
 #define BEGINNING_OF_LINE() {			\
 	while (cls->num) {			\
-		getcmd_putch(CTL_BACKSPACE);	\
+		cls_putch(cls, CTL_BACKSPACE);	\
 		cls->num--;			\
 	}					\
 }
@@ -215,7 +236,7 @@  void cread_print_hist_list(void)
 	if (cls->num < cls->eol_num) {		\
 		printf("%*s", (int)(cls->eol_num - cls->num), ""); \
 		do {					\
-			getcmd_putch(CTL_BACKSPACE);	\
+			cls_putch(cls, CTL_BACKSPACE);	\
 		} while (--cls->eol_num > cls->num);	\
 	}						\
 }
@@ -228,15 +249,15 @@  void cread_print_hist_list(void)
 	}						\
 }
 
-static void cread_add_char(char ichar, int insert, uint *num,
-			   uint *eol_num, char *buf, uint len)
+static void cread_add_char(struct cli_line_state *cls, char ichar, int insert,
+			   uint *num, uint *eol_num, char *buf, uint len)
 {
 	uint wlen;
 
 	/* room ??? */
 	if (insert || *num == *eol_num) {
 		if (*eol_num > len - 1) {
-			getcmd_cbeep();
+			getcmd_cbeep(cls);
 			return;
 		}
 		(*eol_num)++;
@@ -251,7 +272,7 @@  static void cread_add_char(char ichar, int insert, uint *num,
 		putnstr(buf + *num, wlen);
 		(*num)++;
 		while (--wlen)
-			getcmd_putch(CTL_BACKSPACE);
+			cls_putch(cls, CTL_BACKSPACE);
 	} else {
 		/* echo the character */
 		wlen = 1;
@@ -261,11 +282,12 @@  static void cread_add_char(char ichar, int insert, uint *num,
 	}
 }
 
-static void cread_add_str(char *str, int strsize, int insert,
-			  uint *num, uint *eol_num, char *buf, uint len)
+static void cread_add_str(struct cli_line_state *cls, char *str, int strsize,
+			  int insert, uint *num, uint *eol_num, char *buf,
+			  uint len)
 {
 	while (strsize--) {
-		cread_add_char(*str, insert, num, eol_num, buf, len);
+		cread_add_char(cls, *str, insert, num, eol_num, buf, len);
 		str++;
 	}
 }
@@ -293,13 +315,13 @@  int cread_line_process_ch(struct cli_line_state *cls, char ichar)
 		return -EINTR;
 	case CTL_CH('f'):
 		if (cls->num < cls->eol_num) {
-			getcmd_putch(buf[cls->num]);
+			cls_putch(cls, buf[cls->num]);
 			cls->num++;
 		}
 		break;
 	case CTL_CH('b'):
 		if (cls->num) {
-			getcmd_putch(CTL_BACKSPACE);
+			cls_putch(cls, CTL_BACKSPACE);
 			cls->num--;
 		}
 		break;
@@ -314,9 +336,9 @@  int cread_line_process_ch(struct cli_line_state *cls, char ichar)
 				putnstr(buf + cls->num, wlen);
 			}
 
-			getcmd_putch(' ');
+			cls_putch(cls, ' ');
 			do {
-				getcmd_putch(CTL_BACKSPACE);
+				cls_putch(cls, CTL_BACKSPACE);
 			} while (wlen--);
 			cls->eol_num--;
 		}
@@ -346,11 +368,11 @@  int cread_line_process_ch(struct cli_line_state *cls, char ichar)
 			memmove(&buf[base], &buf[cls->num],
 				cls->eol_num - base + 1);
 			cls->num = base;
-			getcmd_putchars(wlen, CTL_BACKSPACE);
+			cls_putchars(cls, wlen, CTL_BACKSPACE);
 			puts(buf + base);
-			getcmd_putchars(wlen, ' ');
-			getcmd_putchars(wlen + cls->eol_num - cls->num,
-					CTL_BACKSPACE);
+			cls_putchars(cls, wlen, ' ');
+			cls_putchars(cls, wlen + cls->eol_num - cls->num,
+				     CTL_BACKSPACE);
 		}
 		break;
 	case CTL_CH('x'):
@@ -367,11 +389,11 @@  int cread_line_process_ch(struct cli_line_state *cls, char ichar)
 			wlen = cls->eol_num - cls->num;
 			cls->num--;
 			memmove(&buf[cls->num], &buf[cls->num + 1], wlen);
-			getcmd_putch(CTL_BACKSPACE);
+			cls_putch(cls, CTL_BACKSPACE);
 			putnstr(buf + cls->num, wlen);
-			getcmd_putch(' ');
+			cls_putch(cls, ' ');
 			do {
-				getcmd_putch(CTL_BACKSPACE);
+				cls_putch(cls, CTL_BACKSPACE);
 			} while (wlen--);
 			cls->eol_num--;
 		}
@@ -387,7 +409,7 @@  int cread_line_process_ch(struct cli_line_state *cls, char ichar)
 				hline = hist_next();
 
 			if (!hline) {
-				getcmd_cbeep();
+				getcmd_cbeep(cls);
 				break;
 			}
 
@@ -411,7 +433,7 @@  int cread_line_process_ch(struct cli_line_state *cls, char ichar)
 
 			/* do not autocomplete when in the middle */
 			if (cls->num < cls->eol_num) {
-				getcmd_cbeep();
+				getcmd_cbeep(cls);
 				break;
 			}
 
@@ -427,8 +449,8 @@  int cread_line_process_ch(struct cli_line_state *cls, char ichar)
 		}
 		fallthrough;
 	default:
-		cread_add_char(ichar, cls->insert, &cls->num, &cls->eol_num,
-			       buf, cls->len);
+		cread_add_char(cls, ichar, cls->insert, &cls->num,
+			       &cls->eol_num, buf, cls->len);
 		break;
 	}
 
@@ -451,8 +473,8 @@  void cli_cread_init(struct cli_line_state *cls, char *buf, uint buf_size)
 	cls->len = buf_size;
 
 	if (init_len)
-		cread_add_str(buf, init_len, 0, &cls->num, &cls->eol_num, buf,
-			      buf_size);
+		cread_add_str(cls, 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,
diff --git a/include/cli.h b/include/cli.h
index e183d561369..88f96c03cb3 100644
--- a/include/cli.h
+++ b/include/cli.h
@@ -36,6 +36,7 @@  struct cli_ch_state {
  *	be set)
  * @buf: Buffer containing line
  * @prompt: Prompt for the line
+ * @putch: Function to call to output a character (NULL to use putc())
  */
 struct cli_line_state {
 	uint num;
@@ -46,6 +47,7 @@  struct cli_line_state {
 	bool cmd_complete;
 	char *buf;
 	const char *prompt;
+	void (*putch)(struct cli_line_state *cls, int ch);
 };
 
 /**