* [PATCH 00/20] fbconsole: support TUI-relevant escape sequences
@ 2026-05-03 8:33 Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 01/20] fbconsole: remove incomplete CSI_CNT state Ahmad Fatoum
` (20 more replies)
0 siblings, 21 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox
A BIOS-style TUI in barebox could go a long way towards making barebox
more user friendly: It could list different parameters with familiar
controls instead of having to juggle around commands like varinfo,
devinfo and magicvar.
This could especially be the case for barebox running on laptops.
Laptops tend to not have a serial port routed out though, so the TUI we
provide will need to be displayed on the framebuffer.
Instead of adding a graphic toolkit to barebox, let's flesh out the
framebuffer console support, so it can display a TUI that can avoid full
redraws. Bonus benefit: The same UI will be usable over serial as well.
A mockup of how this could look like is available at barebox.org/demo when
running the tui command.
Ahmad Fatoum (20):
fbconsole: remove incomplete CSI_CNT state
fbconsole: improve handling of unexpected escape sequences
fbconsole: fix handling of CSI buffer overflow
fbconsole: do not reset cursor visibility alongside attributes
fbconsole: respect hidden cursor everywhere
fbconsole: call fb_blit_area for every drawchar
fbconsole: skip fb_flush when processing escape sequences
fbconsole: factor out helpers for clamped cursor row/col setting
fbconsole: precompute foreground/background colors
fbconsole: collect renderable state into struct fbc_screen_state
fbconsole: implement CSI A/B/C/D cursor movement sequences
fbconsole: restrict cursor visibility to DEC private mode 25
fbconsole: add new clear_chars helper
fbconsole: implement erase entire line CSI
fbconsole: support ESC[0J and ESC[1J partial screen clear
fbconsole: implement DEC save/restore cursor
fbconsole: implement VT100 deferred wrap (last column flag)
fbconsole: implement alternate screen buffer (ESC[?1049h/l)
fbconsole: adapt logging depending on activated streams
netconsole: suppress log message when opening console file
common/console.c | 8 +-
common/serdev.c | 2 +-
drivers/video/fbconsole.c | 541 ++++++++++++++++++++++++++------------
include/console.h | 4 +-
net/netconsole.c | 5 +-
5 files changed, 376 insertions(+), 184 deletions(-)
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 01/20] fbconsole: remove incomplete CSI_CNT state
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 02/20] fbconsole: improve handling of unexpected escape sequences Ahmad Fatoum
` (19 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
DEC private sequences use '?' as a parameter prefix byte (e.g. ESC[?25h
to show the cursor).
The CSI state machine treats any non-digit, non-separator as a final
byte and dispatches fbc_parse_csi(), but '?' is only a prefix with
the real final byte ('h' or 'l') following later.
fbc_parse_csi() handles this by recording '?' in csi_cmd and
transitioning to CSI_CNT, which resumes CSI parameter accumulation
for the digits and final byte that follow.
This introduces a subtle bug: The character directly after the ? is not
saved into the priv->csi buffer. This didn't matter so far, because we
only handle h and l without regard to the number before it.
By simplifying the code and removing that extra state, we also fix this
bug by not having that incomplete CSI_CNT state in-between.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
drivers/video/fbconsole.c | 30 +++++++++---------------------
1 file changed, 9 insertions(+), 21 deletions(-)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index 7946d013b24d..60e9783266b6 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -17,7 +17,6 @@ enum state_t {
LIT, /* Literal input */
ESC, /* Start of escape sequence */
CSI, /* Reading arguments in "CSI Pn ;...*/
- CSI_CNT,
};
enum fbconsole_rotation {
@@ -555,10 +554,6 @@ static void fbc_parse_csi(struct fbc_priv *priv)
case 'm':
fbc_parse_colors(priv);
return;
- case '?': /* vt100: show/hide cursor */
- priv->csi_cmd = last;
- priv->state = CSI_CNT;
- return;
case 'h':
/* suffix for vt100 "[?25h" */
switch (priv->csi_cmd) {
@@ -674,29 +669,22 @@ static void fbc_putc(struct console_device *cdev, char c)
}
switch (c) {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
+ case '?':
+ /* DEC private sequences use '?' as a parameter prefix byte.
+ * Record '?' in csi_cmd and continue CSI parameter
+ * accumulation for digits and the final byte that follows.
+ */
+ priv->csi_cmd = c;
+ break;
+ case '0' ... '9':
case ';':
case ':':
break;
default:
fbc_parse_csi(priv);
- if (priv->state != CSI_CNT)
- priv->state = LIT;
+ priv->state = LIT;
}
break;
- case CSI_CNT:
- priv->state = CSI;
- break;
-
}
priv->in_console = 0;
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 02/20] fbconsole: improve handling of unexpected escape sequences
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 01/20] fbconsole: remove incomplete CSI_CNT state Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 03/20] fbconsole: fix handling of CSI buffer overflow Ahmad Fatoum
` (18 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
enum state_t describes the state the parser is in: literal input,
escape or CSI.
The way we currently parse it has expectations on the input that does
not match standard terminal behavior and may lead the state machine
getting stuck in a mode when getting control characters in an unexpected
order.
Improve this by supporting following corner cases:
- In ESC mode, if a non-'[' character is received, reset to LIT.
- In CSI mode, if ESC is received, transition to ESC.
- In CSI mode, always reset the DEC private mode when transitioning out.
So far this happened only in the successful cases.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
drivers/video/fbconsole.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index 60e9783266b6..2461ab2fb849 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -558,7 +558,6 @@ static void fbc_parse_csi(struct fbc_priv *priv)
/* suffix for vt100 "[?25h" */
switch (priv->csi_cmd) {
case '?': /* cursor visible */
- priv->csi_cmd = -1;
if (!(priv->flags & HIDE_CURSOR))
break;
@@ -572,8 +571,6 @@ static void fbc_parse_csi(struct fbc_priv *priv)
/* suffix for vt100 "[?25l" */
switch (priv->csi_cmd) {
case '?': /* cursor invisible */
- priv->csi_cmd = -1;
-
/* hide cursor now */
video_invertchar(priv, priv->x, priv->y);
priv->flags |= HIDE_CURSOR;
@@ -658,13 +655,23 @@ static void fbc_putc(struct console_device *cdev, char c)
priv->csipos = 0;
memset(priv->csi, 0, 6);
break;
+ default:
+ priv->state = LIT;
+ break;
}
break;
case CSI:
+ if (c == '\033') {
+ priv->state = ESC;
+ priv->csi_cmd = -1;
+ break;
+ }
+
priv->csi[priv->csipos++] = c;
if (priv->csipos == 255) {
priv->csipos = 0;
priv->state = LIT;
+ priv->csi_cmd = -1;
return;
}
@@ -683,6 +690,7 @@ static void fbc_putc(struct console_device *cdev, char c)
default:
fbc_parse_csi(priv);
priv->state = LIT;
+ priv->csi_cmd = -1;
}
break;
}
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 03/20] fbconsole: fix handling of CSI buffer overflow
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 01/20] fbconsole: remove incomplete CSI_CNT state Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 02/20] fbconsole: improve handling of unexpected escape sequences Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 04/20] fbconsole: do not reset cursor visibility alongside attributes Ahmad Fatoum
` (17 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
To avoid overflowing the CSI buffer, a check for csipos == 255
aborts the CSI parse and reverts to expecting literal characters.
This is fine, but afterwards the if clause terminates with a return
instead of a break which results in priv->in_console never being reset.
Use break, so the relevant code after the switch is not skipped.
Fixes: 27f79c05ab42 ("video: implement framebuffer console")
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
drivers/video/fbconsole.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index 2461ab2fb849..547db00208ff 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -672,7 +672,7 @@ static void fbc_putc(struct console_device *cdev, char c)
priv->csipos = 0;
priv->state = LIT;
priv->csi_cmd = -1;
- return;
+ break;
}
switch (c) {
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 04/20] fbconsole: do not reset cursor visibility alongside attributes
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
` (2 preceding siblings ...)
2026-05-03 8:33 ` [PATCH 03/20] fbconsole: fix handling of CSI buffer overflow Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 05/20] fbconsole: respect hidden cursor everywhere Ahmad Fatoum
` (16 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
In fbc_parse_colors, resetting attributes sets priv->flags = 0,
which clears all flags including HIDE_CURSOR.
This is incorrect, the reset should only clear SGR attributes,
not cursor visibility state that just happens to share the
same member, so account for that.
Link: https://terminalguide.namepad.de/seq/csi_sm/
Link: https://terminalguide.namepad.de/mode/p25/
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
drivers/video/fbconsole.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index 547db00208ff..5b7c1a13a8bc 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -68,6 +68,7 @@ struct fbc_priv {
#define ANSI_FLAG_INVERT (1 << 0)
#define ANSI_FLAG_BRIGHT (1 << 1)
+#define SGR_ATTRIBUTES (ANSI_FLAG_INVERT | ANSI_FLAG_BRIGHT)
#define HIDE_CURSOR (1 << 2)
unsigned flags;
@@ -508,7 +509,7 @@ static void fbc_parse_colors(struct fbc_priv *priv)
code = simple_strtoul(str, &str, 10);
switch (code) {
case 0:
- priv->flags = 0;
+ priv->flags &= ~SGR_ATTRIBUTES;
priv->color = DEFAULT_COLOR;
priv->bgcolor = BLACK;
break;
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 05/20] fbconsole: respect hidden cursor everywhere
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
` (3 preceding siblings ...)
2026-05-03 8:33 ` [PATCH 04/20] fbconsole: do not reset cursor visibility alongside attributes Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 06/20] fbconsole: call fb_blit_area for every drawchar Ahmad Fatoum
` (15 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
video_invertchar() unconditionally inverts the cell's color to represent
a cursor. show_cursor() does so only when HIDE_CURSOR has not been set
for the frame buffer console.
Rename show_cursor() to reflect what it really does (toggle the cursor's
visibility state) and replace all outside uses of video_invertchar()
with the new toggle_cursor_visibility() to ensure that an explicitly
hidden cursor is not unintentionally unhidden.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
drivers/video/fbconsole.c | 24 +++++++++++-------------
1 file changed, 11 insertions(+), 13 deletions(-)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index 5b7c1a13a8bc..2833d4200aa1 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -331,10 +331,10 @@ static void video_invertchar(struct fbc_priv *priv, int x, int y)
fb_blit_area(priv, x, y);
}
-static void show_cursor(struct fbc_priv *priv, int x, int y)
+static void toggle_cursor_visibility(struct fbc_priv *priv)
{
if (!(priv->flags & HIDE_CURSOR))
- video_invertchar(priv, x, y);
+ video_invertchar(priv, priv->x, priv->y);
}
static void fb_scroll_up_0(struct fbc_priv *priv, void *adr, int width, int height)
@@ -451,7 +451,7 @@ static void fb_scroll_up(struct fbc_priv *priv)
static void printchar(struct fbc_priv *priv, int c)
{
- video_invertchar(priv, priv->x, priv->y);
+ toggle_cursor_visibility(priv);
switch (c) {
case '\007': /* bell: ignore */
@@ -493,9 +493,7 @@ static void printchar(struct fbc_priv *priv, int c)
priv->y = priv->rows - 1;
}
- show_cursor(priv, priv->x, priv->y);
-
- return;
+ toggle_cursor_visibility(priv);
}
static void fbc_parse_colors(struct fbc_priv *priv)
@@ -564,7 +562,7 @@ static void fbc_parse_csi(struct fbc_priv *priv)
priv->flags &= ~HIDE_CURSOR;
/* show cursor now */
- show_cursor(priv, priv->x, priv->y);
+ toggle_cursor_visibility(priv);
break;
}
break;
@@ -573,7 +571,7 @@ static void fbc_parse_csi(struct fbc_priv *priv)
switch (priv->csi_cmd) {
case '?': /* cursor invisible */
/* hide cursor now */
- video_invertchar(priv, priv->x, priv->y);
+ toggle_cursor_visibility(priv);
priv->flags |= HIDE_CURSOR;
break;
@@ -581,10 +579,10 @@ static void fbc_parse_csi(struct fbc_priv *priv)
break;
case 'J':
cls(priv);
- show_cursor(priv, priv->x, priv->y);
+ toggle_cursor_visibility(priv);
return;
case 'H':
- show_cursor(priv, priv->x, priv->y);
+ toggle_cursor_visibility(priv);
pos = simple_strtoul(priv->csi, &end, 10);
priv->y = clamp(pos - 1, 0, (int) priv->rows - 1);
@@ -592,11 +590,11 @@ static void fbc_parse_csi(struct fbc_priv *priv)
pos = simple_strtoul(end + 1, NULL, 10);
priv->x = clamp(pos - 1, 0, (int) priv->cols - 1);
- show_cursor(priv, priv->x, priv->y);
+ toggle_cursor_visibility(priv);
break;
case 'K':
pos = simple_strtoul(priv->csi, &end, 10);
- video_invertchar(priv, priv->x, priv->y);
+ toggle_cursor_visibility(priv);
switch (pos) {
case 0:
for (i = priv->x; i < priv->cols; i++)
@@ -607,7 +605,7 @@ static void fbc_parse_csi(struct fbc_priv *priv)
drawchar(priv, i, priv->y, ' ');
break;
}
- video_invertchar(priv, priv->x, priv->y);
+ toggle_cursor_visibility(priv);
break;
}
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 06/20] fbconsole: call fb_blit_area for every drawchar
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
` (4 preceding siblings ...)
2026-05-03 8:33 ` [PATCH 05/20] fbconsole: respect hidden cursor everywhere Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 07/20] fbconsole: skip fb_flush when processing escape sequences Ahmad Fatoum
` (14 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Every glyph draw, whether cursor, character or empty space should be
followed by fb_blit_area().
This was missed for the drawchar() used for the empty space however
(erase in line). Fix this by moving fb_blit_area() into drawchar() like
it's already the case for video_invertchar().
fb_blit_area() is moved, so it's defined before being referenced, but
nothing changes in its body.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
drivers/video/fbconsole.c | 87 ++++++++++++++++++++-------------------
1 file changed, 44 insertions(+), 43 deletions(-)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index 2833d4200aa1..c7314bb8cf0d 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -149,6 +149,49 @@ static struct rgb colors[] = {
[BRIGHT + WHITE] = { 255, 255, 255 },
};
+static void fb_blit_area(struct fbc_priv *priv, int x, int y)
+{
+ int startx, starty, width, height, fw, fh;
+ int mx, my;
+
+ mx = priv->margin.left;
+ my = priv->margin.top;
+
+ fw = priv->font->width;
+ fh = priv->font->height;
+
+ switch (priv->rotation) {
+ case FBCONSOLE_ROTATE_0:
+ startx = mx + x * fw;
+ starty = my + y * fh;
+ width = fw;
+ height = fh;
+ break;
+ case FBCONSOLE_ROTATE_90:
+ startx = mx + (priv->rows - y - 1) * fh;
+ starty = my + x * fw;
+ width = fh;
+ height = fw;
+ break;
+ case FBCONSOLE_ROTATE_180:
+ startx = mx + (priv->cols - x - 1) * fw;
+ starty = my + (priv->rows - y - 1) * fh;
+ width = fw;
+ height = fh;
+ break;
+ case FBCONSOLE_ROTATE_270:
+ startx = mx + y * fh;
+ starty = my + (priv->cols - x - 1) * fw;
+ width = fh;
+ height = fw;
+ break;
+ default:
+ return;
+ }
+
+ gu_screen_blit_area(priv->sc, startx, starty, width, height);
+}
+
static void drawchar(struct fbc_priv *priv, int x, int y, int c)
{
void *buf;
@@ -241,49 +284,8 @@ static void drawchar(struct fbc_priv *priv, int x, int y, int c)
inbuf++;
mask = 0x80;
}
-}
-static void fb_blit_area(struct fbc_priv *priv, int x, int y)
-{
- int startx, starty, width, height, fw, fh;
- int mx, my;
-
- mx = priv->margin.left;
- my = priv->margin.top;
-
- fw = priv->font->width;
- fh = priv->font->height;
-
- switch (priv->rotation) {
- case FBCONSOLE_ROTATE_0:
- startx = mx + x * fw;
- starty = my + y * fh;
- width = fw;
- height = fh;
- break;
- case FBCONSOLE_ROTATE_90:
- startx = mx + (priv->rows - y - 1) * fh;
- starty = my + x * fw;
- width = fh;
- height = fw;
- break;
- case FBCONSOLE_ROTATE_180:
- startx = mx + (priv->cols - x - 1) * fw;
- starty = my + (priv->rows - y - 1) * fh;
- width = fw;
- height = fh;
- break;
- case FBCONSOLE_ROTATE_270:
- startx = mx + y * fh;
- starty = my + (priv->cols - x - 1) * fw;
- width = fh;
- height = fw;
- break;
- default:
- return;
- }
-
- gu_screen_blit_area(priv->sc, startx, starty, width, height);
+ fb_blit_area(priv, x, y);
}
static void video_invertchar(struct fbc_priv *priv, int x, int y)
@@ -479,7 +481,6 @@ static void printchar(struct fbc_priv *priv, int c)
default:
drawchar(priv, priv->x, priv->y, c);
- fb_blit_area(priv, priv->x, priv->y);
priv->x++;
if (priv->x >= priv->cols) {
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 07/20] fbconsole: skip fb_flush when processing escape sequences
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
` (5 preceding siblings ...)
2026-05-03 8:33 ` [PATCH 06/20] fbconsole: call fb_blit_area for every drawchar Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 08/20] fbconsole: factor out helpers for clamped cursor row/col setting Ahmad Fatoum
` (13 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
When processing escape sequences or multi-byte characters in future,
calling fb_flush for every single character is unnecessary.
Instead, just take note when the framebuffer is actually changed and
do the fb_flush lazily.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
drivers/video/fbconsole.c | 25 ++++++++++++++-----------
1 file changed, 14 insertions(+), 11 deletions(-)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index c7314bb8cf0d..6e8b4e66630d 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -542,7 +542,7 @@ static void fbc_parse_colors(struct fbc_priv *priv)
}
}
-static void fbc_parse_csi(struct fbc_priv *priv)
+static bool fbc_parse_csi(struct fbc_priv *priv)
{
char *end;
unsigned char last;
@@ -553,7 +553,7 @@ static void fbc_parse_csi(struct fbc_priv *priv)
switch (last) {
case 'm':
fbc_parse_colors(priv);
- return;
+ break;
case 'h':
/* suffix for vt100 "[?25h" */
switch (priv->csi_cmd) {
@@ -564,7 +564,7 @@ static void fbc_parse_csi(struct fbc_priv *priv)
priv->flags &= ~HIDE_CURSOR;
/* show cursor now */
toggle_cursor_visibility(priv);
- break;
+ return true;
}
break;
case 'l':
@@ -574,14 +574,13 @@ static void fbc_parse_csi(struct fbc_priv *priv)
/* hide cursor now */
toggle_cursor_visibility(priv);
priv->flags |= HIDE_CURSOR;
-
- break;
+ return true;
}
break;
case 'J':
cls(priv);
toggle_cursor_visibility(priv);
- return;
+ return true;
case 'H':
toggle_cursor_visibility(priv);
@@ -592,7 +591,7 @@ static void fbc_parse_csi(struct fbc_priv *priv)
priv->x = clamp(pos - 1, 0, (int) priv->cols - 1);
toggle_cursor_visibility(priv);
- break;
+ return true;
case 'K':
pos = simple_strtoul(priv->csi, &end, 10);
toggle_cursor_visibility(priv);
@@ -607,9 +606,10 @@ static void fbc_parse_csi(struct fbc_priv *priv)
break;
}
toggle_cursor_visibility(priv);
-
- break;
+ return true;
}
+
+ return false;
}
static void fbc_putc(struct console_device *cdev, char c)
@@ -617,6 +617,7 @@ static void fbc_putc(struct console_device *cdev, char c)
struct fbc_priv *priv = container_of(cdev,
struct fbc_priv, cdev);
struct fb_info *fb = priv->fb;
+ bool queue_flush = false;
if (priv->in_console)
return;
@@ -644,6 +645,7 @@ static void fbc_putc(struct console_device *cdev, char c)
}
printchar(priv, cp);
+ queue_flush = true;
break;
}
}
@@ -688,7 +690,7 @@ static void fbc_putc(struct console_device *cdev, char c)
case ':':
break;
default:
- fbc_parse_csi(priv);
+ queue_flush = fbc_parse_csi(priv);
priv->state = LIT;
priv->csi_cmd = -1;
}
@@ -696,7 +698,8 @@ static void fbc_putc(struct console_device *cdev, char c)
}
priv->in_console = 0;
- fb_flush(fb);
+ if (queue_flush)
+ fb_flush(fb);
}
static int setup_font(struct fbc_priv *priv)
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 08/20] fbconsole: factor out helpers for clamped cursor row/col setting
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
` (6 preceding siblings ...)
2026-05-03 8:33 ` [PATCH 07/20] fbconsole: skip fb_flush when processing escape sequences Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 09/20] fbconsole: precompute foreground/background colors Ahmad Fatoum
` (12 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
In preparation for adding more escape sequences that need to move
the cursor without exceeding the fbconsole's size, add two helpers
for setting the cursor within the limits.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
drivers/video/fbconsole.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index 6e8b4e66630d..285f22901573 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -542,6 +542,16 @@ static void fbc_parse_colors(struct fbc_priv *priv)
}
}
+static void fbc_set_cursor_row(struct fbc_priv *priv, int y)
+{
+ priv->y = clamp_t(int, y, 0, priv->rows - 1);
+}
+
+static void fbc_set_cursor_col(struct fbc_priv *priv, unsigned int x)
+{
+ priv->x = clamp_t(int, x, 0, priv->cols - 1);
+}
+
static bool fbc_parse_csi(struct fbc_priv *priv)
{
char *end;
@@ -585,10 +595,10 @@ static bool fbc_parse_csi(struct fbc_priv *priv)
toggle_cursor_visibility(priv);
pos = simple_strtoul(priv->csi, &end, 10);
- priv->y = clamp(pos - 1, 0, (int) priv->rows - 1);
+ fbc_set_cursor_row(priv, pos - 1);
pos = simple_strtoul(end + 1, NULL, 10);
- priv->x = clamp(pos - 1, 0, (int) priv->cols - 1);
+ fbc_set_cursor_col(priv, pos - 1);
toggle_cursor_visibility(priv);
return true;
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 09/20] fbconsole: precompute foreground/background colors
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
` (7 preceding siblings ...)
2026-05-03 8:33 ` [PATCH 08/20] fbconsole: factor out helpers for clamped cursor row/col setting Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 10/20] fbconsole: collect renderable state into struct fbc_screen_state Ahmad Fatoum
` (11 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
We did color format conversion on every drawchar(), when it would have
been sufficient to do this only when the color is actually changed.
As clearing the screen should also be color aware and doesn't
necessarily involve a call to drawchar(), optimize the color conversion
to happen on only when the color actually changes.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
drivers/video/fbconsole.c | 77 ++++++++++++++++++++++++++-------------
1 file changed, 52 insertions(+), 25 deletions(-)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index 285f22901573..74b37b429f2a 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -39,6 +39,7 @@ enum ansi_color {
};
#define DEFAULT_COLOR WHITE
+#define DEFAULT_BGCOLOR BLACK
struct fbc_priv {
struct console_device cdev;
@@ -66,6 +67,9 @@ struct fbc_priv {
int color;
int bgcolor;
+ u32 color_rgb;
+ u32 bgcolor_rgb;
+
#define ANSI_FLAG_INVERT (1 << 0)
#define ANSI_FLAG_BRIGHT (1 << 1)
#define SGR_ATTRIBUTES (ANSI_FLAG_INVERT | ANSI_FLAG_BRIGHT)
@@ -200,8 +204,6 @@ static void drawchar(struct fbc_priv *priv, int x, int y, int c)
int i;
const uint8_t *inbuf;
int line_length;
- u32 color, bgcolor;
- struct rgb *rgb;
int xstep;
int ystep;
int startx;
@@ -214,18 +216,6 @@ static void drawchar(struct fbc_priv *priv, int x, int y, int c)
line_length = priv->fb->line_length;
- color = priv->flags & ANSI_FLAG_INVERT ? priv->bgcolor : priv->color;
- bgcolor = priv->flags & ANSI_FLAG_INVERT ? priv->color : priv->bgcolor;
-
- if (priv->flags & ANSI_FLAG_BRIGHT)
- color += BRIGHT;
-
- rgb = &colors[color];
- color = gu_rgb_to_pixel(priv->fb, rgb->r, rgb->g, rgb->b, 0xff);
-
- rgb = &colors[bgcolor];
- bgcolor = gu_rgb_to_pixel(priv->fb, rgb->r, rgb->g, rgb->b, 0x0);
-
adr = buf;
switch (priv->rotation) {
@@ -271,9 +261,11 @@ static void drawchar(struct fbc_priv *priv, int x, int y, int c)
}
if (*inbuf & mask)
- gu_set_pixel(priv->fb, adr + j * xstep, color);
+ gu_set_pixel(priv->fb, adr + j * xstep,
+ priv->color_rgb);
else
- gu_set_pixel(priv->fb, adr + j * xstep, bgcolor);
+ gu_set_pixel(priv->fb, adr + j * xstep,
+ priv->bgcolor_rgb);
mask >>= 1;
@@ -497,8 +489,41 @@ static void printchar(struct fbc_priv *priv, int c)
toggle_cursor_visibility(priv);
}
+static void fbc_update_colors(struct fbc_priv *priv, int color, int bgcolor)
+{
+ struct rgb *rgb;
+
+ if (color >= 0)
+ priv->color = color;
+ if (bgcolor >= 0)
+ priv->bgcolor = bgcolor;
+
+ if (priv->flags & ANSI_FLAG_INVERT) {
+ color = priv->bgcolor;
+ bgcolor = priv->color;
+ } else {
+ color = priv->color;
+ bgcolor = priv->bgcolor;
+ }
+
+ if (priv->flags & ANSI_FLAG_BRIGHT)
+ color += BRIGHT;
+
+ rgb = &colors[color];
+ priv->color_rgb = gu_rgb_to_pixel(priv->fb, rgb->r, rgb->g, rgb->b, 0xff);
+
+ rgb = &colors[bgcolor];
+ priv->bgcolor_rgb = gu_rgb_to_pixel(priv->fb, rgb->r, rgb->g, rgb->b, 0x0);
+}
+
+static void fbc_reset_colors(struct fbc_priv *priv)
+{
+ fbc_update_colors(priv, DEFAULT_COLOR, DEFAULT_BGCOLOR);
+}
+
static void fbc_parse_colors(struct fbc_priv *priv)
{
+ int color = -1, bgcolor = -1;
int code;
char *str;
@@ -509,8 +534,8 @@ static void fbc_parse_colors(struct fbc_priv *priv)
switch (code) {
case 0:
priv->flags &= ~SGR_ATTRIBUTES;
- priv->color = DEFAULT_COLOR;
- priv->bgcolor = BLACK;
+ color = DEFAULT_COLOR;
+ bgcolor = DEFAULT_BGCOLOR;
break;
case 1:
priv->flags |= ANSI_FLAG_BRIGHT;
@@ -519,20 +544,20 @@ static void fbc_parse_colors(struct fbc_priv *priv)
priv->flags |= ANSI_FLAG_INVERT;
break;
case 30 ... 37:
- priv->color = code - 30;
+ color = code - 30;
break;
case 39:
- priv->color = DEFAULT_COLOR;
+ color = DEFAULT_COLOR;
break;
case 40 ... 47:
- priv->bgcolor = code - 40;
+ bgcolor = code - 40;
break;
case 49:
- priv->bgcolor = BLACK;
+ bgcolor = DEFAULT_BGCOLOR;
break;
case 90 ... 97:
priv->flags |= ANSI_FLAG_BRIGHT;
- priv->color = code - 90;
+ color = code - 90;
break;
}
@@ -540,6 +565,8 @@ static void fbc_parse_colors(struct fbc_priv *priv)
break;
str++;
}
+
+ fbc_update_colors(priv, color, bgcolor);
}
static void fbc_set_cursor_row(struct fbc_priv *priv, int y)
@@ -872,8 +899,8 @@ int register_fbconsole(struct fb_info *fb)
priv->fb = fb;
priv->x = 0;
priv->y = 0;
- priv->color = WHITE;
- priv->bgcolor = BLACK;
+
+ fbc_reset_colors(priv);
cdev = &priv->cdev;
cdev->dev = &fb->dev;
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 10/20] fbconsole: collect renderable state into struct fbc_screen_state
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
` (8 preceding siblings ...)
2026-05-03 8:33 ` [PATCH 09/20] fbconsole: precompute foreground/background colors Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 11/20] fbconsole: implement CSI A/B/C/D cursor movement sequences Ahmad Fatoum
` (10 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Group the cursor position, color and attribute flag state that together
describe what the terminal is currently rendering into a single
struct fbc_screen_state.
This will allow us later on to implement DEC save/restore (\e7/\e8)
as single struct assignment.
No functional change.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
drivers/video/fbconsole.c | 129 ++++++++++++++++++++------------------
1 file changed, 69 insertions(+), 60 deletions(-)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index 74b37b429f2a..1c4418757b43 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -41,6 +41,22 @@ enum ansi_color {
#define DEFAULT_COLOR WHITE
#define DEFAULT_BGCOLOR BLACK
+struct fbc_screen_state {
+ unsigned int x, y; /* cursor position */
+
+ int color;
+ int bgcolor;
+
+ u32 color_rgb;
+ u32 bgcolor_rgb;
+
+#define ANSI_FLAG_INVERT (1 << 0)
+#define ANSI_FLAG_BRIGHT (1 << 1)
+#define SGR_ATTRIBUTES (ANSI_FLAG_INVERT | ANSI_FLAG_BRIGHT)
+#define HIDE_CURSOR (1 << 2)
+ unsigned flags;
+};
+
struct fbc_priv {
struct console_device cdev;
struct fb_info *fb;
@@ -59,23 +75,12 @@ struct fbc_priv {
const struct font_desc *font;
unsigned int cols, rows;
- unsigned int x, y; /* cursor position */
+
+ struct fbc_screen_state cur;
unsigned int rotation;
enum state_t state;
- int color;
- int bgcolor;
-
- u32 color_rgb;
- u32 bgcolor_rgb;
-
-#define ANSI_FLAG_INVERT (1 << 0)
-#define ANSI_FLAG_BRIGHT (1 << 1)
-#define SGR_ATTRIBUTES (ANSI_FLAG_INVERT | ANSI_FLAG_BRIGHT)
-#define HIDE_CURSOR (1 << 2)
- unsigned flags;
-
int csipos;
u8 csi[256];
unsigned char csi_cmd;
@@ -262,10 +267,10 @@ static void drawchar(struct fbc_priv *priv, int x, int y, int c)
if (*inbuf & mask)
gu_set_pixel(priv->fb, adr + j * xstep,
- priv->color_rgb);
+ priv->cur.color_rgb);
else
gu_set_pixel(priv->fb, adr + j * xstep,
- priv->bgcolor_rgb);
+ priv->cur.bgcolor_rgb);
mask >>= 1;
@@ -327,8 +332,8 @@ static void video_invertchar(struct fbc_priv *priv, int x, int y)
static void toggle_cursor_visibility(struct fbc_priv *priv)
{
- if (!(priv->flags & HIDE_CURSOR))
- video_invertchar(priv, priv->x, priv->y);
+ if (!(priv->cur.flags & HIDE_CURSOR))
+ video_invertchar(priv, priv->cur.x, priv->cur.y);
}
static void fb_scroll_up_0(struct fbc_priv *priv, void *adr, int width, int height)
@@ -445,45 +450,47 @@ static void fb_scroll_up(struct fbc_priv *priv)
static void printchar(struct fbc_priv *priv, int c)
{
+ struct fbc_screen_state *cur = &priv->cur;
+
toggle_cursor_visibility(priv);
switch (c) {
case '\007': /* bell: ignore */
break;
case '\b':
- if (priv->x > 0) {
- priv->x--;
- } else if (priv->y > 0) {
- priv->x = priv->cols - 1;
- priv->y--;
+ if (cur->x > 0) {
+ cur->x--;
+ } else if (cur->y > 0) {
+ cur->x = priv->cols - 1;
+ cur->y--;
}
break;
case '\n':
case '\013': /* Vertical tab is the same as Line Feed */
- priv->y++;
+ cur->y++;
break;
case '\r':
- priv->x = 0;
+ cur->x = 0;
break;
case '\t':
- priv->x = (priv->x + 8) & ~0x3;
+ cur->x = (cur->x + 8) & ~0x3;
break;
default:
- drawchar(priv, priv->x, priv->y, c);
+ drawchar(priv, priv->cur.x, priv->cur.y, c);
- priv->x++;
- if (priv->x >= priv->cols) {
- priv->y++;
- priv->x = 0;
+ cur->x++;
+ if (cur->x >= priv->cols) {
+ cur->y++;
+ cur->x = 0;
}
}
- if (priv->y >= priv->rows) {
+ if (cur->y >= priv->rows) {
fb_scroll_up(priv);
- priv->y = priv->rows - 1;
+ cur->y = priv->rows - 1;
}
toggle_cursor_visibility(priv);
@@ -491,29 +498,30 @@ static void printchar(struct fbc_priv *priv, int c)
static void fbc_update_colors(struct fbc_priv *priv, int color, int bgcolor)
{
+ struct fbc_screen_state *cur = &priv->cur;
struct rgb *rgb;
if (color >= 0)
- priv->color = color;
+ cur->color = color;
if (bgcolor >= 0)
- priv->bgcolor = bgcolor;
+ cur->bgcolor = bgcolor;
- if (priv->flags & ANSI_FLAG_INVERT) {
- color = priv->bgcolor;
- bgcolor = priv->color;
+ if (cur->flags & ANSI_FLAG_INVERT) {
+ color = cur->bgcolor;
+ bgcolor = cur->color;
} else {
- color = priv->color;
- bgcolor = priv->bgcolor;
+ color = cur->color;
+ bgcolor = cur->bgcolor;
}
- if (priv->flags & ANSI_FLAG_BRIGHT)
+ if (cur->flags & ANSI_FLAG_BRIGHT)
color += BRIGHT;
rgb = &colors[color];
- priv->color_rgb = gu_rgb_to_pixel(priv->fb, rgb->r, rgb->g, rgb->b, 0xff);
+ cur->color_rgb = gu_rgb_to_pixel(priv->fb, rgb->r, rgb->g, rgb->b, 0xff);
rgb = &colors[bgcolor];
- priv->bgcolor_rgb = gu_rgb_to_pixel(priv->fb, rgb->r, rgb->g, rgb->b, 0x0);
+ cur->bgcolor_rgb = gu_rgb_to_pixel(priv->fb, rgb->r, rgb->g, rgb->b, 0x0);
}
static void fbc_reset_colors(struct fbc_priv *priv)
@@ -523,6 +531,7 @@ static void fbc_reset_colors(struct fbc_priv *priv)
static void fbc_parse_colors(struct fbc_priv *priv)
{
+ struct fbc_screen_state *cur = &priv->cur;
int color = -1, bgcolor = -1;
int code;
char *str;
@@ -533,15 +542,15 @@ static void fbc_parse_colors(struct fbc_priv *priv)
code = simple_strtoul(str, &str, 10);
switch (code) {
case 0:
- priv->flags &= ~SGR_ATTRIBUTES;
+ cur->flags &= ~SGR_ATTRIBUTES;
color = DEFAULT_COLOR;
bgcolor = DEFAULT_BGCOLOR;
break;
case 1:
- priv->flags |= ANSI_FLAG_BRIGHT;
+ cur->flags |= ANSI_FLAG_BRIGHT;
break;
case 7:
- priv->flags |= ANSI_FLAG_INVERT;
+ cur->flags |= ANSI_FLAG_INVERT;
break;
case 30 ... 37:
color = code - 30;
@@ -556,7 +565,7 @@ static void fbc_parse_colors(struct fbc_priv *priv)
bgcolor = DEFAULT_BGCOLOR;
break;
case 90 ... 97:
- priv->flags |= ANSI_FLAG_BRIGHT;
+ cur->flags |= ANSI_FLAG_BRIGHT;
color = code - 90;
break;
}
@@ -571,12 +580,12 @@ static void fbc_parse_colors(struct fbc_priv *priv)
static void fbc_set_cursor_row(struct fbc_priv *priv, int y)
{
- priv->y = clamp_t(int, y, 0, priv->rows - 1);
+ priv->cur.y = clamp_t(int, y, 0, priv->rows - 1);
}
static void fbc_set_cursor_col(struct fbc_priv *priv, unsigned int x)
{
- priv->x = clamp_t(int, x, 0, priv->cols - 1);
+ priv->cur.x = clamp_t(int, x, 0, priv->cols - 1);
}
static bool fbc_parse_csi(struct fbc_priv *priv)
@@ -595,10 +604,10 @@ static bool fbc_parse_csi(struct fbc_priv *priv)
/* suffix for vt100 "[?25h" */
switch (priv->csi_cmd) {
case '?': /* cursor visible */
- if (!(priv->flags & HIDE_CURSOR))
+ if (!(priv->cur.flags & HIDE_CURSOR))
break;
- priv->flags &= ~HIDE_CURSOR;
+ priv->cur.flags &= ~HIDE_CURSOR;
/* show cursor now */
toggle_cursor_visibility(priv);
return true;
@@ -610,7 +619,7 @@ static bool fbc_parse_csi(struct fbc_priv *priv)
case '?': /* cursor invisible */
/* hide cursor now */
toggle_cursor_visibility(priv);
- priv->flags |= HIDE_CURSOR;
+ priv->cur.flags |= HIDE_CURSOR;
return true;
}
break;
@@ -634,12 +643,12 @@ static bool fbc_parse_csi(struct fbc_priv *priv)
toggle_cursor_visibility(priv);
switch (pos) {
case 0:
- for (i = priv->x; i < priv->cols; i++)
- drawchar(priv, i, priv->y, ' ');
+ for (i = priv->cur.x; i < priv->cols; i++)
+ drawchar(priv, i, priv->cur.y, ' ');
break;
case 1:
- for (i = 0; i <= priv->x; i++)
- drawchar(priv, i, priv->y, ' ');
+ for (i = 0; i <= priv->cur.x; i++)
+ drawchar(priv, i, priv->cur.y, ' ');
break;
}
toggle_cursor_visibility(priv);
@@ -771,7 +780,7 @@ static int setup_font(struct fbc_priv *priv)
if (priv->rows != newrows || priv->cols != newcols) {
priv->rows = newrows;
priv->cols = newcols;
- priv->x = priv->y = 0;
+ priv->cur.x = priv->cur.y = 0;
}
return 0;
@@ -866,8 +875,8 @@ static int set_rotation(struct param_d *p, void *vpriv)
struct fbc_priv *priv = vpriv;
cls(priv);
- priv->x = 0;
- priv->y = 0;
+ priv->cur.x = 0;
+ priv->cur.y = 0;
setup_font(priv);
return 0;
@@ -897,8 +906,8 @@ int register_fbconsole(struct fb_info *fb)
fbname += 2;
priv->fb = fb;
- priv->x = 0;
- priv->y = 0;
+ priv->cur.x = 0;
+ priv->cur.y = 0;
fbc_reset_colors(priv);
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 11/20] fbconsole: implement CSI A/B/C/D cursor movement sequences
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
` (9 preceding siblings ...)
2026-05-03 8:33 ` [PATCH 10/20] fbconsole: collect renderable state into struct fbc_screen_state Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 12/20] fbconsole: restrict cursor visibility to DEC private mode 25 Ahmad Fatoum
` (9 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
The framebuffer console should be able to display everything that
existing barebox commands can output to it.
The 2048 command makes use of the cursor movement sequences, so
implement them.
Link: https://terminalguide.namepad.de/seq/csi_ca/
Link: https://terminalguide.namepad.de/seq/csi_cb/
Link: https://terminalguide.namepad.de/seq/csi_cc/
Link: https://terminalguide.namepad.de/seq/csi_cd/
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
drivers/video/fbconsole.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index 1c4418757b43..ad3c66b75737 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -638,6 +638,28 @@ static bool fbc_parse_csi(struct fbc_priv *priv)
toggle_cursor_visibility(priv);
return true;
+ case 'A' ... 'D': {
+ pos = simple_strtoul(priv->csi, &end, 10) ?: 1;
+ toggle_cursor_visibility(priv);
+
+ switch (last) {
+ case 'A': /* cursor up */
+ fbc_set_cursor_row(priv, priv->cur.y - pos);
+ break;
+ case 'B': /* cursor down */
+ fbc_set_cursor_row(priv, priv->cur.y + pos);
+ break;
+ case 'C': /* cursor forward */
+ fbc_set_cursor_col(priv, priv->cur.x + pos);
+ break;
+ case 'D': /* cursor back */
+ fbc_set_cursor_col(priv, priv->cur.x - pos);
+ break;
+ }
+
+ toggle_cursor_visibility(priv);
+ return true;
+ }
case 'K':
pos = simple_strtoul(priv->csi, &end, 10);
toggle_cursor_visibility(priv);
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 12/20] fbconsole: restrict cursor visibility to DEC private mode 25
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
` (10 preceding siblings ...)
2026-05-03 8:33 ` [PATCH 11/20] fbconsole: implement CSI A/B/C/D cursor movement sequences Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 13/20] fbconsole: add new clear_chars helper Ahmad Fatoum
` (8 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
`CSI ? Pm h' is DECSET and `CSI ? Pm l` is DECRST.
The Pm parameter can be 25 for cursor visibility control, but can also
mean many other things, e.g. use of the alternate screen buffer.
We currently assume 25 to be the Pm value without any parsing.
To allow support for further DEC private modes, have the DECSET/DECRST
handling code not ignore the Pm value.
No functional change expected as we have no barebox command that emits
any private mode other than 25.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
drivers/video/fbconsole.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index ad3c66b75737..fb6a7d3e5809 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -602,11 +602,13 @@ static bool fbc_parse_csi(struct fbc_priv *priv)
break;
case 'h':
/* suffix for vt100 "[?25h" */
- switch (priv->csi_cmd) {
- case '?': /* cursor visible */
+ if (priv->csi_cmd != '?')
+ break;
+ pos = simple_strtoul(priv->csi + 1, NULL, 10);
+ switch (pos) {
+ case 25: /* cursor visible */
if (!(priv->cur.flags & HIDE_CURSOR))
break;
-
priv->cur.flags &= ~HIDE_CURSOR;
/* show cursor now */
toggle_cursor_visibility(priv);
@@ -615,10 +617,13 @@ static bool fbc_parse_csi(struct fbc_priv *priv)
break;
case 'l':
/* suffix for vt100 "[?25l" */
- switch (priv->csi_cmd) {
- case '?': /* cursor invisible */
- /* hide cursor now */
+ if (priv->csi_cmd != '?')
+ break;
+ pos = simple_strtoul(priv->csi + 1, NULL, 10);
+ switch (pos) {
+ case 25: /* cursor invisible */
toggle_cursor_visibility(priv);
+ /* hide cursor now */
priv->cur.flags |= HIDE_CURSOR;
return true;
}
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 13/20] fbconsole: add new clear_chars helper
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
` (11 preceding siblings ...)
2026-05-03 8:33 ` [PATCH 12/20] fbconsole: restrict cursor visibility to DEC private mode 25 Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 14/20] fbconsole: implement erase entire line CSI Ahmad Fatoum
` (7 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
fbc_parse_csi() implements partial line clearing by printing spaces as
that's an easy way to do background-color aware clearing.
Wrap that idiom in a new clear_chars helper that can be used to clear
between everything between two cells in the future as well.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
drivers/video/fbconsole.c | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index fb6a7d3e5809..f586aabdae43 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -285,6 +285,18 @@ static void drawchar(struct fbc_priv *priv, int x, int y, int c)
fb_blit_area(priv, x, y);
}
+static void clear_chars(struct fbc_priv *priv,
+ int start_x, int start_y, int end_x, int end_y)
+{
+ for (int y = start_y; y <= end_y; y++) {
+ int xs = (y == start_y) ? start_x : 0;
+ int xe = (y == end_y) ? end_x : (int)priv->cols - 1;
+
+ for (int x = xs; x <= xe; x++)
+ drawchar(priv, x, y, ' ');
+ }
+}
+
static void video_invertchar(struct fbc_priv *priv, int x, int y)
{
int startx, starty, width, height, fw, fh;
@@ -592,7 +604,7 @@ static bool fbc_parse_csi(struct fbc_priv *priv)
{
char *end;
unsigned char last;
- int pos, i;
+ int pos;
last = priv->csi[priv->csipos - 1];
@@ -670,12 +682,12 @@ static bool fbc_parse_csi(struct fbc_priv *priv)
toggle_cursor_visibility(priv);
switch (pos) {
case 0:
- for (i = priv->cur.x; i < priv->cols; i++)
- drawchar(priv, i, priv->cur.y, ' ');
+ clear_chars(priv, priv->cur.x, priv->cur.y,
+ priv->cols - 1, priv->cur.y);
break;
case 1:
- for (i = 0; i <= priv->cur.x; i++)
- drawchar(priv, i, priv->cur.y, ' ');
+ clear_chars(priv, 0, priv->cur.y,
+ priv->cur.x, priv->cur.y);
break;
}
toggle_cursor_visibility(priv);
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 14/20] fbconsole: implement erase entire line CSI
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
` (12 preceding siblings ...)
2026-05-03 8:33 ` [PATCH 13/20] fbconsole: add new clear_chars helper Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 15/20] fbconsole: support ESC[0J and ESC[1J partial screen clear Ahmad Fatoum
` (6 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
We already implement CSI 0 K and CSI 1 K to erase from cursor to line
end and from line start to cursor, but were missing erasure of the
entire line, so add that as well.
Link: https://terminalguide.namepad.de/seq/csi_ck-2/
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
drivers/video/fbconsole.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index f586aabdae43..c67e3817ca13 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -689,6 +689,10 @@ static bool fbc_parse_csi(struct fbc_priv *priv)
clear_chars(priv, 0, priv->cur.y,
priv->cur.x, priv->cur.y);
break;
+ case 2:
+ clear_chars(priv, 0, priv->cur.y,
+ priv->cols - 1, priv->cur.y);
+ break;
}
toggle_cursor_visibility(priv);
return true;
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 15/20] fbconsole: support ESC[0J and ESC[1J partial screen clear
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
` (13 preceding siblings ...)
2026-05-03 8:33 ` [PATCH 14/20] fbconsole: implement erase entire line CSI Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 16/20] fbconsole: implement DEC save/restore cursor Ahmad Fatoum
` (5 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
The ESC[J handler always called cls() which clears the entire screen,
regardless of the parameter. Per ECMA-48, the parameter selects the
region to clear:
- 0 (or omitted): from cursor to end of screen
- 1: from start of screen to cursor
- 2: entire screen
Add a clear_chars() helper that clears a partial specified region by
drawing spaces (respecting the current background color), and parse
the ESC[J parameter to dispatch to the appropriate clearing mode.
Link: https://terminalguide.namepad.de/seq/csi_cj-0/
Link: https://terminalguide.namepad.de/seq/csi_cj-1/
Link: https://terminalguide.namepad.de/seq/csi_cj-2/
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
drivers/video/fbconsole.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index c67e3817ca13..6cca3faa9615 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -641,7 +641,20 @@ static bool fbc_parse_csi(struct fbc_priv *priv)
}
break;
case 'J':
- cls(priv);
+ pos = simple_strtoul(priv->csi, &end, 10);
+ toggle_cursor_visibility(priv);
+ switch (pos) {
+ case 0: /* clear from cursor to end of screen */
+ clear_chars(priv, priv->cur.x, priv->cur.y,
+ priv->cols - 1, priv->rows - 1);
+ break;
+ case 1: /* clear from start of screen to cursor */
+ clear_chars(priv, 0, 0, priv->cur.x, priv->cur.y);
+ break;
+ case 2: /* clear entire screen */
+ cls(priv);
+ break;
+ }
toggle_cursor_visibility(priv);
return true;
case 'H':
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 16/20] fbconsole: implement DEC save/restore cursor
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
` (14 preceding siblings ...)
2026-05-03 8:33 ` [PATCH 15/20] fbconsole: support ESC[0J and ESC[1J partial screen clear Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 17/20] fbconsole: implement VT100 deferred wrap (last column flag) Ahmad Fatoum
` (4 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Now that we have all cursor state collected into a common struct, it's
trivial to implement \e7 and \e8.
Implement it, so the resize command and other potential users can operate
correctly.
Link: https://terminalguide.namepad.de/seq/a_esc_a7/
Link: https://terminalguide.namepad.de/seq/a_esc_a8/
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
drivers/video/fbconsole.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index 6cca3faa9615..71e0778368ed 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -77,6 +77,7 @@ struct fbc_priv {
unsigned int cols, rows;
struct fbc_screen_state cur;
+ struct fbc_screen_state saved; /* DEC cursor save (\e7) */
unsigned int rotation;
enum state_t state;
@@ -759,6 +760,17 @@ static void fbc_putc(struct console_device *cdev, char c)
priv->csipos = 0;
memset(priv->csi, 0, 6);
break;
+ case '7': /* DEC save cursor position */
+ priv->saved = priv->cur;
+ priv->state = LIT;
+ break;
+ case '8': /* DEC restore cursor position */
+ toggle_cursor_visibility(priv);
+ priv->cur = priv->saved;
+ toggle_cursor_visibility(priv);
+ priv->state = LIT;
+ queue_flush = true;
+ break;
default:
priv->state = LIT;
break;
@@ -837,6 +849,7 @@ static int setup_font(struct fbc_priv *priv)
priv->rows = newrows;
priv->cols = newcols;
priv->cur.x = priv->cur.y = 0;
+ priv->saved.x = priv->saved.y = 0;
}
return 0;
@@ -931,8 +944,8 @@ static int set_rotation(struct param_d *p, void *vpriv)
struct fbc_priv *priv = vpriv;
cls(priv);
- priv->cur.x = 0;
- priv->cur.y = 0;
+ priv->cur.x = priv->cur.y = 0;
+ priv->saved.x = priv->saved.y = 0;
setup_font(priv);
return 0;
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 17/20] fbconsole: implement VT100 deferred wrap (last column flag)
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
` (15 preceding siblings ...)
2026-05-03 8:33 ` [PATCH 16/20] fbconsole: implement DEC save/restore cursor Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 18/20] fbconsole: implement alternate screen buffer (ESC[?1049h/l) Ahmad Fatoum
` (3 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Quoting Mattias Engdegård's wraptest project[1]:
DEC VT terminals use a mechanism sometimes called "soft-wrap",
"deferred wrap" or the "VT100 glitch", mainly to permit full-length
newline-terminated text lines to be written without being followed
by unsightly blank lines. This is what complicates matters.
The auto-wrap mode flag, DECAWM, controls whether text will wrap around
the edges at all. It can be set and cleared using the DECSET and DECRST
control sequences; terminal emulators usually get this bit right.
In the following text, we assume that auto-wrap is enabled (DECAWM set).
When the terminal receives a printing character and the cursor is in the
rightmost column, what happens depends on the hidden Last Column Flag:
- If the LCF is 0, then the character is drawn at the cursor position
as usual, but instead of advancing the cursor afterwards, the LCF is
set to 1.
- If the LCF is 1, then the cursor is moved to the first column of the
next line, scrolling if necessary. Then LCF is set to 0, the character
is drawn, and the cursor advanced as usual.
The Last Column flag starts out as 0 when the terminal is powered on.
Implement this into barebox, so full screen TUIs do not immediately roll
off their first line off the framebuffer console.
[1]: https://github.com/mattiase/wraptest
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
drivers/video/fbconsole.c | 33 ++++++++++++++++++++++++++++++---
1 file changed, 30 insertions(+), 3 deletions(-)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index 71e0778368ed..bed55de7571e 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -55,6 +55,8 @@ struct fbc_screen_state {
#define SGR_ATTRIBUTES (ANSI_FLAG_INVERT | ANSI_FLAG_BRIGHT)
#define HIDE_CURSOR (1 << 2)
unsigned flags;
+
+ bool pending_wrap; /* deferred wrap: cursor at last column */
};
struct fbc_priv {
@@ -471,6 +473,7 @@ static void printchar(struct fbc_priv *priv, int c)
case '\007': /* bell: ignore */
break;
case '\b':
+ cur->pending_wrap = false;
if (cur->x > 0) {
cur->x--;
} else if (cur->y > 0) {
@@ -480,25 +483,47 @@ static void printchar(struct fbc_priv *priv, int c)
break;
case '\n':
case '\013': /* Vertical tab is the same as Line Feed */
+ cur->pending_wrap = false;
cur->y++;
break;
case '\r':
+ cur->pending_wrap = false;
cur->x = 0;
break;
case '\t':
+ cur->pending_wrap = false;
cur->x = (cur->x + 8) & ~0x3;
break;
default:
- drawchar(priv, priv->cur.x, priv->cur.y, c);
+ /*
+ * VT100 deferred wrap: if the previous character landed at
+ * the last column, perform the wrap NOW before drawing the
+ * next character. This prevents a scroll from occurring
+ * merely by writing to the bottom-right corner cell.
+ */
+ if (cur->pending_wrap) {
+ cur->pending_wrap = false;
+ cur->x = 0;
+ cur->y++;
+
+ if (cur->y >= priv->rows) {
+ fb_scroll_up(priv);
+ cur->y = priv->rows - 1;
+ }
+ }
+
+ drawchar(priv, cur->x, cur->y, c);
cur->x++;
if (cur->x >= priv->cols) {
- cur->y++;
- cur->x = 0;
+ /* Defer the wrap to the next printable character */
+ cur->x = priv->cols - 1;
+ cur->pending_wrap = true;
}
+ break;
}
if (cur->y >= priv->rows) {
@@ -660,6 +685,7 @@ static bool fbc_parse_csi(struct fbc_priv *priv)
return true;
case 'H':
toggle_cursor_visibility(priv);
+ priv->cur.pending_wrap = false;
pos = simple_strtoul(priv->csi, &end, 10);
fbc_set_cursor_row(priv, pos - 1);
@@ -672,6 +698,7 @@ static bool fbc_parse_csi(struct fbc_priv *priv)
case 'A' ... 'D': {
pos = simple_strtoul(priv->csi, &end, 10) ?: 1;
toggle_cursor_visibility(priv);
+ priv->cur.pending_wrap = false;
switch (last) {
case 'A': /* cursor up */
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 18/20] fbconsole: implement alternate screen buffer (ESC[?1049h/l)
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
` (16 preceding siblings ...)
2026-05-03 8:33 ` [PATCH 17/20] fbconsole: implement VT100 deferred wrap (last column flag) Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 19/20] fbconsole: adapt logging depending on activated streams Ahmad Fatoum
` (2 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
The alternate screen buffer is distinct from the scrollback-enabled
primary buffer and meant for full screen applications.
The ?1049 mode in particular is a superset of it that includes saving
the cursor and clearing the buffer when entering.
Implement it via taking a snapshot of the framebuffer's pixels.
Link: https://terminalguide.namepad.de/mode/p1049/
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
drivers/video/fbconsole.c | 46 +++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index bed55de7571e..3a2a680fc719 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -81,6 +81,10 @@ struct fbc_priv {
struct fbc_screen_state cur;
struct fbc_screen_state saved; /* DEC cursor save (\e7) */
+ struct fbc_screen_state altscreen_saved;
+ void *altscreen_buf; /* pixel snapshot while in alternate screen */
+ size_t altscreen_size;
+
unsigned int rotation;
enum state_t state;
@@ -651,6 +655,21 @@ static bool fbc_parse_csi(struct fbc_priv *priv)
/* show cursor now */
toggle_cursor_visibility(priv);
return true;
+ case 1049: /* alternate screen: save pixel buffer and state */
+ if (!priv->altscreen_buf) {
+ void *src = gui_screen_render_buffer(priv->sc);
+ size_t sz = priv->fb->line_length * priv->fb->yres;
+
+ priv->altscreen_buf = memdup(src, sz);
+ if (priv->altscreen_buf) {
+ priv->altscreen_size = sz;
+ priv->altscreen_saved = priv->cur;
+ }
+ priv->cur = (struct fbc_screen_state){};
+ fbc_reset_colors(priv);
+ cls(priv);
+ }
+ return true;
}
break;
case 'l':
@@ -664,6 +683,27 @@ static bool fbc_parse_csi(struct fbc_priv *priv)
/* hide cursor now */
priv->cur.flags |= HIDE_CURSOR;
return true;
+ case 1049: /* alternate screen: restore pixel buffer and state */
+ if (priv->altscreen_buf) {
+ void *dst = gui_screen_render_buffer(priv->sc);
+ size_t sz = priv->fb->line_length * priv->fb->yres;
+
+ if (sz == priv->altscreen_size) {
+ memcpy(dst, priv->altscreen_buf, sz);
+ gu_screen_blit(priv->sc);
+ } else {
+ cls(priv);
+ }
+ free(priv->altscreen_buf);
+ priv->altscreen_buf = NULL;
+ priv->altscreen_size = 0;
+ priv->cur = priv->altscreen_saved;
+ } else {
+ priv->cur = (struct fbc_screen_state){};
+ fbc_reset_colors(priv);
+ cls(priv);
+ }
+ return true;
}
break;
case 'J':
@@ -914,6 +954,12 @@ static int fbc_close(struct console_device *cdev)
struct fbc_priv *priv = container_of(cdev,
struct fbc_priv, cdev);
+ if (priv->altscreen_buf) {
+ free(priv->altscreen_buf);
+ priv->altscreen_buf = NULL;
+ priv->altscreen_size = 0;
+ }
+
if (priv->active) {
fb_close(priv->sc);
priv->active = false;
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 19/20] fbconsole: adapt logging depending on activated streams
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
` (17 preceding siblings ...)
2026-05-03 8:33 ` [PATCH 18/20] fbconsole: implement alternate screen buffer (ESC[?1049h/l) Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 20/20] netconsole: suppress log message when opening console file Ahmad Fatoum
2026-05-07 10:38 ` [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Sascha Hauer
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
The console_device::f_active member is only set after the console has
been successfully opened. Setting it earlier is not feasible as that
means a poller could attempt outputting on a console while it's being
enabled.
Instead, let's pass the activation flags as argument to the optional
open callback. The callback is only used by the netconsole and the
fbconsole.
This resolves two issues at the same time:
- We can suppress log messages when the device is briefly opened via
`echo -a /dev/fbconsoleX MESSAGE`. Otherwise writing a message while
the fbconsole is not activated for a CONSOLE_STD* stream, would each
time result in a message like "fb0: framebuffer console WxH
activated".
- We can replay all accumulated log messages, so users without access
to the serial output still see what had transpired prior to console
activation. This is especially useful for the framebuffer console as
it does not support output on its own.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
common/console.c | 8 ++++----
common/serdev.c | 2 +-
drivers/video/fbconsole.c | 12 ++++++++----
include/console.h | 4 ++--
net/netconsole.c | 2 +-
5 files changed, 16 insertions(+), 12 deletions(-)
diff --git a/common/console.c b/common/console.c
index acb4c772672c..fd859b97bf4a 100644
--- a/common/console.c
+++ b/common/console.c
@@ -48,12 +48,12 @@ static struct kfifo __console_output_fifo;
static struct kfifo *console_input_fifo = &__console_input_fifo;
static struct kfifo *console_output_fifo = &__console_output_fifo;
-int console_open(struct console_device *cdev)
+int console_open(struct console_device *cdev, unsigned activate)
{
int ret;
if (cdev->open && !cdev->open_count) {
- ret = cdev->open(cdev);
+ ret = cdev->open(cdev, activate);
if (ret)
return ret;
}
@@ -102,7 +102,7 @@ int console_set_active(struct console_device *cdev, unsigned flag)
if (ret)
return ret;
} else {
- ret = console_open(cdev);
+ ret = console_open(cdev, flag);
if (ret)
return ret;
}
@@ -311,7 +311,7 @@ static int fops_open(struct cdev *cdev, unsigned long flags)
if ((flags & (O_WRONLY | O_RDWR)) && !priv->puts )
return -EPERM;
- return console_open(priv);
+ return console_open(priv, 0);
}
static int fops_close(struct cdev *dev)
diff --git a/common/serdev.c b/common/serdev.c
index 1c8cfbd59447..319a17580760 100644
--- a/common/serdev.c
+++ b/common/serdev.c
@@ -80,7 +80,7 @@ int serdev_device_open(struct serdev_device *serdev)
if (ret)
goto err_free_fifo;
- ret = console_open(cdev);
+ ret = console_open(cdev, 0);
if (ret)
goto err_poller_unregister;
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index 3a2a680fc719..a86005ada244 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -922,7 +922,7 @@ static int setup_font(struct fbc_priv *priv)
return 0;
}
-static int fbc_open(struct console_device *cdev)
+static int fbc_open(struct console_device *cdev, unsigned activate)
{
struct fbc_priv *priv = container_of(cdev,
struct fbc_priv, cdev);
@@ -941,11 +941,15 @@ static int fbc_open(struct console_device *cdev)
priv->state = LIT;
- dev_info(priv->cdev.dev, "framebuffer console %dx%d activated\n",
- priv->cols, priv->rows);
-
priv->active = true;
+ if (activate)
+ dev_info(priv->cdev.dev, "framebuffer console %dx%d activated\n",
+ priv->cols, priv->rows);
+
+ if (activate & CONSOLE_STDERR)
+ log_print(&priv->cdev, 0, GENMASK(LOGLEVEL, 0));
+
return 0;
}
diff --git a/include/console.h b/include/console.h
index cfd2480f30d3..4b2893d5c4df 100644
--- a/include/console.h
+++ b/include/console.h
@@ -41,7 +41,7 @@ struct console_device {
int (*setbrg)(struct console_device *cdev, int baudrate);
void (*flush)(struct console_device *cdev);
int (*set_mode)(struct console_device *cdev, enum console_mode mode);
- int (*open)(struct console_device *cdev);
+ int (*open)(struct console_device *cdev, unsigned activate);
int (*close)(struct console_device *cdev);
int (*get_size)(struct console_device *cdev, int *width, int *height);
@@ -110,7 +110,7 @@ struct console_device *of_console_get_by_alias(const char *alias);
#define CFG_PBSIZE (CONFIG_CBSIZE+sizeof(CONFIG_PROMPT)+16)
-int console_open(struct console_device *cdev);
+int console_open(struct console_device *cdev, unsigned activate);
int console_close(struct console_device *cdev);
int console_set_active(struct console_device *cdev, unsigned active);
unsigned console_get_active(struct console_device *cdev);
diff --git a/net/netconsole.c b/net/netconsole.c
index 859cfe8e6f35..f2e9447526bf 100644
--- a/net/netconsole.c
+++ b/net/netconsole.c
@@ -94,7 +94,7 @@ static void nc_putc(struct console_device *cdev, char c)
priv->busy = 0;
}
-static int nc_open(struct console_device *cdev)
+static int nc_open(struct console_device *cdev, unsigned activate)
{
struct nc_priv *priv = container_of(cdev,
struct nc_priv, cdev);
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 20/20] netconsole: suppress log message when opening console file
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
` (18 preceding siblings ...)
2026-05-03 8:33 ` [PATCH 19/20] fbconsole: adapt logging depending on activated streams Ahmad Fatoum
@ 2026-05-03 8:33 ` Ahmad Fatoum
2026-05-07 10:38 ` [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Sascha Hauer
20 siblings, 0 replies; 22+ messages in thread
From: Ahmad Fatoum @ 2026-05-03 8:33 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
As is already being done for the framebuffer console, suppress log
messages when the device is briefly opened via echo.
Otherwise, a netconsole not activated for a CONSOLE_STD* stream,
would each time result in a log message.
We do not replay the dmesg, because if the output path works, the remote
side should be able to input a dmesg command.
This is unlike fbconsole, which has no input.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
net/netconsole.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/netconsole.c b/net/netconsole.c
index f2e9447526bf..62dd60806ebd 100644
--- a/net/netconsole.c
+++ b/net/netconsole.c
@@ -118,7 +118,8 @@ static int nc_open(struct console_device *cdev, unsigned activate)
net_udp_bind(priv->con, priv->port);
- pr_info("netconsole initialized with %pI4:%d\n", &priv->ip, priv->port);
+ if (activate)
+ pr_info("netconsole initialized with %pI4:%d\n", &priv->ip, priv->port);
return 0;
}
--
2.47.3
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 00/20] fbconsole: support TUI-relevant escape sequences
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
` (19 preceding siblings ...)
2026-05-03 8:33 ` [PATCH 20/20] netconsole: suppress log message when opening console file Ahmad Fatoum
@ 2026-05-07 10:38 ` Sascha Hauer
20 siblings, 0 replies; 22+ messages in thread
From: Sascha Hauer @ 2026-05-07 10:38 UTC (permalink / raw)
To: barebox, Ahmad Fatoum
On Sun, 03 May 2026 10:33:02 +0200, Ahmad Fatoum wrote:
> A BIOS-style TUI in barebox could go a long way towards making barebox
> more user friendly: It could list different parameters with familiar
> controls instead of having to juggle around commands like varinfo,
> devinfo and magicvar.
>
> This could especially be the case for barebox running on laptops.
> Laptops tend to not have a serial port routed out though, so the TUI we
> provide will need to be displayed on the framebuffer.
>
> [...]
Applied, thanks!
[01/20] fbconsole: remove incomplete CSI_CNT state
https://git.pengutronix.de/cgit/barebox/commit/?id=bcb124745b8e (link may not be stable)
[02/20] fbconsole: improve handling of unexpected escape sequences
https://git.pengutronix.de/cgit/barebox/commit/?id=b6a0b7ad796c (link may not be stable)
[03/20] fbconsole: fix handling of CSI buffer overflow
https://git.pengutronix.de/cgit/barebox/commit/?id=b1411fc06c29 (link may not be stable)
[04/20] fbconsole: do not reset cursor visibility alongside attributes
https://git.pengutronix.de/cgit/barebox/commit/?id=0baace394402 (link may not be stable)
[05/20] fbconsole: respect hidden cursor everywhere
https://git.pengutronix.de/cgit/barebox/commit/?id=eb9b3a6df103 (link may not be stable)
[06/20] fbconsole: call fb_blit_area for every drawchar
https://git.pengutronix.de/cgit/barebox/commit/?id=f777532ed693 (link may not be stable)
[07/20] fbconsole: skip fb_flush when processing escape sequences
https://git.pengutronix.de/cgit/barebox/commit/?id=10017213d4f7 (link may not be stable)
[08/20] fbconsole: factor out helpers for clamped cursor row/col setting
https://git.pengutronix.de/cgit/barebox/commit/?id=edab1bfe5135 (link may not be stable)
[09/20] fbconsole: precompute foreground/background colors
https://git.pengutronix.de/cgit/barebox/commit/?id=0c23c98ef2d3 (link may not be stable)
[10/20] fbconsole: collect renderable state into struct fbc_screen_state
https://git.pengutronix.de/cgit/barebox/commit/?id=66d67d10e68c (link may not be stable)
[11/20] fbconsole: implement CSI A/B/C/D cursor movement sequences
https://git.pengutronix.de/cgit/barebox/commit/?id=68e444aeb157 (link may not be stable)
[12/20] fbconsole: restrict cursor visibility to DEC private mode 25
https://git.pengutronix.de/cgit/barebox/commit/?id=6fb64e7f77e3 (link may not be stable)
[13/20] fbconsole: add new clear_chars helper
https://git.pengutronix.de/cgit/barebox/commit/?id=d19954ffc166 (link may not be stable)
[14/20] fbconsole: implement erase entire line CSI
https://git.pengutronix.de/cgit/barebox/commit/?id=ad5234f31da4 (link may not be stable)
[15/20] fbconsole: support ESC[0J and ESC[1J partial screen clear
https://git.pengutronix.de/cgit/barebox/commit/?id=f8c1647644a1 (link may not be stable)
[16/20] fbconsole: implement DEC save/restore cursor
https://git.pengutronix.de/cgit/barebox/commit/?id=2d4af741a35f (link may not be stable)
[17/20] fbconsole: implement VT100 deferred wrap (last column flag)
https://git.pengutronix.de/cgit/barebox/commit/?id=c24266453a78 (link may not be stable)
[18/20] fbconsole: implement alternate screen buffer (ESC[?1049h/l)
https://git.pengutronix.de/cgit/barebox/commit/?id=b435c4794dd1 (link may not be stable)
[19/20] fbconsole: adapt logging depending on activated streams
https://git.pengutronix.de/cgit/barebox/commit/?id=e429e2ea0c63 (link may not be stable)
[20/20] netconsole: suppress log message when opening console file
https://git.pengutronix.de/cgit/barebox/commit/?id=4b87b1a12dd4 (link may not be stable)
Best regards,
--
Sascha Hauer <s.hauer@pengutronix.de>
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2026-05-07 10:40 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-03 8:33 [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 01/20] fbconsole: remove incomplete CSI_CNT state Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 02/20] fbconsole: improve handling of unexpected escape sequences Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 03/20] fbconsole: fix handling of CSI buffer overflow Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 04/20] fbconsole: do not reset cursor visibility alongside attributes Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 05/20] fbconsole: respect hidden cursor everywhere Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 06/20] fbconsole: call fb_blit_area for every drawchar Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 07/20] fbconsole: skip fb_flush when processing escape sequences Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 08/20] fbconsole: factor out helpers for clamped cursor row/col setting Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 09/20] fbconsole: precompute foreground/background colors Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 10/20] fbconsole: collect renderable state into struct fbc_screen_state Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 11/20] fbconsole: implement CSI A/B/C/D cursor movement sequences Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 12/20] fbconsole: restrict cursor visibility to DEC private mode 25 Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 13/20] fbconsole: add new clear_chars helper Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 14/20] fbconsole: implement erase entire line CSI Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 15/20] fbconsole: support ESC[0J and ESC[1J partial screen clear Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 16/20] fbconsole: implement DEC save/restore cursor Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 17/20] fbconsole: implement VT100 deferred wrap (last column flag) Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 18/20] fbconsole: implement alternate screen buffer (ESC[?1049h/l) Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 19/20] fbconsole: adapt logging depending on activated streams Ahmad Fatoum
2026-05-03 8:33 ` [PATCH 20/20] netconsole: suppress log message when opening console file Ahmad Fatoum
2026-05-07 10:38 ` [PATCH 00/20] fbconsole: support TUI-relevant escape sequences Sascha Hauer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox