[PATCH 17/19] expo: Separate requested bbox from actual

Simon Glass sjg at chromium.org
Mon May 5 17:42:55 CEST 2025


With fixed coordinates it is difficult to create scenes that can work on
any display size. Add a separate 'requested' bounding box for each
object and sync it when required.

With future work, this will allow scaling the nominal coordinates so
that an expo can be rendered correctly on a wider range of displays.

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

 boot/scene.c          | 74 +++++++++++++++++++++++++++++++++----------
 boot/scene_internal.h | 10 ++++++
 boot/scene_menu.c     | 13 ++++----
 boot/scene_textline.c | 10 +++---
 include/expo.h        | 25 ++++++++++++++-
 test/boot/expo.c      | 18 +++++------
 6 files changed, 111 insertions(+), 39 deletions(-)

diff --git a/boot/scene.c b/boot/scene.c
index 0d8599f955c..40ef3e0138c 100644
--- a/boot/scene.c
+++ b/boot/scene.c
@@ -253,12 +253,13 @@ int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
 	obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
 	if (!obj)
 		return log_msg_ret("find", -ENOENT);
-	w = obj->bbox.x1 - obj->bbox.x0;
-	h = obj->bbox.y1 - obj->bbox.y0;
-	obj->bbox.x0 = x;
-	obj->bbox.y0 = y;
-	obj->bbox.x1 = obj->bbox.x0 + w;
-	obj->bbox.y1 = obj->bbox.y0 + h;
+	w = obj->req_bbox.x1 - obj->req_bbox.x0;
+	h = obj->req_bbox.y1 - obj->req_bbox.y0;
+	obj->req_bbox.x0 = x;
+	obj->req_bbox.y0 = y;
+	obj->req_bbox.x1 = obj->req_bbox.x0 + w;
+	obj->req_bbox.y1 = obj->req_bbox.y0 + h;
+	obj->flags |= SCENEOF_SYNC_POS;
 
 	return 0;
 }
@@ -270,9 +271,10 @@ int scene_obj_set_size(struct scene *scn, uint id, int w, int h)
 	obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
 	if (!obj)
 		return log_msg_ret("find", -ENOENT);
-	obj->bbox.x1 = obj->bbox.x0 + w;
-	obj->bbox.y1 = obj->bbox.y0 + h;
-	obj->flags |= SCENEOF_SIZE_VALID;
+	log_debug("obj %s w %d h %d\n", obj->name, w, h);
+	obj->req_bbox.x1 = obj->req_bbox.x0 + w;
+	obj->req_bbox.y1 = obj->req_bbox.y0 + h;
+	obj->flags |= SCENEOF_SIZE_VALID | SCENEOF_SYNC_SIZE;
 
 	return 0;
 }
@@ -284,7 +286,8 @@ int scene_obj_set_width(struct scene *scn, uint id, int w)
 	obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
 	if (!obj)
 		return log_msg_ret("find", -ENOENT);
-	obj->bbox.x1 = obj->bbox.x0 + w;
+	obj->req_bbox.x1 = obj->req_bbox.x0 + w;
+	obj->flags |= SCENEOF_SYNC_WIDTH;
 
 	return 0;
 }
@@ -297,11 +300,11 @@ int scene_obj_set_bbox(struct scene *scn, uint id, int x0, int y0, int x1,
 	obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
 	if (!obj)
 		return log_msg_ret("find", -ENOENT);
-	obj->bbox.x0 = x0;
-	obj->bbox.y0 = y0;
-	obj->bbox.x1 = x1;
-	obj->bbox.y1 = y1;
-	obj->flags |= SCENEOF_SIZE_VALID;
+	obj->req_bbox.x0 = x0;
+	obj->req_bbox.y0 = y0;
+	obj->req_bbox.x1 = x1;
+	obj->req_bbox.y1 = y1;
+	obj->flags |= SCENEOF_SIZE_VALID | SCENEOF_SYNC_BBOX;
 
 	return 0;
 }
@@ -448,7 +451,8 @@ int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
 		}
 
 		limit = obj->flags & SCENEOF_SIZE_VALID ?
-			obj->bbox.x1 - obj->bbox.x0 : -1;
+			obj->req_bbox.x1 - obj->req_bbox.x0 : -1;
+		log_debug("obj %s limit %d\n", obj->name, limit);
 
 		ret = vidconsole_measure(scn->expo->cons, gen->font_name,
 					 gen->font_size, str, limit, &bbox,
@@ -819,6 +823,9 @@ int scene_arrange(struct scene *scn)
 		}
 		}
 	}
+	ret = scene_sync_bbox(scn);
+	if (ret)
+		return log_msg_ret("saf", ret);
 
 	return 0;
 }
@@ -1034,6 +1041,41 @@ int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox bbox[])
 	return 0;
 }
 
+int scene_sync_bbox(struct scene *scn)
+{
+	struct scene_obj *obj;
+
+	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;
+
+		if (obj->flags & SCENEOF_SYNC_POS) {
+			if (obj->flags & SCENEOF_SIZE_VALID) {
+				int width = obj->bbox.x1 - obj->bbox.x0;
+				int height = obj->bbox.y1 - obj->bbox.y0;
+
+				obj->bbox.x1 = obj->req_bbox.x0 + width;
+				obj->bbox.y1 = obj->req_bbox.y0 + height;
+			}
+			obj->bbox.x0 = obj->req_bbox.x0;
+			obj->bbox.y0 = obj->req_bbox.y0;
+		}
+
+		if (obj->flags & SCENEOF_SYNC_SIZE) {
+			obj->bbox.x1 = obj->bbox.x0 + req_width;
+			obj->bbox.y1 = obj->bbox.y0 + req_height;
+		}
+		if (obj->flags & SCENEOF_SYNC_WIDTH)
+			obj->bbox.x1 = obj->bbox.x0 + req_width;
+		if (obj->flags & SCENEOF_SYNC_BBOX)
+			obj->bbox = obj->req_bbox;
+		obj->flags &= ~(SCENEOF_SYNC_POS | SCENEOF_SYNC_SIZE |
+				SCENEOF_SYNC_WIDTH | SCENEOF_SYNC_BBOX);
+	}
+
+	return 0;
+}
+
 int scene_calc_dims(struct scene *scn)
 {
 	struct scene_obj *obj;
diff --git a/boot/scene_internal.h b/boot/scene_internal.h
index cca7c18e9f5..ba05e05d99a 100644
--- a/boot/scene_internal.h
+++ b/boot/scene_internal.h
@@ -112,6 +112,16 @@ int scene_obj_add(struct scene *scn, const char *name, uint id,
  */
 int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set);
 
+/**
+ * scene_sync_bbox() - Apply any requested changes to object positions
+ *
+ * Updates each object's bbox to match the req_bbox using the SCENEOF_SYNC_...
+ * flags
+ *
+ * @scn: Scene to update
+ */
+int scene_sync_bbox(struct scene *scn);
+
 /**
  * scene_calc_dims() - Calculate the dimensions of the scene objects
  *
diff --git a/boot/scene_menu.c b/boot/scene_menu.c
index b33e0a7d3b4..54c20d3e137 100644
--- a/boot/scene_menu.c
+++ b/boot/scene_menu.c
@@ -102,8 +102,8 @@ static int update_pointers(struct scene_obj_menu *menu, uint id, bool point)
 		label = scene_obj_find(scn, item->label_id, SCENEOBJT_NONE);
 
 		ret = scene_obj_set_pos(scn, menu->pointer_id,
-					menu->obj.bbox.x0 + menu->pointer_xofs,
-					label->bbox.y0);
+					menu->obj.req_bbox.x0 +
+					menu->pointer_xofs, label->req_bbox.y0);
 		if (ret < 0)
 			return log_msg_ret("ptr", ret);
 	}
@@ -238,8 +238,8 @@ int scene_menu_arrange(struct scene *scn, struct expo_arrange_info *arr,
 	memset(dims, '\0', sizeof(dims));
 	scene_menu_calc_dims(scn, menu, dims);
 
-	startx = menu->obj.bbox.x0;
-	y = menu->obj.bbox.y0;
+	startx = menu->obj.req_bbox.x0;
+	y = menu->obj.req_bbox.y0;
 	if (menu->title_id) {
 		int width;
 
@@ -358,9 +358,8 @@ int scene_menu_arrange(struct scene *scn, struct expo_arrange_info *arr,
 	if (sel_id)
 		menu_point_to_item(menu, sel_id);
 	menu->obj.dims.y = dims[SCENEBB_all].y;
-	menu->obj.bbox.x1 = menu->obj.bbox.x0 + menu->obj.dims.x;
-	menu->obj.bbox.y1 = menu->obj.bbox.y0 + menu->obj.dims.y;
-	menu->obj.flags |= SCENEOF_SIZE_VALID;
+	scene_obj_set_size(scn, menu->obj.id, menu->obj.dims.x,
+			   menu->obj.dims.y);
 
 	return 0;
 }
diff --git a/boot/scene_textline.c b/boot/scene_textline.c
index 07711d147c3..e55db98ec30 100644
--- a/boot/scene_textline.c
+++ b/boot/scene_textline.c
@@ -93,16 +93,14 @@ int scene_textline_arrange(struct scene *scn, struct expo_arrange_info *arr,
 	int x, y;
 	int ret;
 
-	x = tline->obj.bbox.x0;
-	y = tline->obj.bbox.y0;
+	x = tline->obj.req_bbox.x0;
+	y = tline->obj.req_bbox.y0;
 	if (tline->label_id) {
-		ret = scene_obj_set_pos(scn, tline->label_id,
-					tline->obj.bbox.x0, y);
+		ret = scene_obj_set_pos(scn, tline->label_id, x, y);
 		if (ret < 0)
 			return log_msg_ret("tit", ret);
 
-		ret = scene_obj_set_pos(scn, tline->edit_id,
-					tline->obj.bbox.x0 + 200, y);
+		ret = scene_obj_set_pos(scn, tline->edit_id, x + 200, y);
 		if (ret < 0)
 			return log_msg_ret("tit", ret);
 
diff --git a/include/expo.h b/include/expo.h
index 4a07a72cf7e..b67eb53b0c9 100644
--- a/include/expo.h
+++ b/include/expo.h
@@ -284,12 +284,20 @@ enum scene_obj_align {
  * can be selected)
  * @SCENEOF_SIZE_VALID: object's size (width/height) is valid, so any adjustment
  * to x0/y0 should maintain the width/height of the object
+ * @SCENEOF_SYNC_POS: object's position has changed
+ * @SCENEOF_SYNC_SIZE: object's size (width/height) has changed
+ * @SCENEOF_SYNC_WIDTH: object's widget has changed
+ * @SCENEOF_SYNC_BBOX: object's bounding box has changed
  */
 enum scene_obj_flags_t {
 	SCENEOF_HIDE	= 1 << 0,
 	SCENEOF_POINT	= 1 << 1,
 	SCENEOF_OPEN	= 1 << 2,
 	SCENEOF_SIZE_VALID	= BIT(3),
+	SCENEOF_SYNC_POS		= BIT(4),
+	SCENEOF_SYNC_SIZE	= BIT(5),
+	SCENEOF_SYNC_WIDTH	= BIT(6),
+	SCENEOF_SYNC_BBOX	= BIT(7),
 };
 
 enum {
@@ -304,7 +312,9 @@ enum {
  * @name: Name of the object (allocated)
  * @id: ID number of the object
  * @type: Type of this object
- * @bbox: Bounding box for this object
+ * @req_bbox: Requested bounding box for this object, synced to @bbox when scene is
+ * arranged
+ * @bbox: Bounding box for this object (internal use only)
  * @ofs: Offset from x0, y0 where the object is drawn (internal use only)
  * @dims: Dimensions of the text/image; may be smaller than bbox
  * (internal use only)
@@ -320,6 +330,7 @@ struct scene_obj {
 	char *name;
 	uint id;
 	enum scene_obj_t type;
+	struct scene_obj_bbox req_bbox;
 	struct scene_obj_bbox bbox;
 	struct scene_obj_offset ofs;
 	struct scene_obj_dims dims;
@@ -821,6 +832,9 @@ int scene_txted_set_font(struct scene *scn, uint id, const char *font_name,
 /**
  * scene_obj_set_pos() - Set the postion of an object
  *
+ * The given position is marked as 'requested' and will be applied when the
+ * scene is next arranged
+ *
  * @scn: Scene to update
  * @id: ID of object to update
  * @x: x position, in pixels from left side
@@ -832,6 +846,9 @@ int scene_obj_set_pos(struct scene *scn, uint id, int x, int y);
 /**
  * scene_obj_set_size() - Set the size of an object
  *
+ * The given size is marked as 'requested' and will be applied when the scene
+ * is next arranged
+ *
  * @scn: Scene to update
  * @id: ID of object to update
  * @w: width in pixels
@@ -843,6 +860,9 @@ int scene_obj_set_size(struct scene *scn, uint id, int w, int h);
 /**
  * scene_obj_set_width() - Set the width of an object
  *
+ * The given width is marked as 'requested' and will be applied when the scene
+ * is next arranged
+ *
  * @scn: Scene to update
  * @id: ID of object to update
  * @w: width in pixels
@@ -853,6 +873,9 @@ int scene_obj_set_width(struct scene *scn, uint id, int w);
 /**
  * scene_obj_set_bbox() - Set the bounding box of an object
  *
+ * The given bounding box is marked as 'requested' and will be applied when the
+ * scene is next arranged
+ *
  * @scn: Scene to update
  * @id: ID of object to update
  * @x0: x position, in pixels from left side
diff --git a/test/boot/expo.c b/test/boot/expo.c
index 517fb09d767..b97a04e1102 100644
--- a/test/boot/expo.c
+++ b/test/boot/expo.c
@@ -277,8 +277,8 @@ static int expo_object_attr(struct unit_test_state *uts)
 	ut_assert(id > 0);
 
 	ut_assertok(scene_obj_set_pos(scn, OBJ_LOGO, 123, 456));
-	ut_asserteq(123, img->obj.bbox.x0);
-	ut_asserteq(456, img->obj.bbox.y0);
+	ut_asserteq(123, img->obj.req_bbox.x0);
+	ut_asserteq(456, img->obj.req_bbox.y0);
 
 	ut_asserteq(-ENOENT, scene_obj_set_pos(scn, OBJ_TEXT2, 0, 0));
 
@@ -367,8 +367,8 @@ static int expo_object_menu(struct unit_test_state *uts)
 	ut_asserteq(0, menu->pointer_id);
 
 	ut_assertok(scene_obj_set_pos(scn, OBJ_MENU, 50, 400));
-	ut_asserteq(50, menu->obj.bbox.x0);
-	ut_asserteq(400, menu->obj.bbox.y0);
+	ut_asserteq(50, menu->obj.req_bbox.x0);
+	ut_asserteq(400, menu->obj.req_bbox.y0);
 
 	id = scene_txt_str(scn, "title", OBJ_MENU_TITLE, STR_MENU_TITLE,
 			   "Main Menu", &tit);
@@ -660,12 +660,12 @@ static int expo_render_image(struct unit_test_state *uts)
 	ut_asserteq(400 + 160, obj->bbox.y1);
 
 	scene_obj_set_width(scn, OBJ_MENU, 170);
-	ut_asserteq(50 + 170, obj->bbox.x1);
+	ut_asserteq(50 + 170, obj->req_bbox.x1);
 	scene_obj_set_bbox(scn, OBJ_MENU, 60, 410, 50 + 160, 400 + 160);
-	ut_asserteq(60, obj->bbox.x0);
-	ut_asserteq(410, obj->bbox.y0);
-	ut_asserteq(50 + 160, obj->bbox.x1);
-	ut_asserteq(400 + 160, obj->bbox.y1);
+	ut_asserteq(60, obj->req_bbox.x0);
+	ut_asserteq(410, obj->req_bbox.y0);
+	ut_asserteq(50 + 160, obj->req_bbox.x1);
+	ut_asserteq(400 + 160, obj->req_bbox.y1);
 
 	/* reset back to normal */
 	scene_obj_set_bbox(scn, OBJ_MENU, 50, 400, 50 + 160, 400 + 160);
-- 
2.43.0



More information about the U-Boot mailing list