[Concept,37/42] video: Save overwritten pixels when drawing the cursor

Message ID 20250919201507.4024144-38-sjg@u-boot.org
State New
Headers
Series video: Support a cursor more generally |

Commit Message

Simon Glass Sept. 19, 2025, 8:14 p.m. UTC
  From: Simon Glass <sjg@chromium.org>

Save the pixels overwritten by the cursor into the save area.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 drivers/video/console_core.c        | 63 ++++++++++++++++++++++++++---
 drivers/video/vidconsole-uclass.c   | 36 +++++++++++++++++
 drivers/video/vidconsole_internal.h | 15 ++++++-
 include/video_console.h             | 16 +++++++-
 4 files changed, 122 insertions(+), 8 deletions(-)
  

Patch

diff --git a/drivers/video/console_core.c b/drivers/video/console_core.c
index 4fab5a2605e..3f3efb94ab1 100644
--- a/drivers/video/console_core.c
+++ b/drivers/video/console_core.c
@@ -190,8 +190,9 @@  int fill_char_horizontally(uchar *pfont, void **line, struct video_priv *vid_pri
 int cursor_show(struct vidconsole_cursor *curs, struct video_priv *vid_priv,
 		bool direction)
 {
-	int step, line_step, pbytes, ret;
+	int step, line_step, pbytes, ret, row;
 	void *line, *dst;
+	u32 *save_ptr;
 	uint value;
 
 	ret = check_bpix_support(vid_priv->bpix);
@@ -207,20 +208,72 @@  int cursor_show(struct vidconsole_cursor *curs, struct video_priv *vid_priv,
 		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;
-
-	for (int row = 0; row < curs->height; row++) {
+	save_ptr = curs->save_data;
+	for (row = 0; row < curs->height; row++) {
 		dst = line;
 
 		for (int col = 0; col < VIDCONSOLE_CURSOR_WIDTH; col++)
-			fill_pixel_and_goto_next(&dst, value, pbytes, step);
-
+			*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;
 }
diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c
index 15b62d160cd..3621714c742 100644
--- a/drivers/video/vidconsole-uclass.c
+++ b/drivers/video/vidconsole-uclass.c
@@ -71,6 +71,9 @@  static int vidconsole_back(struct udevice *dev)
 			return ret;
 	}
 
+	/* Hide cursor at old position if it's visible */
+	vidconsole_hide_cursor(dev);
+
 	priv->xcur_frac -= VID_TO_POS(priv->x_charsize);
 	if (priv->xcur_frac < priv->xstart_frac) {
 		priv->xcur_frac = (priv->cols - 1) *
@@ -128,6 +131,9 @@  void vidconsole_set_cursor_pos(struct udevice *dev, int x, int y)
 {
 	struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
 
+	/* Hide cursor at old position if it's visible */
+	vidconsole_hide_cursor(dev);
+
 	priv->xcur_frac = VID_TO_POS(x);
 	priv->xstart_frac = priv->xcur_frac;
 	priv->ycur = y;
@@ -473,6 +479,9 @@  int vidconsole_put_char(struct udevice *dev, char ch)
 	struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
 	int cp, ret;
 
+	/* Hide cursor to avoid artifacts */
+	vidconsole_hide_cursor(dev);
+
 	if (priv->escape) {
 		vidconsole_escape_char(dev, ch);
 		return 0;
@@ -752,6 +761,33 @@  int vidconsole_show_cursor(struct udevice *dev)
 	return 0;
 }
 
+int vidconsole_hide_cursor(struct udevice *dev)
+{
+	struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+	struct vidconsole_cursor *curs = &priv->curs;
+	int ret;
+
+	if (!curs->visible)
+		return 0;
+
+	/* If the driver stored cursor line and height, use them for drawing */
+	if (curs->height) {
+		struct udevice *vid = dev_get_parent(dev);
+		struct video_priv *vid_priv = dev_get_uclass_priv(vid);
+
+		ret = cursor_hide(curs, vid_priv, NORMAL_DIRECTION);
+		if (ret)
+			return ret;
+
+		/* Update display damage for cursor area */
+		video_damage(vid, curs->x, curs->y, VIDCONSOLE_CURSOR_WIDTH,
+			     curs->height);
+	}
+
+	curs->visible = false;
+
+	return 0;
+}
 #endif /* CONFIG_CURSOR */
 
 int vidconsole_mark_start(struct udevice *dev)
diff --git a/drivers/video/vidconsole_internal.h b/drivers/video/vidconsole_internal.h
index 93f9c7b4e56..241be149ac9 100644
--- a/drivers/video/vidconsole_internal.h
+++ b/drivers/video/vidconsole_internal.h
@@ -112,7 +112,7 @@  int fill_char_horizontally(uchar *pfont, void **line, struct video_priv *vid_pri
 			   struct video_fontdata *fontdata, bool direction);
 
 /**
- * cursor_show() - Draw a simple vertical cursor
+ * cursor_show() - Show cursor by saving and drawing pixels
  *
  * @curs: cursor information
  * @vid_priv: video-device info
@@ -130,11 +130,22 @@  int fill_char_horizontally(uchar *pfont, void **line, struct video_priv *vid_pri
  *|---!!we're starting from upper left char corner|
  *|-----------------------------------------------|
  *
- * Return: 0, if success, or else error code.
+ * Return: 0 on success, -EINVAL if cursor data already saved
  */
 int cursor_show(struct vidconsole_cursor *curs, struct video_priv *vid_priv,
 		bool direction);
 
+/**
+ * cursor_hide() - Hide cursor by restoring saved pixels
+ *
+ * @curs: cursor information
+ * @vid_priv: video-device info
+ * @direction: controls cursor orientation (normal or flipped)
+ * Return: 0 if success, -EINVAL if no cursor data was saved
+ */
+int cursor_hide(struct vidconsole_cursor *curs, struct video_priv *vid_priv,
+		bool direction);
+
 /**
  * console_alloc_cursor() - Allocate cursor save buffer
  *
diff --git a/include/video_console.h b/include/video_console.h
index ffe331c5803..9005e9f4442 100644
--- a/include/video_console.h
+++ b/include/video_console.h
@@ -462,6 +462,16 @@  int vidconsole_entry_restore(struct udevice *dev, struct abuf *buf);
  */
 int vidconsole_show_cursor(struct udevice *dev);
 
+/**
+ * vidconsole_hide_cursor() - Hide the cursor
+ *
+ * Hides the cursor if it's currently visible
+ *
+ * @dev: Console device to use
+ * Return: 0 if OK, -ve on error
+ */
+int vidconsole_hide_cursor(struct udevice *dev);
+
 /**
  * vidconsole_readline_start() - Enable cursor for all video consoles
  *
@@ -485,6 +495,11 @@  static inline int vidconsole_show_cursor(struct udevice *dev)
 	return 0;
 }
 
+static inline int vidconsole_hide_cursor(struct udevice *dev)
+{
+	return 0;
+}
+
 static inline void vidconsole_readline_start(bool indent)
 {
 }
@@ -501,7 +516,6 @@  static inline void cli_index_adjust(struct vidconsole_priv *priv, int by)
 }
 
 /**
-
  * vidconsole_push_colour() - Temporarily change the font colour
  *
  * @dev:	Device to adjust