dm: video: Add basic ANSI escape sequence support

Really just the subset that is needed by efi_console.  Perhaps more will
be added later, for example color support would be useful to implement
efi_cout_set_attribute().

Signed-off-by: Rob Clark <robdclark@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Rob Clark 2017-09-13 18:12:21 -04:00 committed by Anatolij Gustschin
parent 889808da9b
commit a085aa1f27
5 changed files with 135 additions and 3 deletions

View file

@ -65,6 +65,14 @@ config VIDEO_BPP32
this option, such displays will not be supported and console output this option, such displays will not be supported and console output
will be empty. will be empty.
config VIDEO_ANSI
bool "Support ANSI escape sequences in video console"
depends on DM_VIDEO
default y if DM_VIDEO
help
Enable ANSI escape sequence decoding for a more fully functional
console.
config CONSOLE_NORMAL config CONSOLE_NORMAL
bool "Support a simple text console" bool "Support a simple text console"
depends on DM_VIDEO depends on DM_VIDEO

View file

@ -9,6 +9,7 @@
*/ */
#include <common.h> #include <common.h>
#include <linux/ctype.h>
#include <dm.h> #include <dm.h>
#include <video.h> #include <video.h>
#include <video_console.h> #include <video_console.h>
@ -107,12 +108,119 @@ static void vidconsole_newline(struct udevice *dev)
video_sync(dev->parent); video_sync(dev->parent);
} }
static char *parsenum(char *s, int *num)
{
char *end;
*num = simple_strtol(s, &end, 10);
return end;
}
/*
* Process a character while accumulating an escape string. Chars are
* accumulated into escape_buf until the end of escape sequence is
* found, at which point the sequence is parsed and processed.
*/
static void vidconsole_escape_char(struct udevice *dev, char ch)
{
struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
if (!IS_ENABLED(CONFIG_VIDEO_ANSI))
goto error;
/* Sanity checking for bogus ESC sequences: */
if (priv->escape_len >= sizeof(priv->escape_buf))
goto error;
if (priv->escape_len == 0 && ch != '[')
goto error;
priv->escape_buf[priv->escape_len++] = ch;
/*
* Escape sequences are terminated by a letter, so keep
* accumulating until we get one:
*/
if (!isalpha(ch))
return;
/*
* clear escape mode first, otherwise things will get highly
* surprising if you hit any debug prints that come back to
* this console.
*/
priv->escape = 0;
switch (ch) {
case 'H':
case 'f': {
int row, col;
char *s = priv->escape_buf;
/*
* Set cursor position: [%d;%df or [%d;%dH
*/
s++; /* [ */
s = parsenum(s, &row);
s++; /* ; */
s = parsenum(s, &col);
priv->ycur = row * priv->y_charsize;
priv->xcur_frac = priv->xstart_frac +
VID_TO_POS(col * priv->x_charsize);
break;
}
case 'J': {
int mode;
/*
* Clear part/all screen:
* [J or [0J - clear screen from cursor down
* [1J - clear screen from cursor up
* [2J - clear entire screen
*
* TODO we really only handle entire-screen case, others
* probably require some additions to video-uclass (and
* are not really needed yet by efi_console)
*/
parsenum(priv->escape_buf + 1, &mode);
if (mode == 2) {
video_clear(dev->parent);
video_sync(dev->parent);
priv->ycur = 0;
priv->xcur_frac = priv->xstart_frac;
} else {
debug("unsupported clear mode: %d\n", mode);
}
break;
}
default:
debug("unrecognized escape sequence: %*s\n",
priv->escape_len, priv->escape_buf);
}
return;
error:
/* something went wrong, just revert to normal mode: */
priv->escape = 0;
}
int vidconsole_put_char(struct udevice *dev, char ch) int vidconsole_put_char(struct udevice *dev, char ch)
{ {
struct vidconsole_priv *priv = dev_get_uclass_priv(dev); struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
int ret; int ret;
if (priv->escape) {
vidconsole_escape_char(dev, ch);
return 0;
}
switch (ch) { switch (ch) {
case '\x1b':
priv->escape_len = 0;
priv->escape = 1;
break;
case '\a': case '\a':
/* beep */ /* beep */
break; break;

View file

@ -87,7 +87,7 @@ int video_reserve(ulong *addrp)
return 0; return 0;
} }
static int video_clear(struct udevice *dev) void video_clear(struct udevice *dev)
{ {
struct video_priv *priv = dev_get_uclass_priv(dev); struct video_priv *priv = dev_get_uclass_priv(dev);
@ -100,8 +100,6 @@ static int video_clear(struct udevice *dev)
} else { } else {
memset(priv->fb, priv->colour_bg, priv->fb_size); memset(priv->fb, priv->colour_bg, priv->fb_size);
} }
return 0;
} }
/* Flush video activity to the caches */ /* Flush video activity to the caches */

View file

@ -114,6 +114,13 @@ struct video_ops {
*/ */
int video_reserve(ulong *addrp); int video_reserve(ulong *addrp);
/**
* video_clear() - Clear a device's frame buffer to background color.
*
* @dev: Device to clear
*/
void video_clear(struct udevice *dev);
/** /**
* video_sync() - Sync a device's frame buffer with its hardware * video_sync() - Sync a device's frame buffer with its hardware
* *

View file

@ -29,6 +29,9 @@
* @xsize_frac: Width of the display in fractional units * @xsize_frac: Width of the display in fractional units
* @xstart_frac: Left margin for the text console in fractional units * @xstart_frac: Left margin for the text console in fractional units
* @last_ch: Last character written to the text console on this line * @last_ch: Last character written to the text console on this line
* @escape: TRUE if currently accumulating an ANSI escape sequence
* @escape_len: Length of accumulated escape sequence so far
* @escape_buf: Buffer to accumulate escape sequence
*/ */
struct vidconsole_priv { struct vidconsole_priv {
struct stdio_dev sdev; struct stdio_dev sdev;
@ -42,6 +45,14 @@ struct vidconsole_priv {
int xsize_frac; int xsize_frac;
int xstart_frac; int xstart_frac;
int last_ch; int last_ch;
/*
* ANSI escape sequences are accumulated character by character,
* starting after the ESC char (0x1b) until the entire sequence
* is consumed at which point it is acted upon.
*/
int escape;
int escape_len;
char escape_buf[32];
}; };
/** /**