[PATCH 00/14] expo: Continue development of expo with mouse
From: Simon Glass <sjg@chromium.org> This series integrates mouse support into expo and starts to build the infrastructure for drawing the mouse pointer more efficiently (i.e. without re-rendering the entire scene). It also adds a few more structs to video_defs.h for position and size. This is part D. Simon Glass (14): video: Add struct vid_pos for coordinates mouse: Use struct vid_pos for click position mouse: Update mouse_get_click() to use struct vid_pos expo: Update poll_mouse() to use struct vid_pos video: Add struct vid_size for width/height mouse: Track last mouse position expo: Store mouse pointer and size in expo expo: Show the mouse when enabled expo: Drop struct scene_obj_bbox in favour of vid_bbox expo: Add damage tracking expo: Set dirty flag when an object bbox changes expo: Add a way to calculate the bbox of dirty objects expo: Add selective rendering for dirty objects in scene expo: Support drawing only the dirty portion of an expo boot/cedit.c | 12 ++- boot/expo.c | 141 ++++++++++++++++++++++++++++++++--- boot/scene.c | 79 +++++++++++++++++++- boot/scene_internal.h | 3 +- cmd/bootflow.c | 12 ++- drivers/input/mouse-uclass.c | 34 +++++++-- include/expo.h | 83 ++++++++++++++++----- include/mouse.h | 23 ++++-- include/video_defs.h | 23 ++++++ test/boot/cedit.c | 21 ++++-- test/dm/mouse.c | 29 ++++--- 11 files changed, 383 insertions(+), 77 deletions(-) -- 2.43.0 base-commit: a3435107db9ff9d6fe1af66859d36b24d0d8b094 branch: prod
From: Simon Glass <sjg@chromium.org> Add a simple struct to represent x,y coordinates. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- include/video_defs.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/video_defs.h b/include/video_defs.h index 3ff9e786abc..397764a5d3c 100644 --- a/include/video_defs.h +++ b/include/video_defs.h @@ -47,6 +47,18 @@ static inline bool vid_bbox_valid(const struct vid_bbox *bbox) { return bbox->x1 > bbox->x0 && bbox->y1 > bbox->y0; } + +/** + * struct vid_pos - Represents a position for video operations + * + * @x: X coordinate in pixels from the left + * @y: Y coordinate in pixels from the top + */ +struct vid_pos { + int x; + int y; +}; + #endif /* __ASSEMBLY__ */ #endif /* __VIDEO_DEFS_H */ -- 2.43.0
From: Simon Glass <sjg@chromium.org> Use the new vid_pos struct instead of separate x/y fields. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/input/mouse-uclass.c | 8 ++++---- include/mouse.h | 7 +++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/input/mouse-uclass.c b/drivers/input/mouse-uclass.c index 256642ef55e..0e5faccf1e2 100644 --- a/drivers/input/mouse-uclass.c +++ b/drivers/input/mouse-uclass.c @@ -44,8 +44,8 @@ int mouse_get_click(struct udevice *dev, int *xp, int *yp) if (uc_priv->left_button_state == BUTTON_PRESSED && new_state == BUTTON_RELEASED) { pending = true; - uc_priv->click_x = event.button.x; - uc_priv->click_y = event.button.y; + uc_priv->click_pos.x = event.button.x; + uc_priv->click_pos.y = event.button.y; } /* Update button state */ @@ -54,9 +54,9 @@ int mouse_get_click(struct udevice *dev, int *xp, int *yp) /* If we just detected a click, return it */ if (pending) { if (xp) - *xp = uc_priv->click_x; + *xp = uc_priv->click_pos.x; if (yp) - *yp = uc_priv->click_y; + *yp = uc_priv->click_pos.y; return 0; } diff --git a/include/mouse.h b/include/mouse.h index 0d20f8ffdbc..8e65ac79b94 100644 --- a/include/mouse.h +++ b/include/mouse.h @@ -9,6 +9,7 @@ #define _MOUSE_H #include <stdbool.h> +#include <video_defs.h> struct udevice; @@ -35,13 +36,11 @@ enum mouse_press_state_t { * struct mouse_uc_priv - private data for mouse uclass * * @left_button_state: Current state of left button (BUTTON_PRESSED/BUTTON_RELEASED) - * @click_x: X coordinate where the click occurred - * @click_y: Y coordinate where the click occurred + * @click_pos: Position where the click occurred */ struct mouse_uc_priv { enum mouse_press_state_t left_button_state; - int click_x; - int click_y; + struct vid_pos click_pos; }; /** -- 2.43.0
From: Simon Glass <sjg@chromium.org> Change the API to use struct vid_pos instead of separate x/y pointers. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/expo.c | 9 +++++---- drivers/input/mouse-uclass.c | 8 ++------ include/mouse.h | 5 ++--- test/dm/mouse.c | 29 ++++++++++++++--------------- 4 files changed, 23 insertions(+), 28 deletions(-) diff --git a/boot/expo.c b/boot/expo.c index 00924a19d27..1051133e200 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -413,18 +413,19 @@ static int poll_keys(struct expo *exp) static int poll_mouse(struct expo *exp, int *xp, int *yp) { - int ret, x, y; + struct vid_pos pos; + int ret; if (!exp->mouse_enabled) return -EAGAIN; /* First check if we have a click available */ - ret = mouse_get_click(exp->mouse, &x, &y); + ret = mouse_get_click(exp->mouse, &pos); if (ret) return log_msg_ret("epm", ret); - *xp = x; - *yp = y; + *xp = pos.x; + *yp = pos.y; return 0; /* Click available */ } diff --git a/drivers/input/mouse-uclass.c b/drivers/input/mouse-uclass.c index 0e5faccf1e2..1bb836ff59d 100644 --- a/drivers/input/mouse-uclass.c +++ b/drivers/input/mouse-uclass.c @@ -23,7 +23,7 @@ int mouse_get_event(struct udevice *dev, struct mouse_event *evt) return 0; } -int mouse_get_click(struct udevice *dev, int *xp, int *yp) +int mouse_get_click(struct udevice *dev, struct vid_pos *pos) { struct mouse_uc_priv *uc_priv = dev_get_uclass_priv(dev); struct mouse_event event; @@ -53,11 +53,7 @@ int mouse_get_click(struct udevice *dev, int *xp, int *yp) /* If we just detected a click, return it */ if (pending) { - if (xp) - *xp = uc_priv->click_pos.x; - if (yp) - *yp = uc_priv->click_pos.y; - + *pos = uc_priv->click_pos; return 0; } } diff --git a/include/mouse.h b/include/mouse.h index 8e65ac79b94..76f9c789b7b 100644 --- a/include/mouse.h +++ b/include/mouse.h @@ -95,10 +95,9 @@ int mouse_get_event(struct udevice *dev, struct mouse_event *event); * mouse_get_click() - Check if a left mouse button click has occurred * * @dev: Mouse device - * @xp: Returns X coordinate of click (can be NULL) - * @yp: Returns Y coordinate of click (can be NULL) + * @pos: Returns position of click * Returns: 0 if a click has occurred, -EAGAIN if no click pending */ -int mouse_get_click(struct udevice *dev, int *xp, int *py); +int mouse_get_click(struct udevice *dev, struct vid_pos *pos); #endif diff --git a/test/dm/mouse.c b/test/dm/mouse.c index 1b4c2b5f60f..3efff4a0d7d 100644 --- a/test/dm/mouse.c +++ b/test/dm/mouse.c @@ -99,7 +99,7 @@ static int dm_test_mouse_click(struct unit_test_state *uts) { struct udevice *dev; struct mouse_event inject; - int x, y; + struct vid_pos pos; ut_assertok(uclass_first_device_err(UCLASS_MOUSE, &dev)); @@ -107,7 +107,7 @@ static int dm_test_mouse_click(struct unit_test_state *uts) sandbox_mouse_set_test_mode(dev, true); /* test that no click is detected initially */ - ut_asserteq(-EAGAIN, mouse_get_click(dev, &x, &y)); + ut_asserteq(-EAGAIN, mouse_get_click(dev, &pos)); /* inject a left button press */ inject.type = MOUSE_EV_BUTTON; @@ -123,7 +123,7 @@ static int dm_test_mouse_click(struct unit_test_state *uts) * calling mouse_get_click() should not detect a click yet (press * only) */ - ut_asserteq(-EAGAIN, mouse_get_click(dev, &x, &y)); + ut_asserteq(-EAGAIN, mouse_get_click(dev, &pos)); /* inject a left button release */ inject.type = MOUSE_EV_BUTTON; @@ -136,12 +136,12 @@ static int dm_test_mouse_click(struct unit_test_state *uts) sandbox_mouse_inject(dev, &inject); /* now mouse_get_click() should detect the click */ - ut_assertok(mouse_get_click(dev, &x, &y)); - ut_asserteq(300, x); - ut_asserteq(400, y); + ut_assertok(mouse_get_click(dev, &pos)); + ut_asserteq(300, pos.x); + ut_asserteq(400, pos.y); /* verify no more clicks are pending */ - ut_asserteq(-EAGAIN, mouse_get_click(dev, &x, &y)); + ut_asserteq(-EAGAIN, mouse_get_click(dev, &pos)); return 0; } @@ -151,6 +151,7 @@ static int dm_test_mouse_click_no_coordinates(struct unit_test_state *uts) { struct udevice *dev; struct mouse_event inject; + struct vid_pos pos; ut_assertok(uclass_first_device_err(UCLASS_MOUSE, &dev)); @@ -167,15 +168,13 @@ static int dm_test_mouse_click_no_coordinates(struct unit_test_state *uts) sandbox_mouse_inject(dev, &inject); /* process the press event */ - ut_asserteq(-EAGAIN, mouse_get_click(dev, NULL, NULL)); + ut_asserteq(-EAGAIN, mouse_get_click(dev, &pos)); inject.button.press_state = BUTTON_RELEASED; sandbox_mouse_inject(dev, &inject); - /* - * now test that click is detected without coordinate return - */ - ut_assertok(mouse_get_click(dev, NULL, NULL)); + /* now test that click is detected (coordinates are ignored) */ + ut_assertok(mouse_get_click(dev, &pos)); return 0; } @@ -185,7 +184,7 @@ static int dm_test_mouse_right_button(struct unit_test_state *uts) { struct udevice *dev; struct mouse_event inject; - int x, y; + struct vid_pos pos; ut_assertok(uclass_first_device_err(UCLASS_MOUSE, &dev)); @@ -204,13 +203,13 @@ static int dm_test_mouse_right_button(struct unit_test_state *uts) inject.button.y = 200; sandbox_mouse_inject(dev, &inject); - ut_asserteq(-EAGAIN, mouse_get_click(dev, &x, &y)); + ut_asserteq(-EAGAIN, mouse_get_click(dev, &pos)); inject.button.press_state = BUTTON_RELEASED; sandbox_mouse_inject(dev, &inject); /* still no click detected since it was right button */ - ut_asserteq(-EAGAIN, mouse_get_click(dev, &x, &y)); + ut_asserteq(-EAGAIN, mouse_get_click(dev, &pos)); return 0; } -- 2.43.0
From: Simon Glass <sjg@chromium.org> Simplify the function to use struct vid_pos instead of separate x/y pointers. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/expo.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/boot/expo.c b/boot/expo.c index 1051133e200..b5c54291220 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -411,22 +411,18 @@ static int poll_keys(struct expo *exp) return key ? key : -EAGAIN; } -static int poll_mouse(struct expo *exp, int *xp, int *yp) +static int poll_mouse(struct expo *exp, struct vid_pos *pos) { - struct vid_pos pos; int ret; if (!exp->mouse_enabled) return -EAGAIN; /* First check if we have a click available */ - ret = mouse_get_click(exp->mouse, &pos); + ret = mouse_get_click(exp->mouse, pos); if (ret) return log_msg_ret("epm", ret); - *xp = pos.x; - *yp = pos.y; - return 0; /* Click available */ } @@ -438,11 +434,11 @@ int expo_poll(struct expo *exp, struct expo_action *act) if (key != -EAGAIN) { ret = expo_send_key(exp, key); } else if (IS_ENABLED(CONFIG_MOUSE)) { - int x, y; + struct vid_pos pos; - ret = poll_mouse(exp, &x, &y); + ret = poll_mouse(exp, &pos); if (!ret) - ret = expo_send_click(exp, x, y); + ret = expo_send_click(exp, pos.x, pos.y); } if (ret) return log_msg_ret("epk", ret); -- 2.43.0
From: Simon Glass <sjg@chromium.org> Add struct vid_size with w and h members to represent dimensions in video operations, complementing struct vid_pos for positions and struct vid_bbox for bounding boxes. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/video_defs.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/video_defs.h b/include/video_defs.h index 397764a5d3c..ba60765bb4a 100644 --- a/include/video_defs.h +++ b/include/video_defs.h @@ -59,6 +59,17 @@ struct vid_pos { int y; }; +/** + * struct vid_size - Represents a size for video operations + * + * @w: Width in pixels + * @h: Height in pixels + */ +struct vid_size { + int w; + int h; +}; + #endif /* __ASSEMBLY__ */ #endif /* __VIDEO_DEFS_H */ -- 2.43.0
From: Simon Glass <sjg@chromium.org> Add tracking of the last mouse position received, separate from the click position. This allows callers to query the current mouse position using mouse_get_pos() regardless of whether a click occurred. The position is updated in mouse_get_event() for both motion and button events, ensuring it always reflects the most recent mouse coordinates. This avoid the problem of mouse_get_click() 'swallowing' motion events so that a position change is not noticed, e.g. for showing a pointer. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/input/mouse-uclass.c | 22 ++++++++++++++++++++++ include/mouse.h | 11 +++++++++++ 2 files changed, 33 insertions(+) diff --git a/drivers/input/mouse-uclass.c b/drivers/input/mouse-uclass.c index 1bb836ff59d..ee983397ef3 100644 --- a/drivers/input/mouse-uclass.c +++ b/drivers/input/mouse-uclass.c @@ -10,6 +10,7 @@ int mouse_get_event(struct udevice *dev, struct mouse_event *evt) { + struct mouse_uc_priv *uc_priv = dev_get_uclass_priv(dev); struct mouse_ops *ops = mouse_get_ops(dev); int ret; @@ -20,6 +21,18 @@ int mouse_get_event(struct udevice *dev, struct mouse_event *evt) if (ret) return ret; + /* Update last position for motion events */ + if (evt->type == MOUSE_EV_MOTION) { + uc_priv->last_pos.x = evt->motion.x; + uc_priv->last_pos.y = evt->motion.y; + } + + /* Update last position for button events */ + if (evt->type == MOUSE_EV_BUTTON) { + uc_priv->last_pos.x = evt->button.x; + uc_priv->last_pos.y = evt->button.y; + } + return 0; } @@ -61,6 +74,15 @@ int mouse_get_click(struct udevice *dev, struct vid_pos *pos) return -EAGAIN; } +int mouse_get_pos(struct udevice *dev, struct vid_pos *pos) +{ + struct mouse_uc_priv *uc_priv = dev_get_uclass_priv(dev); + + *pos = uc_priv->last_pos; + + return 0; +} + UCLASS_DRIVER(mouse) = { .id = UCLASS_MOUSE, .name = "mouse", diff --git a/include/mouse.h b/include/mouse.h index 76f9c789b7b..7fe263b289f 100644 --- a/include/mouse.h +++ b/include/mouse.h @@ -37,10 +37,12 @@ enum mouse_press_state_t { * * @left_button_state: Current state of left button (BUTTON_PRESSED/BUTTON_RELEASED) * @click_pos: Position where the click occurred + * @last_pos: Last position received from mouse */ struct mouse_uc_priv { enum mouse_press_state_t left_button_state; struct vid_pos click_pos; + struct vid_pos last_pos; }; /** @@ -100,4 +102,13 @@ int mouse_get_event(struct udevice *dev, struct mouse_event *event); */ int mouse_get_click(struct udevice *dev, struct vid_pos *pos); +/** + * mouse_get_pos() - Get the current mouse position + * + * @dev: Mouse device + * @pos: Returns last position + * Returns: 0 if position is available, -ve on error + */ +int mouse_get_pos(struct udevice *dev, struct vid_pos *pos); + #endif -- 2.43.0
From: Simon Glass <sjg@chromium.org> Store the mouse pointer image and its dimensions in the expo structure when mouse support is enabled. This avoids repeatedly looking up the image and calculating its size. Use struct vid_size to store the mouse pointer dimensions. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/cedit.c | 12 +++++++++--- boot/expo.c | 23 ++++++++++++++++++++++- cmd/bootflow.c | 12 ++++++++++-- include/expo.h | 25 +++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 6 deletions(-) diff --git a/boot/cedit.c b/boot/cedit.c index ad2c98aa7a5..691780512ca 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -245,20 +245,26 @@ int cedit_run(struct expo *exp) expo_set_mouse_enable(exp, true); + expo_enter_mode(exp); + exp->done = false; exp->save = false; do { struct expo_action act; ret = expo_render(exp); - if (ret) + if (ret) { + expo_exit_mode(exp); return log_msg_ret("cer", ret); + } ret = expo_poll(exp, &act); - if (!ret) + if (!ret) { cedit_do_action(exp, scn, vid_priv, &act); - else if (ret != -EAGAIN) + } else if (ret != -EAGAIN) { + expo_exit_mode(exp); return log_msg_ret("cep", ret); + } } while (!exp->done); if (ret) diff --git a/boot/expo.c b/boot/expo.c index b5c54291220..42109b88e72 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -168,7 +168,7 @@ int expo_set_mouse_enable(struct expo *exp, bool enable) { int ret; - if (!enable) { + if (!IS_ENABLED(CONFIG_MOUSE) || !enable) { exp->mouse_enabled = false; return 0; } @@ -177,6 +177,17 @@ int expo_set_mouse_enable(struct expo *exp, bool enable) if (ret) return log_msg_ret("sme", ret); + /* Get mouse pointer image and dimensions */ + exp->mouse_ptr = video_image_getptr(riscos_arrow); + if (exp->mouse_ptr) { + ulong width, height; + uint bpix; + + video_bmp_get_info(exp->mouse_ptr, &width, &height, &bpix); + exp->mouse_size.w = width; + exp->mouse_size.h = height; + } + exp->mouse_enabled = true; return 0; @@ -456,3 +467,13 @@ void expo_req_size(struct expo *exp, int width, int height) exp->req_width = width; exp->req_height = height; } + +void expo_enter_mode(struct expo *exp) +{ + video_manual_sync(exp->display, true); +} + +void expo_exit_mode(struct expo *exp) +{ + video_manual_sync(exp->display, false); +} diff --git a/cmd/bootflow.c b/cmd/bootflow.c index c9f36a364dd..caff52fcc7c 100644 --- a/cmd/bootflow.c +++ b/cmd/bootflow.c @@ -55,19 +55,27 @@ __maybe_unused static int bootflow_handle_menu(struct bootstd_priv *std, if (ret) return log_msg_ret("bhs", ret); + expo_enter_mode(exp); + ret = -ERESTART; do { if (ret == -ERESTART) { ret = expo_arrange(exp); - if (ret) + if (ret) { + expo_exit_mode(exp); return log_msg_ret("bha", ret); + } ret = expo_render(exp); - if (ret) + if (ret) { + expo_exit_mode(exp); return log_msg_ret("bhr", ret); + } } ret = bootflow_menu_poll(exp, &seq); } while (ret == -EAGAIN || ret == -ERESTART || ret == -EREMCHG); + expo_exit_mode(exp); + if (ret == -EPIPE) { printf("Nothing chosen\n"); std->cur_bootflow = NULL; diff --git a/include/expo.h b/include/expo.h index e359da1343b..3250ecee40e 100644 --- a/include/expo.h +++ b/include/expo.h @@ -9,6 +9,7 @@ #include <abuf.h> #include <alist.h> +#include <video_defs.h> #include <dm/ofnode_decl.h> #include <linux/bitops.h> #include <linux/list.h> @@ -131,6 +132,8 @@ struct expo_theme { * @popup: true to use popup menus, instead of showing all items * @show_highlight: show a highlight bar on the selected menu item * @mouse_enabled: true if the mouse is enabled + * @mouse_ptr: Pointer to mouse pointer image data (BMP format) + * @mouse_size: Size of mouse pointer (width and height in pixels) * @priv: Private data for the controller * @done: Indicates that a cedit session is complete and the user has quit * @save: Indicates that cedit data should be saved, rather than discarded @@ -153,6 +156,8 @@ struct expo { bool popup; bool show_highlight; bool mouse_enabled; + const void *mouse_ptr; + struct vid_size mouse_size; void *priv; bool done; bool save; @@ -1185,4 +1190,24 @@ int expo_poll(struct expo *exp, struct expo_action *act); */ void expo_req_size(struct expo *exp, int width, int height); +/** + * expo_enter_mode() - Enter expo mode for the video subsystem + * + * @exp: Expo to update + * + * This suppresses automatic video sync operations to allow expo to control + * rendering timing. Should be called before starting the expo loop. + */ +void expo_enter_mode(struct expo *exp); + +/** + * expo_exit_mode() - Exit expo mode for the video subsystem + * + * @exp: Expo to update + * + * This restores normal video sync operations. Should be called after + * finishing the expo loop. + */ +void expo_exit_mode(struct expo *exp); + #endif /*__EXPO_H */ -- 2.43.0
From: Simon Glass <sjg@chromium.org> When the mouse is enabled, show it at its new position each time the expo is rendered. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/expo.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/expo.h | 2 ++ 2 files changed, 61 insertions(+) diff --git a/boot/expo.c b/boot/expo.c index 42109b88e72..295b779dce8 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -12,6 +12,7 @@ #include <expo.h> #include <log.h> #include <malloc.h> +#include <mapmem.h> #include <menu.h> #include <mouse.h> #include <video.h> @@ -249,6 +250,56 @@ int expo_arrange(struct expo *exp) return 0; } +static int update_mouse_position(struct expo *exp) +{ + struct mouse_event event; + int ret; + + if (!IS_ENABLED(CONFIG_MOUSE) || !exp->mouse_enabled) + return 0; + + /* Process all available mouse events to get latest position */ + while (1) { + ret = mouse_get_event(exp->mouse, &event); + if (ret) + break; /* No more events available */ + + if (event.type == MOUSE_EV_MOTION) { + exp->mouse_pos.x = event.motion.x; + exp->mouse_pos.y = event.motion.y; + } else if (event.type == MOUSE_EV_BUTTON) { + exp->mouse_pos.x = event.button.x; + exp->mouse_pos.y = event.button.y; + } + } + + return 0; +} + +/** + * render_mouse_pointer() - Render the mouse pointer if enabled and visible + * + * @exp: Expo containing mouse state + * Return: 0 if OK, -ve on error + */ +static int render_mouse_pointer(struct expo *exp) +{ + struct udevice *dev = exp->display; + int ret; + + if (!IS_ENABLED(CONFIG_MOUSE) || !exp->mouse_enabled || !exp->mouse_ptr) + return 0; + + /* Use white (0xffffff) as transparent color */ + ret = video_bmp_displaya(dev, map_to_sysmem(exp->mouse_ptr), + exp->mouse_pos.x, exp->mouse_pos.y, false, true, + 0xffffff); + if (ret) + log_debug("Failed to display mouse pointer: %d\n", ret); + + return 0; +} + int expo_render(struct expo *exp) { struct udevice *dev = exp->display; @@ -274,6 +325,11 @@ int expo_render(struct expo *exp) return log_msg_ret("ren", ret); } + /* Render mouse pointer if mouse is enabled */ + ret = render_mouse_pointer(exp); + if (ret) + return log_msg_ret("mou", ret); + video_sync(dev, true); return scn ? 0 : -ECHILD; @@ -441,6 +497,9 @@ int expo_poll(struct expo *exp, struct expo_action *act) { int key, ret = -EAGAIN; + /* update mouse position if mouse is enabled */ + update_mouse_position(exp); + key = poll_keys(exp); if (key != -EAGAIN) { ret = expo_send_key(exp, key); diff --git a/include/expo.h b/include/expo.h index 3250ecee40e..622088f395b 100644 --- a/include/expo.h +++ b/include/expo.h @@ -134,6 +134,7 @@ struct expo_theme { * @mouse_enabled: true if the mouse is enabled * @mouse_ptr: Pointer to mouse pointer image data (BMP format) * @mouse_size: Size of mouse pointer (width and height in pixels) + * @mouse_pos: Current mouse position * @priv: Private data for the controller * @done: Indicates that a cedit session is complete and the user has quit * @save: Indicates that cedit data should be saved, rather than discarded @@ -158,6 +159,7 @@ struct expo { bool mouse_enabled; const void *mouse_ptr; struct vid_size mouse_size; + struct vid_pos mouse_pos; void *priv; bool done; bool save; -- 2.43.0
From: Simon Glass <sjg@chromium.org> Replace the scene_obj_bbox with the common vid_bbox structure to avoid having the same structs with different names. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/scene.c | 4 ++-- include/expo.h | 19 ++----------------- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/boot/scene.c b/boot/scene.c index 96130f160fc..a451ee1d325 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -386,7 +386,7 @@ int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set) static void handle_alignment(enum scene_obj_align horiz, enum scene_obj_align vert, - struct scene_obj_bbox *bbox, + struct vid_bbox *bbox, struct scene_obj_dims *dims, int xsize, int ysize, struct scene_obj_offset *offset) @@ -555,7 +555,7 @@ static int scene_txt_render(struct expo *exp, struct udevice *dev, struct vidconsole_colour old; enum colour_idx fore, back; struct scene_obj_dims dims; - struct scene_obj_bbox bbox; + struct vid_bbox bbox; const char *str; int ret; diff --git a/include/expo.h b/include/expo.h index 622088f395b..a6e6b2b780b 100644 --- a/include/expo.h +++ b/include/expo.h @@ -234,21 +234,6 @@ enum scene_obj_t { SCENEOBJT_TEXTLINE, }; -/** - * struct scene_obj_bbox - Dimensions of an object - * - * @x0: x position, in pixels from left side - * @y0: y position, in pixels from top - * @x1: x position of right size - * @y1: y position of bottom - */ -struct scene_obj_bbox { - int x0; - int y0; - int x1; - int y1; -}; - /** * struct scene_obj_offset - Offsets for drawing the object * @@ -367,8 +352,8 @@ struct scene_obj { char *name; uint id; enum scene_obj_t type; - struct scene_obj_bbox req_bbox; - struct scene_obj_bbox bbox; + struct vid_bbox req_bbox; + struct vid_bbox bbox; struct scene_obj_offset ofs; struct scene_obj_dims dims; enum scene_obj_align horiz; -- 2.43.0
From: Simon Glass <sjg@chromium.org> As a few step towards making rendering more efficient, add support for tracking video damage within an expo. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/expo.c | 30 ++++++++++++++++++++++++++++++ include/expo.h | 23 +++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/boot/expo.c b/boot/expo.c index 295b779dce8..5cbe06b1c28 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -536,3 +536,33 @@ void expo_exit_mode(struct expo *exp) { video_manual_sync(exp->display, false); } + +void expo_damage_reset(struct expo *exp) +{ + exp->damage.x0 = 0; + exp->damage.y0 = 0; + exp->damage.x1 = 0; + exp->damage.y1 = 0; +} + +void expo_damage_add(struct expo *exp, const struct vid_bbox *bbox) +{ + /* If bbox is invalid (empty), do nothing */ + if (bbox->x1 <= bbox->x0 || bbox->y1 <= bbox->y0) + return; + + /* If current damage is empty, set it to the new bbox */ + if (exp->damage.x1 <= exp->damage.x0 || exp->damage.y1 <= exp->damage.y0) { + exp->damage = *bbox; + } else { + /* Expand damage area to include new bbox */ + if (bbox->x0 < exp->damage.x0) + exp->damage.x0 = bbox->x0; + if (bbox->y0 < exp->damage.y0) + exp->damage.y0 = bbox->y0; + if (bbox->x1 > exp->damage.x1) + exp->damage.x1 = bbox->x1; + if (bbox->y1 > exp->damage.y1) + exp->damage.y1 = bbox->y1; + } +} diff --git a/include/expo.h b/include/expo.h index a6e6b2b780b..487b58fb916 100644 --- a/include/expo.h +++ b/include/expo.h @@ -135,6 +135,7 @@ struct expo_theme { * @mouse_ptr: Pointer to mouse pointer image data (BMP format) * @mouse_size: Size of mouse pointer (width and height in pixels) * @mouse_pos: Current mouse position + * @damage: Bounding box of the area that needs to be redrawn * @priv: Private data for the controller * @done: Indicates that a cedit session is complete and the user has quit * @save: Indicates that cedit data should be saved, rather than discarded @@ -160,6 +161,7 @@ struct expo { const void *mouse_ptr; struct vid_size mouse_size; struct vid_pos mouse_pos; + struct vid_bbox damage; void *priv; bool done; bool save; @@ -1197,4 +1199,25 @@ void expo_enter_mode(struct expo *exp); */ void expo_exit_mode(struct expo *exp); +/** + * expo_damage_reset() - Reset the damage tracking area + * + * @exp: Expo to reset damage tracking for + * + * Clears the damage area, indicating that no part of the display needs + * to be redrawn. + */ +void expo_damage_reset(struct expo *exp); + +/** + * expo_damage_add() - Add a damaged area to the expo damage tracking + * + * @exp: Expo to add damage to + * @bbox: Bounding box of the damaged area to add + * + * Expands the current damage area to include the new damaged region. + * If there is no existing damage, the damage area is set to the new region. + */ +void expo_damage_add(struct expo *exp, const struct vid_bbox *bbox); + #endif /*__EXPO_H */ -- 2.43.0
From: Simon Glass <sjg@chromium.org> Add a flag to indicate that am object must be redrawn. Set this flag when an object's bounding box changes. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/scene.c | 7 +++++++ include/expo.h | 2 ++ test/boot/cedit.c | 21 +++++++++++++-------- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/boot/scene.c b/boot/scene.c index a451ee1d325..242f21bbacf 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -1300,6 +1300,7 @@ int scene_sync_bbox(struct scene *scn) list_for_each_entry(obj, &scn->obj_head, sibling) { int req_width = obj->req_bbox.x1 - obj->req_bbox.x0; int req_height = obj->req_bbox.y1 - obj->req_bbox.y0; + struct vid_bbox old_bbox = obj->bbox; if (obj->flags & SCENEOF_SYNC_POS) { if (obj->flags & SCENEOF_SIZE_VALID) { @@ -1321,6 +1322,12 @@ int scene_sync_bbox(struct scene *scn) obj->bbox.x1 = obj->bbox.x0 + req_width; if (obj->flags & SCENEOF_SYNC_BBOX) obj->bbox = obj->req_bbox; + + /* Set dirty flag if bbox changed */ + if (old_bbox.x0 != obj->bbox.x0 || old_bbox.y0 != obj->bbox.y0 || + old_bbox.x1 != obj->bbox.x1 || old_bbox.y1 != obj->bbox.y1) + obj->flags |= SCENEOF_DIRTY; + obj->flags &= ~(SCENEOF_SYNC_POS | SCENEOF_SYNC_SIZE | SCENEOF_SYNC_WIDTH | SCENEOF_SYNC_BBOX); } diff --git a/include/expo.h b/include/expo.h index 487b58fb916..fcc090d54f1 100644 --- a/include/expo.h +++ b/include/expo.h @@ -308,6 +308,7 @@ enum scene_obj_align { * @SCENEOF_SYNC_WIDTH: object's widget has changed * @SCENEOF_SYNC_BBOX: object's bounding box has changed * @SCENEOF_MANUAL: manually arrange the items associated with this object + * @SCENEOF_DIRTY: object has been modified and needs to be redrawn * @SCENEOF_LAST: used just as a check for the size of the flags mask */ enum scene_obj_flags_t { @@ -320,6 +321,7 @@ enum scene_obj_flags_t { SCENEOF_SYNC_WIDTH = BIT(6), SCENEOF_SYNC_BBOX = BIT(7), SCENEOF_MANUAL = BIT(8), + SCENEOF_DIRTY = BIT(9), SCENEOF_LAST, /* check for size of flags below */ }; diff --git a/test/boot/cedit.c b/test/boot/cedit.c index 80150a29d37..bccc93f8926 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -593,20 +593,22 @@ static int cedit_mouse(struct unit_test_state *uts) ut_assertok(click_check(uts, scn, item->label_id, EXPOACT_OPEN, &act)); ut_asserteq(ID_CPU_SPEED, act.select.id); ut_assertok(cedit_do_action(exp, scn, vid_priv, &act)); - ut_asserteq(SCENEOF_OPEN | SCENEOF_SIZE_VALID, speed->obj.flags); + ut_asserteq(SCENEOF_OPEN | SCENEOF_SIZE_VALID | SCENEOF_DIRTY, + speed->obj.flags); /* click outside the label to close the menu */ ut_assertok(scene_send_click(scn, 10, 10, &act)); ut_asserteq(EXPOACT_CLOSE, act.type); ut_asserteq(ID_CPU_SPEED, act.select.id); ut_assertok(cedit_do_action(exp, scn, vid_priv, &act)); - ut_asserteq(SCENEOF_SIZE_VALID, speed->obj.flags); + ut_asserteq(SCENEOF_SIZE_VALID | SCENEOF_DIRTY, speed->obj.flags); /* click on CPU speed to open it again */ ut_assertok(click_check(uts, scn, item->label_id, EXPOACT_OPEN, &act)); ut_asserteq(ID_CPU_SPEED, act.select.id); ut_assertok(cedit_do_action(exp, scn, vid_priv, &act)); - ut_asserteq(SCENEOF_OPEN | SCENEOF_SIZE_VALID, speed->obj.flags); + ut_asserteq(SCENEOF_OPEN | SCENEOF_SIZE_VALID | SCENEOF_DIRTY, + speed->obj.flags); /* click on the second item (1.5 GHz) */ item = scene_menuitem_find_seq(speed, 1); @@ -619,7 +621,7 @@ static int cedit_mouse(struct unit_test_state *uts) /* verify that the second item is now selected and menu is closed */ ut_asserteq(ID_CPU_SPEED_2, speed->cur_item_id); - ut_asserteq(SCENEOF_SIZE_VALID, speed->obj.flags); + ut_asserteq(SCENEOF_SIZE_VALID | SCENEOF_DIRTY, speed->obj.flags); ut_asserteq(ID_CPU_SPEED, scn->highlight_id); /* click on the power loss menu to open it */ @@ -629,7 +631,8 @@ static int cedit_mouse(struct unit_test_state *uts) &act)); ut_asserteq(ID_POWER_LOSS, act.select.id); ut_assertok(cedit_do_action(exp, scn, vid_priv, &act)); - ut_asserteq(SCENEOF_OPEN | SCENEOF_SIZE_VALID, loss->obj.flags); + ut_asserteq(SCENEOF_OPEN | SCENEOF_SIZE_VALID | SCENEOF_DIRTY, + loss->obj.flags); /* click on CPU speed to open it again */ item = scene_menuitem_find_seq(speed, 0); @@ -637,7 +640,8 @@ static int cedit_mouse(struct unit_test_state *uts) &act)); ut_asserteq(ID_CPU_SPEED, act.select.id); ut_assertok(cedit_do_action(exp, scn, vid_priv, &act)); - ut_asserteq(SCENEOF_OPEN | SCENEOF_SIZE_VALID, speed->obj.flags); + ut_asserteq(SCENEOF_OPEN | SCENEOF_SIZE_VALID | SCENEOF_DIRTY, + speed->obj.flags); /* click on the lineedit */ ut_assertok(click_check(uts, scn, mach->edit_id, @@ -647,8 +651,9 @@ static int cedit_mouse(struct unit_test_state *uts) ut_assertok(cedit_do_action(exp, scn, vid_priv, &act)); ut_asserteq(ID_CPU_SPEED_2, speed->cur_item_id); ut_asserteq(ID_MACHINE_NAME, scn->highlight_id); - ut_asserteq(SCENEOF_SIZE_VALID, loss->obj.flags); - ut_asserteq(SCENEOF_OPEN | SCENEOF_SIZE_VALID, mach->obj.flags); + ut_asserteq(SCENEOF_SIZE_VALID | SCENEOF_DIRTY, loss->obj.flags); + ut_asserteq(SCENEOF_OPEN | SCENEOF_SIZE_VALID | SCENEOF_DIRTY, + mach->obj.flags); return 0; } -- 2.43.0
From: Simon Glass <sjg@chromium.org> When rendering an expo we should normally only need to draw the objects which are marked dirty. Add a way to calculate the bounding box of these. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/scene.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/boot/scene.c b/boot/scene.c index 242f21bbacf..332d11fc751 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -895,12 +895,53 @@ int scene_render_deps(struct scene *scn, uint id) return 0; } +/** + * scene_get_dirty_bbox() - Get bounding box of all dirty objects in a scene + * + * @scn: Scene to scan + * @bbox: Returns bounding box of all dirty objects + * Return: 0 if dirty objects found, -ENOENT if no dirty objects + */ +static int scene_get_dirty_bbox(struct scene *scn, struct vid_bbox *bbox) +{ + struct scene_obj *obj; + bool found_dirty = false; + + list_for_each_entry(obj, &scn->obj_head, sibling) { + if (obj->flags & SCENEOF_DIRTY) { + if (!found_dirty) { + /* First dirty object - initialize bbox */ + *bbox = obj->bbox; + found_dirty = true; + } else { + /* Expand bbox to include this object */ + if (obj->bbox.x0 < bbox->x0) + bbox->x0 = obj->bbox.x0; + if (obj->bbox.y0 < bbox->y0) + bbox->y0 = obj->bbox.y0; + if (obj->bbox.x1 > bbox->x1) + bbox->x1 = obj->bbox.x1; + if (obj->bbox.y1 > bbox->y1) + bbox->y1 = obj->bbox.y1; + } + } + } + + return found_dirty ? 0 : -ENOENT; +} + int scene_render(struct scene *scn) { struct expo *exp = scn->expo; struct scene_obj *obj; + struct vid_bbox dirty_bbox; int ret; + /* Get bounding box of dirty objects and add to expo damage */ + ret = scene_get_dirty_bbox(scn, &dirty_bbox); + if (!ret) + expo_damage_add(exp, &dirty_bbox); + list_for_each_entry(obj, &scn->obj_head, sibling) { if (!(obj->flags & SCENEOF_HIDE)) { ret = scene_obj_render(obj, exp->text_mode); -- 2.43.0
From: Simon Glass <sjg@chromium.org> Support rendering only the dirty objects in a scene. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/expo.c | 2 +- boot/scene.c | 27 +++++++++++++++++++++++++-- boot/scene_internal.h | 3 ++- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/boot/expo.c b/boot/expo.c index 5cbe06b1c28..8423a304eb0 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -320,7 +320,7 @@ int expo_render(struct expo *exp) if (!scn) return log_msg_ret("scn", -ENOENT); - ret = scene_render(scn); + ret = scene_render(scn, false); if (ret) return log_msg_ret("ren", ret); } diff --git a/boot/scene.c b/boot/scene.c index 332d11fc751..ec0c5899bca 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -930,7 +930,21 @@ static int scene_get_dirty_bbox(struct scene *scn, struct vid_bbox *bbox) return found_dirty ? 0 : -ENOENT; } -int scene_render(struct scene *scn) +/** + * bbox_intersects() - Check if two bounding boxes intersect + * + * @bbox1: First bounding box + * @bbox2: Second bounding box + * Return: true if bounding boxes intersect, false otherwise + */ +static bool bbox_intersects(const struct vid_bbox *bbox1, + const struct vid_bbox *bbox2) +{ + return !(bbox1->x1 <= bbox2->x0 || bbox2->x1 <= bbox1->x0 || + bbox1->y1 <= bbox2->y0 || bbox2->y1 <= bbox1->y0); +} + +int scene_render(struct scene *scn, bool dirty_only) { struct expo *exp = scn->expo; struct scene_obj *obj; @@ -943,7 +957,16 @@ int scene_render(struct scene *scn) expo_damage_add(exp, &dirty_bbox); list_for_each_entry(obj, &scn->obj_head, sibling) { - if (!(obj->flags & SCENEOF_HIDE)) { + bool render = true; + + if (obj->flags & SCENEOF_HIDE) + continue; + + /* render objects that intersect with dirty bbox */ + if (dirty_only && !ret) + render = bbox_intersects(&obj->bbox, &dirty_bbox); + + if (render) { ret = scene_obj_render(obj, exp->text_mode); if (ret && ret != -ENOTSUPP) return log_msg_ret("ren", ret); diff --git a/boot/scene_internal.h b/boot/scene_internal.h index 0bc6f45cdcb..00696979f7d 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -237,9 +237,10 @@ void scene_destroy(struct scene *scn); * This is called from expo_render() * * @scn: Scene to render + * @dirty_only: If true, only render objects that intersect with dirty areas * Returns: 0 if OK, -ve on error */ -int scene_render(struct scene *scn); +int scene_render(struct scene *scn, bool dirty_only); /** * scene_send_key() - set a keypress to a scene -- 2.43.0
From: Simon Glass <sjg@chromium.org> To optimise drawing on the display, provide a new function which draws only the part of the display which is marked as dirty. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/expo.c | 14 ++++++++++++-- include/expo.h | 12 ++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/boot/expo.c b/boot/expo.c index 8423a304eb0..8ec301d4dcf 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -300,7 +300,7 @@ static int render_mouse_pointer(struct expo *exp) return 0; } -int expo_render(struct expo *exp) +static int expo_render_(struct expo *exp, bool dirty_only) { struct udevice *dev = exp->display; struct video_priv *vid_priv = dev_get_uclass_priv(dev); @@ -320,7 +320,7 @@ int expo_render(struct expo *exp) if (!scn) return log_msg_ret("scn", -ENOENT); - ret = scene_render(scn, false); + ret = scene_render(scn, dirty_only); if (ret) return log_msg_ret("ren", ret); } @@ -335,6 +335,16 @@ int expo_render(struct expo *exp) return scn ? 0 : -ECHILD; } +int expo_render(struct expo *exp) +{ + return expo_render_(exp, false); +} + +int expo_render_dirty(struct expo *exp) +{ + return expo_render_(exp, true); +} + int expo_send_key(struct expo *exp, int key) { struct scene *scn = NULL; diff --git a/include/expo.h b/include/expo.h index fcc090d54f1..8ad7415b5a4 100644 --- a/include/expo.h +++ b/include/expo.h @@ -668,6 +668,18 @@ int expo_first_scene_id(struct expo *exp); */ int expo_render(struct expo *exp); +/** + * expo_render_dirty() - render the dirty portion of expo on the display + * + * Only the objects within the damage bbox are rendered. The others are + * assumed to be up-to-date. + * + * @exp: Expo to render + * Return: 0 if OK, -ECHILD if there is no current scene, -ENOENT if the + * current scene is not found, other error if something else goes wrong + */ +int expo_render_dirty(struct expo *exp); + /** * expo_arrange() - Arrange the current scene to deal with object sizes * -- 2.43.0
participants (1)
- 
                
Simon Glass