u-boot/drivers/video/console_core.c
Simon Glass 37db20d0a6 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 <sjg@chromium.org>
2023-10-11 15:43:55 -04:00

249 lines
5.6 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 <video.h>
#include <video_console.h>
#include <dm.h>
#include <video_font.h>
#include "vidconsole_internal.h"
/**
* console_set_font() - prepare vidconsole for chosen font.
*
* @dev vidconsole device
* @fontdata pointer to font data struct
*/
static int console_set_font(struct udevice *dev, struct video_fontdata *fontdata)
{
struct console_simple_priv *priv = dev_get_priv(dev);
struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
debug("console_simple: setting %s font\n", fontdata->name);
debug("width: %d\n", fontdata->width);
debug("byte width: %d\n", fontdata->byte_width);
debug("height: %d\n", fontdata->height);
priv->fontdata = fontdata;
vc_priv->x_charsize = fontdata->width;
vc_priv->y_charsize = fontdata->height;
if (vid_priv->rot % 2) {
vc_priv->cols = vid_priv->ysize / fontdata->width;
vc_priv->rows = vid_priv->xsize / fontdata->height;
vc_priv->xsize_frac = VID_TO_POS(vid_priv->ysize);
} else {
vc_priv->cols = vid_priv->xsize / fontdata->width;
vc_priv->rows = vid_priv->ysize / fontdata->height;
}
return 0;
}
int check_bpix_support(int bpix)
{
if (bpix == VIDEO_BPP8 && CONFIG_IS_ENABLED(VIDEO_BPP8))
return 0;
else if (bpix == VIDEO_BPP16 && CONFIG_IS_ENABLED(VIDEO_BPP16))
return 0;
else if (bpix == VIDEO_BPP32 && CONFIG_IS_ENABLED(VIDEO_BPP32))
return 0;
else
return -ENOSYS;
}
inline void fill_pixel_and_goto_next(void **dstp, u32 value, int pbytes, int step)
{
u8 *dst_byte = *dstp;
if (pbytes == 4) {
u32 *dst = *dstp;
*dst = value;
}
if (pbytes == 2) {
u16 *dst = *dstp;
*dst = value;
}
if (pbytes == 1) {
u8 *dst = *dstp;
*dst = value;
}
*dstp = dst_byte + step;
}
int fill_char_vertically(uchar *pfont, void **line, struct video_priv *vid_priv,
struct video_fontdata *fontdata, bool direction)
{
int step, line_step, pbytes, bitcount, width_remainder, ret;
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;
}
width_remainder = fontdata->width % 8;
for (int row = 0; row < fontdata->height; row++) {
uchar bits;
bitcount = 8;
dst = *line;
for (int col = 0; col < fontdata->byte_width; col++) {
if (width_remainder) {
bool is_last_col = (fontdata->byte_width - col == 1);
if (is_last_col)
bitcount = width_remainder;
}
bits = pfont[col];
for (int bit = 0; bit < bitcount; bit++) {
u32 value = (bits & 0x80) ?
vid_priv->colour_fg :
vid_priv->colour_bg;
fill_pixel_and_goto_next(&dst,
value,
pbytes,
step
);
bits <<= 1;
}
}
*line += line_step;
pfont += fontdata->byte_width;
}
return ret;
}
int fill_char_horizontally(uchar *pfont, void **line, struct video_priv *vid_priv,
struct video_fontdata *fontdata, bool direction)
{
int step, line_step, pbytes, bitcount = 8, width_remainder, ret;
void *dst;
u8 mask;
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;
}
width_remainder = fontdata->width % 8;
for (int col = 0; col < fontdata->byte_width; col++) {
mask = 0x80;
if (width_remainder) {
bool is_last_col = (fontdata->byte_width - col == 1);
if (is_last_col)
bitcount = width_remainder;
}
for (int bit = 0; bit < bitcount; bit++) {
dst = *line;
for (int row = 0; row < fontdata->height; row++) {
u32 value = (pfont[row * fontdata->byte_width + col]
& mask) ? vid_priv->colour_fg : vid_priv->colour_bg;
fill_pixel_and_goto_next(&dst,
value,
pbytes,
step
);
}
*line += line_step;
mask >>= 1;
}
}
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);
}
const char *console_simple_get_font_size(struct udevice *dev, uint *sizep)
{
struct console_simple_priv *priv = dev_get_priv(dev);
*sizep = priv->fontdata->width;
return priv->fontdata->name;
}
int console_simple_get_font(struct udevice *dev, int seq, struct vidfont_info *info)
{
info->name = fonts[seq].name;
return 0;
}
int console_simple_select_font(struct udevice *dev, const char *name, uint size)
{
struct video_fontdata *font;
if (!name) {
if (fonts->name)
console_set_font(dev, fonts);
return 0;
}
for (font = fonts; font->name; font++) {
if (!strcmp(name, font->name)) {
console_set_font(dev, font);
return 0;
}
};
printf("no such font: %s, make sure it's name has <width>x<height> format\n", name);
return -ENOENT;
}