Files
u-boot/drivers/video/console_core.c
Simon Glass 8962a5fbe2 video: Save overwritten pixels when drawing the cursor
Save the pixels overwritten by the cursor into the save area.

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

391 lines
8.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 <malloc.h>
#include <spl.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);
priv->fontdata = fontdata;
vidconsole_set_bitmap_font(dev, fontdata);
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;
}
inline u32 swap_pixel_and_goto_next(void **dstp, u32 value, int pbytes, int step)
{
u8 *dst_byte = *dstp;
u32 old_value = 0;
if (pbytes == 4) {
u32 *dst = *dstp;
old_value = *dst;
*dst = value;
}
if (pbytes == 2) {
u16 *dst = *dstp;
old_value = *dst;
*dst = value;
}
if (pbytes == 1) {
u8 *dst = *dstp;
old_value = *dst;
*dst = value;
}
*dstp = dst_byte + step;
return old_value;
}
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 cursor_show(struct vidconsole_cursor *curs, struct video_priv *vid_priv,
bool direction)
{
int step, line_step, pbytes, ret, row;
void *line, *dst;
u32 *save_ptr;
uint value;
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;
}
/* we should not already have saved data */
if (curs->saved) {
debug("Trying to show cursor but data is already saved\n");
return -EINVAL;
}
/* Figure out where to write the cursor in the frame buffer */
line = vid_priv->fb + curs->y * vid_priv->line_length +
curs->x * VNBYTES(vid_priv->bpix);
/* save pixels under cursor and draw new cursor in one pass */
value = vid_priv->colour_fg;
save_ptr = curs->save_data;
for (row = 0; row < curs->height; row++) {
dst = line;
for (int col = 0; col < VIDCONSOLE_CURSOR_WIDTH; col++)
*save_ptr++ = swap_pixel_and_goto_next(&dst, value,
pbytes, step);
line += line_step;
}
curs->saved = true;
return 0;
}
int cursor_hide(struct vidconsole_cursor *curs, struct video_priv *vid_priv,
bool direction)
{
int step, line_step, pbytes, ret;
void *line, *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;
}
/* Trying to hide cursor - we should have saved data */
if (!curs->saved) {
debug("Trying to hide cursor but no data was saved\n");
return -EINVAL;
}
/* Figure out where to write the cursor in the frame buffer */
line = vid_priv->fb + curs->y * vid_priv->line_length +
curs->x * VNBYTES(vid_priv->bpix);
/* Restore saved pixels */
u32 *save_ptr = curs->save_data;
dst = line;
for (int row = 0; row < curs->height; row++) {
void *row_dst = dst;
for (int col = 0; col < VIDCONSOLE_CURSOR_WIDTH; col++)
fill_pixel_and_goto_next(&row_dst, *save_ptr++, pbytes,
step);
dst += line_step;
}
curs->saved = false;
return 0;
}
int console_alloc_cursor(struct udevice *dev)
{
struct vidconsole_priv *vc_priv;
struct vidconsole_cursor *curs;
struct video_priv *vid_priv;
struct udevice *vid;
int save_count;
if (!CONFIG_IS_ENABLED(CURSOR) || xpl_phase() < PHASE_BOARD_R)
return 0;
vc_priv = dev_get_uclass_priv(dev);
vid = dev_get_parent(dev);
vid_priv = dev_get_uclass_priv(vid);
curs = &vc_priv->curs;
/* Allocate cursor save buffer for maximum possible cursor height */
save_count = vid_priv->ysize * VIDCONSOLE_CURSOR_WIDTH;
curs->save_data = malloc(save_count * sizeof(u32));
if (!curs->save_data)
return -ENOMEM;
return 0;
}
int console_probe(struct udevice *dev)
{
int ret;
ret = console_set_font(dev, fonts);
if (ret)
return ret;
if (CONFIG_IS_ENABLED(CURSOR) && xpl_phase() == PHASE_BOARD_R) {
ret = console_alloc_cursor(dev);
if (ret)
return ret;
}
return 0;
}
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 info->name ? 0 : -ENOENT;
}
int console_fixed_putc_xy(struct udevice *dev, uint x_frac, uint y, int cp,
struct video_fontdata *fontdata)
{
struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
struct udevice *vid = dev->parent;
struct video_priv *vid_priv = dev_get_uclass_priv(vid);
int pbytes = VNBYTES(vid_priv->bpix);
int x, linenum, ret;
void *start, *line;
u8 ch = console_utf_to_cp437(cp);
uchar *pfont = fontdata->video_fontdata +
ch * fontdata->char_pixel_bytes;
if (x_frac + VID_TO_POS(vc_priv->x_charsize) > vc_priv->xsize_frac)
return -EAGAIN;
linenum = y;
x = VID_TO_PIXEL(x_frac);
start = vid_priv->fb + linenum * vid_priv->line_length + x * pbytes;
line = start;
ret = fill_char_vertically(pfont, &line, vid_priv, fontdata, NORMAL_DIRECTION);
if (ret)
return ret;
video_damage(dev->parent,
x,
y,
fontdata->width,
fontdata->height);
return VID_TO_POS(fontdata->width);
}
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;
}