Files
u-boot/drivers/video/console_normal.c
Simon Glass 082bed3807 video: Support a cursor in multi-line text
For expo, lineedit only supports a single line.

For the CLI, the text can extend across multiple lines. Add support for
this in the normal console, calculating the x and y offset of the cursor
position based on the display width and font size.

The truetype console already works, since it has had this tracking for a
while.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-09-19 12:56:02 -06:00

203 lines
5.1 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2015 Google, Inc
* (C) Copyright 2015
* Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
* (C) Copyright 2023 Dzmitry Sankouski <dsankouski@gmail.com>
*/
#include <charset.h>
#include <dm.h>
#include <spl.h>
#include <video.h>
#include <video_console.h>
#include <video_font.h> /* Get font data, width and height */
#include "vidconsole_internal.h"
struct console_store {
int xpos_frac;
int ypos;
int cli_index;
};
static int console_set_row(struct udevice *dev, uint row, int clr)
{
struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
struct console_simple_priv *priv = dev_get_priv(dev);
struct video_fontdata *fontdata = priv->fontdata;
void *line, *dst, *end;
int pixels = fontdata->height * vid_priv->xsize;
int ret;
int i;
int pbytes;
ret = check_bpix_support(vid_priv->bpix);
if (ret)
return ret;
line = vid_priv->fb + row * fontdata->height * vid_priv->line_length;
dst = line;
pbytes = VNBYTES(vid_priv->bpix);
for (i = 0; i < pixels; i++)
fill_pixel_and_goto_next(&dst, clr, pbytes, pbytes);
end = dst;
video_damage(dev->parent,
0,
fontdata->height * row,
vid_priv->xsize,
fontdata->height);
return 0;
}
static int console_move_rows(struct udevice *dev, uint rowdst,
uint rowsrc, uint count)
{
struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
struct console_simple_priv *priv = dev_get_priv(dev);
struct video_fontdata *fontdata = priv->fontdata;
void *dst;
void *src;
int size;
dst = vid_priv->fb + rowdst * fontdata->height * vid_priv->line_length;
src = vid_priv->fb + rowsrc * fontdata->height * vid_priv->line_length;
size = fontdata->height * vid_priv->line_length * count;
memmove(dst, src, size);
video_damage(dev->parent,
0,
fontdata->height * rowdst,
vid_priv->xsize,
fontdata->height * count);
return 0;
}
int console_normal_putc_xy(struct udevice *dev, uint x_frac, uint y, int cp)
{
struct console_simple_priv *priv = dev_get_priv(dev);
return console_fixed_putc_xy(dev, x_frac, y, cp, priv->fontdata);
}
static __maybe_unused int console_get_cursor_info(struct udevice *dev)
{
struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct console_simple_priv *priv = dev_get_priv(dev);
struct video_fontdata *fontdata = priv->fontdata;
struct vidconsole_cursor *curs = &vc_priv->curs;
int x, y, index, xspace, xpos;
/* for now, this is not used outside expo */
if (!IS_ENABLED(CONFIG_EXPO))
return -ENOSYS;
x = VID_TO_PIXEL(vc_priv->xmark_frac);
y = vc_priv->ymark;
index = vc_priv->cli_index;
/* rounded up character position in this line */
xpos = (x + vc_priv->x_charsize - 1) / vc_priv->x_charsize;
/* number of characters which can fit on this (first) line */
xspace = vc_priv->cols - xpos;
if (!curs->indent && index > xspace) {
/* move to the next line */
y += vc_priv->y_charsize;
index -= xspace;
/* figure out the available space in subsequent lines */
if (!curs->indent) {
xspace = vc_priv->cols;
x = 0;
}
/* calculate the line based on that */
y += index / xspace;
x += (index % xspace) * fontdata->width;
} else {
x += index * fontdata->width;
}
/* place the cursor 1 pixel before the start of the next char */
if (x > 0)
x -= 1;
/* Store line pointer and height in cursor struct */
curs->x = x;
curs->y = y;
curs->height = vc_priv->y_charsize;
curs->index = vc_priv->cli_index;
return 0;
}
static __maybe_unused int normal_entry_save(struct udevice *dev,
struct abuf *buf)
{
struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct console_store store;
const uint size = sizeof(store);
if (xpl_phase() <= PHASE_SPL)
return -ENOSYS;
if (!abuf_realloc(buf, size))
return log_msg_ret("sav", -ENOMEM);
store.xpos_frac = vc_priv->xcur_frac;
store.ypos = vc_priv->ycur;
store.cli_index = vc_priv->cli_index;
memcpy(abuf_data(buf), &store, size);
return 0;
}
static __maybe_unused int normal_entry_restore(struct udevice *dev,
struct abuf *buf)
{
struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct console_store store;
if (xpl_phase() <= PHASE_SPL)
return -ENOSYS;
memcpy(&store, abuf_data(buf), sizeof(store));
vc_priv->xcur_frac = store.xpos_frac;
vc_priv->ycur = store.ypos;
vc_priv->cli_index = store.cli_index;
return 0;
}
static int console_putc_xy(struct udevice *dev, uint x_frac, uint y, int cp)
{
return console_normal_putc_xy(dev, x_frac, y, cp);
}
struct vidconsole_ops console_ops = {
.putc_xy = console_putc_xy,
.move_rows = console_move_rows,
.set_row = console_set_row,
.get_font_size = console_simple_get_font_size,
.get_font = console_simple_get_font,
.select_font = console_simple_select_font,
#ifdef CONFIG_CURSOR
.get_cursor_info = console_get_cursor_info,
.entry_save = normal_entry_save,
.entry_restore = normal_entry_restore,
#endif
};
U_BOOT_DRIVER(vidconsole_normal) = {
.name = "vidconsole0",
.id = UCLASS_VIDEO_CONSOLE,
.ops = &console_ops,
.probe = console_probe,
.priv_auto = sizeof(struct console_simple_priv),
};