[PATCH 00/18] expo: Extend the boot menu
From: Simon Glass <sjg@chromium.org> The current boot menu works but it is still fairly basic. This series makes various improvements: - adds buttons for settings, help - adds a way to show other items for each OS (e.g. distro version-name) - adds a second logo - allows a box to be drawn around each item This series also provides a way to dump the expo as a text file, so that it can be examined or compared with another one. This is useful for debugging. A 'cedit dump' command allows dumping the information. A few fixes are included as well: - for mouse operation, avoid dropping click events - for keyboard, fix support for escape sequences Simon Glass (18): test: video: Fix 16bpp BMP pixel format conversion test: Add a way to printf() into a membuf input: Correct handling of mouse clicks expo: Correct key-polling timeout expo: Provide user-friendly output of scene enums expo: Provide a way to dump an expo expo: Provide a command to dump a cedit expo expo: Provide a version of scene_within() with takes an obj expo: Export scene_find_obj_within() and add a test expo: Allow searching click positions from top to bottom expo: Allow searching for any object type by position expo: Support clicking on any type of object expo: Add another logo in the bootmenu expo: Add a way to select settings expo: Add a help button expo: Add a way to display the distro version-name expo: Add a box around each item expo: Add an image that indicates verification MAINTAINERS | 3 + boot/Kconfig | 9 ++ boot/Makefile | 1 + boot/bootflow_internal.h | 24 +++- boot/bootflow_menu.c | 3 + boot/cedit.c | 4 + boot/expo.c | 8 +- boot/expo_dump.c | 271 +++++++++++++++++++++++++++++++++++ boot/scene.c | 131 +++++++++++++---- boot/scene_internal.h | 30 ++++ boot/scene_menu.c | 3 + cmd/Kconfig | 9 ++ cmd/cedit.c | 38 +++++ doc/usage/cmd/cedit.rst | 141 ++++++++++++++++++ drivers/input/mouse-uclass.c | 7 +- include/bootflow.h | 3 +- include/expo.h | 22 +++ include/membuf.h | 13 ++ lib/membuf.c | 14 ++ test/boot/expo.c | 192 +++++++++++++++++++++++-- test/dm/video.c | 21 ++- test/lib/membuf.c | 40 ++++++ 22 files changed, 932 insertions(+), 55 deletions(-) create mode 100644 boot/expo_dump.c -- 2.43.0 base-commit: cfffa4bf16338ed559487fc52a07296e6e475e20 branch: prog
From: Simon Glass <sjg@chromium.org> The video_write_bmp() function was writing 16bpp framebuffer data directly to BMP files without proper format conversion. The framebuffer uses RGB565 format (5 red, 6 green, 5 blue), but standard Windows BMP 16bpp format uses RGB555 (5 red, 5 green, 5 blue). Convert pixels from RGB565 to RGB555 by: - Extracting the 5-bit red, 6-bit green, and 5-bit blue components - Dropping the LSB of the green channel to convert from 6 to 5 bits - Reconstructing as RGB555 with the same R/B order This fixes incorrect colors in BMP output files (e.g., orange appearing as blue). Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- test/dm/video.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/test/dm/video.c b/test/dm/video.c index b06facec6e1..e51ff7789c7 100644 --- a/test/dm/video.c +++ b/test/dm/video.c @@ -70,7 +70,7 @@ static int video_write_bmp(struct unit_test_state *uts, struct udevice *dev, void *bmp_data; int ret, y; - /* Support 16bpp and 32bpp */ + /* Support 16bpp (converted to 15bpp for BMP) and 32bpp */ switch (priv->bpix) { case VIDEO_BPP16: bpp = 16; @@ -112,7 +112,24 @@ static int video_write_bmp(struct unit_test_state *uts, struct udevice *dev, void *src = priv->fb + (height - 1 - y) * priv->line_length; void *dst = bmp_data + y * row_bytes; - memcpy(dst, src, width * bytes_per_pixel); + if (bpp == 16) { + /* Convert RGB565 to RGB555 for BMP format */ + u16 *src16 = (u16 *)src; + u16 *dst16 = (u16 *)dst; + int x; + + for (x = 0; x < width; x++) { + u16 pixel = src16[x]; + /* Extract RGB565 components */ + u16 r = (pixel >> 11) & 0x1f; /* 5 bits */ + u16 g = (pixel >> 5) & 0x3f; /* 6 bits */ + u16 b = pixel & 0x1f; /* 5 bits */ + /* Convert to RGB555: drop LSB of green */ + dst16[x] = (r << 10) | ((g >> 1) << 5) | b; + } + } else { + memcpy(dst, src, width * bytes_per_pixel); + } } ret = os_write_file(fname, bmp, bmp_size); -- 2.43.0
From: Simon Glass <sjg@chromium.org> Add a membuf_printf() function which supports writing a formatted string into a membuf. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- include/membuf.h | 13 +++++++++++++ lib/membuf.c | 14 ++++++++++++++ test/lib/membuf.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/include/membuf.h b/include/membuf.h index 3352faa0606..17fe721ee3c 100644 --- a/include/membuf.h +++ b/include/membuf.h @@ -268,4 +268,17 @@ int membuf_new(struct membuf *mb, int size); */ void membuf_dispose(struct membuf *mb); +/** + * membuf_printf() - write a formatted string to a membuff + * + * Formats a string and writes it to the membuff. Returns the number of bytes + * written (not including the terminating nul). + * + * @mb: membuff to write to + * @fmt: format string + * @...: arguments for format string + * Return: number of bytes written, or negative error + */ +int membuf_printf(struct membuf *mb, const char *fmt, ...); + #endif diff --git a/lib/membuf.c b/lib/membuf.c index 47a1b06664a..207dff5625b 100644 --- a/lib/membuf.c +++ b/lib/membuf.c @@ -9,6 +9,7 @@ #include <errno.h> #include <log.h> #include <malloc.h> +#include <vsprintf.h> #include "membuf.h" static inline bool is_full(const struct membuf *mb) @@ -435,3 +436,16 @@ void membuf_dispose(struct membuf *mb) free(mb->start); membuf_uninit(mb); } + +int membuf_printf(struct membuf *mb, const char *fmt, ...) +{ + char buf[256]; + va_list args; + int len; + + va_start(args, fmt); + len = vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + return membuf_put(mb, buf, len); +} diff --git a/test/lib/membuf.c b/test/lib/membuf.c index 2e7de9cdc57..7ae9c079a4c 100644 --- a/test/lib/membuf.c +++ b/test/lib/membuf.c @@ -261,3 +261,43 @@ static int lib_test_membuf_init(struct unit_test_state *uts) return 0; } LIB_TEST(lib_test_membuf_init, 0); + +/* test membuf_printf() */ +static int lib_test_membuf_printf(struct unit_test_state *uts) +{ + struct membuf mb; + int ret, exp_len; + char buf[100]; + char out[100]; + + /* Initialize membuf with a buffer */ + membuf_init(&mb, buf, sizeof(buf)); + + /* Test simple string */ + ret = membuf_printf(&mb, "Hello"); + ut_asserteq(5, ret); + ut_asserteq(5, membuf_get(&mb, out, sizeof(out))); + out[5] = '\0'; + ut_asserteq_str("Hello", out); + + /* Test formatted string with integers */ + exp_len = 9; + membuf_purge(&mb); + ret = membuf_printf(&mb, "Value: %d", 42); + ut_asserteq(exp_len, ret); + ut_asserteq(exp_len, membuf_get(&mb, out, sizeof(out))); + out[exp_len] = '\0'; + ut_asserteq_str("Value: 42", out); + + /* Test formatted string with multiple arguments */ + membuf_purge(&mb); + exp_len = 10; + ret = membuf_printf(&mb, "x=%d y=%d", 10, 200); + ut_asserteq(exp_len, ret); + ut_asserteq(exp_len, membuf_get(&mb, out, sizeof(out))); + out[exp_len] = '\0'; + ut_asserteq_str("x=10 y=200", out); + + return 0; +} +LIB_TEST(lib_test_membuf_printf, 0); -- 2.43.0
From: Simon Glass <sjg@chromium.org> It is possible that there is already a mouse click available, so mouse_get_click() should check that first, before reading any further events. Signed-off-by: Simon Glass <sjg@chromium.org> Fixes: 90e109789e3 ("mouse: Move click detection into mouse_get_event()") --- drivers/input/mouse-uclass.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/input/mouse-uclass.c b/drivers/input/mouse-uclass.c index 7cbe961af35..16a391532ae 100644 --- a/drivers/input/mouse-uclass.c +++ b/drivers/input/mouse-uclass.c @@ -57,15 +57,14 @@ int mouse_get_click(struct udevice *dev, struct vid_pos *pos) /* Process all available events until we find a click */ while (true) { - if (mouse_get_event(dev, &event)) - return -EAGAIN; /* No more events */ - - /* Check if this event resulted in a click */ if (uc_priv->click_pending) { *pos = uc_priv->click_pos; uc_priv->click_pending = false; break; } + + if (mouse_get_event(dev, &event)) + return -EAGAIN; /* No more events */ } return 0; -- 2.43.0
From: Simon Glass <sjg@chromium.org> When a character is received it should reset the timeout, even if the character does not result in output from cli_ch_process() Fix this, so that keypresses which consist of escape codes are correctly interpreted. Signed-off-by: Simon Glass <sjg@chromium.org> Fixes: a5c5b3b2fb6 ("expo: Speed up polling the keyboard") --- boot/expo.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/boot/expo.c b/boot/expo.c index 5704dd9fecc..1fb6d6e9417 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -486,17 +486,17 @@ int expo_iter_scene_objs(struct expo *exp, expo_scene_obj_iterator iter, static int poll_keys(struct expo *exp) { - int ichar, key; + int ch = 0, ichar, key; ichar = cli_ch_process(&exp->cch, 0); if (!ichar) { /* Check once for available input */ if (tstc()) { - ichar = getchar(); - ichar = cli_ch_process(&exp->cch, ichar); + ch = getchar(); + ichar = cli_ch_process(&exp->cch, ch); } - if (!ichar && get_timer(exp->last_key_ms) >= 10) + if (!ch && get_timer(exp->last_key_ms) >= 10) ichar = cli_ch_process(&exp->cch, -ETIMEDOUT); } -- 2.43.0
From: Simon Glass <sjg@chromium.org> Add a few functions which can convert a flag and an object type to strings. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/scene.c | 42 ++++++++++++++++++++++++++++++++++++++++++ boot/scene_internal.h | 16 ++++++++++++++++ test/boot/expo.c | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/boot/scene.c b/boot/scene.c index ec0c5899bca..c2e4d8e1330 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -19,6 +19,29 @@ #include <linux/input.h> #include "scene_internal.h" +static const char *const scene_flag_names[] = { + "hide", + "point", + "open", + "size_valid", + "sync_pos", + "sync_size", + "sync_width", + "sync_bbox", + "manual", + "dirty", +}; + +static const char *const scene_obj_type_names[] = { + "none", + "image", + "text", + "box", + "textedit", + "menu", + "textline", +}; + int scene_new(struct expo *exp, const char *name, uint id, struct scene **scnp) { struct scene *scn; @@ -1634,3 +1657,22 @@ int scene_dims_union(struct scene *scn, uint id, struct scene_obj_dims *dims) return 0; } + +const char *scene_flag_name(uint flag) +{ + int bit; + + bit = ffs(flag) - 1; + if (bit < 0 || bit >= ARRAY_SIZE(scene_flag_names)) + return "(none)"; + + return scene_flag_names[bit]; +} + +const char *scene_obj_type_name(enum scene_obj_t type) +{ + if (type >= ARRAY_SIZE(scene_obj_type_names)) + return "unknown"; + + return scene_obj_type_names[type]; +} diff --git a/boot/scene_internal.h b/boot/scene_internal.h index 00696979f7d..9bf2cb8f8f2 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -540,4 +540,20 @@ int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr); int scene_txt_generic_init(struct expo *exp, struct scene_txt_generic *gen, const char *name, uint str_id, const char *str); +/** + * scene_flag_name() - Get the name of a scene flag + * + * @flag: Single-bit flag mask (e.g. BIT(7)) + * Return: Flag name, or "(none)" if flag is 0 or out of range + */ +const char *scene_flag_name(uint flag); + +/** + * scene_obj_type_name() - Get the name of a scene object type + * + * @type: Object type + * Return: Type name, or "unknown" if out of range + */ +const char *scene_obj_type_name(enum scene_obj_t type); + #endif /* __SCENE_INTERNAL_H */ diff --git a/test/boot/expo.c b/test/boot/expo.c index 8a401ba9884..7c27da165c5 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -1220,3 +1220,41 @@ static int expo_test_calc_fps(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(expo_test_calc_fps, 0); + +/* Test scene_flag_name() */ +static int expo_scene_flag_name(struct unit_test_state *uts) +{ + /* Test valid flags */ + ut_asserteq_str("hide", scene_flag_name(SCENEOF_HIDE)); + ut_asserteq_str("point", scene_flag_name(SCENEOF_POINT)); + ut_asserteq_str("open", scene_flag_name(SCENEOF_OPEN)); + ut_asserteq_str("manual", scene_flag_name(SCENEOF_MANUAL)); + + /* Test invalid flag (0) */ + ut_asserteq_str("(none)", scene_flag_name(0)); + + /* Test invalid flag (out of range) */ + ut_asserteq_str("(none)", scene_flag_name(BIT(20))); + + return 0; +} +BOOTSTD_TEST(expo_scene_flag_name, 0); + +/* Test scene_obj_type_name() */ +static int expo_scene_obj_type_name(struct unit_test_state *uts) +{ + /* Test all valid object types */ + ut_asserteq_str("none", scene_obj_type_name(SCENEOBJT_NONE)); + ut_asserteq_str("image", scene_obj_type_name(SCENEOBJT_IMAGE)); + ut_asserteq_str("text", scene_obj_type_name(SCENEOBJT_TEXT)); + ut_asserteq_str("box", scene_obj_type_name(SCENEOBJT_BOX)); + ut_asserteq_str("menu", scene_obj_type_name(SCENEOBJT_MENU)); + ut_asserteq_str("textline", scene_obj_type_name(SCENEOBJT_TEXTLINE)); + ut_asserteq_str("textedit", scene_obj_type_name(SCENEOBJT_TEXTEDIT)); + + /* Test invalid type (out of range) */ + ut_asserteq_str("unknown", scene_obj_type_name(SCENEOBJT_TEXTLINE + 1)); + + return 0; +} +BOOTSTD_TEST(expo_scene_obj_type_name, 0); -- 2.43.0
From: Simon Glass <sjg@chromium.org> For debugging it is sometimes helpful to dump an expo. Add an implementation of this, writing to a membuf. Add a MAINTAINERS entry for expo, including this next file. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- MAINTAINERS | 3 + boot/Kconfig | 9 ++ boot/Makefile | 1 + boot/expo_dump.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++ include/expo.h | 18 ++++ test/boot/expo.c | 46 ++++++++ 6 files changed, 348 insertions(+) create mode 100644 boot/expo_dump.c diff --git a/MAINTAINERS b/MAINTAINERS index e858331455c..2a2a42aec5e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -944,6 +944,8 @@ F: boot/bootflow.c F: boot/bootm_final.c F: boot/bootmeth*.c F: boot/bootstd.c +F: boot/expo*.c +F: boot/scene*.c F: cmd/bootdev.c F: cmd/bootflow.c F: doc/develop/bootstd/ @@ -955,6 +957,7 @@ F: include/bootdev.h F: include/bootflow.h F: include/bootmeth.h F: include/bootstd.h +F: include/expo.h F: net/eth_bootdevice.c F: test/boot/ diff --git a/boot/Kconfig b/boot/Kconfig index fb34a10106b..b255245335b 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -982,6 +982,15 @@ config EXPO The expo can be presented in graphics form using a vidconsole, or in text form on a serial console. +config EXPO_DUMP + bool "Allow dumping the contents of an expo" + depends on EXPO + default y if SANDBOX + help + This provides a way to dump an expo as test, including most of the + details in the structures. This can be useful when debugging or + comparing two expos. + config EXPO_TEST bool "Enable test mode for expo" depends on EXPO diff --git a/boot/Makefile b/boot/Makefile index bb1888f1656..3cb138ae022 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_CMD_ADTIMG) += image-android-dt.o obj-$(CONFIG_$(PHASE_)LOAD_FIT) += common_fit.o obj-$(CONFIG_$(PHASE_)EXPO) += expo.o scene.o expo_build.o +obj-$(CONFIG_$(PHASE_)EXPO_DUMP) += expo_dump.o obj-$(CONFIG_$(PHASE_)EXPO) += scene_menu.o scene_textline.o scene_textedit.o obj-$(CONFIG_$(PHASE_)EXPO_TEST) += expo_test.o ifdef CONFIG_COREBOOT_SYSINFO diff --git a/boot/expo_dump.c b/boot/expo_dump.c new file mode 100644 index 00000000000..711b26eb220 --- /dev/null +++ b/boot/expo_dump.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Dump functions for expo objects + * + * Copyright 2025 Canonical Ltd + * Written by Simon Glass <simon.glass@canonical.com> + */ + +#define LOG_CATEGORY LOGC_EXPO + +#include <dm.h> +#include <expo.h> +#include <mapmem.h> +#include <membuf.h> +#include <video.h> +#include "scene_internal.h" + +/** + * struct dump_ctx - Context for dumping expo structures + * + * @mb: Membuf to write output to + * @scn: Current scene being dumped (or NULL if not in a scene) + * @indent: Current indentation level (number of spaces) + */ +struct dump_ctx { + struct membuf *mb; + struct scene *scn; + int indent; +}; + +/** + * outf() - Output a formatted string with indentation + * + * @ctx: Dump context containing membuf, scene, and indent level + * @fmt: Format string + * @...: Arguments for format string + */ +static void outf(struct dump_ctx *ctx, const char *fmt, ...) +{ + char buf[256]; + va_list args; + int len; + + va_start(args, fmt); + membuf_printf(ctx->mb, "%*s", ctx->indent, ""); + len = vsnprintf(buf, sizeof(buf), fmt, args); + membuf_put(ctx->mb, buf, len); + va_end(args); +} + +static const char *obj_name(struct dump_ctx *ctx, uint id) +{ + struct scene_obj *obj; + + if (!id) + return "(none)"; + + obj = scene_obj_find(ctx->scn, id, SCENEOBJT_NONE); + if (!obj) + return "(not found)"; + + return obj->name; +} + +static void dump_menu(struct dump_ctx *ctx, struct scene_obj_menu *menu) +{ + struct scene_obj *obj = &menu->obj; + struct scene_menitem *item; + + outf(ctx, "Menu: pointer_id %d title_id %d manual %d\n", + menu->pointer_id, menu->title_id, + !!(obj->flags & SCENEOF_MANUAL)); + + ctx->indent += 2; + list_for_each_entry(item, &menu->item_head, sibling) { + outf(ctx, "Item %d: name '%s' label_id %d desc_id %d\n", + item->id, item->name, item->label_id, item->desc_id); + } + ctx->indent -= 2; +} + +static void dump_text(struct dump_ctx *ctx, struct scene_obj_txt *txt) +{ + const char *str = expo_get_str(ctx->scn->expo, txt->gen.str_id); + + outf(ctx, "Text: str_id %d font_name '%s' font_size %d\n", + txt->gen.str_id, + txt->gen.font_name ? txt->gen.font_name : "(default)", + txt->gen.font_size); + ctx->indent += 2; + outf(ctx, "str '%s'\n", str ? str : "(null)"); + ctx->indent -= 2; +} + +static void dump_box(struct dump_ctx *ctx, struct scene_obj_box *box) +{ + outf(ctx, "Box: fill %d width %d\n", box->fill, box->width); +} + +static void dump_image(struct dump_ctx *ctx, struct scene_obj_img *img) +{ + outf(ctx, "Image: data %lx\n", (ulong)map_to_sysmem(img->data)); +} + +static void dump_textline(struct dump_ctx *ctx, + struct scene_obj_textline *tline) +{ + outf(ctx, "Textline: label_id %d edit_id %d\n", + tline->label_id, tline->edit_id); + ctx->indent += 2; + outf(ctx, "max_chars %d pos %d\n", tline->max_chars, tline->pos); + ctx->indent -= 2; +} + +static void dump_textedit(struct dump_ctx *ctx, + struct scene_obj_txtedit *tedit) +{ + outf(ctx, "Textedit: str_id %d font_name '%s' font_size %d\n", + tedit->gen.str_id, + tedit->gen.font_name ? tedit->gen.font_name : "(default)", + tedit->gen.font_size); +} + +static void obj_dump_(struct dump_ctx *ctx, struct scene_obj *obj) +{ + char flags_buf[256]; + bool first = true; + int bit; + int pos = 0; + + outf(ctx, "Object %d (%s): type %s\n", obj->id, obj->name, + scene_obj_type_name(obj->type)); + ctx->indent += 2; + + /* Build flags string */ + for (bit = 0; bit < 16; bit++) { + uint flag = BIT(bit); + + if (obj->flags & flag) { + pos += snprintf(flags_buf + pos, sizeof(flags_buf) - pos, + "%s%s", first ? "" : ", ", + scene_flag_name(flag)); + first = false; + } + } + outf(ctx, "flags %s\n", pos > 0 ? flags_buf : ""); + outf(ctx, "bbox: (%d,%d)-(%d,%d)\n", + obj->bbox.x0, obj->bbox.y0, obj->bbox.x1, obj->bbox.y1); + outf(ctx, "dims: %dx%d\n", obj->dims.x, obj->dims.y); + + switch (obj->type) { + case SCENEOBJT_NONE: + break; + case SCENEOBJT_IMAGE: + dump_image(ctx, (struct scene_obj_img *)obj); + break; + case SCENEOBJT_TEXT: + dump_text(ctx, (struct scene_obj_txt *)obj); + break; + case SCENEOBJT_BOX: + dump_box(ctx, (struct scene_obj_box *)obj); + break; + case SCENEOBJT_MENU: + dump_menu(ctx, (struct scene_obj_menu *)obj); + break; + case SCENEOBJT_TEXTLINE: + dump_textline(ctx, (struct scene_obj_textline *)obj); + break; + case SCENEOBJT_TEXTEDIT: + dump_textedit(ctx, (struct scene_obj_txtedit *)obj); + break; + } + ctx->indent -= 2; +} + +static void scene_dump_(struct dump_ctx *ctx) +{ + struct scene_obj *obj; + + outf(ctx, "Scene %d: name '%s'\n", ctx->scn->id, ctx->scn->name); + ctx->indent += 2; + outf(ctx, "title_id %d (%s)\n", + ctx->scn->title_id, obj_name(ctx, ctx->scn->title_id)); + outf(ctx, "highlight_id %d (%s)\n", + ctx->scn->highlight_id, obj_name(ctx, ctx->scn->highlight_id)); + + list_for_each_entry(obj, &ctx->scn->obj_head, sibling) { + /* Skip hidden objects */ + if (obj->flags & SCENEOF_HIDE) + continue; + obj_dump_(ctx, obj); + } + ctx->indent -= 2; +} + +void scene_dump(struct membuf *mb, struct scene *scn, int indent) +{ + struct dump_ctx ctx; + + ctx.mb = mb; + ctx.scn = scn; + ctx.indent = indent; + + scene_dump_(&ctx); +} + +static void expo_dump_(struct dump_ctx *ctx, struct expo *exp) +{ + struct scene *scn; + struct expo_theme *theme = &exp->theme; + + outf(ctx, "Expo: name '%s'\n", exp->name); + ctx->indent = 2; + outf(ctx, "display %s\n", + exp->display ? exp->display->name : "(null)"); + outf(ctx, "cons %s\n", exp->cons ? exp->cons->name : "(none)"); + outf(ctx, "mouse %s\n", exp->mouse ? exp->mouse->name : "(none)"); + outf(ctx, "scene_id %d\n", exp->scene_id); + outf(ctx, "next_id %d\n", exp->next_id); + outf(ctx, "req_width %d\n", exp->req_width); + outf(ctx, "req_height %d\n", exp->req_height); + outf(ctx, "text_mode %d\n", exp->text_mode); + outf(ctx, "popup %d\n", exp->popup); + outf(ctx, "show_highlight %d\n", exp->show_highlight); + outf(ctx, "mouse_enabled %d\n", exp->mouse_enabled); + outf(ctx, "mouse_ptr %p\n", exp->mouse_ptr); + outf(ctx, "mouse_size %dx%d\n", exp->mouse_size.w, + exp->mouse_size.h); + outf(ctx, "mouse_pos (%d,%d)\n", exp->mouse_pos.x, + exp->mouse_pos.y); + outf(ctx, "damage (%d,%d)-(%d,%d)\n", exp->damage.x0, exp->damage.y0, + exp->damage.x1, exp->damage.y1); + outf(ctx, "done %d\n", exp->done); + outf(ctx, "save %d\n", exp->save); + outf(ctx, "last_key_ms %ld\n", exp->last_key_ms); + + if (exp->display) { + struct video_priv *vid_priv = dev_get_uclass_priv(exp->display); + + outf(ctx, "video: %dx%d white_on_black %d\n", + vid_priv->xsize, vid_priv->ysize, + vid_priv->white_on_black); + } + + outf(ctx, "Theme:\n"); + ctx->indent = 4; + outf(ctx, "font_size %d\n", theme->font_size); + outf(ctx, "white_on_black %d\n", theme->white_on_black); + outf(ctx, "menu_inset %d\n", theme->menu_inset); + outf(ctx, "menuitem_gap_y %d\n", theme->menuitem_gap_y); + + ctx->indent = 0; + outf(ctx, "\nScenes:\n"); + ctx->indent = 2; + list_for_each_entry(scn, &exp->scene_head, sibling) { + ctx->scn = scn; + scene_dump_(ctx); + outf(ctx, "\n"); + } +} + +void expo_dump(struct expo *exp, struct membuf *mb) +{ + struct dump_ctx ctx; + + ctx.mb = mb; + ctx.scn = NULL; + ctx.indent = 0; + + expo_dump_(&ctx, exp); +} diff --git a/include/expo.h b/include/expo.h index b51d946f367..619af627840 100644 --- a/include/expo.h +++ b/include/expo.h @@ -14,6 +14,7 @@ #include <linux/bitops.h> #include <linux/list.h> +struct membuf; struct udevice; #include <cli.h> @@ -1240,4 +1241,21 @@ void expo_damage_reset(struct expo *exp); */ void expo_damage_add(struct expo *exp, const struct vid_bbox *bbox); +/** + * expo_dump() - Dump expo structure to a membuf + * + * @mb: membuf to write to + * @exp: Expo to dump + */ +void expo_dump(struct expo *exp, struct membuf *mb); + +/** + * scene_dump() - Dump scene structure to a membuf + * + * @mb: membuf to write to + * @scn: Scene to dump + * @indent: Indentation level + */ +void scene_dump(struct membuf *mb, struct scene *scn, int indent); + #endif /*__EXPO_H */ diff --git a/test/boot/expo.c b/test/boot/expo.c index 7c27da165c5..e8716b86991 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -8,6 +8,7 @@ #include <dm.h> #include <expo.h> #include <expo_test.h> +#include <membuf.h> #include <menu.h> #include <video.h> #include <linux/input.h> @@ -1258,3 +1259,48 @@ static int expo_scene_obj_type_name(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(expo_scene_obj_type_name, 0); + +/* Test expo_dump() */ +static int expo_dump_test(struct unit_test_state *uts) +{ + struct scene_obj_menu *menu; + struct abuf buf, logo_copy; + struct scene *scn; + struct expo *exp; + struct membuf mb; + char mb_buf[4096]; + char *data; + int len; + + membuf_init(&mb, mb_buf, sizeof(mb_buf)); + + ut_assertok(create_test_expo(uts, &exp, &scn, &menu, &buf, &logo_copy)); + + /* Arrange the scene so objects have proper dimensions */ + ut_assertok(scene_arrange(scn)); + + /* Dump the expo */ + expo_dump(exp, &mb); + + /* Get the dumped data */ + len = membuf_getraw(&mb, sizeof(mb_buf), false, &data); + ut_assert(len > 0); + ut_assertnonnull(data); + + /* Nul-terminate for strstr to work */ + if (len < sizeof(mb_buf)) + data[len] = '\0'; + + /* Check for a few elements in the output */ + ut_assert(strstr(data, "Expo: name")); + ut_assert(strstr(data, "my menus")); + ut_assert(strstr(data, "Scene")); + ut_assert(strstr(data, "main")); + + abuf_uninit(&buf); + abuf_uninit(&logo_copy); + expo_destroy(exp); + + return 0; +} +BOOTSTD_TEST(expo_dump_test, UTF_DM | UTF_SCAN_FDT); -- 2.43.0
From: Simon Glass <sjg@chromium.org> Add a new 'cedit dump' command which dumps the contents of an expo for debugging. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- cmd/Kconfig | 9 +++ cmd/cedit.c | 38 +++++++++++ doc/usage/cmd/cedit.rst | 141 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+) diff --git a/cmd/Kconfig b/cmd/Kconfig index 0c6d1c4b185..224d5a83987 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -485,6 +485,15 @@ config CMD_CEDIT providing a UI for the user to adjust settings. Subcommands allow loading and saving of configuration as well as showing an editor. +config CMD_CEDIT_DUMP + bool "Allow dumping the contents of a cedit expo" + depends on CMD_CEDIT && EXPO_DUMP + default y + help + Provides a 'cedit dump' command to dump the expo in a human-readable + format. This can be useful for debugging or for checking that the + expo contains the expected objects. + config CMD_ELF bool "bootelf" default y diff --git a/cmd/cedit.c b/cmd/cedit.c index 6db5806e851..e446f61b3ba 100644 --- a/cmd/cedit.c +++ b/cmd/cedit.c @@ -14,6 +14,7 @@ #include <fs_legacy.h> #include <malloc.h> #include <mapmem.h> +#include <membuf.h> #include <dm/ofnode.h> #include <linux/sizes.h> @@ -291,6 +292,37 @@ static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +#ifdef CONFIG_CMD_EDIT_DUMP +static int do_cedit_dump(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct membuf mb; + char buf[256]; + int len; + + if (check_cur_expo()) + return CMD_RET_FAILURE; + + if (membuf_new(&mb, 131072)) { + printf("Failed to allocate membuf\n"); + return CMD_RET_FAILURE; + } + + expo_dump(cur_exp, &mb); + + /* Output the data in chunks */ + do { + len = membuf_get(&mb, buf, sizeof(buf) - 1); + buf[len] = '\0'; + puts(buf); + } while (len); + + membuf_dispose(&mb); + + return 0; +} +#endif /* CONFIG_CMD_EDIT_DUMP */ + U_BOOT_LONGHELP(cedit, "load <interface> <dev[:part]> <filename> - load config editor\n" #ifdef CONFIG_COREBOOT_SYSINFO @@ -302,6 +334,9 @@ U_BOOT_LONGHELP(cedit, "cedit write_env [-v] - write settings to env vars\n" "cedit read_cmos [-v] [dev] - read settings from CMOS RAM\n" "cedit write_cmos [-v] [dev] - write settings to CMOS RAM\n" +#ifdef CONFIG_CMD_EDIT_DUMP + "cedit dump - dump expo structure\n" +#endif "cedit run - run config editor"); U_BOOT_CMD_WITH_SUBCMDS(cedit, "Configuration editor", cedit_help_text, @@ -315,5 +350,8 @@ U_BOOT_CMD_WITH_SUBCMDS(cedit, "Configuration editor", cedit_help_text, U_BOOT_SUBCMD_MKENT(write_env, 2, 1, do_cedit_write_env), U_BOOT_SUBCMD_MKENT(read_cmos, 2, 1, do_cedit_read_cmos), U_BOOT_SUBCMD_MKENT(write_cmos, 2, 1, do_cedit_write_cmos), +#ifdef CONFIG_CMD_EDIT_DUMP + U_BOOT_SUBCMD_MKENT(dump, 1, 1, do_cedit_dump), +#endif U_BOOT_SUBCMD_MKENT(run, 1, 1, do_cedit_run), ); diff --git a/doc/usage/cmd/cedit.rst b/doc/usage/cmd/cedit.rst index e54ea204b9f..0eb8f09dc2c 100644 --- a/doc/usage/cmd/cedit.rst +++ b/doc/usage/cmd/cedit.rst @@ -19,6 +19,7 @@ Synopsis cedit read_env [-v] cedit write_cmos [-v] [dev] cedit cb_load + cedit dump Description ----------- @@ -101,6 +102,20 @@ cedit cb_load This is supported only on x86 devices booted from coreboot. It creates a new configuration editor which can be used to edit CMOS settings. +cedit dump +~~~~~~~~~~ + +Dumps the current expo structure to the console. This shows the expo, its +scenes, objects, and their properties in a human-readable format. This is useful +for debugging and understanding the structure of the configuration editor. + +Configuration +------------- + +The cedit command is only available if CONFIG_CMD_CEDIT=y. + +The 'cedit dump' subcommand is only available if CONFIG_CMD_CEDIT_DUMP=y. + Example ------- @@ -234,3 +249,129 @@ Update the checksum in CMOS RAM:: Checksum 7100 written => cbcmos check => + +This shows dumping the cedit:: + + Expo: name 'name' + display (null) + cons (none) + mouse (none) + scene_id 0 + next_id 42 + req_width 0 + req_height 0 + text_mode 0 + popup 0 + show_highlight 0 + mouse_enabled 0 + mouse_ptr 0000000000000000 + mouse_size 0x0 + mouse_pos (0,0) + damage (0,0)-(0,0) + done 0 + save 0 + last_key_ms 271450936 + Theme: + font_size 0 + white_on_black 0 + menu_inset 0 + menuitem_gap_y 0 + + Scenes: + Scene 6: name 'main' + title_id 19 (title) + highlight_id 0 ((none)) + Object 19 (title): type text + flags + bbox: (0,0)-(0,0) + dims: 0x0 + Text: str_id 20 font_name '(default)' font_size 0 + str 'Test Configuration' + Object 21 (prompt): type text + flags + bbox: (0,0)-(0,0) + dims: 0x0 + Text: str_id 22 font_name '(default)' font_size 0 + str 'UP and DOWN to choose, ENTER to select' + Object 8 (cpu-speed): type menu + flags + bbox: (0,0)-(0,0) + dims: 0x0 + Menu: pointer_id 0 title_id 23 manual 0 + Item 10: name '00' label_id 25 desc_id 0 + Item 11: name '01' label_id 27 desc_id 0 + Item 12: name '02' label_id 29 desc_id 0 + Object 23 (title): type text + flags + bbox: (0,0)-(0,0) + dims: 0x0 + Text: str_id 24 font_name '(default)' font_size 0 + str 'CPU speed' + Object 25 (item-label): type text + flags + bbox: (0,0)-(0,0) + dims: 0x0 + Text: str_id 26 font_name '(default)' font_size 0 + str '2 GHz' + Object 27 (item-label): type text + flags + bbox: (0,0)-(0,0) + dims: 0x0 + Text: str_id 28 font_name '(default)' font_size 0 + str '2.5 GHz' + Object 29 (item-label): type text + flags + bbox: (0,0)-(0,0) + dims: 0x0 + Text: str_id 30 font_name '(default)' font_size 0 + str '3 GHz' + Object 13 (power-loss): type menu + flags + bbox: (0,0)-(0,0) + dims: 0x0 + Menu: pointer_id 0 title_id 31 manual 0 + Item 14: name '00' label_id 33 desc_id 0 + Item 15: name '01' label_id 35 desc_id 0 + Item 16: name '02' label_id 37 desc_id 0 + Object 31 (title): type text + flags + bbox: (0,0)-(0,0) + dims: 0x0 + Text: str_id 32 font_name '(default)' font_size 0 + str 'AC Power' + Object 33 (item-label): type text + flags + bbox: (0,0)-(0,0) + dims: 0x0 + Text: str_id 34 font_name '(default)' font_size 0 + str 'Always Off' + Object 35 (item-label): type text + flags + bbox: (0,0)-(0,0) + dims: 0x0 + Text: str_id 36 font_name '(default)' font_size 0 + str 'Always On' + Object 37 (item-label): type text + flags + bbox: (0,0)-(0,0) + dims: 0x0 + Text: str_id 38 font_name '(default)' font_size 0 + str 'Memory' + Object 17 (machine-name): type textline + flags + bbox: (0,0)-(0,0) + dims: 0x0 + Textline: label_id 39 edit_id 18 + max_chars 20 pos 20 + Object 39 (title): type text + flags + bbox: (0,0)-(0,0) + dims: 0x0 + Text: str_id 40 font_name '(default)' font_size 0 + str 'Machine name' + Object 18 (edit): type text + flags + bbox: (0,0)-(0,0) + dims: 0x0 + Text: str_id 41 font_name '(default)' font_size 0 + str '' -- 2.43.0
From: Simon Glass <sjg@chromium.org> When the object pointer is already available we don't want to have to look it up. Provide a new is_within() function which takes an object pointer instead of an ID. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/scene.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/boot/scene.c b/boot/scene.c index c2e4d8e1330..c1ecbfcc67a 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -1129,6 +1129,22 @@ int scene_send_key(struct scene *scn, int key, struct expo_action *event) return 0; } +/** + * is_within() - check if a point is considered within an object ID + * + * @scn: Scene to check + * @obj: object to check + * @x: X coordinate of the point + * @y: Y coordinate of the point + * Return: true if the point is considered within the object, false if not + */ +static bool is_within(const struct scene_obj *obj, int x, int y) +{ + /* Check if point (x, y) is within object's bounding box */ + return (x >= obj->bbox.x0 && x <= obj->bbox.x1 && + y >= obj->bbox.y0 && y <= obj->bbox.y1); +} + bool scene_within(const struct scene *scn, uint id, int x, int y) { struct scene_obj *obj; @@ -1141,9 +1157,7 @@ bool scene_within(const struct scene *scn, uint id, int x, int y) log_debug("- id %d: '%s' bbox x0 %d y0 %d x1 %d y1 %d\n", id, obj->name, obj->bbox.x0, obj->bbox.y0, obj->bbox.x1, obj->bbox.x1); - /* Check if point (x, y) is within object's bounding box */ - return (x >= obj->bbox.x0 && x <= obj->bbox.x1 && - y >= obj->bbox.y0 && y <= obj->bbox.y1); + return is_within(obj, x, y); } bool scene_obj_within(const struct scene *scn, struct scene_obj *obj, int x, -- 2.43.0
From: Simon Glass <sjg@chromium.org> It is easier to test this function directly than via click_check(). Set up a test expo with an extra overlapping object and add some tests. Update the existing render test to take account of the new object. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/scene.c | 11 +------ boot/scene_internal.h | 10 ++++++ test/boot/expo.c | 71 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 72 insertions(+), 20 deletions(-) diff --git a/boot/scene.c b/boot/scene.c index c1ecbfcc67a..a9e0d1f1266 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -1193,16 +1193,7 @@ bool scene_obj_within(const struct scene *scn, struct scene_obj *obj, int x, return within; } -/** - * scene_find_obj_within() - Find an object that is within the coords - * - * @scn: Scene to check - * @x: X coordinates of the click - * @y: Y coordinate of the click - * Return: object that is being clicked on, NULL if none - */ -static struct scene_obj *scene_find_obj_within(const struct scene *scn, int x, - int y) +struct scene_obj *scene_find_obj_within(const struct scene *scn, int x, int y) { struct scene_obj *obj; diff --git a/boot/scene_internal.h b/boot/scene_internal.h index 9bf2cb8f8f2..8e61067d4b4 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -556,4 +556,14 @@ const char *scene_flag_name(uint flag); */ const char *scene_obj_type_name(enum scene_obj_t type); +/** + * scene_find_obj_within() - Find an object that is within the coords + * + * @scn: Scene to check + * @x: X coordinates of the click + * @y: Y coordinate of the click + * Return: object that is being clicked on, NULL if none + */ +struct scene_obj *scene_find_obj_within(const struct scene *scn, int x, int y); + #endif /* __SCENE_INTERNAL_H */ diff --git a/test/boot/expo.c b/test/boot/expo.c index e8716b86991..db4a54e7cd7 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -34,6 +34,7 @@ enum { OBJ_BOX, OBJ_BOX2, OBJ_TEXTED, + OBJ_OVERLAP, /* strings */ STR_SCENE_TITLE, @@ -55,6 +56,8 @@ enum { STR_ITEM2_KEY, STR_ITEM2_PREVIEW, + STR_OVERLAP, + /* menu items */ ITEM1, ITEM1_LABEL, @@ -599,6 +602,14 @@ static int create_test_expo(struct unit_test_state *uts, struct expo **expp, abuf_printf(text, "This\nis the initial contents of the text editor " "but it is quite likely that more will be added later"); + /* + * Add an extra text object that overlaps with OBJ_TEXT to test reverse + * search order. OBJ_TEXT is at (400, 100), so let's add one nearby + */ + ut_assert(scene_txt_str(scn, "overlap", OBJ_OVERLAP, STR_OVERLAP, + "overlap text", NULL) > 0); + ut_assertok(scene_obj_set_pos(scn, OBJ_OVERLAP, 405, 105)); + *expp = exp; *scnp = scn; *menup = menu; @@ -720,17 +731,17 @@ static int expo_render_image(struct unit_test_state *uts) /* render it */ expo_set_scene_id(exp, SCENE1); ut_assertok(expo_render(exp)); - ut_asserteq(18782, video_compress_fb(uts, dev, false)); + ut_asserteq(19065, video_compress_fb(uts, dev, false)); ut_asserteq(0, scn->highlight_id); ut_assertok(scene_arrange(scn)); ut_asserteq(0, scn->highlight_id); ut_assertok(expo_render(exp)); - ut_asserteq(20373, video_compress_fb(uts, dev, false)); + ut_asserteq(20707, video_compress_fb(uts, dev, false)); ut_assertok(scene_arrange(scn)); ut_assertok(expo_render(exp)); - ut_asserteq(20373, video_compress_fb(uts, dev, false)); + ut_asserteq(20707, video_compress_fb(uts, dev, false)); scene_set_highlight_id(scn, OBJ_MENU); ut_asserteq(OBJ_MENU, scn->highlight_id); @@ -742,7 +753,7 @@ static int expo_render_image(struct unit_test_state *uts) ut_assert(!(obj->flags & SCENEOF_HIDE)); ut_assertok(expo_render(exp)); - ut_asserteq(20373, video_compress_fb(uts, dev, false)); + ut_asserteq(20707, video_compress_fb(uts, dev, false)); /* move down */ ut_assertok(expo_send_key(exp, BKEY_DOWN)); @@ -755,7 +766,7 @@ static int expo_render_image(struct unit_test_state *uts) ut_asserteq(ITEM2, scene_menu_get_cur_item(scn, OBJ_MENU)); ut_assertok(scene_arrange(scn)); ut_assertok(expo_render(exp)); - ut_asserteq(19649, video_compress_fb(uts, dev, false)); + ut_asserteq(19953, video_compress_fb(uts, dev, false)); ut_assertok(video_check_copy_fb(uts, dev)); /* hide the text editor since the following tests don't need it */ @@ -764,18 +775,18 @@ static int expo_render_image(struct unit_test_state *uts) /* do some alignment checks */ ut_assertok(scene_obj_set_halign(scn, OBJ_TEXT3, SCENEOA_CENTRE)); ut_assertok(expo_render(exp)); - ut_asserteq(16323, video_compress_fb(uts, dev, false)); + ut_asserteq(16626, video_compress_fb(uts, dev, false)); ut_assertok(scene_obj_set_halign(scn, OBJ_TEXT3, SCENEOA_RIGHT)); ut_assertok(expo_render(exp)); - ut_asserteq(16240, video_compress_fb(uts, dev, false)); + ut_asserteq(16634, video_compress_fb(uts, dev, false)); ut_assertok(scene_obj_set_halign(scn, OBJ_TEXT3, SCENEOA_LEFT)); ut_assertok(scene_obj_set_valign(scn, OBJ_TEXT3, SCENEOA_CENTRE)); ut_assertok(expo_render(exp)); - ut_asserteq(18714, video_compress_fb(uts, dev, false)); + ut_asserteq(19056, video_compress_fb(uts, dev, false)); ut_assertok(scene_obj_set_valign(scn, OBJ_TEXT3, SCENEOA_BOTTOM)); ut_assertok(expo_render(exp)); - ut_asserteq(18670, video_compress_fb(uts, dev, false)); + ut_asserteq(19024, video_compress_fb(uts, dev, false)); /* make sure only the preview for the second item is shown */ obj = scene_obj_find(scn, ITEM1_PREVIEW, SCENEOBJT_NONE); @@ -801,7 +812,7 @@ static int expo_render_image(struct unit_test_state *uts) exp->show_highlight = true; ut_assertok(scene_arrange(scn)); ut_assertok(expo_render(exp)); - ut_asserteq(18830, video_compress_fb(uts, dev, false)); + ut_asserteq(19181, video_compress_fb(uts, dev, false)); /* now try in text mode */ expo_set_text_mode(exp, true); @@ -1260,6 +1271,46 @@ static int expo_scene_obj_type_name(struct unit_test_state *uts) } BOOTSTD_TEST(expo_scene_obj_type_name, 0); +/* Test scene_find_obj_within() */ +static int expo_find_obj_within(struct unit_test_state *uts) +{ + struct scene_obj_menu *menu; + struct abuf buf, logo_copy; + struct scene_obj *obj; + struct scene *scn; + struct expo *exp; + + ut_assertok(create_test_expo(uts, &exp, &scn, &menu, &buf, &logo_copy)); + + /* Arrange the scene so objects have proper bounding boxes */ + ut_assertok(scene_arrange(scn)); + + /* + * Check finding a menu by 'clicking' on a menu item label - menu items + * are at (50,436) for ITEM1 and (50,454) for ITEM2 + */ + obj = scene_find_obj_within(scn, 60, 440); + ut_assertnonnull(obj); + ut_asserteq(OBJ_MENU, obj->id); + + /* logo and text are not highlightable, so they should not be found */ + ut_assertnull(scene_find_obj_within(scn, 60, 30)); + ut_assertnull(scene_find_obj_within(scn, 410, 110)); + + /* empty space */ + ut_assertnull(scene_find_obj_within(scn, 10, 10)); + + /* way outside bounds */ + ut_assertnull(scene_find_obj_within(scn, 9999, 9999)); + + abuf_uninit(&buf); + abuf_uninit(&logo_copy); + expo_destroy(exp); + + return 0; +} +BOOTSTD_TEST(expo_find_obj_within, UTF_DM | UTF_SCAN_FDT); + /* Test expo_dump() */ static int expo_dump_test(struct unit_test_state *uts) { -- 2.43.0
From: Simon Glass <sjg@chromium.org> Since the scene is drawn by iterating through the list of objects, when the user clicks somewhere we should look at the top-most object under the mouse first. This is not true when a menu is popped up, since we only care about the menu in that case. Add a way to reverse the direction of the object search. For now there are no new test cases, since OBJ_OVERLAP is a text object and cannot currently be clicked on. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/scene.c | 45 +++++++++++++++++++++++++++++-------------- boot/scene_internal.h | 5 ++++- test/boot/expo.c | 10 +++++----- 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/boot/scene.c b/boot/scene.c index a9e0d1f1266..05a683dd1da 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -1193,20 +1193,37 @@ bool scene_obj_within(const struct scene *scn, struct scene_obj *obj, int x, return within; } -struct scene_obj *scene_find_obj_within(const struct scene *scn, int x, int y) +struct scene_obj *scene_find_obj_within(const struct scene *scn, int x, int y, + bool reverse) { struct scene_obj *obj; - log_debug("within: x %d y %d\n", x, y); - list_for_each_entry(obj, &scn->obj_head, sibling) { - log_debug(" - obj %d '%s' can_highlight %d within %d\n", - obj->id, obj->name, scene_obj_can_highlight(obj), - scene_obj_within(scn, obj, x, y)); - if (scene_obj_can_highlight(obj) && - scene_obj_within(scn, obj, x, y)) { - log_debug("- returning obj %d '%s'\n", obj->id, - obj->name); - return obj; + log_debug("within: x %d y %d reverse %d\n", x, y, reverse); + if (reverse) { + list_for_each_entry_reverse(obj, &scn->obj_head, sibling) { + log_debug(" - obj %d '%s' can_highlight %d within %d\n", + obj->id, obj->name, + scene_obj_can_highlight(obj), + scene_obj_within(scn, obj, x, y)); + if (scene_obj_can_highlight(obj) && + scene_obj_within(scn, obj, x, y)) { + log_debug("- returning obj %d '%s'\n", obj->id, + obj->name); + return obj; + } + } + } else { + list_for_each_entry(obj, &scn->obj_head, sibling) { + log_debug(" - obj %d '%s' can_highlight %d within %d\n", + obj->id, obj->name, + scene_obj_can_highlight(obj), + scene_obj_within(scn, obj, x, y)); + if (scene_obj_can_highlight(obj) && + scene_obj_within(scn, obj, x, y)) { + log_debug("- returning obj %d '%s'\n", obj->id, + obj->name); + return obj; + } } } log_debug("- no object\n"); @@ -1238,7 +1255,7 @@ static void send_click_obj(struct scene *scn, struct scene_obj *obj, int x, } log_debug("no object; finding...\n"); - obj = scene_find_obj_within(scn, x, y); + obj = scene_find_obj_within(scn, x, y, false); if (obj) { event->type = EXPOACT_POINT_OPEN; event->select.id = obj->id; @@ -1264,7 +1281,7 @@ static int scene_click_popup(struct scene *scn, int x, int y, } /* check that the click is within our object */ - chk = scene_find_obj_within(scn, x, y); + chk = scene_find_obj_within(scn, x, y, false); log_debug("chk %d '%s' (obj %d '%s')\n", chk ? chk->id : -1, chk ? chk->name : "(none)", obj->id, obj->name); if (!chk) { @@ -1323,7 +1340,7 @@ int scene_send_click(struct scene *scn, int x, int y, struct expo_action *event) return 0; } - obj = scene_find_obj_within(scn, x, y); + obj = scene_find_obj_within(scn, x, y, false); log_debug("non-popup obj %d '%s'\n", obj ? obj->id : -1, obj ? obj->name : "(none)"); if (!obj) diff --git a/boot/scene_internal.h b/boot/scene_internal.h index 8e61067d4b4..e2f4cc066d0 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -562,8 +562,11 @@ const char *scene_obj_type_name(enum scene_obj_t type); * @scn: Scene to check * @x: X coordinates of the click * @y: Y coordinate of the click + * @reverse: true to search from top to bottom (reverse order), false for + * bottom to top * Return: object that is being clicked on, NULL if none */ -struct scene_obj *scene_find_obj_within(const struct scene *scn, int x, int y); +struct scene_obj *scene_find_obj_within(const struct scene *scn, int x, int y, + bool reverse); #endif /* __SCENE_INTERNAL_H */ diff --git a/test/boot/expo.c b/test/boot/expo.c index db4a54e7cd7..e32550a6685 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -1289,19 +1289,19 @@ static int expo_find_obj_within(struct unit_test_state *uts) * Check finding a menu by 'clicking' on a menu item label - menu items * are at (50,436) for ITEM1 and (50,454) for ITEM2 */ - obj = scene_find_obj_within(scn, 60, 440); + obj = scene_find_obj_within(scn, 60, 440, false); ut_assertnonnull(obj); ut_asserteq(OBJ_MENU, obj->id); /* logo and text are not highlightable, so they should not be found */ - ut_assertnull(scene_find_obj_within(scn, 60, 30)); - ut_assertnull(scene_find_obj_within(scn, 410, 110)); + ut_assertnull(scene_find_obj_within(scn, 60, 30, false)); + ut_assertnull(scene_find_obj_within(scn, 410, 110, false)); /* empty space */ - ut_assertnull(scene_find_obj_within(scn, 10, 10)); + ut_assertnull(scene_find_obj_within(scn, 10, 10, false)); /* way outside bounds */ - ut_assertnull(scene_find_obj_within(scn, 9999, 9999)); + ut_assertnull(scene_find_obj_within(scn, 9999, 9999, false)); abuf_uninit(&buf); abuf_uninit(&logo_copy); -- 2.43.0
From: Simon Glass <sjg@chromium.org> At present only highlightable objects can be clicked on, i.e. menus and textlines. Update scene_find_obj_within() so that it can find any type of object, if requested. Update all the callers to false, so things work the same. Since the scene is drawn by iterating through the list of objects, when the user clicks somewhere we should look at the top-most object under the mouse first. So reverse the direction of the object search. Update the tests to cover this new feature. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/scene.c | 15 ++++++------- boot/scene_internal.h | 3 ++- test/boot/expo.c | 49 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 53 insertions(+), 14 deletions(-) diff --git a/boot/scene.c b/boot/scene.c index 05a683dd1da..4cf1b6d5852 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -1194,18 +1194,19 @@ bool scene_obj_within(const struct scene *scn, struct scene_obj *obj, int x, } struct scene_obj *scene_find_obj_within(const struct scene *scn, int x, int y, - bool reverse) + bool reverse, bool allow_any) { struct scene_obj *obj; - log_debug("within: x %d y %d reverse %d\n", x, y, reverse); + log_debug("within: x %d y %d reverse %d allow_any %d\n", x, y, reverse, + allow_any); if (reverse) { list_for_each_entry_reverse(obj, &scn->obj_head, sibling) { log_debug(" - obj %d '%s' can_highlight %d within %d\n", obj->id, obj->name, scene_obj_can_highlight(obj), scene_obj_within(scn, obj, x, y)); - if (scene_obj_can_highlight(obj) && + if ((allow_any || scene_obj_can_highlight(obj)) && scene_obj_within(scn, obj, x, y)) { log_debug("- returning obj %d '%s'\n", obj->id, obj->name); @@ -1218,7 +1219,7 @@ struct scene_obj *scene_find_obj_within(const struct scene *scn, int x, int y, obj->id, obj->name, scene_obj_can_highlight(obj), scene_obj_within(scn, obj, x, y)); - if (scene_obj_can_highlight(obj) && + if ((allow_any || scene_obj_can_highlight(obj)) && scene_obj_within(scn, obj, x, y)) { log_debug("- returning obj %d '%s'\n", obj->id, obj->name); @@ -1255,7 +1256,7 @@ static void send_click_obj(struct scene *scn, struct scene_obj *obj, int x, } log_debug("no object; finding...\n"); - obj = scene_find_obj_within(scn, x, y, false); + obj = scene_find_obj_within(scn, x, y, false, false); if (obj) { event->type = EXPOACT_POINT_OPEN; event->select.id = obj->id; @@ -1281,7 +1282,7 @@ static int scene_click_popup(struct scene *scn, int x, int y, } /* check that the click is within our object */ - chk = scene_find_obj_within(scn, x, y, false); + chk = scene_find_obj_within(scn, x, y, false, false); log_debug("chk %d '%s' (obj %d '%s')\n", chk ? chk->id : -1, chk ? chk->name : "(none)", obj->id, obj->name); if (!chk) { @@ -1340,7 +1341,7 @@ int scene_send_click(struct scene *scn, int x, int y, struct expo_action *event) return 0; } - obj = scene_find_obj_within(scn, x, y, false); + obj = scene_find_obj_within(scn, x, y, false, false); log_debug("non-popup obj %d '%s'\n", obj ? obj->id : -1, obj ? obj->name : "(none)"); if (!obj) diff --git a/boot/scene_internal.h b/boot/scene_internal.h index e2f4cc066d0..2bfbb5dcf50 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -564,9 +564,10 @@ const char *scene_obj_type_name(enum scene_obj_t type); * @y: Y coordinate of the click * @reverse: true to search from top to bottom (reverse order), false for * bottom to top + * @allow_any: true to allow searching non-highlight objects * Return: object that is being clicked on, NULL if none */ struct scene_obj *scene_find_obj_within(const struct scene *scn, int x, int y, - bool reverse); + bool reverse, bool allow_any); #endif /* __SCENE_INTERNAL_H */ diff --git a/test/boot/expo.c b/test/boot/expo.c index e32550a6685..66fd5a2873f 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -1289,19 +1289,56 @@ static int expo_find_obj_within(struct unit_test_state *uts) * Check finding a menu by 'clicking' on a menu item label - menu items * are at (50,436) for ITEM1 and (50,454) for ITEM2 */ - obj = scene_find_obj_within(scn, 60, 440, false); + obj = scene_find_obj_within(scn, 60, 440, false, false); ut_assertnonnull(obj); ut_asserteq(OBJ_MENU, obj->id); - /* logo and text are not highlightable, so they should not be found */ - ut_assertnull(scene_find_obj_within(scn, 60, 30, false)); - ut_assertnull(scene_find_obj_within(scn, 410, 110, false)); + /* + * Check with allow_any=false for non-highlightable objects - logo and + * text are not highlightable, so they should not be found + */ + ut_assertnull(scene_find_obj_within(scn, 60, 30, false, false)); + ut_assertnull(scene_find_obj_within(scn, 410, 110, false, false)); + + /* Test with allow_any=true for non-highlightable objects */ + obj = scene_find_obj_within(scn, 60, 30, false, true); + ut_assertnonnull(obj); + ut_asserteq(OBJ_LOGO, obj->id); + + /* + * Check reversing search order with allow_any=true at the overlapping + * position. OBJ_TEXT was created first at (400, 100), and the + * "overlap" text object was created second at (405, 105). They + * overlap at position (410, 110). + * + * With reverse=false, we search from start of list (bottom to top) and + * find OBJ_TEXT first. + */ + obj = scene_find_obj_within(scn, 410, 110, false, true); + ut_assertnonnull(obj); + ut_asserteq(OBJ_TEXT, obj->id); + + /* + * With reverse=true, we search from end of list (top to bottom) and + * find the OBJ_OVERLAP_TEST object first. + */ + obj = scene_find_obj_within(scn, 410, 110, true, true); + ut_assertnonnull(obj); + ut_asserteq(OBJ_OVERLAP, obj->id); + + /* + * Test reverse=true with a non-overlapping object - should get same + * result as reverse=false + */ + obj = scene_find_obj_within(scn, 60, 30, true, true); + ut_assertnonnull(obj); + ut_asserteq(OBJ_LOGO, obj->id); /* empty space */ - ut_assertnull(scene_find_obj_within(scn, 10, 10, false)); + ut_assertnull(scene_find_obj_within(scn, 10, 10, false, false)); /* way outside bounds */ - ut_assertnull(scene_find_obj_within(scn, 9999, 9999, false)); + ut_assertnull(scene_find_obj_within(scn, 9999, 9999, false, false)); abuf_uninit(&buf); abuf_uninit(&logo_copy); -- 2.43.0
From: Simon Glass <sjg@chromium.org> It is sometimes useful to be able to click on an image (sometimes called an icon). Allow this within expo and return new click action. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow_menu.c | 1 + boot/cedit.c | 3 +++ boot/scene.c | 14 +++++++++++--- include/expo.h | 2 ++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/boot/bootflow_menu.c b/boot/bootflow_menu.c index 3365b5c3b06..bbc9a189c98 100644 --- a/boot/bootflow_menu.c +++ b/boot/bootflow_menu.c @@ -313,6 +313,7 @@ int bootflow_menu_poll(struct expo *exp, int *seqp) } case EXPOACT_QUIT: return -EPIPE; + case EXPOACT_CLICK: default: return -EAGAIN; } diff --git a/boot/cedit.c b/boot/cedit.c index 691780512ca..70a0c22fe68 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -220,6 +220,9 @@ int cedit_do_action(struct expo *exp, struct scene *scn, log_debug("quitting\n"); exp->done = true; break; + case EXPOACT_CLICK: + /* not supported by cedit */ + break; } return 0; diff --git a/boot/scene.c b/boot/scene.c index 4cf1b6d5852..dd13b74b4d7 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -1167,9 +1167,11 @@ bool scene_obj_within(const struct scene *scn, struct scene_obj *obj, int x, switch (obj->type) { case SCENEOBJT_NONE: + break; case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: case SCENEOBJT_BOX: + within = is_within(obj, x, y); break; case SCENEOBJT_MENU: { struct scene_obj_menu *menu; @@ -1344,15 +1346,21 @@ int scene_send_click(struct scene *scn, int x, int y, struct expo_action *event) obj = scene_find_obj_within(scn, x, y, false, false); log_debug("non-popup obj %d '%s'\n", obj ? obj->id : -1, obj ? obj->name : "(none)"); - if (!obj) - return 0; + if (!obj) { + obj = scene_find_obj_within(scn, x, y, true, true); + log_debug("non-popup any obj %d '%s'\n", obj ? obj->id : -1, + obj ? obj->name : "(none)"); + if (!obj) + return 0; + } switch (obj->type) { case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: case SCENEOBJT_BOX: - /* These objects don't handle clicks directly */ + event->type = EXPOACT_CLICK; + event->select.id = obj->id; break; case SCENEOBJT_MENU: { struct scene_obj_menu *menu; diff --git a/include/expo.h b/include/expo.h index 619af627840..05f61cc9bd9 100644 --- a/include/expo.h +++ b/include/expo.h @@ -56,6 +56,7 @@ enum expo_id_t { * @EXPOACT_REPOINT_OPEN: menu closed, another menu opened (@prev_id indicates * the menu closed, @id indicates menu opened) * @EXPOACT_QUIT: request to exit the menu + * @EXPOACT_CLICK: click on an object */ enum expoact_type { EXPOACT_NONE, @@ -68,6 +69,7 @@ enum expoact_type { EXPOACT_POINT_CLOSE, EXPOACT_REPOINT_OPEN, EXPOACT_QUIT, + EXPOACT_CLICK, }; /** -- 2.43.0
From: Simon Glass <sjg@chromium.org> In some cases we want to show a logo other than the U-Boot one. Add a field for this, so it can be used by layouts which need it. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow_internal.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/boot/bootflow_internal.h b/boot/bootflow_internal.h index 0b17a23bfd7..b488ecc6102 100644 --- a/boot/bootflow_internal.h +++ b/boot/bootflow_internal.h @@ -12,6 +12,8 @@ /** * enum boomenu_id_t - expo IDs for elements of the bootflow menu * + * @OBJ_OTHER_LOGO: Second logo (separate from the U-Boot logo) + * * The ranges below are as follows: * * @ITEM: Menu items @@ -44,6 +46,7 @@ enum boomenu_id_t { OBJ_MENU_TITLE, OBJ_POINTER, OBJ_AUTOBOOT, + OBJ_OTHER_LOGO, /* strings for menu items */ STR_LABEL = 100, -- 2.43.0
From: Simon Glass <sjg@chromium.org> In some cases it is useful to provide a settings scene, or perhaps just a button to change the layout. Add support for this. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow_internal.h | 2 ++ boot/bootflow_menu.c | 2 ++ boot/cedit.c | 1 + boot/scene_menu.c | 3 +++ include/bootflow.h | 3 ++- include/expo.h | 2 ++ 6 files changed, 12 insertions(+), 1 deletion(-) diff --git a/boot/bootflow_internal.h b/boot/bootflow_internal.h index b488ecc6102..0ae5b330bd4 100644 --- a/boot/bootflow_internal.h +++ b/boot/bootflow_internal.h @@ -13,6 +13,7 @@ * enum boomenu_id_t - expo IDs for elements of the bootflow menu * * @OBJ_OTHER_LOGO: Second logo (separate from the U-Boot logo) + * @OBJ_SETTINGS: Select settings / change layout * * The ranges below are as follows: * @@ -47,6 +48,7 @@ enum boomenu_id_t { OBJ_POINTER, OBJ_AUTOBOOT, OBJ_OTHER_LOGO, + OBJ_SETTINGS, /* strings for menu items */ STR_LABEL = 100, diff --git a/boot/bootflow_menu.c b/boot/bootflow_menu.c index bbc9a189c98..1f24215f392 100644 --- a/boot/bootflow_menu.c +++ b/boot/bootflow_menu.c @@ -314,6 +314,8 @@ int bootflow_menu_poll(struct expo *exp, int *seqp) case EXPOACT_QUIT: return -EPIPE; case EXPOACT_CLICK: + if (act.select.id == OBJ_SETTINGS) + return -ECOMM; /* layout change request */ default: return -EAGAIN; } diff --git a/boot/cedit.c b/boot/cedit.c index 70a0c22fe68..c82519a0eb3 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -221,6 +221,7 @@ int cedit_do_action(struct expo *exp, struct scene *scn, exp->done = true; break; case EXPOACT_CLICK: + case EXPOACT_SETTINGS: /* not supported by cedit */ break; } diff --git a/boot/scene_menu.c b/boot/scene_menu.c index d54b99979e1..f52d561094b 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -485,6 +485,9 @@ int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key, log_debug("menu quit\n"); } break; + case ' ': + event->type = EXPOACT_SETTINGS; + break; case '0'...'9': key_item = scene_menu_find_key(scn, menu, key); if (key_item) { diff --git a/include/bootflow.h b/include/bootflow.h index 351a6539978..17ecc80c2eb 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -743,7 +743,8 @@ int bootflow_menu_start(struct bootstd_priv *std, bool text_mode, * Return: 0 if a bootflow was chosen, -EAGAIN if nothing is chosen yet, -EPIPE * if the user quit, -EREMCHG if the expo needs refreshing, -ERESTART if * the user tried to move to a new selection but was unable (e.g. already - * at the top and tried to move up) + * at the top and tried to move up), -ECOMM if the user requests settings + * to be opened */ int bootflow_menu_poll(struct expo *exp, int *seqp); diff --git a/include/expo.h b/include/expo.h index 05f61cc9bd9..aa8e41af176 100644 --- a/include/expo.h +++ b/include/expo.h @@ -57,6 +57,7 @@ enum expo_id_t { * the menu closed, @id indicates menu opened) * @EXPOACT_QUIT: request to exit the menu * @EXPOACT_CLICK: click on an object + * @EXPOACT_SETTINGS: select menu settings */ enum expoact_type { EXPOACT_NONE, @@ -70,6 +71,7 @@ enum expoact_type { EXPOACT_REPOINT_OPEN, EXPOACT_QUIT, EXPOACT_CLICK, + EXPOACT_SETTINGS, }; /** -- 2.43.0
From: Simon Glass <sjg@chromium.org> Add a definition for a button which displays help. For now this is not used in the standard layout. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow_internal.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/boot/bootflow_internal.h b/boot/bootflow_internal.h index 0ae5b330bd4..ae7bb16ce8d 100644 --- a/boot/bootflow_internal.h +++ b/boot/bootflow_internal.h @@ -14,6 +14,7 @@ * * @OBJ_OTHER_LOGO: Second logo (separate from the U-Boot logo) * @OBJ_SETTINGS: Select settings / change layout + * @OBJ_HELP: Select help * * The ranges below are as follows: * @@ -49,6 +50,7 @@ enum boomenu_id_t { OBJ_AUTOBOOT, OBJ_OTHER_LOGO, OBJ_SETTINGS, + OBJ_HELP, /* strings for menu items */ STR_LABEL = 100, -- 2.43.0
From: Simon Glass <sjg@chromium.org> Some distros have a name in addition to the version. Add an object for that in case it is needed. Move the existing objects down to make room. Add a definition for a button which displays help. For now this is not used in the standard layout. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow_internal.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/boot/bootflow_internal.h b/boot/bootflow_internal.h index ae7bb16ce8d..8731e9950fd 100644 --- a/boot/bootflow_internal.h +++ b/boot/bootflow_internal.h @@ -23,6 +23,7 @@ * @ITEM_DESC: Longer description or pretty name, e.g. "Ubuntu 2024.04 LTS" * @ITEM_KEY: Keypress to select this item, e.g. "1" * @ITEM_PREVIEW: Preview image for the OS + * @ITEM_VERSION_NAME: Distro's name for the version, e.g. 'Noble Numbat' */ enum boomenu_id_t { START, @@ -56,13 +57,15 @@ enum boomenu_id_t { STR_LABEL = 100, STR_DESC = 200, STR_KEY = 300, + STR_VERSION_NAME = 400, /* menu items / components (bootflow number is added to these) */ - ITEM = 400, - ITEM_LABEL = 500, - ITEM_DESC = 600, - ITEM_KEY = 700, - ITEM_PREVIEW = 800, + ITEM = 600, + ITEM_LABEL = 700, + ITEM_DESC = 800, + ITEM_KEY = 900, + ITEM_PREVIEW = 1000, + ITEM_VERSION_NAME = 1100, /* left margin for the main menu */ MARGIN_LEFT = 100, -- 2.43.0
From: Simon Glass <sjg@chromium.org> Some layouts show a box around each menu item. Add an item for that. For now this is not used in the standard layout. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow_internal.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/boot/bootflow_internal.h b/boot/bootflow_internal.h index 8731e9950fd..dc5f587e83e 100644 --- a/boot/bootflow_internal.h +++ b/boot/bootflow_internal.h @@ -24,6 +24,7 @@ * @ITEM_KEY: Keypress to select this item, e.g. "1" * @ITEM_PREVIEW: Preview image for the OS * @ITEM_VERSION_NAME: Distro's name for the version, e.g. 'Noble Numbat' + * @ITEM_BOX: Box around the item (normally hidden) */ enum boomenu_id_t { START, @@ -66,6 +67,7 @@ enum boomenu_id_t { ITEM_KEY = 900, ITEM_PREVIEW = 1000, ITEM_VERSION_NAME = 1100, + ITEM_BOX = 1200, /* left margin for the main menu */ MARGIN_LEFT = 100, -- 2.43.0
From: Simon Glass <sjg@chromium.org> Where an OS is verified by a vendor, add a way to show a suitable symbol to indicate this. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow_internal.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/boot/bootflow_internal.h b/boot/bootflow_internal.h index dc5f587e83e..519c3ee782a 100644 --- a/boot/bootflow_internal.h +++ b/boot/bootflow_internal.h @@ -25,6 +25,7 @@ * @ITEM_PREVIEW: Preview image for the OS * @ITEM_VERSION_NAME: Distro's name for the version, e.g. 'Noble Numbat' * @ITEM_BOX: Box around the item (normally hidden) + * @ITEM_VERIFIED: Indicates that the item is verified by the vendor */ enum boomenu_id_t { START, @@ -68,6 +69,7 @@ enum boomenu_id_t { ITEM_PREVIEW = 1000, ITEM_VERSION_NAME = 1100, ITEM_BOX = 1200, + ITEM_VERIFIED = 1300, /* left margin for the main menu */ MARGIN_LEFT = 100, -- 2.43.0
participants (1)
-
Simon Glass