From def898c458e65a71fb64cda370b4281cd026e48c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:27 -0600 Subject: expo: Convert to using a string ID for the scene title This is easier to deal with if it uses the existing string handling, since we will be able to use translations, etc. in the future. Update it to use an ID instead of a string. Signed-off-by: Simon Glass --- include/expo.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/expo.h b/include/expo.h index d242f48e30c..8827f4b0b45 100644 --- a/include/expo.h +++ b/include/expo.h @@ -92,7 +92,7 @@ struct expo_string { * @expo: Expo this scene is part of * @name: Name of the scene (allocated) * @id: ID number of the scene - * @title: Title of the scene (allocated) + * @title_id: String ID of title of the scene (allocated) * @sibling: Node to link this scene to its siblings * @obj_head: List of objects in the scene */ @@ -100,7 +100,7 @@ struct scene { struct expo *expo; char *name; uint id; - char *title; + uint title_id; struct list_head sibling; struct list_head obj_head; }; @@ -338,10 +338,10 @@ struct scene *expo_lookup_scene_id(struct expo *exp, uint scene_id); * scene_title_set() - set the scene title * * @scn: Scene to update - * @title: Title to set, NULL if none (this is allocated by this call) - * Returns: 0 if OK, -ENOMEM if out of memory + * @title_id: Title ID to set + * Returns: 0 if OK */ -int scene_title_set(struct scene *scn, const char *title); +int scene_title_set(struct scene *scn, uint title_id); /** * scene_obj_count() - Count the number of objects in a scene -- cgit v1.3.1 From 18030d9fa225569cc0ff3e87baca52c6e2558e91 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:29 -0600 Subject: test: Restore test behaviour on failure A recent change makes test continue to run after failure. This results in a lot of useless output and may lead to a segfault. Fix this by adding back the 'return' statement. Fixes: fa847bb409d ("test: Wrap assert macros in ({ ... }) and fix") Signed-off-by: Simon Glass --- include/test/ut.h | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/test/ut.h b/include/test/ut.h index dddf9ad241f..ea6ee95d734 100644 --- a/include/test/ut.h +++ b/include/test/ut.h @@ -130,7 +130,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); \ if (!(cond)) { \ ut_fail(uts, __FILE__, __LINE__, __func__, #cond); \ - __ret = CMD_RET_FAILURE; \ + return CMD_RET_FAILURE; \ } \ __ret; \ }) @@ -142,7 +142,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); if (!(cond)) { \ ut_failf(uts, __FILE__, __LINE__, __func__, #cond, \ fmt, ##args); \ - __ret = CMD_RET_FAILURE; \ + return CMD_RET_FAILURE; \ } \ __ret; \ }) @@ -157,7 +157,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); #expr1 " == " #expr2, \ "Expected %#x (%d), got %#x (%d)", \ _val1, _val1, _val2, _val2); \ - __ret = CMD_RET_FAILURE; \ + return CMD_RET_FAILURE; \ } \ __ret; \ }) @@ -175,7 +175,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); (unsigned long long)_val1, \ (unsigned long long)_val2, \ (unsigned long long)_val2); \ - __ret = CMD_RET_FAILURE; \ + return CMD_RET_FAILURE; \ } \ __ret; \ }) @@ -189,7 +189,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); ut_failf(uts, __FILE__, __LINE__, __func__, \ #expr1 " = " #expr2, \ "Expected \"%s\", got \"%s\"", _val1, _val2); \ - __ret = CMD_RET_FAILURE; \ + return CMD_RET_FAILURE; \ } \ __ret; \ }) @@ -208,7 +208,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); #expr1 " = " #expr2, \ "Expected \"%.*s\", got \"%.*s\"", \ _len, _val1, _len, _val2); \ - __ret = CMD_RET_FAILURE; \ + return CMD_RET_FAILURE; \ } \ __ret; \ }) @@ -228,7 +228,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); #expr1 " = " #expr2, \ "Expected \"%s\", got \"%s\"", \ __buf1, __buf2); \ - __ret = CMD_RET_FAILURE; \ + return CMD_RET_FAILURE; \ } \ __ret; \ }) @@ -242,7 +242,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); ut_failf(uts, __FILE__, __LINE__, __func__, \ #expr1 " = " #expr2, \ "Expected %p, got %p", _val1, _val2); \ - __ret = CMD_RET_FAILURE; \ + return CMD_RET_FAILURE; \ } \ __ret; \ }) @@ -257,7 +257,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); ut_failf(uts, __FILE__, __LINE__, __func__, \ #expr1 " = " #expr2, \ "Expected %lx, got %lx", _val1, _val2); \ - __ret = CMD_RET_FAILURE; \ + return CMD_RET_FAILURE; \ } \ __ret; \ }) @@ -271,7 +271,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); ut_failf(uts, __FILE__, __LINE__, __func__, \ #expr " != NULL", \ "Expected NULL, got %p", _val); \ - __ret = CMD_RET_FAILURE; \ + return CMD_RET_FAILURE; \ } \ __ret; \ }) @@ -285,7 +285,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); ut_failf(uts, __FILE__, __LINE__, __func__, \ #expr " = NULL", \ "Expected non-null, got NULL"); \ - __ret = CMD_RET_FAILURE; \ + return CMD_RET_FAILURE; \ } \ __ret; \ }) @@ -300,7 +300,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); #expr " = NULL", \ "Expected pointer, got error %ld", \ PTR_ERR(_val)); \ - __ret = CMD_RET_FAILURE; \ + return CMD_RET_FAILURE; \ } \ __ret; \ }) @@ -316,7 +316,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); ut_failf(uts, __FILE__, __LINE__, __func__, \ "console", "\nExpected '%s',\n got '%s'", \ uts->expect_str, uts->actual_str); \ - __ret = CMD_RET_FAILURE; \ + return CMD_RET_FAILURE; \ } \ __ret; \ }) @@ -329,7 +329,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); ut_failf(uts, __FILE__, __LINE__, __func__, \ "console", "\nExpected '%s',\n got '%s'", \ uts->expect_str, uts->actual_str); \ - __ret = CMD_RET_FAILURE; \ + return CMD_RET_FAILURE; \ } \ __ret; \ }) @@ -341,7 +341,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); if (ut_check_skipline(uts)) { \ ut_failf(uts, __FILE__, __LINE__, __func__, \ "console", "\nExpected a line, got end"); \ - __ret = CMD_RET_FAILURE; \ + return CMD_RET_FAILURE; \ } \ __ret; \ }) @@ -354,7 +354,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); ut_failf(uts, __FILE__, __LINE__, __func__, \ "console", "\nExpected '%s',\n got to '%s'", \ uts->expect_str, uts->actual_str); \ - __ret = CMD_RET_FAILURE; \ + return CMD_RET_FAILURE; \ } \ __ret; \ }) @@ -367,7 +367,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); ut_failf(uts, __FILE__, __LINE__, __func__, \ "console", "Expected no more output, got '%s'",\ uts->actual_str); \ - __ret = CMD_RET_FAILURE; \ + return CMD_RET_FAILURE; \ } \ __ret; \ }) @@ -381,7 +381,7 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); "console", \ "Expected dump of length %x bytes, got '%s'", \ total_bytes, uts->actual_str); \ - __ret = CMD_RET_FAILURE; \ + return CMD_RET_FAILURE; \ } \ __ret; \ }) -- cgit v1.3.1 From 0ab4f91a107832692781a367a1ef2173af75f108 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:33 -0600 Subject: video: Provide a way to clear part of the console This is useful when the background colour must be written before text is updated, to avoid strange display artifacts. Add a function for this, using the existing code from the truetype console. Signed-off-by: Simon Glass --- drivers/video/console_truetype.c | 72 ++-------------------------------------- drivers/video/video-uclass.c | 52 +++++++++++++++++++++++++++++ include/video.h | 16 +++++++++ 3 files changed, 71 insertions(+), 69 deletions(-) (limited to 'include') diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c index 0ea8a9f6215..63d7557c71a 100644 --- a/drivers/video/console_truetype.c +++ b/drivers/video/console_truetype.c @@ -378,72 +378,6 @@ static int console_truetype_putc_xy(struct udevice *dev, uint x, uint y, return width_frac; } -/** - * console_truetype_erase() - Erase a character - * - * This is used for backspace. We erase a square of the display within the - * given bounds. - * - * @dev: Device to update - * @xstart: X start position in pixels from the left - * @ystart: Y start position in pixels from the top - * @xend: X end position in pixels from the left - * @yend: Y end position in pixels from the top - * @clr: Value to write - * Return: 0 if OK, -ENOSYS if the display depth is not supported - */ -static int console_truetype_erase(struct udevice *dev, int xstart, int ystart, - int xend, int yend, int clr) -{ - struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent); - void *start, *line; - int pixels = xend - xstart; - int row, i, ret; - - start = vid_priv->fb + ystart * vid_priv->line_length; - start += xstart * VNBYTES(vid_priv->bpix); - line = start; - for (row = ystart; row < yend; row++) { - switch (vid_priv->bpix) { - case VIDEO_BPP8: { - uint8_t *dst = line; - - if (IS_ENABLED(CONFIG_VIDEO_BPP8)) { - for (i = 0; i < pixels; i++) - *dst++ = clr; - } - break; - } - case VIDEO_BPP16: { - uint16_t *dst = line; - - if (IS_ENABLED(CONFIG_VIDEO_BPP16)) { - for (i = 0; i < pixels; i++) - *dst++ = clr; - } - break; - } - case VIDEO_BPP32: { - uint32_t *dst = line; - - if (IS_ENABLED(CONFIG_VIDEO_BPP32)) { - for (i = 0; i < pixels; i++) - *dst++ = clr; - } - break; - } - default: - return -ENOSYS; - } - line += vid_priv->line_length; - } - ret = vidconsole_sync_copy(dev, start, line); - if (ret) - return ret; - - return 0; -} - /** * console_truetype_backspace() - Handle a backspace operation * @@ -482,9 +416,9 @@ static int console_truetype_backspace(struct udevice *dev) else xend = vid_priv->xsize; - console_truetype_erase(dev, VID_TO_PIXEL(pos->xpos_frac), pos->ypos, - xend, pos->ypos + vc_priv->y_charsize, - vid_priv->colour_bg); + video_fill_part(vid_dev, VID_TO_PIXEL(pos->xpos_frac), pos->ypos, + xend, pos->ypos + vc_priv->y_charsize, + vid_priv->colour_bg); /* Move the cursor back to where it was when we pushed this record */ vc_priv->xcur_frac = pos->xpos_frac; diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c index 1b66a8061a7..d304e92c244 100644 --- a/drivers/video/video-uclass.c +++ b/drivers/video/video-uclass.c @@ -142,6 +142,58 @@ int video_reserve(ulong *addrp) return 0; } +int video_fill_part(struct udevice *dev, int xstart, int ystart, int xend, + int yend, u32 colour) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + void *start, *line; + int pixels = xend - xstart; + int row, i, ret; + + start = priv->fb + ystart * priv->line_length; + start += xstart * VNBYTES(priv->bpix); + line = start; + for (row = ystart; row < yend; row++) { + switch (priv->bpix) { + case VIDEO_BPP8: { + u8 *dst = line; + + if (IS_ENABLED(CONFIG_VIDEO_BPP8)) { + for (i = 0; i < pixels; i++) + *dst++ = colour; + } + break; + } + case VIDEO_BPP16: { + u16 *dst = line; + + if (IS_ENABLED(CONFIG_VIDEO_BPP16)) { + for (i = 0; i < pixels; i++) + *dst++ = colour; + } + break; + } + case VIDEO_BPP32: { + u32 *dst = line; + + if (IS_ENABLED(CONFIG_VIDEO_BPP32)) { + for (i = 0; i < pixels; i++) + *dst++ = colour; + } + break; + } + default: + return -ENOSYS; + } + line += priv->line_length; + } + ret = video_sync_copy(dev, start, line); + if (ret) + return ret; + + return 0; +} + int video_fill(struct udevice *dev, u32 colour) { struct video_priv *priv = dev_get_uclass_priv(dev); diff --git a/include/video.h b/include/video.h index 03434a81234..6dc42d464b0 100644 --- a/include/video.h +++ b/include/video.h @@ -204,6 +204,22 @@ int video_clear(struct udevice *dev); */ int video_fill(struct udevice *dev, u32 colour); +/** + * video_fill_part() - Erase a region + * + * Erase a rectangle of the display within the given bounds. + * + * @dev: Device to update + * @xstart: X start position in pixels from the left + * @ystart: Y start position in pixels from the top + * @xend: X end position in pixels from the left + * @yend: Y end position in pixels from the top + * @colour: Value to write + * Return: 0 if OK, -ENOSYS if the display depth is not supported + */ +int video_fill_part(struct udevice *dev, int xstart, int ystart, int xend, + int yend, u32 colour); + /** * video_sync() - Sync a device's frame buffer with its hardware * -- cgit v1.3.1 From 42b18494bdaf677c4726ef47a839b16a1a3daba2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:34 -0600 Subject: expo: Store the console in the expo Rather than finding this each time, keep a pointer to it. This simplifies the code a little. Signed-off-by: Simon Glass --- boot/expo.c | 9 +++++++++ boot/scene.c | 10 ++-------- include/expo.h | 2 ++ 3 files changed, 13 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/boot/expo.c b/boot/expo.c index 05950a17603..cd1b1a3de50 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -83,7 +83,16 @@ const char *expo_get_str(struct expo *exp, uint id) int expo_set_display(struct expo *exp, struct udevice *dev) { + struct udevice *cons; + int ret; + + ret = device_find_first_child_by_uclass(dev, UCLASS_VIDEO_CONSOLE, + &cons); + if (ret) + return log_msg_ret("con", ret); + exp->display = dev; + exp->cons = cons; return 0; } diff --git a/boot/scene.c b/boot/scene.c index d2f77c008cf..7e9ba047f2d 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -278,16 +278,10 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode) { struct scene *scn = obj->scene; struct expo *exp = scn->expo; - struct udevice *cons, *dev = exp->display; + struct udevice *dev = exp->display; + struct udevice *cons = text_mode ? NULL : exp->cons; int x, y, ret; - cons = NULL; - if (!text_mode) { - ret = device_find_first_child_by_uclass(dev, - UCLASS_VIDEO_CONSOLE, - &cons); - } - x = obj->x; y = obj->y; diff --git a/include/expo.h b/include/expo.h index 8827f4b0b45..06f5629e03f 100644 --- a/include/expo.h +++ b/include/expo.h @@ -50,6 +50,7 @@ struct expo_action { * * @name: Name of the expo (allocated) * @display: Display to use (`UCLASS_VIDEO`), or NULL to use text mode + * @cons: Console to use (`UCLASS_VIDEO_CONSOLE`), or NULL to use text mode * @scene_id: Current scene ID (0 if none) * @next_id: Next ID number to use, for automatic allocation * @action: Action selected by user. At present only one is supported, with the @@ -62,6 +63,7 @@ struct expo_action { struct expo { char *name; struct udevice *display; + struct udevice *cons; uint scene_id; uint next_id; struct expo_action action; -- cgit v1.3.1 From 5904d953a1183e191e9ec4ab15c31c6522f625ad Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:37 -0600 Subject: expo: Rename exp_set_text_mode() Rename this function to match its peers, using the full "expo' prefix. Signed-off-by: Simon Glass --- boot/bootflow_menu.c | 2 +- boot/expo.c | 2 +- include/expo.h | 4 ++-- test/boot/expo.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/boot/bootflow_menu.c b/boot/bootflow_menu.c index de0f4453569..7c1abe5772c 100644 --- a/boot/bootflow_menu.c +++ b/boot/bootflow_menu.c @@ -209,7 +209,7 @@ int bootflow_menu_run(struct bootstd_priv *std, bool text_mode, return log_msg_ret("scn", ret); if (text_mode) - exp_set_text_mode(exp, text_mode); + expo_set_text_mode(exp, text_mode); done = false; do { diff --git a/boot/expo.c b/boot/expo.c index bfdda9570c6..e7c81a3983d 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -97,7 +97,7 @@ int expo_set_display(struct expo *exp, struct udevice *dev) return 0; } -void exp_set_text_mode(struct expo *exp, bool text_mode) +void expo_set_text_mode(struct expo *exp, bool text_mode) { exp->text_mode = text_mode; } diff --git a/include/expo.h b/include/expo.h index 06f5629e03f..f77ee708519 100644 --- a/include/expo.h +++ b/include/expo.h @@ -306,12 +306,12 @@ int expo_set_scene_id(struct expo *exp, uint scene_id); int expo_render(struct expo *exp); /** - * exp_set_text_mode() - Controls whether the expo renders in text mode + * expo_set_text_mode() - Controls whether the expo renders in text mode * * @exp: Expo to update * @text_mode: true to use text mode, false to use the console */ -void exp_set_text_mode(struct expo *exp, bool text_mode); +void expo_set_text_mode(struct expo *exp, bool text_mode); /** * scene_new() - create a new scene in a expo diff --git a/test/boot/expo.c b/test/boot/expo.c index 56a22ba9b06..70750d307ff 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -507,7 +507,7 @@ static int expo_render_image(struct unit_test_state *uts) ut_assert_console_end(); /* now try in text mode */ - exp_set_text_mode(exp, true); + expo_set_text_mode(exp, true); ut_assertok(expo_render(exp)); ut_assert_nextline("U-Boot : Boot Menu"); -- cgit v1.3.1 From de7b5a8a1ac0b711db683711b3ccd800e00c4c46 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:38 -0600 Subject: fs: Create functions to load and allocate a file This functionality current sits in bootstd, but it is more generally useful. Add a function to load a file into memory, allocating it as needed. Adjust bootstd to use this version. Note: Tests are added in the subsequent patch which converts the 'cat' command to use this function. Signed-off-by: Simon Glass --- boot/bootmeth-uclass.c | 30 ++------------------------ fs/fs.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/fs.h | 38 +++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c index 3b3e0614daf..701ee8ad1c8 100644 --- a/boot/bootmeth-uclass.c +++ b/boot/bootmeth-uclass.c @@ -301,32 +301,6 @@ int bootmeth_try_file(struct bootflow *bflow, struct blk_desc *desc, return 0; } -static int alloc_file(const char *fname, uint size, void **bufp) -{ - loff_t bytes_read; - ulong addr; - char *buf; - int ret; - - buf = malloc(size + 1); - if (!buf) - return log_msg_ret("buf", -ENOMEM); - addr = map_to_sysmem(buf); - - ret = fs_read(fname, addr, 0, size, &bytes_read); - if (ret) { - free(buf); - return log_msg_ret("read", ret); - } - if (size != bytes_read) - return log_msg_ret("bread", -EIO); - buf[size] = '\0'; - - *bufp = buf; - - return 0; -} - int bootmeth_alloc_file(struct bootflow *bflow, uint size_limit, uint align) { void *buf; @@ -338,7 +312,7 @@ int bootmeth_alloc_file(struct bootflow *bflow, uint size_limit, uint align) if (size > size_limit) return log_msg_ret("chk", -E2BIG); - ret = alloc_file(bflow->fname, bflow->size, &buf); + ret = fs_read_alloc(bflow->fname, bflow->size, align, &buf); if (ret) return log_msg_ret("all", ret); @@ -374,7 +348,7 @@ int bootmeth_alloc_other(struct bootflow *bflow, const char *fname, if (ret) return log_msg_ret("fs", ret); - ret = alloc_file(path, size, &buf); + ret = fs_read_alloc(path, size, 0, &buf); if (ret) return log_msg_ret("all", ret); diff --git a/fs/fs.c b/fs/fs.c index 8324b4a22f2..2b815b1db0f 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -1008,3 +1010,59 @@ int do_fs_types(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) puts("\n"); return CMD_RET_SUCCESS; } + +int fs_read_alloc(const char *fname, ulong size, uint align, void **bufp) +{ + loff_t bytes_read; + ulong addr; + char *buf; + int ret; + + buf = memalign(align, size + 1); + if (!buf) + return log_msg_ret("buf", -ENOMEM); + addr = map_to_sysmem(buf); + + ret = fs_read(fname, addr, 0, size, &bytes_read); + if (ret) { + free(buf); + return log_msg_ret("read", ret); + } + if (size != bytes_read) + return log_msg_ret("bread", -EIO); + buf[size] = '\0'; + + *bufp = buf; + + return 0; +} + +int fs_load_alloc(const char *ifname, const char *dev_part_str, + const char *fname, ulong max_size, ulong align, void **bufp, + ulong *sizep) +{ + loff_t size; + void *buf; + int ret; + + if (fs_set_blk_dev(ifname, dev_part_str, FS_TYPE_ANY)) + return log_msg_ret("set", -ENOMEDIUM); + + ret = fs_size(fname, &size); + if (ret) + return log_msg_ret("sz", -ENOENT); + + if (size >= (max_size ?: SZ_1G)) + return log_msg_ret("sz", -E2BIG); + + if (fs_set_blk_dev(ifname, dev_part_str, FS_TYPE_ANY)) + return log_msg_ret("set", -ENOMEDIUM); + + ret = fs_read_alloc(fname, size, align, &buf); + if (ret) + return log_msg_ret("al", ret); + *sizep = size; + *bufp = buf; + + return 0; +} diff --git a/include/fs.h b/include/fs.h index 8370d88cb20..e341a0ed01b 100644 --- a/include/fs.h +++ b/include/fs.h @@ -300,4 +300,42 @@ int do_fs_type(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); */ int do_fs_types(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]); +/** + * fs_read_alloc() - Allocate space for a file and read it + * + * You must call fs_set_blk_dev() or a similar function before calling this, + * since that sets up the block device to use. + * + * The file is terminated with a nul character + * + * @fname: Filename to read + * @size: Size of file to read (must be correct!) + * @align: Alignment to use for memory allocation (0 for default) + * @bufp: On success, returns the allocated buffer with the nul-terminated file + * in it + * Return: 0 if OK, -ENOMEM if out of memory, -EIO if read failed + */ +int fs_read_alloc(const char *fname, ulong size, uint align, void **bufp); + +/** + * fs_load_alloc() - Load a file into allocated space + * + * The file is terminated with a nul character + * + * @ifname: Interface name to read from (e.g. "mmc") + * @dev_part_str: Device and partition string (e.g. "1:2") + * @fname: Filename to read + * @max_size: Maximum allowed size for the file (use 0 for 1GB) + * @align: Alignment to use for memory allocation (0 for default) + * @bufp: On success, returns the allocated buffer with the nul-terminated file + * in it + * @sizep: On success, returns the size of the file + * Return: 0 if OK, -ENOMEM if out of memory, -ENOENT if the file does not + * exist, -ENOMEDIUM if the device does not exist, -E2BIG if the file is too + * large (greater than @max_size), -EIO if read failed + */ +int fs_load_alloc(const char *ifname, const char *dev_part_str, + const char *fname, ulong max_size, ulong align, void **bufp, + ulong *sizep); + #endif /* _FS_H */ -- cgit v1.3.1 From 9cf39bbe96753d1099e3626d0c5be1d6e11c7f42 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:40 -0600 Subject: fdt: Align the start of the livetree Ensure that the block of memory used by live tree is aligned according to the default for structures. This ensures that the root node appears at the start of the block, so it can be used with free(), rather than being 4 bytes later in some cases. This corrects a rather obscure bug in unflatten_device_tree(). Fixes: 8b50d526ea5 ("dm: Add a function to create a 'live' device tree") Signed-off-by: Simon Glass --- include/dm/of.h | 2 ++ lib/of_live.c | 5 ++++- test/dm/ofnode.c | 26 ++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/dm/of.h b/include/dm/of.h index fce7cef0ff6..b1c934f610d 100644 --- a/include/dm/of.h +++ b/include/dm/of.h @@ -63,6 +63,8 @@ struct device_node { struct device_node *sibling; }; +#define BAD_OF_ROOT 0xdead11e3 + #define OF_MAX_PHANDLE_ARGS 16 /** diff --git a/lib/of_live.c b/lib/of_live.c index 1b5964d09a9..05588d5ed28 100644 --- a/lib/of_live.c +++ b/lib/of_live.c @@ -287,9 +287,12 @@ int unflatten_device_tree(const void *blob, struct device_node **mynodes) debug(" size is %lx, allocating...\n", size); /* Allocate memory for the expanded device tree */ - mem = malloc(size + 4); + mem = memalign(__alignof__(struct device_node), size + 4); memset(mem, '\0', size); + /* Set up value for dm_test_livetree_align() */ + *(u32 *)mem = BAD_OF_ROOT; + *(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef); debug(" unflattening %p...\n", mem); diff --git a/test/dm/ofnode.c b/test/dm/ofnode.c index 473a8cef578..64baaf68db3 100644 --- a/test/dm/ofnode.c +++ b/test/dm/ofnode.c @@ -1240,3 +1240,29 @@ static int dm_test_ofnode_copy_props_ot(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_ofnode_copy_props_ot, UT_TESTF_SCAN_FDT | UT_TESTF_OTHER_FDT); + +/* check that the livetree is aligned to a structure boundary */ +static int dm_test_livetree_align(struct unit_test_state *uts) +{ + const int align = __alignof__(struct unit_test_state); + struct device_node *node; + u32 *sentinel; + ulong start; + + start = (ulong)gd_of_root(); + ut_asserteq(start, ALIGN(start, align)); + + node = gd_of_root(); + sentinel = (void *)node - sizeof(u32); + + /* + * The sentinel should be overwritten with the root node. If it isn't, + * then the root node is not at the very start of the livetree memory + * area, and free(root) will fail to free the memory used by the + * livetree. + */ + ut_assert(*sentinel != BAD_OF_ROOT); + + return 0; +} +DM_TEST(dm_test_livetree_align, UT_TESTF_LIVE_TREE); -- cgit v1.3.1 From a8f2ac2ae6f15b675a55bcf3a20181f3bb223562 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:42 -0600 Subject: fdt: Allow more general use of livetree At present livetree can only be used for the control FDT. It is useful to be able to use the ofnode API for other FDTs, e.g. those used by the upcoming configuration editor. We already have most of the support present, and tests can be marked with the UT_TESTF_OTHER_FDT flag to use another FDT as a special case. But with this change, the functionality becomes more generally available. Plumb in the require support. Signed-off-by: Simon Glass --- drivers/core/ofnode.c | 26 +++++++++++++++++++++----- include/dm/ofnode.h | 10 ++++++++++ include/of_live.h | 10 ++++++++++ lib/of_live.c | 6 ++++++ test/dm/ofnode.c | 19 +++++++++++++++++++ 5 files changed, 66 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index dee890b5527..8df16e56af5 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,20 @@ static oftree oftree_ensure(void *fdt) oftree tree; int i; + if (of_live_active()) { + struct device_node *root; + int ret; + + ret = unflatten_device_tree(fdt, &root); + if (ret) { + log_err("Failed to create live tree: err=%d\n", ret); + return oftree_null(); + } + tree = oftree_from_np(root); + + return tree; + } + if (gd->flags & GD_FLG_RELOC) { i = oftree_find(fdt); if (i == -1) { @@ -60,11 +75,6 @@ static oftree oftree_ensure(void *fdt) return oftree_null(); } - if (of_live_active()) { - log_err("Cannot register a flattree when OF_LIVE is active\n"); - return oftree_null(); - } - /* register the new tree */ i = oftree_count++; oftree_list[i] = fdt; @@ -82,6 +92,12 @@ static oftree oftree_ensure(void *fdt) return tree; } +void oftree_dispose(oftree tree) +{ + if (of_live_active()) + of_live_free(tree.np); +} + void *ofnode_lookup_fdt(ofnode node) { if (gd->flags & GD_FLG_RELOC) { diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h index 443db6252dd..0f38b3e736d 100644 --- a/include/dm/ofnode.h +++ b/include/dm/ofnode.h @@ -352,6 +352,16 @@ static inline oftree oftree_from_np(struct device_node *root) return tree; } +/** + * oftree_dispose() - Dispose of an oftree + * + * This can be used to dispose of a tree that has been created (other than + * the control FDT which must not be disposed) + * + * @tree: Tree to dispose + */ +void oftree_dispose(oftree tree); + /** * ofnode_name_eq() - Check if the node name is equivalent to a given name * ignoring the unit address diff --git a/include/of_live.h b/include/of_live.h index f59d6af3350..05e86ac06b1 100644 --- a/include/of_live.h +++ b/include/of_live.h @@ -36,4 +36,14 @@ int of_live_build(const void *fdt_blob, struct device_node **rootp); */ int unflatten_device_tree(const void *blob, struct device_node **mynodes); +/** + * of_live_free() - Dispose of a livetree + * + * This frees memory used by the tree, after which @root becomes invalid and + * cannot be used + * + * @root: Tree to dispose + */ +void of_live_free(struct device_node *root); + #endif diff --git a/lib/of_live.c b/lib/of_live.c index 05588d5ed28..25f7af61061 100644 --- a/lib/of_live.c +++ b/lib/of_live.c @@ -330,3 +330,9 @@ int of_live_build(const void *fdt_blob, struct device_node **rootp) return ret; } + +void of_live_free(struct device_node *root) +{ + /* the tree is stored as a contiguous block of memory */ + free(root); +} diff --git a/test/dm/ofnode.c b/test/dm/ofnode.c index 64baaf68db3..6fbebc7da08 100644 --- a/test/dm/ofnode.c +++ b/test/dm/ofnode.c @@ -1266,3 +1266,22 @@ static int dm_test_livetree_align(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_livetree_align, UT_TESTF_LIVE_TREE); + +/* check that it is possible to load an arbitrary livetree */ +static int dm_test_livetree_ensure(struct unit_test_state *uts) +{ + oftree tree; + ofnode node; + + /* read from other.dtb */ + ut_assertok(test_load_other_fdt(uts)); + tree = oftree_from_fdt(uts->other_fdt); + ut_assert(oftree_valid(tree)); + node = oftree_path(tree, "/node/subnode"); + ut_assert(ofnode_valid(node)); + ut_asserteq_str("sandbox-other2", + ofnode_read_string(node, "compatible")); + + return 0; +} +DM_TEST(dm_test_livetree_ensure, 0); -- cgit v1.3.1 From c98cb512521e5e035774c49ab9a855cbb6b13813 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:43 -0600 Subject: bootstd: Add a separate log category for expo This feature is different enough from bootstd that it probably deserves its own log category. It cannot use a uclass since it is not a device. Add a new category. Signed-off-by: Simon Glass --- boot/expo.c | 2 ++ boot/scene.c | 2 ++ boot/scene_menu.c | 2 +- common/log.c | 1 + include/log.h | 2 ++ 5 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/boot/expo.c b/boot/expo.c index e7c81a3983d..8b966b6c793 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -6,6 +6,8 @@ * Written by Simon Glass */ +#define LOG_CATEGORY LOGC_EXPO + #include #include #include diff --git a/boot/scene.c b/boot/scene.c index 1383be20321..43c978e6ee8 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -6,6 +6,8 @@ * Written by Simon Glass */ +#define LOG_CATEGORY LOGC_EXPO + #include #include #include diff --git a/boot/scene_menu.c b/boot/scene_menu.c index 9ee911f2fa3..8b04d440313 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -6,7 +6,7 @@ * Written by Simon Glass */ -#define LOG_CATEGORY LOGC_BOOT +#define LOG_CATEGORY LOGC_EXPO #include #include diff --git a/common/log.c b/common/log.c index 7cfc49bc28a..6f02a25c593 100644 --- a/common/log.c +++ b/common/log.c @@ -31,6 +31,7 @@ static const char *const log_cat_name[] = { "boot", "event", "fs", + "expo", }; _Static_assert(ARRAY_SIZE(log_cat_name) == LOGC_COUNT - LOGC_NONE, diff --git a/include/log.h b/include/log.h index 3bab40b6171..6e84f080ef3 100644 --- a/include/log.h +++ b/include/log.h @@ -102,6 +102,8 @@ enum log_category_t { LOGC_EVENT, /** @LOGC_FS: Related to filesystems */ LOGC_FS, + /** @LOGC_EXPO: Related to expo handling */ + LOGC_EXPO, /** @LOGC_COUNT: Number of log categories */ LOGC_COUNT, /** @LOGC_END: Sentinel value for lists of log categories */ -- cgit v1.3.1 From 7ea207db2afc1ef70d8dc88fd2914b3ffcfbb1c7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:44 -0600 Subject: video: Correct docs for video_index_to_colour() This uses the private data of the video uclass, not the console uclass (its child). Update the comment to avoid confusion. Signed-off-by: Simon Glass Fixes: a032e4b55ea ("video: Move console colours to the video uclass") --- include/video.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/video.h b/include/video.h index 6dc42d464b0..55e9f32f593 100644 --- a/include/video.h +++ b/include/video.h @@ -163,7 +163,7 @@ enum colour_idx { * The caller has to guarantee that the color index is less than * VID_COLOR_COUNT. * - * @priv private data of the console device + * @priv private data of the video device (UCLASS_VIDEO) * @idx color index * Return: color value */ -- cgit v1.3.1 From 648a4991d0713af656a2fa50ec8e708c2beb044e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:45 -0600 Subject: video: Allow temporary colour changes It is sometimes necessary to highlight some text in a different colour. Add an easy way to do this and then restore the original console colours. Signed-off-by: Simon Glass --- drivers/video/vidconsole-uclass.c | 20 ++++++++++++++++++++ include/video_console.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) (limited to 'include') diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c index a21fde0e1d4..3f89537c47b 100644 --- a/drivers/video/vidconsole-uclass.c +++ b/drivers/video/vidconsole-uclass.c @@ -596,6 +596,26 @@ int vidconsole_select_font(struct udevice *dev, const char *name, uint size) return ops->select_font(dev, name, size); } +void vidconsole_push_colour(struct udevice *dev, enum colour_idx fg, + enum colour_idx bg, struct vidconsole_colour *old) +{ + struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent); + + old->colour_fg = vid_priv->colour_fg; + old->colour_bg = vid_priv->colour_bg; + + vid_priv->colour_fg = video_index_to_colour(vid_priv, fg); + vid_priv->colour_bg = video_index_to_colour(vid_priv, bg); +} + +void vidconsole_pop_colour(struct udevice *dev, struct vidconsole_colour *old) +{ + struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent); + + vid_priv->colour_fg = old->colour_fg; + vid_priv->colour_bg = old->colour_bg; +} + /* Set up the number of rows and colours (rotated drivers override this) */ static int vidconsole_pre_probe(struct udevice *dev) { diff --git a/include/video_console.h b/include/video_console.h index 3db9a7e1fb9..81d4c4d874d 100644 --- a/include/video_console.h +++ b/include/video_console.h @@ -71,6 +71,17 @@ struct vidfont_info { const char *name; }; +/** + * struct vidconsole_colour - Holds colour information + * + * @colour_fg: Foreground colour (pixel value) + * @colour_bg: Background colour (pixel value) + */ +struct vidconsole_colour { + u32 colour_fg; + u32 colour_bg; +}; + /** * struct vidconsole_ops - Video console operations * @@ -204,6 +215,25 @@ int vidconsole_get_font(struct udevice *dev, int seq, */ int vidconsole_select_font(struct udevice *dev, const char *name, uint size); +/** + * vidconsole_push_colour() - Temporarily change the font colour + * + * @dev: Device to adjust + * @fg: Foreground colour to select + * @bg: Background colour to select + * @old: Place to store the current colour, so it can be restored + */ +void vidconsole_push_colour(struct udevice *dev, enum colour_idx fg, + enum colour_idx bg, struct vidconsole_colour *old); + +/** + * vidconsole_pop_colour() - Restore the original colour + * + * @dev: Device to adjust + * @old: Old colour to be restored + */ +void vidconsole_pop_colour(struct udevice *dev, struct vidconsole_colour *old); + /** * vidconsole_putc_xy() - write a single character to a position * -- cgit v1.3.1 From b828ed7d79295cfebcb0f958f26a33664fae045c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:46 -0600 Subject: console: Allow measuring the bounding box of text For laying out text accurately it is necessary to know the width and height of the text. Add a measure() method to the console API, so this can be supported. Add an implementation for truetype and a base implementation for the normal console. Signed-off-by: Simon Glass --- drivers/video/console_truetype.c | 64 +++++++++++++++++++++++++++++++++++++-- drivers/video/vidconsole-uclass.c | 22 ++++++++++++++ include/video_console.h | 48 +++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c index 6f3fc82f9b0..288123a2e06 100644 --- a/drivers/video/console_truetype.c +++ b/drivers/video/console_truetype.c @@ -614,8 +614,8 @@ static void select_metrics(struct udevice *dev, struct console_tt_metrics *met) vc_priv->tab_width_frac = VID_TO_POS(met->font_size) * 8 / 2; } -static int truetype_select_font(struct udevice *dev, const char *name, - uint size) +static int get_metrics(struct udevice *dev, const char *name, uint size, + struct console_tt_metrics **metp) { struct console_tt_priv *priv = dev_get_priv(dev); struct console_tt_metrics *met; @@ -653,11 +653,70 @@ static int truetype_select_font(struct udevice *dev, const char *name, met = priv->metrics; } + *metp = met; + + return 0; +} + +static int truetype_select_font(struct udevice *dev, const char *name, + uint size) +{ + struct console_tt_metrics *met; + int ret; + + ret = get_metrics(dev, name, size, &met); + if (ret) + return log_msg_ret("sel", ret); + select_metrics(dev, met); return 0; } +int truetype_measure(struct udevice *dev, const char *name, uint size, + const char *text, struct vidconsole_bbox *bbox) +{ + struct console_tt_metrics *met; + stbtt_fontinfo *font; + int lsb, advance; + const char *s; + int width; + int last; + int ret; + + ret = get_metrics(dev, name, size, &met); + if (ret) + return log_msg_ret("sel", ret); + + bbox->valid = false; + if (!*text) + return 0; + + font = &met->font; + width = 0; + for (last = 0, s = text; *s; s++) { + int ch = *s; + + /* Used kerning to fine-tune the position of this character */ + if (last) + width += stbtt_GetCodepointKernAdvance(font, last, ch); + + /* First get some basic metrics about this character */ + stbtt_GetCodepointHMetrics(font, ch, &advance, &lsb); + + width += advance; + last = ch; + } + + bbox->valid = true; + bbox->x0 = 0; + bbox->y0 = 0; + bbox->x1 = tt_ceil((double)width * met->scale); + bbox->y1 = met->font_size; + + return 0; +} + const char *console_truetype_get_font_size(struct udevice *dev, uint *sizep) { struct console_tt_priv *priv = dev_get_priv(dev); @@ -709,6 +768,7 @@ struct vidconsole_ops console_truetype_ops = { .get_font = console_truetype_get_font, .get_font_size = console_truetype_get_font_size, .select_font = truetype_select_font, + .measure = truetype_measure, }; U_BOOT_DRIVER(vidconsole_truetype) = { diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c index 3f89537c47b..05f93047809 100644 --- a/drivers/video/vidconsole-uclass.c +++ b/drivers/video/vidconsole-uclass.c @@ -596,6 +596,28 @@ int vidconsole_select_font(struct udevice *dev, const char *name, uint size) return ops->select_font(dev, name, size); } +int vidconsole_measure(struct udevice *dev, const char *name, uint size, + const char *text, struct vidconsole_bbox *bbox) +{ + struct vidconsole_priv *priv = dev_get_uclass_priv(dev); + struct vidconsole_ops *ops = vidconsole_get_ops(dev); + int ret; + + if (ops->select_font) { + ret = ops->measure(dev, name, size, text, bbox); + if (ret != -ENOSYS) + return ret; + } + + bbox->valid = true; + bbox->x0 = 0; + bbox->y0 = 0; + bbox->x1 = priv->x_charsize * strlen(text); + bbox->y1 = priv->y_charsize; + + return 0; +} + void vidconsole_push_colour(struct udevice *dev, enum colour_idx fg, enum colour_idx bg, struct vidconsole_colour *old) { diff --git a/include/video_console.h b/include/video_console.h index 81d4c4d874d..2694e44f6ec 100644 --- a/include/video_console.h +++ b/include/video_console.h @@ -82,6 +82,27 @@ struct vidconsole_colour { u32 colour_bg; }; +/** + * struct vidconsole_bbox - Bounding box of text + * + * This describes the bounding box of something, measured in pixels. The x0/y0 + * pair is inclusive; the x1/y2 pair is exclusive, meaning that it is one pixel + * beyond the extent of the object + * + * @valid: Values are valid (bounding box is known) + * @x0: left x position, in pixels from left side + * @y0: top y position, in pixels from top + * @x1: right x position + 1 + * @y1: botton y position + 1 + */ +struct vidconsole_bbox { + bool valid; + int x0; + int y0; + int x1; + int y1; +}; + /** * struct vidconsole_ops - Video console operations * @@ -189,6 +210,20 @@ struct vidconsole_ops { * Returns: 0 on success, -ENOENT if no such font */ int (*select_font)(struct udevice *dev, const char *name, uint size); + + /** + * measure() - Measure the bounds of some text + * + * @dev: Device to adjust + * @name: Font name to use (NULL to use default) + * @size: Font size to use (0 to use default) + * @text: Text to measure + * @bbox: Returns bounding box of text, assuming it is positioned + * at 0,0 + * Returns: 0 on success, -ENOENT if no such font + */ + int (*measure)(struct udevice *dev, const char *name, uint size, + const char *text, struct vidconsole_bbox *bbox); }; /* Get a pointer to the driver operations for a video console device */ @@ -215,6 +250,19 @@ int vidconsole_get_font(struct udevice *dev, int seq, */ int vidconsole_select_font(struct udevice *dev, const char *name, uint size); +/* + * vidconsole_measure() - Measuring the bounding box of some text + * + * @dev: Console device to use + * @name: Font name, NULL for default + * @size: Font size, ignored if @name is NULL + * @text: Text to measure + * @bbox: Returns nounding box of text + * Returns: 0 if OK, -ve on error + */ +int vidconsole_measure(struct udevice *dev, const char *name, uint size, + const char *text, struct vidconsole_bbox *bbox); + /** * vidconsole_push_colour() - Temporarily change the font colour * -- cgit v1.3.1 From 9af341502c1cdf1d40a41e8fe6c3922f1e409f33 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:47 -0600 Subject: expo: Allow setting the start of the dynamic-ID range Provide a way to set this value so that it is easy to separate the statically allocated IDs (generated by the caller) from those generated dynamically by expo itself. Signed-off-by: Simon Glass --- boot/expo.c | 15 +++++++++++++++ boot/scene.c | 10 ---------- doc/develop/expo.rst | 3 +++ include/expo.h | 19 +++++++++++++++++++ 4 files changed, 37 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/boot/expo.c b/boot/expo.c index 8b966b6c793..be11cfd4e94 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -56,6 +56,21 @@ void expo_destroy(struct expo *exp) free(exp); } +uint resolve_id(struct expo *exp, uint id) +{ + if (!id) + id = exp->next_id++; + else if (id >= exp->next_id) + exp->next_id = id + 1; + + return id; +} + +void expo_set_dynamic_start(struct expo *exp, uint dyn_start) +{ + exp->next_id = dyn_start; +} + int expo_str(struct expo *exp, const char *name, uint id, const char *str) { struct expo_string *estr; diff --git a/boot/scene.c b/boot/scene.c index 43c978e6ee8..2ac9bfcdbd5 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -18,16 +18,6 @@ #include #include "scene_internal.h" -uint resolve_id(struct expo *exp, uint id) -{ - if (!id) - id = exp->next_id++; - else if (id >= exp->next_id) - exp->next_id = id + 1; - - return id; -} - int scene_new(struct expo *exp, const char *name, uint id, struct scene **scnp) { struct scene *scn; diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst index 32dd7f09030..9565974a28e 100644 --- a/doc/develop/expo.rst +++ b/doc/develop/expo.rst @@ -85,6 +85,9 @@ or even the IDs of objects. Programmatic creation of many items in a loop can be handled by allocating space in the enum for a maximum number of items, then adding the loop count to the enum values to obtain unique IDs. +Where dynamic IDs are need, use expo_set_dynamic_start() to set the start value, +so that they are allocated above the starting (enum) IDs. + All text strings are stored in a structure attached to the expo, referenced by a text ID. This makes it easier at some point to implement multiple languages or to support Unicode strings. diff --git a/include/expo.h b/include/expo.h index f77ee708519..b8f5327f266 100644 --- a/include/expo.h +++ b/include/expo.h @@ -257,6 +257,25 @@ int expo_new(const char *name, void *priv, struct expo **expp); */ void expo_destroy(struct expo *exp); +/** + * expo_set_dynamic_start() - Set the start of the 'dynamic' IDs + * + * It is common for a set of 'static' IDs to be used to refer to objects in the + * expo. These typically use an enum so that they are defined in sequential + * order. + * + * Dynamic IDs (for objects not in the enum) are intended to be used for + * objects to which the code does not need to refer. These are ideally located + * above the static IDs. + * + * Use this function to set the start of the dynamic range, making sure that the + * value is higher than all the statically allocated IDs. + * + * @exp: Expo to update + * @dyn_start: Start ID that expo should use for dynamic allocation + */ +void expo_set_dynamic_start(struct expo *exp, uint dyn_start); + /** * expo_str() - add a new string to an expo * -- cgit v1.3.1 From 2d6ee92c6af06eeb7f7410180d5faa8f5e840bbb Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:48 -0600 Subject: video: Use enum with video_index_to_colour() Use the provided enum with this function, so it is clearer what should be passed to it. Signed-off-by: Simon Glass --- drivers/video/video-uclass.c | 2 +- include/video.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c index d304e92c244..95e874b770b 100644 --- a/drivers/video/video-uclass.c +++ b/drivers/video/video-uclass.c @@ -260,7 +260,7 @@ static const struct vid_rgb colours[VID_COLOUR_COUNT] = { { 0xff, 0xff, 0xff }, /* white */ }; -u32 video_index_to_colour(struct video_priv *priv, unsigned int idx) +u32 video_index_to_colour(struct video_priv *priv, enum colour_idx idx) { switch (priv->bpix) { case VIDEO_BPP16: diff --git a/include/video.h b/include/video.h index 55e9f32f593..e98d0f9c895 100644 --- a/include/video.h +++ b/include/video.h @@ -164,10 +164,10 @@ enum colour_idx { * VID_COLOR_COUNT. * * @priv private data of the video device (UCLASS_VIDEO) - * @idx color index + * @idx color index (e.g. VID_YELLOW) * Return: color value */ -u32 video_index_to_colour(struct video_priv *priv, unsigned int idx); +u32 video_index_to_colour(struct video_priv *priv, enum colour_idx idx); /** * video_reserve() - Reserve frame-buffer memory for video devices -- cgit v1.3.1 From ae45d6cf5a564851a9b9d58e05425e4cf1dfe8aa Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:49 -0600 Subject: expo: Add width and height to objects At present objects only have a position so it is not possible to determine the amount of space they take up on the display. Add width and height properties, using a struct to keep all the dimensions together. For now this is not used. Future work will set up these new properties. Signed-off-by: Simon Glass --- boot/scene.c | 8 ++++---- boot/scene_menu.c | 12 ++++++------ include/expo.h | 21 +++++++++++++++++---- test/boot/expo.c | 32 ++++++++++++++++---------------- 4 files changed, 43 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/boot/scene.c b/boot/scene.c index 2ac9bfcdbd5..8033d77fb26 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -201,8 +201,8 @@ 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); - obj->x = x; - obj->y = y; + obj->dim.x = x; + obj->dim.y = y; return 0; } @@ -272,8 +272,8 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode) struct udevice *cons = text_mode ? NULL : exp->cons; int x, y, ret; - x = obj->x; - y = obj->y; + x = obj->dim.x; + y = obj->dim.y; switch (obj->type) { case SCENEOBJT_NONE: diff --git a/boot/scene_menu.c b/boot/scene_menu.c index 8b04d440313..eed7565f6a6 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -49,9 +49,9 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu) int y, cur_y; int ret; - y = menu->obj.y; + y = menu->obj.dim.y; if (menu->title_id) { - ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.x, y); + ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.dim.x, y); if (ret < 0) return log_msg_ret("tit", ret); @@ -89,18 +89,18 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu) * pointer, then the key and the description */ if (item->label_id) { - ret = scene_obj_set_pos(scn, item->label_id, menu->obj.x, + ret = scene_obj_set_pos(scn, item->label_id, menu->obj.dim.x, y); if (ret < 0) return log_msg_ret("nam", ret); } - ret = scene_obj_set_pos(scn, item->key_id, menu->obj.x + 230, + ret = scene_obj_set_pos(scn, item->key_id, menu->obj.dim.x + 230, y); if (ret < 0) return log_msg_ret("key", ret); - ret = scene_obj_set_pos(scn, item->desc_id, menu->obj.x + 280, + ret = scene_obj_set_pos(scn, item->desc_id, menu->obj.dim.x + 280, y); if (ret < 0) return log_msg_ret("des", ret); @@ -134,7 +134,7 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu) * points to */ ret = scene_obj_set_pos(scn, menu->pointer_id, - menu->obj.x + 200, cur_y); + menu->obj.dim.x + 200, cur_y); if (ret < 0) return log_msg_ret("ptr", ret); } diff --git a/include/expo.h b/include/expo.h index b8f5327f266..5135954ba13 100644 --- a/include/expo.h +++ b/include/expo.h @@ -122,6 +122,21 @@ enum scene_obj_t { SCENEOBJT_MENU, }; +/** + * struct scene_dim - Dimensions of an object + * + * @x: x position, in pixels from left side + * @y: y position, in pixels from top + * @w: width, in pixels + * @h: height, in pixels + */ +struct scene_dim { + int x; + int y; + int w; + int h; +}; + /** * struct scene_obj - information about an object in a scene * @@ -129,8 +144,7 @@ enum scene_obj_t { * @name: Name of the object (allocated) * @id: ID number of the object * @type: Type of this object - * @x: x position, in pixels from left side - * @y: y position, in pixels from top + * @dim: Dimensions for this object * @hide: true if the object should be hidden * @sibling: Node to link this object to its siblings */ @@ -139,8 +153,7 @@ struct scene_obj { char *name; uint id; enum scene_obj_t type; - int x; - int y; + struct scene_dim dim; bool hide; struct list_head sibling; }; diff --git a/test/boot/expo.c b/test/boot/expo.c index 70750d307ff..10cb7b246f3 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -250,8 +250,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.x); - ut_asserteq(456, img->obj.y); + ut_asserteq(123, img->obj.dim.x); + ut_asserteq(456, img->obj.dim.y); ut_asserteq(-ENOENT, scene_obj_set_pos(scn, OBJ_TEXT2, 0, 0)); @@ -307,8 +307,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.x); - ut_asserteq(400, menu->obj.y); + ut_asserteq(50, menu->obj.dim.x); + ut_asserteq(400, menu->obj.dim.y); id = scene_txt_str(scn, "title", OBJ_MENU_TITLE, STR_MENU_TITLE, "Main Menu", &tit); @@ -354,24 +354,24 @@ static int expo_object_menu(struct unit_test_state *uts) ut_asserteq(id, menu->cur_item_id); /* the title should be at the top */ - ut_asserteq(menu->obj.x, tit->obj.x); - ut_asserteq(menu->obj.y, tit->obj.y); + ut_asserteq(menu->obj.dim.x, tit->obj.dim.x); + ut_asserteq(menu->obj.dim.y, tit->obj.dim.y); /* the first item should be next */ - ut_asserteq(menu->obj.x, name1->obj.x); - ut_asserteq(menu->obj.y + 32, name1->obj.y); + ut_asserteq(menu->obj.dim.x, name1->obj.dim.x); + ut_asserteq(menu->obj.dim.y + 32, name1->obj.dim.y); - ut_asserteq(menu->obj.x + 230, key1->obj.x); - ut_asserteq(menu->obj.y + 32, key1->obj.y); + ut_asserteq(menu->obj.dim.x + 230, key1->obj.dim.x); + ut_asserteq(menu->obj.dim.y + 32, key1->obj.dim.y); - ut_asserteq(menu->obj.x + 200, ptr->obj.x); - ut_asserteq(menu->obj.y + 32, ptr->obj.y); + ut_asserteq(menu->obj.dim.x + 200, ptr->obj.dim.x); + ut_asserteq(menu->obj.dim.y + 32, ptr->obj.dim.y); - ut_asserteq(menu->obj.x + 280, desc1->obj.x); - ut_asserteq(menu->obj.y + 32, desc1->obj.y); + ut_asserteq(menu->obj.dim.x + 280, desc1->obj.dim.x); + ut_asserteq(menu->obj.dim.y + 32, desc1->obj.dim.y); - ut_asserteq(-4, prev1->obj.x); - ut_asserteq(menu->obj.y + 32, prev1->obj.y); + ut_asserteq(-4, prev1->obj.dim.x); + ut_asserteq(menu->obj.dim.y + 32, prev1->obj.dim.y); ut_asserteq(false, prev1->obj.hide); expo_destroy(exp); -- cgit v1.3.1 From ce72c9ec260d18cc127c275daf3ec1d18c230e2a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:50 -0600 Subject: expo: Use flags for objects We currently have just a 'hide' property for each object. In preparation for adding more properties, convert the struct to use a flags value, instead of individual booleans. This is more extensible. Signed-off-by: Simon Glass --- boot/scene.c | 17 +++++++++++++++-- boot/scene_internal.h | 11 +++++++++++ include/expo.h | 13 +++++++++++-- test/boot/expo.c | 6 +++--- 4 files changed, 40 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/boot/scene.c b/boot/scene.c index 8033d77fb26..dd1472d4f9a 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -208,13 +208,26 @@ int scene_obj_set_pos(struct scene *scn, uint id, int x, int y) } int scene_obj_set_hide(struct scene *scn, uint id, bool hide) +{ + int ret; + + ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE, + hide ? SCENEOF_HIDE : 0); + if (ret) + return log_msg_ret("flg", ret); + + return 0; +} + +int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set) { struct scene_obj *obj; obj = scene_obj_find(scn, id, SCENEOBJT_NONE); if (!obj) return log_msg_ret("find", -ENOENT); - obj->hide = hide; + obj->flags &= ~clr; + obj->flags |= set; return 0; } @@ -358,7 +371,7 @@ int scene_render(struct scene *scn) int ret; list_for_each_entry(obj, &scn->obj_head, sibling) { - if (!obj->hide) { + if (!(obj->flags & SCENEOF_HIDE)) { 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 9f173dd749f..24a2ba6a6a3 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -54,6 +54,17 @@ void *scene_obj_find(struct scene *scn, uint id, enum scene_obj_t type); int scene_obj_add(struct scene *scn, const char *name, uint id, enum scene_obj_t type, uint size, struct scene_obj **objp); +/** + * scene_obj_flag_clrset() - Adjust object flags + * + * @scn: Scene to update + * @id: ID of object to update + * @clr: Bits to clear in the object's flags + * @set: Bits to set in the object's flags + * Returns 0 if OK, -ENOENT if the object was not found + */ +int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set); + /** * scene_menu_arrange() - Set the position of things in the menu * diff --git a/include/expo.h b/include/expo.h index 5135954ba13..b6777cebcbe 100644 --- a/include/expo.h +++ b/include/expo.h @@ -137,6 +137,15 @@ struct scene_dim { int h; }; +/** + * enum scene_obj_flags_t - flags for objects + * + * @SCENEOF_HIDE: object should be hidden + */ +enum scene_obj_flags_t { + SCENEOF_HIDE = 1 << 0, +}; + /** * struct scene_obj - information about an object in a scene * @@ -145,7 +154,7 @@ struct scene_dim { * @id: ID number of the object * @type: Type of this object * @dim: Dimensions for this object - * @hide: true if the object should be hidden + * @flags: Flags for this object * @sibling: Node to link this object to its siblings */ struct scene_obj { @@ -154,7 +163,7 @@ struct scene_obj { uint id; enum scene_obj_t type; struct scene_dim dim; - bool hide; + int flags; struct list_head sibling; }; diff --git a/test/boot/expo.c b/test/boot/expo.c index 10cb7b246f3..5088776f7bd 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -372,7 +372,7 @@ static int expo_object_menu(struct unit_test_state *uts) ut_asserteq(-4, prev1->obj.dim.x); ut_asserteq(menu->obj.dim.y + 32, prev1->obj.dim.y); - ut_asserteq(false, prev1->obj.hide); + ut_asserteq(false, prev1->obj.flags & SCENEOF_HIDE); expo_destroy(exp); @@ -488,10 +488,10 @@ static int expo_render_image(struct unit_test_state *uts) /* make sure only the preview for the second item is shown */ obj = scene_obj_find(scn, ITEM1_PREVIEW, SCENEOBJT_NONE); - ut_asserteq(true, obj->hide); + ut_asserteq(true, obj->flags & SCENEOF_HIDE); obj = scene_obj_find(scn, ITEM2_PREVIEW, SCENEOBJT_NONE); - ut_asserteq(false, obj->hide); + ut_asserteq(false, obj->flags & SCENEOF_HIDE); /* select it */ ut_assertok(expo_send_key(exp, BKEY_SELECT)); -- cgit v1.3.1 From 699b0acb522fd808b67b745b541bacf18c275d15 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:52 -0600 Subject: expo: Set up the width and height of objects Provide a way to set the full dimensions of objects, i.e. including the width and height. For menus, calculate the bounding box of all objects in the menu. Set all labels to be the same size, so that highlighting works correct, once implemented. Signed-off-by: Simon Glass --- boot/expo.c | 24 ++++++++++++++++ boot/scene.c | 52 +++++++++++++++++++++++++++++++++ boot/scene_internal.h | 21 ++++++++++++++ boot/scene_menu.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++ doc/develop/expo.rst | 2 -- include/expo.h | 21 ++++++++++++++ test/boot/expo.c | 42 +++++++++++++++++++++++++++ 7 files changed, 239 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/boot/expo.c b/boot/expo.c index be11cfd4e94..67cae3c7e28 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -114,6 +114,30 @@ int expo_set_display(struct expo *exp, struct udevice *dev) return 0; } +int expo_calc_dims(struct expo *exp) +{ + struct scene *scn; + int ret; + + if (!exp->cons) + return log_msg_ret("dim", -ENOTSUPP); + + list_for_each_entry(scn, &exp->scene_head, sibling) { + /* + * Do the menus last so that all the menus' text objects + * are dimensioned + */ + ret = scene_calc_dims(scn, false); + if (ret) + return log_msg_ret("scn", ret); + ret = scene_calc_dims(scn, true); + if (ret) + return log_msg_ret("scn", ret); + } + + return 0; +} + void expo_set_text_mode(struct expo *exp, bool text_mode) { exp->text_mode = text_mode; diff --git a/boot/scene.c b/boot/scene.c index 981a18b3ba1..6d5e3c1f03d 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -207,6 +207,19 @@ int scene_obj_set_pos(struct scene *scn, uint id, int x, int y) return 0; } +int scene_obj_set_size(struct scene *scn, uint id, int w, int h) +{ + struct scene_obj *obj; + + obj = scene_obj_find(scn, id, SCENEOBJT_NONE); + if (!obj) + return log_msg_ret("find", -ENOENT); + obj->dim.w = w; + obj->dim.h = h; + + return 0; +} + int scene_obj_set_hide(struct scene *scn, uint id, bool hide) { int ret; @@ -414,3 +427,42 @@ int scene_send_key(struct scene *scn, int key, struct expo_action *event) return 0; } + +int scene_calc_dims(struct scene *scn, bool do_menus) +{ + struct scene_obj *obj; + int ret; + + list_for_each_entry(obj, &scn->obj_head, sibling) { + switch (obj->type) { + case SCENEOBJT_NONE: + case SCENEOBJT_TEXT: + case SCENEOBJT_IMAGE: { + int width; + + if (!do_menus) { + ret = scene_obj_get_hw(scn, obj->id, &width); + if (ret < 0) + return log_msg_ret("get", ret); + obj->dim.w = width; + obj->dim.h = ret; + } + break; + } + case SCENEOBJT_MENU: { + struct scene_obj_menu *menu; + + if (do_menus) { + menu = (struct scene_obj_menu *)obj; + + ret = scene_menu_calc_dims(menu); + if (ret) + return log_msg_ret("men", ret); + } + break; + } + } + } + + return 0; +} diff --git a/boot/scene_internal.h b/boot/scene_internal.h index 24a2ba6a6a3..00085a2f55d 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -65,6 +65,17 @@ 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_calc_dims() - Calculate the dimensions of the scene objects + * + * Updates the width and height of all objects based on their contents + * + * @scn: Scene to update + * @do_menus: true to calculate only menus, false to calculate everything else + * Returns 0 if OK, -ENOTSUPP if there is no graphical console + */ +int scene_calc_dims(struct scene *scn, bool do_menus); + /** * scene_menu_arrange() - Set the position of things in the menu * @@ -133,4 +144,14 @@ int scene_render(struct scene *scn); */ int scene_send_key(struct scene *scn, int key, struct expo_action *event); +/** + * scene_menu_calc_dims() - Calculate the dimensions of a menu + * + * Updates the width and height of the menu based on its contents + * + * @menu: Menu to update + * Returns 0 if OK, -ENOTSUPP if there is no graphical console + */ +int scene_menu_calc_dims(struct scene_obj_menu *menu); + #endif /* __SCENE_INTERNAL_H */ diff --git a/boot/scene_menu.c b/boot/scene_menu.c index eed7565f6a6..fa79cecdbd9 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -43,6 +43,85 @@ static void menu_point_to_item(struct scene_obj_menu *menu, uint item_id) menu->cur_item_id = item_id; } +static int scene_bbox_union(struct scene *scn, uint id, + struct vidconsole_bbox *bbox) +{ + struct scene_obj *obj; + + if (!id) + return 0; + obj = scene_obj_find(scn, id, SCENEOBJT_NONE); + if (!obj) + return log_msg_ret("obj", -ENOENT); + if (bbox->valid) { + bbox->x0 = min(bbox->x0, obj->dim.x); + bbox->y0 = min(bbox->y0, obj->dim.y); + bbox->x1 = max(bbox->x1, obj->dim.x + obj->dim.w); + bbox->y1 = max(bbox->y1, obj->dim.y + obj->dim.h); + } else { + bbox->x0 = obj->dim.x; + bbox->y0 = obj->dim.y; + bbox->x1 = obj->dim.x + obj->dim.w; + bbox->y1 = obj->dim.y + obj->dim.h; + bbox->valid = true; + } + + return 0; +} + +/** + * scene_menu_calc_bbox() - Calculate bounding boxes for the menu + * + * @menu: Menu to process + * @bbox: Returns bounding box of menu including prompts + * @label_bbox: Returns bounding box of labels + */ +static void scene_menu_calc_bbox(struct scene_obj_menu *menu, + struct vidconsole_bbox *bbox, + struct vidconsole_bbox *label_bbox) +{ + const struct scene_menitem *item; + + bbox->valid = false; + scene_bbox_union(menu->obj.scene, menu->title_id, bbox); + + label_bbox->valid = false; + + list_for_each_entry(item, &menu->item_head, sibling) { + scene_bbox_union(menu->obj.scene, item->label_id, bbox); + scene_bbox_union(menu->obj.scene, item->key_id, bbox); + scene_bbox_union(menu->obj.scene, item->desc_id, bbox); + scene_bbox_union(menu->obj.scene, item->preview_id, bbox); + + /* Get the bounding box of all labels */ + scene_bbox_union(menu->obj.scene, item->label_id, label_bbox); + } +} + +int scene_menu_calc_dims(struct scene_obj_menu *menu) +{ + struct vidconsole_bbox bbox, label_bbox; + const struct scene_menitem *item; + + scene_menu_calc_bbox(menu, &bbox, &label_bbox); + + /* Make all labels the same size */ + if (label_bbox.valid) { + list_for_each_entry(item, &menu->item_head, sibling) { + scene_obj_set_size(menu->obj.scene, item->label_id, + label_bbox.x1 - label_bbox.x0, + label_bbox.y1 - label_bbox.y0); + } + } + + if (bbox.valid) { + menu->obj.dim.w = bbox.x1 - bbox.x0; + menu->obj.dim.h = bbox.y1 - bbox.y0; + } + + return 0; +} + int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu) { struct scene_menitem *item; diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst index 9565974a28e..54861b93acf 100644 --- a/doc/develop/expo.rst +++ b/doc/develop/expo.rst @@ -182,8 +182,6 @@ Some ideas for future work: - Add a Kconfig option to drop the names to save code / data space - Add a Kconfig option to disable vidconsole support to save code / data space - Support both graphical and text menus at the same time on different devices -- Implement proper measurement of object bounding boxes, to permit more exact - layout. This would tidy up the layout when Truetype is not used - Support unicode - Support curses for proper serial-terminal menus diff --git a/include/expo.h b/include/expo.h index b6777cebcbe..6c45c403cf7 100644 --- a/include/expo.h +++ b/include/expo.h @@ -327,6 +327,16 @@ const char *expo_get_str(struct expo *exp, uint id); */ int expo_set_display(struct expo *exp, struct udevice *dev); +/** + * expo_calc_dims() - Calculate the dimensions of the objects + * + * Updates the width and height of all objects based on their contents + * + * @exp: Expo to update + * Returns 0 if OK, -ENOTSUPP if there is no graphical console + */ +int expo_calc_dims(struct expo *exp); + /** * expo_set_scene_id() - Set the current scene ID * @@ -468,6 +478,17 @@ int scene_txt_set_font(struct scene *scn, uint id, const char *font_name, */ int scene_obj_set_pos(struct scene *scn, uint id, int x, int y); +/** + * scene_obj_set_size() - Set the size of an object + * + * @scn: Scene to update + * @id: ID of object to update + * @w: width in pixels + * @h: height in pixels + * Returns: 0 if OK, -ENOENT if @id is invalid + */ +int scene_obj_set_size(struct scene *scn, uint id, int w, int h); + /** * scene_obj_set_hide() - Set whether an object is hidden * diff --git a/test/boot/expo.c b/test/boot/expo.c index 5088776f7bd..493d050baf8 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -473,6 +473,48 @@ static int expo_render_image(struct unit_test_state *uts) /* render without a scene */ ut_asserteq(-ECHILD, expo_render(exp)); + ut_assertok(expo_calc_dims(exp)); + ut_assertok(scene_arrange(scn)); + + /* check dimensions of text */ + obj = scene_obj_find(scn, OBJ_TEXT, SCENEOBJT_NONE); + ut_assertnonnull(obj); + ut_asserteq(400, obj->dim.x); + ut_asserteq(100, obj->dim.y); + ut_asserteq(126, obj->dim.w); + ut_asserteq(40, obj->dim.h); + + /* check dimensions of image */ + obj = scene_obj_find(scn, OBJ_LOGO, SCENEOBJT_NONE); + ut_assertnonnull(obj); + ut_asserteq(50, obj->dim.x); + ut_asserteq(20, obj->dim.y); + ut_asserteq(160, obj->dim.w); + ut_asserteq(160, obj->dim.h); + + /* check dimensions of menu labels - both should be the same width */ + obj = scene_obj_find(scn, ITEM1_LABEL, SCENEOBJT_NONE); + ut_assertnonnull(obj); + ut_asserteq(50, obj->dim.x); + ut_asserteq(436, obj->dim.y); + ut_asserteq(29, obj->dim.w); + ut_asserteq(18, obj->dim.h); + + obj = scene_obj_find(scn, ITEM2_LABEL, SCENEOBJT_NONE); + ut_assertnonnull(obj); + ut_asserteq(50, obj->dim.x); + ut_asserteq(454, obj->dim.y); + ut_asserteq(29, obj->dim.w); + ut_asserteq(18, obj->dim.h); + + /* check dimensions of menu */ + obj = scene_obj_find(scn, OBJ_MENU, SCENEOBJT_NONE); + ut_assertnonnull(obj); + ut_asserteq(50, obj->dim.x); + ut_asserteq(400, obj->dim.y); + ut_asserteq(160, obj->dim.w); + ut_asserteq(160, obj->dim.h); + /* render it */ expo_set_scene_id(exp, SCENE1); ut_assertok(expo_render(exp)); -- cgit v1.3.1 From 2e59389704cd1e46101f7ffda2dac3f44f2fa332 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:53 -0600 Subject: expo: Support simple themes It is a pain to manually set the fonts of all objects to be consistent. Some spacing settings are also better set globally than by manually positioning each object. Add a 'theme' to the expo, to hold this information. For now it includes only the font size. Signed-off-by: Simon Glass --- boot/expo.c | 20 ++++++++++++++++++++ boot/scene.c | 28 ++++++++++++++++++++++++++++ boot/scene_internal.h | 9 +++++++++ doc/develop/expo.rst | 9 +++++++-- include/expo.h | 24 ++++++++++++++++++++++++ test/boot/expo.c | 8 +++++++- 6 files changed, 95 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/boot/expo.c b/boot/expo.c index 67cae3c7e28..d5e935966bf 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -231,3 +231,23 @@ int expo_action_get(struct expo *exp, struct expo_action *act) return act->type == EXPOACT_NONE ? -EAGAIN : 0; } + +int expo_apply_theme(struct expo *exp, ofnode node) +{ + struct scene *scn; + struct expo_theme *theme = &exp->theme; + int ret; + + log_debug("Applying theme %s\n", ofnode_get_name(node)); + + memset(theme, '\0', sizeof(struct expo_theme)); + ofnode_read_u32(node, "font-size", &theme->font_size); + + list_for_each_entry(scn, &exp->scene_head, sibling) { + ret = scene_apply_theme(scn, theme); + if (ret) + return log_msg_ret("app", ret); + } + + return 0; +} diff --git a/boot/scene.c b/boot/scene.c index 6d5e3c1f03d..4dbe12a2b74 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -466,3 +466,31 @@ int scene_calc_dims(struct scene *scn, bool do_menus) return 0; } + +int scene_apply_theme(struct scene *scn, struct expo_theme *theme) +{ + struct scene_obj *obj; + int ret; + + /* Avoid error-checking optional items */ + scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size); + + list_for_each_entry(obj, &scn->obj_head, sibling) { + switch (obj->type) { + case SCENEOBJT_NONE: + case SCENEOBJT_IMAGE: + case SCENEOBJT_MENU: + break; + case SCENEOBJT_TEXT: + scene_txt_set_font(scn, obj->id, NULL, + theme->font_size); + break; + } + } + + ret = scene_arrange(scn); + if (ret) + return log_msg_ret("arr", ret); + + return 0; +} diff --git a/boot/scene_internal.h b/boot/scene_internal.h index 00085a2f55d..3387a90761a 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -89,6 +89,15 @@ int scene_calc_dims(struct scene *scn, bool do_menus); */ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu); +/** + * scene_apply_theme() - Apply a theme to a scene + * + * @scn: Scene to update + * @theme: Theme to apply + * Returns: 0 if OK, -ve on error + */ +int scene_apply_theme(struct scene *scn, struct expo_theme *theme); + /** * scene_menu_send_key() - Send a key to a menu for processing * diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst index 54861b93acf..2f4882899b6 100644 --- a/doc/develop/expo.rst +++ b/doc/develop/expo.rst @@ -155,8 +155,13 @@ such as scanning devices for more bootflows. Themes ------ -Expo does not itself support themes. The bootflow_menu implement supposed a -basic theme, applying font sizes to the various text objects in the expo. +Expo supports simple themes, for setting the font size, for example. Use the +expo_apply_theme() function to load a theme, passing a node with the required +properties: + +font-size + Font size to use for all text (type: u32) + API documentation ----------------- diff --git a/include/expo.h b/include/expo.h index 6c45c403cf7..ea8f38913d8 100644 --- a/include/expo.h +++ b/include/expo.h @@ -7,6 +7,7 @@ #ifndef __SCENE_H #define __SCENE_H +#include #include struct udevice; @@ -42,6 +43,19 @@ struct expo_action { }; }; +/** + * struct expo_theme - theme for the expo + * + * @font_size: Default font size for all text + * @menu_inset: Inset width (on each side and top/bottom) for menu items + * @menuitem_gap_y: Gap between menu items in pixels + */ +struct expo_theme { + u32 font_size; + u32 menu_inset; + u32 menuitem_gap_y; +}; + /** * struct expo - information about an expo * @@ -57,6 +71,7 @@ struct expo_action { * type set to EXPOACT_NONE if there is no action * @text_mode: true to use text mode for the menu (no vidconsole) * @priv: Private data for the controller + * @theme: Information about fonts styles, etc. * @scene_head: List of scenes * @str_head: list of strings */ @@ -69,6 +84,7 @@ struct expo { struct expo_action action; bool text_mode; void *priv; + struct expo_theme theme; struct list_head scene_head; struct list_head str_head; }; @@ -583,4 +599,12 @@ int expo_send_key(struct expo *exp, int key); */ int expo_action_get(struct expo *exp, struct expo_action *act); +/** + * expo_apply_theme() - Apply a theme to an expo + * + * @exp: Expo to update + * @node: Node containing the theme + */ +int expo_apply_theme(struct expo *exp, ofnode node); + #endif /*__SCENE_H */ diff --git a/test/boot/expo.c b/test/boot/expo.c index 493d050baf8..c0ef03b5911 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -226,7 +226,7 @@ static int expo_object(struct unit_test_state *uts) } BOOTSTD_TEST(expo_object, UT_TESTF_DM | UT_TESTF_SCAN_FDT); -/* Check setting object attributes */ +/* Check setting object attributes and using themes */ static int expo_object_attr(struct unit_test_state *uts) { struct scene_obj_menu *menu; @@ -236,6 +236,7 @@ static int expo_object_attr(struct unit_test_state *uts) struct expo *exp; ulong start_mem; char name[100]; + ofnode node; char *data; int id; @@ -273,6 +274,11 @@ static int expo_object_attr(struct unit_test_state *uts) ut_asserteq(-ENOENT, scene_menu_set_title(scn, OBJ_TEXT2, OBJ_TEXT)); ut_asserteq(-EINVAL, scene_menu_set_title(scn, OBJ_MENU, OBJ_TEXT2)); + node = ofnode_path("/bootstd/theme"); + ut_assert(ofnode_valid(node)); + ut_assertok(expo_apply_theme(exp, node)); + ut_asserteq(30, txt->font_size); + expo_destroy(exp); ut_assertok(ut_check_delta(start_mem)); -- cgit v1.3.1 From d3db0216dc163594309a9930b31e3161261cd873 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:55 -0600 Subject: expo: Support drawing of popup menus At present only a single menu is supported. All items are shown and a pointer object points to the current item. Add support for multiple menus, one of which is highlighted, indicated by the highlight_id property in the scene. The highlighted menu item has a SCENEOF_POINT flag, indicating that it is currently pointed to. The popup menu is normally closed, in which case it shows only the current menu item. When it is opened, it shows all items, allowing the user to select one. Rather than requiring the menu item to have a description, require it to have a label. Use the label (only) for the popup menu. With this, most of the drawing and layout logic is complete. Signed-off-by: Simon Glass --- boot/scene_menu.c | 58 +++++++++++++++++++++++++++++++++++-------------------- include/expo.h | 9 +++++++++ 2 files changed, 46 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/boot/scene_menu.c b/boot/scene_menu.c index 154be7f0127..f2832b4ddfd 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -56,6 +56,7 @@ static struct scene_menitem *scene_menuitem_find(struct scene_obj_menu *menu, static int update_pointers(struct scene_obj_menu *menu, uint id, bool point) { struct scene *scn = menu->obj.scene; + const bool stack = scn->expo->popup; const struct scene_menitem *item; int ret; @@ -75,6 +76,12 @@ static int update_pointers(struct scene_obj_menu *menu, uint id, bool point) return log_msg_ret("ptr", ret); } + if (stack) { + point &= scn->highlight_id == menu->obj.id; + scene_obj_flag_clrset(scn, item->label_id, SCENEOF_POINT, + point ? SCENEOF_POINT : 0); + } + return 0; } @@ -172,11 +179,15 @@ int scene_menu_calc_dims(struct scene_obj_menu *menu) int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu) { + const bool open = menu->obj.flags & SCENEOF_OPEN; + struct expo *exp = scn->expo; + const bool stack = exp->popup; struct scene_menitem *item; uint sel_id; - int y, cur_y; + int x, y; int ret; + x = menu->obj.dim.x; y = menu->obj.dim.y; if (menu->title_id) { ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.dim.x, y); @@ -187,7 +198,10 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu) if (ret < 0) return log_msg_ret("hei", ret); - y += ret * 2; + if (stack) + x += 200; + else + y += ret * 2; } /* @@ -198,9 +212,10 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu) */ sel_id = menu->cur_item_id; list_for_each_entry(item, &menu->item_head, sibling) { + bool selected; int height; - ret = scene_obj_get_hw(scn, item->desc_id, NULL); + ret = scene_obj_get_hw(scn, item->label_id, NULL); if (ret < 0) return log_msg_ret("get", ret); height = ret; @@ -212,29 +227,29 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu) if (!sel_id) sel_id = item->id; + selected = sel_id == item->id; + /* * Put the label on the left, then leave a space for the * pointer, then the key and the description */ - if (item->label_id) { - ret = scene_obj_set_pos(scn, item->label_id, menu->obj.dim.x, - y); - if (ret < 0) - return log_msg_ret("nam", ret); - } - - ret = scene_obj_set_pos(scn, item->key_id, menu->obj.dim.x + 230, - y); + ret = scene_obj_set_pos(scn, item->label_id, x, y); if (ret < 0) - return log_msg_ret("key", ret); + return log_msg_ret("nam", ret); + scene_obj_set_hide(scn, item->label_id, + stack && !open && !selected); - ret = scene_obj_set_pos(scn, item->desc_id, menu->obj.dim.x + 280, - y); - if (ret < 0) - return log_msg_ret("des", ret); + if (item->key_id) { + ret = scene_obj_set_pos(scn, item->key_id, x + 230, y); + if (ret < 0) + return log_msg_ret("key", ret); + } - if (menu->cur_item_id == item->id) - cur_y = y; + if (item->desc_id) { + ret = scene_obj_set_pos(scn, item->desc_id, x + 280, y); + if (ret < 0) + return log_msg_ret("des", ret); + } if (item->preview_id) { bool hide; @@ -253,7 +268,8 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu) return log_msg_ret("hid", ret); } - y += height; + if (!stack || open) + y += height; } if (sel_id) @@ -380,7 +396,7 @@ int scene_menuitem(struct scene *scn, uint menu_id, const char *name, uint id, return log_msg_ret("find", -ENOENT); /* Check that the text ID is valid */ - if (!scene_obj_find(scn, desc_id, SCENEOBJT_TEXT)) + if (!scene_obj_find(scn, label_id, SCENEOBJT_TEXT)) return log_msg_ret("txt", -EINVAL); item = calloc(1, sizeof(struct scene_obj_menu)); diff --git a/include/expo.h b/include/expo.h index ea8f38913d8..0c55d60f71a 100644 --- a/include/expo.h +++ b/include/expo.h @@ -70,6 +70,7 @@ struct expo_theme { * @action: Action selected by user. At present only one is supported, with the * type set to EXPOACT_NONE if there is no action * @text_mode: true to use text mode for the menu (no vidconsole) + * @popup: true to use popup menus, instead of showing all items * @priv: Private data for the controller * @theme: Information about fonts styles, etc. * @scene_head: List of scenes @@ -83,6 +84,7 @@ struct expo { uint next_id; struct expo_action action; bool text_mode; + bool popup; void *priv; struct expo_theme theme; struct list_head scene_head; @@ -111,6 +113,7 @@ struct expo_string { * @name: Name of the scene (allocated) * @id: ID number of the scene * @title_id: String ID of title of the scene (allocated) + * @highlight_id: ID of highlighted object, if any * @sibling: Node to link this scene to its siblings * @obj_head: List of objects in the scene */ @@ -119,6 +122,7 @@ struct scene { char *name; uint id; uint title_id; + uint highlight_id; struct list_head sibling; struct list_head obj_head; }; @@ -157,9 +161,14 @@ struct scene_dim { * enum scene_obj_flags_t - flags for objects * * @SCENEOF_HIDE: object should be hidden + * @SCENEOF_POINT: object should be highlighted + * @SCENEOF_OPEN: object should be opened (e.g. menu is opened so that an option + * can be selected) */ enum scene_obj_flags_t { SCENEOF_HIDE = 1 << 0, + SCENEOF_POINT = 1 << 1, + SCENEOF_OPEN = 1 << 2, }; /** -- cgit v1.3.1 From 3f33b9c722a41e1577df110470521a014713ce2e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:56 -0600 Subject: expo: Rename EXPOACT_POINT to EXPOACT_POINT_ITEM At present we only support a single menu, so all that can be pointed to is the current menu item. Rename this action so that we can also add an action for pointing to an object. This will allow cycling through the objects in a scene. Signed-off-by: Simon Glass --- boot/scene_menu.c | 4 ++-- include/expo.h | 6 +++--- test/boot/expo.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/boot/scene_menu.c b/boot/scene_menu.c index f2832b4ddfd..892099557a9 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -347,7 +347,7 @@ int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key, struct scene_menitem, sibling)) { item = list_entry(item->sibling.prev, struct scene_menitem, sibling); - event->type = EXPOACT_POINT; + event->type = EXPOACT_POINT_ITEM; event->select.id = item->id; log_debug("up to item %d\n", event->select.id); } @@ -356,7 +356,7 @@ int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key, if (!list_is_last(&item->sibling, &menu->item_head)) { item = list_entry(item->sibling.next, struct scene_menitem, sibling); - event->type = EXPOACT_POINT; + event->type = EXPOACT_POINT_ITEM; event->select.id = item->id; log_debug("down to item %d\n", event->select.id); } diff --git a/include/expo.h b/include/expo.h index 0c55d60f71a..0f438889788 100644 --- a/include/expo.h +++ b/include/expo.h @@ -16,13 +16,13 @@ struct udevice; * enum expoact_type - types of actions reported by the expo * * @EXPOACT_NONE: no action - * @EXPOACT_POINT: menu item was highlighted (@id indicates which) + * @EXPOACT_POINT_ITEM: menu item was highlighted (@id indicates which) * @EXPOACT_SELECT: menu item was selected (@id indicates which) * @EXPOACT_QUIT: request to exit the menu */ enum expoact_type { EXPOACT_NONE, - EXPOACT_POINT, + EXPOACT_POINT_ITEM, EXPOACT_SELECT, EXPOACT_QUIT, }; @@ -31,7 +31,7 @@ enum expoact_type { * struct expo_action - an action report by the expo * * @type: Action type (EXPOACT_NONE if there is no action) - * @select: Used for EXPOACT_POINT and EXPOACT_SELECT + * @select: Used for EXPOACT_POINT_ITEM and EXPOACT_SELECT * @id: ID number of the object affected. */ struct expo_action { diff --git a/test/boot/expo.c b/test/boot/expo.c index 453316ece7c..c34eaeedd66 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -530,7 +530,7 @@ static int expo_render_image(struct unit_test_state *uts) ut_assertok(expo_action_get(exp, &act)); - ut_asserteq(EXPOACT_POINT, act.type); + ut_asserteq(EXPOACT_POINT_ITEM, act.type); ut_asserteq(ITEM2, act.select.id); ut_assertok(expo_render(exp)); @@ -570,7 +570,7 @@ static int expo_render_image(struct unit_test_state *uts) ut_assertok(expo_action_get(exp, &act)); - ut_asserteq(EXPOACT_POINT, act.type); + ut_asserteq(EXPOACT_POINT_ITEM, act.type); ut_asserteq(ITEM1, act.select.id); ut_assertok(expo_render(exp)); -- cgit v1.3.1 From 756c9559e60a0ef8434128205adced937240925d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:57 -0600 Subject: expo: Draw popup menus in both opened and closed states When a popup menu is closed it shows only the selected item. When it is open it shows a background and all items, with a highlight that can be moved between the items. Add the drawing logic for this. Signed-off-by: Simon Glass --- boot/scene.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++-- boot/scene_internal.h | 7 ++++++ boot/scene_menu.c | 29 ++++++++++++++++++++++ include/expo.h | 30 +++++++++++++++++++++++ 4 files changed, 130 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/boot/scene.c b/boot/scene.c index 4dbe12a2b74..fb199ef2953 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -345,14 +345,44 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode) } if (ret && ret != -ENOSYS) return log_msg_ret("font", ret); - vidconsole_set_cursor_pos(cons, x, y); str = expo_get_str(exp, txt->str_id); - if (str) + if (str) { + struct video_priv *vid_priv; + struct vidconsole_colour old; + enum colour_idx fore, back; + + if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) { + fore = VID_BLACK; + back = VID_WHITE; + } else { + fore = VID_LIGHT_GRAY; + back = VID_BLACK; + } + + vid_priv = dev_get_uclass_priv(dev); + if (obj->flags & SCENEOF_POINT) { + vidconsole_push_colour(cons, fore, back, &old); + video_fill_part(dev, x, y, + x + obj->dim.w, y + obj->dim.h, + vid_priv->colour_bg); + } + vidconsole_set_cursor_pos(cons, x, y); vidconsole_put_string(cons, str); + if (obj->flags & SCENEOF_POINT) + vidconsole_pop_colour(cons, &old); + } break; } case SCENEOBJT_MENU: { struct scene_obj_menu *menu = (struct scene_obj_menu *)obj; + + if (exp->popup && (obj->flags & SCENEOF_OPEN)) { + if (!cons) + return -ENOTSUPP; + + /* draw a background behind the menu items */ + scene_menu_render(menu); + } /* * With a vidconsole, the text and item pointer are rendered as * normal objects so we don't need to do anything here. The menu @@ -494,3 +524,35 @@ int scene_apply_theme(struct scene *scn, struct expo_theme *theme) return 0; } + +void scene_set_highlight_id(struct scene *scn, uint id) +{ + scn->highlight_id = id; +} + +void scene_highlight_first(struct scene *scn) +{ + struct scene_obj *obj; + + list_for_each_entry(obj, &scn->obj_head, sibling) { + switch (obj->type) { + case SCENEOBJT_MENU: + scene_set_highlight_id(scn, obj->id); + return; + default: + break; + } + } +} + +int scene_set_open(struct scene *scn, uint id, bool open) +{ + int ret; + + ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN, + open ? SCENEOF_OPEN : 0); + if (ret) + return log_msg_ret("flg", ret); + + return 0; +} diff --git a/boot/scene_internal.h b/boot/scene_internal.h index 3387a90761a..2544c961dab 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -153,6 +153,13 @@ int scene_render(struct scene *scn); */ int scene_send_key(struct scene *scn, int key, struct expo_action *event); +/** + * scene_menu_render() - Render the background behind a menu + * + * @menu: Menu to render + */ +void scene_menu_render(struct scene_obj_menu *menu); + /** * scene_menu_calc_dims() - Calculate the dimensions of a menu * diff --git a/boot/scene_menu.c b/boot/scene_menu.c index 892099557a9..20ded91fb3d 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -515,3 +515,32 @@ int scene_menu_display(struct scene_obj_menu *menu) return -ENOTSUPP; } + +void scene_menu_render(struct scene_obj_menu *menu) +{ + struct expo *exp = menu->obj.scene->expo; + const struct expo_theme *theme = &exp->theme; + struct vidconsole_bbox bbox, label_bbox; + struct udevice *dev = exp->display; + struct video_priv *vid_priv; + struct udevice *cons = exp->cons; + struct vidconsole_colour old; + enum colour_idx fore, back; + + if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) { + fore = VID_BLACK; + back = VID_WHITE; + } else { + fore = VID_LIGHT_GRAY; + back = VID_BLACK; + } + + scene_menu_calc_bbox(menu, &bbox, &label_bbox); + vidconsole_push_colour(cons, fore, back, &old); + vid_priv = dev_get_uclass_priv(dev); + video_fill_part(dev, label_bbox.x0 - theme->menu_inset, + label_bbox.y0 - theme->menu_inset, + label_bbox.x1, label_bbox.y1 + theme->menu_inset, + vid_priv->colour_fg); + vidconsole_pop_colour(cons, &old); +} diff --git a/include/expo.h b/include/expo.h index 0f438889788..0699cdb4c19 100644 --- a/include/expo.h +++ b/include/expo.h @@ -412,6 +412,36 @@ int scene_new(struct expo *exp, const char *name, uint id, struct scene **scnp); */ struct scene *expo_lookup_scene_id(struct expo *exp, uint scene_id); +/** + * scene_highlight_first() - Highlight the first item in a scene + * + * This highlights the first item, so that the user can see that it is pointed + * to + * + * @scn: Scene to update + */ +void scene_highlight_first(struct scene *scn); + +/** + * scene_set_highlight_id() - Set the object which is highlighted + * + * Sets a new object to highlight in the scene + * + * @scn: Scene to update + * @id: ID of object to highlight + */ +void scene_set_highlight_id(struct scene *scn, uint id); + +/** + * scene_set_open() - Set whether an item is open or not + * + * @scn: Scene to update + * @id: ID of object to update + * @open: true to open the object, false to close it + * Returns: 0 if OK, -ENOENT if @id is invalid + */ +int scene_set_open(struct scene *scn, uint id, bool open); + /** * scene_title_set() - set the scene title * -- cgit v1.3.1 From 4e64beeba7de7720b42714cbc542c9f7dfbba841 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:59 -0600 Subject: expo: Implement the keypress logic for popup menus In 'popup' mode, the expo allows moving around the objects in a scene. When 'enter' is pressed on a menu, it opens and the user can move around the items in the menu. Implement this using keypress handles and actions. Signed-off-by: Simon Glass --- boot/scene.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++ boot/scene_menu.c | 10 +++++-- doc/develop/expo.rst | 3 +- include/expo.h | 7 +++++ 4 files changed, 96 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/boot/scene.c b/boot/scene.c index bc213bc08b8..ea94b90584e 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -469,11 +470,90 @@ int scene_render(struct scene *scn) return 0; } +/** + * send_key_obj() - Handle a keypress for moving between objects + * + * @scn: Scene to receive the key + * @key: Key to send (KEYCODE_UP) + * @event: Returns resulting event from this keypress + * Returns: 0 if OK, -ve on error + */ +static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key, + struct expo_action *event) +{ + switch (key) { + case BKEY_UP: + while (obj != list_first_entry(&scn->obj_head, struct scene_obj, + sibling)) { + obj = list_entry(obj->sibling.prev, + struct scene_obj, sibling); + if (obj->type == SCENEOBJT_MENU) { + event->type = EXPOACT_POINT_OBJ; + event->select.id = obj->id; + log_debug("up to obj %d\n", event->select.id); + break; + } + } + break; + case BKEY_DOWN: + while (!list_is_last(&obj->sibling, &scn->obj_head)) { + obj = list_entry(obj->sibling.next, struct scene_obj, + sibling); + if (obj->type == SCENEOBJT_MENU) { + event->type = EXPOACT_POINT_OBJ; + event->select.id = obj->id; + log_debug("down to obj %d\n", event->select.id); + break; + } + } + break; + case BKEY_SELECT: + if (obj->type == SCENEOBJT_MENU) { + event->type = EXPOACT_OPEN; + event->select.id = obj->id; + log_debug("open obj %d\n", event->select.id); + } + break; + case BKEY_QUIT: + event->type = EXPOACT_QUIT; + log_debug("obj quit\n"); + break; + } +} + int scene_send_key(struct scene *scn, int key, struct expo_action *event) { + struct scene_obj_menu *menu; struct scene_obj *obj; int ret; + event->type = EXPOACT_NONE; + + /* + * In 'popup' mode, arrow keys move betwen objects, unless a menu is + * opened + */ + if (scn->expo->popup) { + obj = NULL; + if (scn->highlight_id) { + obj = scene_obj_find(scn, scn->highlight_id, + SCENEOBJT_NONE); + } + if (!obj) + return 0; + + if (!(obj->flags & SCENEOF_OPEN)) { + send_key_obj(scn, obj, key, event); + return 0; + } + + menu = (struct scene_obj_menu *)obj, + ret = scene_menu_send_key(scn, menu, key, event); + if (ret) + return log_msg_ret("key", ret); + return 0; + } + list_for_each_entry(obj, &scn->obj_head, sibling) { if (obj->type == SCENEOBJT_MENU) { struct scene_obj_menu *menu; diff --git a/boot/scene_menu.c b/boot/scene_menu.c index 6aab27611d7..dfe5692d6ce 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -323,6 +323,7 @@ static struct scene_menitem *scene_menu_find_key(struct scene *scn, int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key, struct expo_action *event) { + const bool open = menu->obj.flags & SCENEOF_OPEN; struct scene_menitem *item, *cur, *key_item; cur = NULL; @@ -367,8 +368,13 @@ int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key, log_debug("select item %d\n", event->select.id); break; case BKEY_QUIT: - event->type = EXPOACT_QUIT; - log_debug("quit\n"); + if (scn->expo->popup && open) { + event->type = EXPOACT_CLOSE; + event->select.id = menu->obj.id; + } else { + event->type = EXPOACT_QUIT; + log_debug("menu quit\n"); + } break; case '0'...'9': key_item = scene_menu_find_key(scn, menu, key); diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst index 2f4882899b6..80e435c5e65 100644 --- a/doc/develop/expo.rst +++ b/doc/develop/expo.rst @@ -178,8 +178,7 @@ Some ideas for future work: - Image formats other than BMP - Use of ANSI sequences to control a serial terminal - Colour selection -- Better support for handling lots of settings, e.g. with multiple menus and - radio/option widgets +- Better support for handling lots of settings, e.g. with radio/option widgets - Mouse support - Integrate Nuklear, NxWidgets or some other library for a richer UI - Optimise rendering by only updating the display with changes since last render diff --git a/include/expo.h b/include/expo.h index 0699cdb4c19..f7febe1c9ae 100644 --- a/include/expo.h +++ b/include/expo.h @@ -16,14 +16,21 @@ struct udevice; * enum expoact_type - types of actions reported by the expo * * @EXPOACT_NONE: no action + * @EXPOACT_POINT_OBJ: object was highlighted (@id indicates which) * @EXPOACT_POINT_ITEM: menu item was highlighted (@id indicates which) * @EXPOACT_SELECT: menu item was selected (@id indicates which) + * @EXPOACT_OPEN: menu was opened, so an item can be selected (@id indicates + * which menu object) + * @EXPOACT_CLOSE: menu was closed (@id indicates which menu object) * @EXPOACT_QUIT: request to exit the menu */ enum expoact_type { EXPOACT_NONE, + EXPOACT_POINT_OBJ, EXPOACT_POINT_ITEM, EXPOACT_SELECT, + EXPOACT_OPEN, + EXPOACT_CLOSE, EXPOACT_QUIT, }; -- cgit v1.3.1 From 82cafee133ee5c087449761988c096fc26a17cf6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:23:01 -0600 Subject: expo: Support building an expo from a description file The only way to create an expo at present is by calling the functions to create each object. It is useful to have more data-driven approach, where the objects can be specified in a suitable file format and created from that. This makes testing easier as well. Add support for describing an expo in a devicetree node. This allows more complex tests to be set up, as well as providing an easier format for users. It also provides a better basis for the upcoming configuration editor. Signed-off-by: Simon Glass --- arch/sandbox/dts/cedit.dtsi | 64 +++++++ arch/sandbox/dts/test.dts | 5 + boot/Makefile | 2 +- boot/expo.c | 1 + boot/expo_build.c | 400 ++++++++++++++++++++++++++++++++++++++++++++ doc/develop/expo.rst | 282 ++++++++++++++++++++++++++++++- include/expo.h | 14 ++ include/test/cedit-test.h | 29 ++++ test/boot/expo.c | 80 +++++++++ 9 files changed, 870 insertions(+), 7 deletions(-) create mode 100644 arch/sandbox/dts/cedit.dtsi create mode 100644 boot/expo_build.c create mode 100644 include/test/cedit-test.h (limited to 'include') diff --git a/arch/sandbox/dts/cedit.dtsi b/arch/sandbox/dts/cedit.dtsi new file mode 100644 index 00000000000..a9eb4c2d594 --- /dev/null +++ b/arch/sandbox/dts/cedit.dtsi @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Expo definition for the configuration editor + * + * This used for testing building an expo from a data file. This devicetree + * provides a description of the objects to be created. + */ + +#include + +&cedit { + dynamic-start = ; + + scenes { + main { + id = ; + + /* value refers to the matching id in /strings */ + title-id = ; + + /* simple string is used as it is */ + prompt = "UP and DOWN to choose, ENTER to select"; + + /* defines a menu within the scene */ + cpu-speed { + type = "menu"; + id = ; + + /* + * has both string and ID. The string is ignored + * if the ID is present and points to a string + */ + title = "CPU speed"; + title-id = ; + + /* menu items as simple strings */ + item-label = "2 GHz", "2.5 GHz", "3 GHz"; + + /* IDs for the menu items */ + item-id = ; + }; + + power-loss { + type = "menu"; + id = ; + + title = "AC Power"; + item-label = "Always Off", "Always On", + "Memory"; + + item-id = ; + }; + }; + }; + + strings { + title { + id = ; + value = "Test Configuration"; + value-es = "configuración de prueba"; + }; + }; +}; diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 38d5739421f..442222e4b99 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -141,6 +141,9 @@ }; }; + cedit: cedit { + }; + fuzzing-engine { compatible = "sandbox,fuzzing-engine"; }; @@ -1830,3 +1833,5 @@ #ifdef CONFIG_SANDBOX_VPL #include "sandbox_vpl.dtsi" #endif + +#include "cedit.dtsi" diff --git a/boot/Makefile b/boot/Makefile index f94c31d922d..28c4e55ca65 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -50,7 +50,7 @@ ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_LOAD_FIT) += common_fit.o endif -obj-$(CONFIG_$(SPL_TPL_)EXPO) += expo.o scene.o scene_menu.o +obj-$(CONFIG_$(SPL_TPL_)EXPO) += expo.o scene.o scene_menu.o expo_build.o obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE) += vbe.o obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_REQUEST) += vbe_request.o diff --git a/boot/expo.c b/boot/expo.c index 8c6fbc0e30b..db837f7b492 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -58,6 +58,7 @@ void expo_destroy(struct expo *exp) uint resolve_id(struct expo *exp, uint id) { + log_debug("resolve id %d\n", id); if (!id) id = exp->next_id++; else if (id >= exp->next_id) diff --git a/boot/expo_build.c b/boot/expo_build.c new file mode 100644 index 00000000000..7e61ab06a8d --- /dev/null +++ b/boot/expo_build.c @@ -0,0 +1,400 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Building an expo from an FDT description + * + * Copyright 2022 Google LLC + * Written by Simon Glass + */ + +#define LOG_CATEGORY LOGC_EXPO + +#include +#include +#include +#include +#include +#include +#include + +/** + * struct build_info - Information to use when building + * + * @str_for_id: String for each ID in use, NULL if empty. The string is NULL + * if there is nothing for this ID. Since ID 0 is never used, the first + * element of this array is always NULL + * @str_count: Number of entries in @str_for_id + */ +struct build_info { + const char **str_for_id; + int str_count; +}; + +/** + * add_txt_str - Add a string or lookup its ID, then add to expo + * + * @info: Build information + * @node: Node describing scene + * @scn: Scene to add to + * @find_name: Name to look for (e.g. "title"). This will find a property called + * "title" if it exists, else will look up the string for "title-id" + * Return: ID of added string, or -ve on error + */ +int add_txt_str(struct build_info *info, ofnode node, struct scene *scn, + const char *find_name, uint obj_id) +{ + const char *text; + uint str_id; + int ret; + + text = ofnode_read_string(node, find_name); + if (!text) { + char name[40]; + u32 id; + + snprintf(name, sizeof(name), "%s-id", find_name); + ret = ofnode_read_u32(node, name, &id); + if (ret) + return log_msg_ret("id", -EINVAL); + + if (id >= info->str_count) + return log_msg_ret("id", -E2BIG); + text = info->str_for_id[id]; + if (!text) + return log_msg_ret("id", -EINVAL); + } + + ret = expo_str(scn->expo, find_name, 0, text); + if (ret < 0) + return log_msg_ret("add", ret); + str_id = ret; + + ret = scene_txt_str(scn, find_name, obj_id, str_id, text, NULL); + if (ret < 0) + return log_msg_ret("add", ret); + + return ret; +} + +/** + * add_txt_str_list - Add a list string or lookup its ID, then add to expo + * + * @info: Build information + * @node: Node describing scene + * @scn: Scene to add to + * @find_name: Name to look for (e.g. "title"). This will find a string-list + * property called "title" if it exists, else will look up the string in the + * "title-id" string list. + * Return: ID of added string, or -ve on error + */ +int add_txt_str_list(struct build_info *info, ofnode node, struct scene *scn, + const char *find_name, int index, uint obj_id) +{ + const char *text; + uint str_id; + int ret; + + ret = ofnode_read_string_index(node, find_name, index, &text); + if (ret) { + char name[40]; + u32 id; + + snprintf(name, sizeof(name), "%s-id", find_name); + ret = ofnode_read_u32_index(node, name, index, &id); + if (ret) + return log_msg_ret("id", -ENOENT); + + if (id >= info->str_count) + return log_msg_ret("id", -E2BIG); + text = info->str_for_id[id]; + if (!text) + return log_msg_ret("id", -EINVAL); + } + + ret = expo_str(scn->expo, find_name, 0, text); + if (ret < 0) + return log_msg_ret("add", ret); + str_id = ret; + + ret = scene_txt_str(scn, find_name, obj_id, str_id, text, NULL); + if (ret < 0) + return log_msg_ret("add", ret); + + return ret; +} + +/* + * build_element() - Handle creating a text object from a label + * + * Look up a property called @label or @label-id and create a string for it + */ +int build_element(void *ldtb, int node, const char *label) +{ + return 0; +} + +/** + * read_strings() - Read in the list of strings + * + * Read the strings into an ID-indexed list, so they can be used for building + * an expo. The strings are in a /strings node and each has its own subnode + * containing the ID and the string itself: + * + * example { + * id = <123>; + * value = "This is a test"; + * }; + * + * Future work may add support for unicode and multiple languages + * + * @info: Build information + * @root: Root node to read from + * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format + * error + */ +static int read_strings(struct build_info *info, ofnode root) +{ + ofnode strings, node; + + strings = ofnode_find_subnode(root, "strings"); + if (!ofnode_valid(strings)) + return log_msg_ret("str", -EINVAL); + + ofnode_for_each_subnode(node, strings) { + const char *val; + int ret; + u32 id; + + ret = ofnode_read_u32(node, "id", &id); + if (ret) + return log_msg_ret("id", -EINVAL); + val = ofnode_read_string(node, "value"); + if (!val) + return log_msg_ret("val", -EINVAL); + + if (id >= info->str_count) { + int new_count = info->str_count + 20; + void *new_arr; + + new_arr = realloc(info->str_for_id, + new_count * sizeof(char *)); + if (!new_arr) + return log_msg_ret("id", -ENOMEM); + memset(new_arr + info->str_count, '\0', + (new_count - info->str_count) * sizeof(char *)); + info->str_for_id = new_arr; + info->str_count = new_count; + } + + info->str_for_id[id] = val; + } + + return 0; +} + +/** + * list_strings() - List the available strings with their IDs + * + * @info: Build information + */ +static void list_strings(struct build_info *info) +{ + int i; + + for (i = 0; i < info->str_count; i++) { + if (info->str_for_id[i]) + printf("%3d %s\n", i, info->str_for_id[i]); + } +} + +/** + * menu_build() - Build a menu and add it to a scene + * + * See doc/developer/expo.rst for a description of the format + * + * @info: Build information + * @node: Node containing the menu description + * @scn: Scene to add the menu to + * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format + * error, -ENOENT if there is a references to a non-existent string + */ +static int menu_build(struct build_info *info, ofnode node, struct scene *scn) +{ + struct scene_obj_menu *menu; + uint title_id, menu_id; + const u32 *item_ids; + int ret, size, i; + const char *name; + u32 id; + + name = ofnode_get_name(node); + ret = ofnode_read_u32(node, "id", &id); + if (ret) + return log_msg_ret("id", -EINVAL); + + ret = scene_menu(scn, name, id, &menu); + if (ret < 0) + return log_msg_ret("men", ret); + menu_id = ret; + + /* Set the title */ + ret = add_txt_str(info, node, scn, "title", 0); + if (ret < 0) + return log_msg_ret("tit", ret); + title_id = ret; + ret = scene_menu_set_title(scn, menu_id, title_id); + + item_ids = ofnode_read_prop(node, "item-id", &size); + if (!item_ids) + return log_msg_ret("itm", -EINVAL); + if (!size || size % sizeof(u32)) + return log_msg_ret("isz", -EINVAL); + size /= sizeof(u32); + + for (i = 0; i < size; i++) { + struct scene_menitem *item; + uint label, key, desc; + + ret = add_txt_str_list(info, node, scn, "item-label", i, 0); + if (ret < 0 && ret != -ENOENT) + return log_msg_ret("lab", ret); + label = max(0, ret); + + ret = add_txt_str_list(info, node, scn, "key-label", i, 0); + if (ret < 0 && ret != -ENOENT) + return log_msg_ret("key", ret); + key = max(0, ret); + + ret = add_txt_str_list(info, node, scn, "desc-label", i, 0); + if (ret < 0 && ret != -ENOENT) + return log_msg_ret("lab", ret); + desc = max(0, ret); + + ret = scene_menuitem(scn, menu_id, simple_xtoa(i), + fdt32_to_cpu(item_ids[i]), key, label, + desc, 0, 0, &item); + if (ret < 0) + return log_msg_ret("mi", ret); + } + + return 0; +} + +/** + * menu_build() - Build an expo object and add it to a scene + * + * See doc/developer/expo.rst for a description of the format + * + * @info: Build information + * @node: Node containing the object description + * @scn: Scene to add the object to + * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format + * error, -ENOENT if there is a references to a non-existent string + */ +static int obj_build(struct build_info *info, ofnode node, struct scene *scn) +{ + const char *type; + u32 id; + int ret; + + log_debug("- object %s\n", ofnode_get_name(node)); + ret = ofnode_read_u32(node, "id", &id); + if (ret) + return log_msg_ret("id", -EINVAL); + + type = ofnode_read_string(node, "type"); + if (!type) + return log_msg_ret("typ", -EINVAL); + + if (!strcmp("menu", type)) + ret = menu_build(info, node, scn); + else + ret = -EINVAL; + if (ret) + return log_msg_ret("bld", ret); + + return 0; +} + +/** + * scene_build() - Build a scene and all its objects + * + * See doc/developer/expo.rst for a description of the format + * + * @info: Build information + * @node: Node containing the scene description + * @scn: Scene to add the object to + * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format + * error, -ENOENT if there is a references to a non-existent string + */ +static int scene_build(struct build_info *info, ofnode scn_node, + struct expo *exp) +{ + const char *name; + struct scene *scn; + uint id, title_id; + ofnode node; + int ret; + + name = ofnode_get_name(scn_node); + log_debug("Building scene %s\n", name); + ret = ofnode_read_u32(scn_node, "id", &id); + if (ret) + return log_msg_ret("id", -EINVAL); + + ret = scene_new(exp, name, id, &scn); + if (ret < 0) + return log_msg_ret("scn", ret); + + ret = add_txt_str(info, scn_node, scn, "title", 0); + if (ret < 0) + return log_msg_ret("tit", ret); + title_id = ret; + scene_title_set(scn, title_id); + + ret = add_txt_str(info, scn_node, scn, "prompt", 0); + if (ret < 0) + return log_msg_ret("pr", ret); + + ofnode_for_each_subnode(node, scn_node) { + ret = obj_build(info, node, scn); + if (ret < 0) + return log_msg_ret("mit", ret); + } + + return 0; +} + +int expo_build(ofnode root, struct expo **expp) +{ + struct build_info info; + ofnode scenes, node; + struct expo *exp; + u32 dyn_start; + int ret; + + memset(&info, '\0', sizeof(info)); + ret = read_strings(&info, root); + if (ret) + return log_msg_ret("str", ret); + list_strings(&info); + + ret = expo_new("name", NULL, &exp); + if (ret) + return log_msg_ret("exp", ret); + + if (!ofnode_read_u32(root, "dynamic-start", &dyn_start)) + expo_set_dynamic_start(exp, dyn_start); + + scenes = ofnode_find_subnode(root, "scenes"); + if (!ofnode_valid(scenes)) + return log_msg_ret("sno", -EINVAL); + + ofnode_for_each_subnode(node, scenes) { + ret = scene_build(&info, node, exp); + if (ret < 0) + return log_msg_ret("scn", ret); + } + *expp = exp; + + return 0; +} diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst index bd593dc2b3f..f5caadbfd98 100644 --- a/doc/develop/expo.rst +++ b/doc/develop/expo.rst @@ -100,10 +100,13 @@ objects first, then create the menu item, passing in the relevant IDs. Creating an expo ---------------- -To create an expo, use `expo_new()` followed by `scene_new()` to create a scene. -Then add objects to the scene, using functions like `scene_txt_str()` and -`scene_menu()`. For every menu item, add text and image objects, then create -the menu item with `scene_menuitem()`, referring to those objects. +To create an expo programmatically, use `expo_new()` followed by `scene_new()` +to create a scene. Then add objects to the scene, using functions like +`scene_txt_str()` and `scene_menu()`. For every menu item, add text and image +objects, then create the menu item with `scene_menuitem()`, referring to those +objects. + +To create an expo using a description file, see :ref:`expo_format` below. Layout ------ @@ -168,6 +171,273 @@ menu-inset menuitem-gap-y Number of pixels between menu items +.. _expo_format: + +Pop-up mode +----------- + +Expos support two modes. The simple mode is used for selecting from a single +menu, e.g. when choosing with OS to boot. In this mode the menu items are shown +in a list (label, > pointer, key and description) and can be chosen using arrow +keys and enter:: + + U-Boot Boot Menu + + UP and DOWN to choose, ENTER to select + + mmc1 > 0 Fedora-Workstation-armhfp-31-1.9 + mmc3 1 Armbian + +The popup mode allows multiple menus to be present in a scene. Each is shown +just as its title and label, as with the `CPU Speed` and `AC Power` menus here:: + + Test Configuration + + + CPU Speed <2 GHz> (highlighted) + + AC Power Always Off + + + UP and DOWN to choose, ENTER to select + + +Expo Format +----------- + +It can be tedious to create a complex expo using code. Expo supports a +data-driven approach, where the expo description is in a devicetree file. This +makes it easier and faster to create and edit the description. An expo builder +is provided to convert this format into an expo structure. + +Layout of the expo scenes is handled automatically, based on a set of simple +rules. + +Top-level node +~~~~~~~~~~~~~~ + +The top-level node has the following properties: + +dynamic-start + type: u32, optional + + Specifies the start of the dynamically allocated objects. This results in + a call to expo_set_dynamic_start(). + +The top-level node has the following subnodes: + +scenes + Specifies the scenes in the expo, each one being a subnode + +strings + Specifies the strings in the expo, each one being a subnode + +`scenes` node +~~~~~~~~~~~~~ + +Contains a list of scene subnodes. The name of each subnode is passed as the +name to `scene_new()`. + +`strings` node +~~~~~~~~~~~~~~ + +Contains a list of string subnodes. The name of each subnode is ignored. + +`strings` subnodes +~~~~~~~~~~~~~~~~~~ + +Each subnode defines a string which can be used by scenes and objects. Each +string has an ID number which is used to refer to it. + +The `strings` subnodes have the following properties: + +id + type: u32, required + + Specifies the ID number for the string. + +value: + type: string, required + + Specifies the string text. For now only a single value is supported. Future + work may add support for multiple languages by using a value for each + language. + +Scene nodes (`scenes` subnodes) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Each subnode of the `scenes` node contains a scene description. + +Most properties can use either a string or a string ID. For example, a `title` +property can be used to provide the title for a menu; alternatively a `title-id` +property can provide the string ID of the title. If both are present, the +ID takes preference, except that if a string with that ID does not exist, it +falls back to using the string from the property (`title` in this example). The +description below shows these are alternative properties with the same +description. + +The scene nodes have the following properties: + +id + type: u32, required + + Specifies the ID number for the string. + +title / title-id + type: string / u32, required + + Specifies the title of the scene. This is shown at the top of the scene. + +prompt / prompt-id + type: string / u32, required + + Specifies a prompt for the scene. This is shown at the bottom of the scene. + +The scene nodes have a subnode for each object in the scene. + +Object nodes +~~~~~~~~~~~~ + +The object-node name is used as the name of the object, e.g. when calling +`scene_menu()` to create a menu. + +Object nodes have the following common properties: + +type + type: string, required + + Specifies the type of the object. Valid types are: + + "menu" + Menu containing items which can be selected by the user + +id + type: u32, required + + Specifies the ID of the object. This is used when referring to the object. + + +Menu nodes have the following additional properties: + +title / title-id + type: string / u32, required + + Specifies the title of the menu. This is shown to the left of the area for + this menu. + +item-id + type: u32 list, required + + Specifies the ID for each menu item. These are used for checking which item + has been selected. + +item-label / item-label-id + type: string list / u32 list, required + + Specifies the label for each item in the menu. These are shown to the user. + In 'popup' mode these form the items in the menu. + +key-label / key-label-id + type: string list / u32 list, optional + + Specifies the key for each item in the menu. These are currently only + intended for use in simple mode. + +desc-label / desc-label-id + type: string list / u32 list, optional + + Specifies the description for each item in the menu. These are currently + only intended for use in simple mode. + + +Expo layout +~~~~~~~~~~~ + +The `expo_arrange()` function can be called to arrange the expo objects in a +suitable manner. For each scene it puts the title at the top, the prompt at the +bottom and the objects in order from top to bottom. + +Expo format example +~~~~~~~~~~~~~~~~~~~ + +This example shows an expo with a single scene consisting of two menus. The +scene title is specified using a string from the strings table, but all other +strings are provided inline in the nodes where they are used. + +:: + + #define ID_PROMPT 1 + #define ID_SCENE1 2 + #define ID_SCENE1_TITLE 3 + + #define ID_CPU_SPEED 4 + #define ID_CPU_SPEED_TITLE 5 + #define ID_CPU_SPEED_1 6 + #define ID_CPU_SPEED_2 7 + #define ID_CPU_SPEED_3 8 + + #define ID_POWER_LOSS 9 + #define ID_AC_OFF 10 + #define ID_AC_ON 11 + #define ID_AC_MEMORY 12 + + #define ID_DYNAMIC_START 13 + + &cedit { + dynamic-start = ; + + scenes { + main { + id = ; + + /* value refers to the matching id in /strings */ + title-id = ; + + /* simple string is used as it is */ + prompt = "UP and DOWN to choose, ENTER to select"; + + /* defines a menu within the scene */ + cpu-speed { + type = "menu"; + id = ; + + /* + * has both string and ID. The string is ignored + * if the ID is present and points to a string + */ + title = "CPU speed"; + title-id = ; + + /* menu items as simple strings */ + item-label = "2 GHz", "2.5 GHz", "3 GHz"; + + /* IDs for the menu items */ + item-id = ; + }; + + power-loss { + type = "menu"; + id = ; + + title = "AC Power"; + item-label = "Always Off", "Always On", + "Memory"; + + item-id = ; + }; + }; + }; + + strings { + title { + id = ; + value = "Test Configuration"; + value-es = "configuración de prueba"; + }; + }; + }; + API documentation ----------------- @@ -180,11 +450,10 @@ Future ideas Some ideas for future work: - Default menu item and a timeout -- Higher-level / automatic / more flexible layout of objects - Image formats other than BMP - Use of ANSI sequences to control a serial terminal - Colour selection -- Better support for handling lots of settings, e.g. with radio/option widgets +- Support for more widgets, e.g. text, numeric, radio/option - Mouse support - Integrate Nuklear, NxWidgets or some other library for a richer UI - Optimise rendering by only updating the display with changes since last render @@ -194,6 +463,7 @@ Some ideas for future work: - Support both graphical and text menus at the same time on different devices - Support unicode - Support curses for proper serial-terminal menus +- Add support for large menus which need to scroll .. Simon Glass .. 7-Oct-22 diff --git a/include/expo.h b/include/expo.h index f7febe1c9ae..9fec4d0cd84 100644 --- a/include/expo.h +++ b/include/expo.h @@ -653,4 +653,18 @@ int expo_action_get(struct expo *exp, struct expo_action *act); */ int expo_apply_theme(struct expo *exp, ofnode node); +/** + * expo_build() - Build an expo from an FDT description + * + * Build a complete expo from a description in the provided devicetree. + * + * See doc/developer/expo.rst for a description of the format + * + * @root: Root node for expo description + * @expp: Returns the new expo + * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format + * error, -ENOENT if there is a references to a non-existent string + */ +int expo_build(ofnode root, struct expo **expp); + #endif /*__SCENE_H */ diff --git a/include/test/cedit-test.h b/include/test/cedit-test.h new file mode 100644 index 00000000000..349df75b16d --- /dev/null +++ b/include/test/cedit-test.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Binding shared between cedit.dtsi and test/boot/expo.c + * + * Copyright 2023 Google LLC + * Written by Simon Glass + */ + +#ifndef __cedit_test_h +#define __cedit_test_h + +#define ID_PROMPT 1 +#define ID_SCENE1 2 +#define ID_SCENE1_TITLE 3 + +#define ID_CPU_SPEED 4 +#define ID_CPU_SPEED_TITLE 5 +#define ID_CPU_SPEED_1 6 +#define ID_CPU_SPEED_2 7 +#define ID_CPU_SPEED_3 8 + +#define ID_POWER_LOSS 9 +#define ID_AC_OFF 10 +#define ID_AC_ON 11 +#define ID_AC_MEMORY 12 + +#define ID_DYNAMIC_START 13 + +#endif diff --git a/test/boot/expo.c b/test/boot/expo.c index c34eaeedd66..e7148024fe3 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -13,6 +13,7 @@ #include #include #include "bootstd_common.h" +#include #include "../../boot/scene_internal.h" enum { @@ -588,3 +589,82 @@ static int expo_render_image(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(expo_render_image, UT_TESTF_DM | UT_TESTF_SCAN_FDT); + +/* Check building an expo from a devicetree description */ +static int expo_test_build(struct unit_test_state *uts) +{ + struct scene_obj_menu *menu; + struct scene_menitem *item; + struct scene_obj_txt *txt; + struct scene_obj *obj; + struct scene *scn; + struct expo *exp; + int count; + ofnode node; + + node = ofnode_path("/cedit"); + ut_assert(ofnode_valid(node)); + ut_assertok(expo_build(node, &exp)); + + ut_asserteq_str("name", exp->name); + ut_asserteq(0, exp->scene_id); + ut_asserteq(ID_DYNAMIC_START + 20, exp->next_id); + ut_asserteq(false, exp->popup); + + /* check the scene */ + scn = expo_lookup_scene_id(exp, ID_SCENE1); + ut_assertnonnull(scn); + ut_asserteq_str("main", scn->name); + ut_asserteq(ID_SCENE1, scn->id); + ut_asserteq(ID_DYNAMIC_START + 1, scn->title_id); + ut_asserteq(0, scn->highlight_id); + + /* check the title */ + txt = scene_obj_find(scn, scn->title_id, SCENEOBJT_NONE); + ut_assertnonnull(txt); + obj = &txt->obj; + ut_asserteq_ptr(scn, obj->scene); + ut_asserteq_str("title", obj->name); + ut_asserteq(scn->title_id, obj->id); + ut_asserteq(SCENEOBJT_TEXT, obj->type); + ut_asserteq(0, obj->flags); + ut_asserteq_str("Test Configuration", expo_get_str(exp, txt->str_id)); + + /* check the menu */ + menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_NONE); + obj = &menu->obj; + ut_asserteq_ptr(scn, obj->scene); + ut_asserteq_str("cpu-speed", obj->name); + ut_asserteq(ID_CPU_SPEED, obj->id); + ut_asserteq(SCENEOBJT_MENU, obj->type); + ut_asserteq(0, obj->flags); + + txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_NONE); + ut_asserteq_str("CPU speed", expo_get_str(exp, txt->str_id)); + + ut_asserteq(0, menu->cur_item_id); + ut_asserteq(0, menu->pointer_id); + + /* check the items */ + item = list_first_entry(&menu->item_head, struct scene_menitem, + sibling); + ut_asserteq_str("00", item->name); + ut_asserteq(ID_CPU_SPEED_1, item->id); + ut_asserteq(0, item->key_id); + ut_asserteq(0, item->desc_id); + ut_asserteq(0, item->preview_id); + ut_asserteq(0, item->flags); + + txt = scene_obj_find(scn, item->label_id, SCENEOBJT_NONE); + ut_asserteq_str("2 GHz", expo_get_str(exp, txt->str_id)); + + count = 0; + list_for_each_entry(item, &menu->item_head, sibling) + count++; + ut_asserteq(3, count); + + expo_destroy(exp); + + return 0; +} +BOOTSTD_TEST(expo_test_build, UT_TESTF_DM); -- cgit v1.3.1 From a0874dc4ac7153f49f72c3fb818422d940ecbfea Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:23:02 -0600 Subject: expo: Add a configuration editor Add a new 'cedit' command which allows editing configuration using an expo. The configuration items appear as menus on the display. This is extremely basic, only supporting menus and not providing any way to load or save the configuration. Signed-off-by: Simon Glass --- boot/Kconfig | 14 ++++ boot/Makefile | 1 + boot/cedit.c | 163 ++++++++++++++++++++++++++++++++++++++++++++++ boot/expo_build.c | 3 +- boot/scene.c | 12 ++++ boot/scene_internal.h | 8 +++ cmd/Kconfig | 9 +++ cmd/Makefile | 1 + cmd/cedit.c | 93 ++++++++++++++++++++++++++ configs/sandbox_defconfig | 1 + doc/develop/expo.rst | 8 ++- doc/usage/cmd/cedit.rst | 31 +++++++++ doc/usage/index.rst | 1 + include/expo.h | 29 +++++++++ 14 files changed, 370 insertions(+), 4 deletions(-) create mode 100644 boot/cedit.c create mode 100644 cmd/cedit.c create mode 100644 doc/usage/cmd/cedit.rst (limited to 'include') diff --git a/boot/Kconfig b/boot/Kconfig index a643a3d1286..c8b8f36d835 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -1630,4 +1630,18 @@ config SAVE_PREV_BL_INITRAMFS_START_ADDR If no initramfs was provided by previous bootloader, no env variables will be created. +menu "Configuration editor" + +config CEDIT + bool "Configuration editor" + depends on BOOTSTD + help + Provides a way to deal with board configuration and present it to + the user for adjustment. + + This is intended to provide both graphical and text-based user + interfaces, but only graphical is support at present. + +endmenu # Configuration editor + endmenu # Booting diff --git a/boot/Makefile b/boot/Makefile index 28c4e55ca65..f828f870a37 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -33,6 +33,7 @@ ifdef CONFIG_$(SPL_TPL_)BOOTSTD_FULL obj-$(CONFIG_CMD_BOOTEFI_BOOTMGR) += bootmeth_efi_mgr.o obj-$(CONFIG_$(SPL_TPL_)EXPO) += bootflow_menu.o obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootflow_menu.o +obj-$(CONFIG_$(SPL_TPL_)CEDIT) += cedit.o endif obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o diff --git a/boot/cedit.c b/boot/cedit.c new file mode 100644 index 00000000000..ee24658917b --- /dev/null +++ b/boot/cedit.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Implementation of configuration editor + * + * Copyright 2023 Google LLC + * Written by Simon Glass + */ + +#include +#include +#include +#include +#include +#include +#include +#include "scene_internal.h" + +int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) +{ + struct scene_obj_txt *txt; + struct scene_obj *obj; + struct scene *scn; + int y; + + scn = expo_lookup_scene_id(exp, scene_id); + if (!scn) + return log_msg_ret("scn", -ENOENT); + + txt = scene_obj_find_by_name(scn, "prompt"); + if (txt) + scene_obj_set_pos(scn, txt->obj.id, 0, vpriv->ysize - 50); + + txt = scene_obj_find_by_name(scn, "title"); + if (txt) + scene_obj_set_pos(scn, txt->obj.id, 200, 10); + + y = 100; + list_for_each_entry(obj, &scn->obj_head, sibling) { + if (obj->type == SCENEOBJT_MENU) { + scene_obj_set_pos(scn, obj->id, 50, y); + scene_menu_arrange(scn, (struct scene_obj_menu *)obj); + y += 50; + } + } + + return 0; +} + +int cedit_run(struct expo *exp) +{ + struct cli_ch_state s_cch, *cch = &s_cch; + struct video_priv *vid_priv; + uint scene_id; + struct udevice *dev; + struct scene *scn; + bool done; + int ret; + + cli_ch_init(cch); + + /* For now we only support a video console */ + ret = uclass_first_device_err(UCLASS_VIDEO, &dev); + if (ret) + return log_msg_ret("vid", ret); + ret = expo_set_display(exp, dev); + if (ret) + return log_msg_ret("dis", ret); + + ret = expo_first_scene_id(exp); + if (ret < 0) + return log_msg_ret("scn", ret); + scene_id = ret; + + ret = expo_set_scene_id(exp, scene_id); + if (ret) + return log_msg_ret("sid", ret); + + exp->popup = true; + + /* This is not supported for now */ + if (0) + expo_set_text_mode(exp, true); + + vid_priv = dev_get_uclass_priv(dev); + + scn = expo_lookup_scene_id(exp, scene_id); + scene_highlight_first(scn); + + cedit_arange(exp, vid_priv, scene_id); + + ret = expo_calc_dims(exp); + if (ret) + return log_msg_ret("dim", ret); + + done = false; + do { + struct expo_action act; + int ichar, key; + + ret = expo_render(exp); + if (ret) + break; + + ichar = cli_ch_process(cch, 0); + if (!ichar) { + while (!ichar && !tstc()) { + schedule(); + mdelay(2); + ichar = cli_ch_process(cch, -ETIMEDOUT); + } + if (!ichar) { + ichar = getchar(); + ichar = cli_ch_process(cch, ichar); + } + } + + key = 0; + if (ichar) { + key = bootmenu_conv_key(ichar); + if (key == BKEY_NONE) + key = ichar; + } + if (!key) + continue; + + ret = expo_send_key(exp, key); + if (ret) + break; + + ret = expo_action_get(exp, &act); + if (!ret) { + switch (act.type) { + case EXPOACT_POINT_OBJ: + scene_set_highlight_id(scn, act.select.id); + cedit_arange(exp, vid_priv, scene_id); + break; + case EXPOACT_OPEN: + scene_set_open(scn, act.select.id, true); + cedit_arange(exp, vid_priv, scene_id); + break; + case EXPOACT_CLOSE: + scene_set_open(scn, act.select.id, false); + cedit_arange(exp, vid_priv, scene_id); + break; + case EXPOACT_SELECT: + scene_set_open(scn, scn->highlight_id, false); + cedit_arange(exp, vid_priv, scene_id); + break; + case EXPOACT_QUIT: + log_debug("quitting\n"); + done = true; + break; + default: + break; + } + } + } while (!done); + + if (ret) + return log_msg_ret("end", ret); + + return 0; +} diff --git a/boot/expo_build.c b/boot/expo_build.c index 7e61ab06a8d..22f62eb54bc 100644 --- a/boot/expo_build.c +++ b/boot/expo_build.c @@ -376,7 +376,8 @@ int expo_build(ofnode root, struct expo **expp) ret = read_strings(&info, root); if (ret) return log_msg_ret("str", ret); - list_strings(&info); + if (_DEBUG) + list_strings(&info); ret = expo_new("name", NULL, &exp); if (ret) diff --git a/boot/scene.c b/boot/scene.c index 6fbc1fc578c..e52333371f9 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -92,6 +92,18 @@ void *scene_obj_find(struct scene *scn, uint id, enum scene_obj_t type) return NULL; } +void *scene_obj_find_by_name(struct scene *scn, const char *name) +{ + struct scene_obj *obj; + + list_for_each_entry(obj, &scn->obj_head, sibling) { + if (!strcmp(name, obj->name)) + return obj; + } + + return NULL; +} + int scene_obj_add(struct scene *scn, const char *name, uint id, enum scene_obj_t type, uint size, struct scene_obj **objp) { diff --git a/boot/scene_internal.h b/boot/scene_internal.h index dc98ecd0214..fb1ea5533b9 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -40,6 +40,14 @@ uint resolve_id(struct expo *exp, uint id); */ void *scene_obj_find(struct scene *scn, uint id, enum scene_obj_t type); +/** + * scene_obj_find_by_name() - Find an object in a scene by name + * + * @scn: Scene to search + * @name: Name to search for + */ +void *scene_obj_find_by_name(struct scene *scn, const char *name); + /** * scene_obj_add() - Add a new object to a scene * diff --git a/cmd/Kconfig b/cmd/Kconfig index c1941849f98..f6b10e01f84 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -428,6 +428,15 @@ config CMD_ABOOTIMG See doc/android/boot-image.rst for details. +config CMD_CEDIT + bool "cedit - Configuration editor" + depends on CEDIT + default y + help + Provides a command to allow editing of board configuration and + providing a UI for the user to adjust settings. Subcommands allow + loading and saving of configuration as well as showing an editor. + config CMD_ELF bool "bootelf, bootvx" default y diff --git a/cmd/Makefile b/cmd/Makefile index 6c37521b4e2..9f8c0b058be 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_CMD_BUTTON) += button.o obj-$(CONFIG_CMD_CAT) += cat.o obj-$(CONFIG_CMD_CACHE) += cache.o obj-$(CONFIG_CMD_CBFS) += cbfs.o +obj-$(CONFIG_CMD_CEDIT) += cedit.o obj-$(CONFIG_CMD_CLK) += clk.o obj-$(CONFIG_CMD_CLS) += cls.o obj-$(CONFIG_CMD_CONFIG) += config.o diff --git a/cmd/cedit.c b/cmd/cedit.c new file mode 100644 index 00000000000..0cae304c4ad --- /dev/null +++ b/cmd/cedit.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * 'cedit' command + * + * Copyright 2023 Google LLC + * Written by Simon Glass + */ + +#include +#include +#include +#include +#include +#include + +struct expo *cur_exp; + +static int do_cedit_load(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *fname; + struct expo *exp; + oftree tree; + ulong size; + void *buf; + int ret; + + if (argc < 4) + return CMD_RET_USAGE; + fname = argv[3]; + + ret = fs_load_alloc(argv[1], argv[2], argv[3], SZ_1M, 0, &buf, &size); + if (ret) { + printf("File not found\n"); + return CMD_RET_FAILURE; + } + + tree = oftree_from_fdt(buf); + if (!oftree_valid(tree)) { + printf("Cannot create oftree\n"); + return CMD_RET_FAILURE; + } + + ret = expo_build(oftree_root(tree), &exp); + oftree_dispose(tree); + if (ret) { + printf("Failed to build expo: %dE\n", ret); + return CMD_RET_FAILURE; + } + + cur_exp = exp; + + return 0; +} + +static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ofnode node; + int ret; + + if (!cur_exp) { + printf("No expo loaded\n"); + return CMD_RET_FAILURE; + } + + node = ofnode_path("/cedit-theme"); + if (ofnode_valid(node)) { + ret = expo_apply_theme(cur_exp, node); + if (ret) + return CMD_RET_FAILURE; + } else { + log_warning("No theme found\n"); + } + ret = cedit_run(cur_exp); + if (ret) { + log_err("Failed (err=%dE)\n", ret); + return CMD_RET_FAILURE; + } + + return 0; +} + +#ifdef CONFIG_SYS_LONGHELP +static char cedit_help_text[] = + "load - load config editor\n" + "cedit run - run config editor"; +#endif /* CONFIG_SYS_LONGHELP */ + +U_BOOT_CMD_WITH_SUBCMDS(cedit, "Configuration editor", cedit_help_text, + U_BOOT_SUBCMD_MKENT(load, 5, 1, do_cedit_load), + U_BOOT_SUBCMD_MKENT(run, 1, 1, do_cedit_run), +); diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 1ec44d5b33b..4cef6c51539 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -30,6 +30,7 @@ CONFIG_AUTOBOOT_STOP_STR_ENABLE=y CONFIG_AUTOBOOT_STOP_STR_CRYPT="$5$rounds=640000$HrpE65IkB8CM5nCL$BKT3QdF98Bo8fJpTr9tjZLZQyzqPASBY20xuK5Rent9" CONFIG_IMAGE_PRE_LOAD=y CONFIG_IMAGE_PRE_LOAD_SIG=y +CONFIG_CEDIT=y CONFIG_CONSOLE_RECORD=y CONFIG_CONSOLE_RECORD_OUT_SIZE=0x6000 CONFIG_PRE_CONSOLE_BUFFER=y diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst index f5caadbfd98..2ac4af232da 100644 --- a/doc/develop/expo.rst +++ b/doc/develop/expo.rst @@ -171,8 +171,6 @@ menu-inset menuitem-gap-y Number of pixels between menu items -.. _expo_format: - Pop-up mode ----------- @@ -202,6 +200,8 @@ just as its title and label, as with the `CPU Speed` and `AC Power` menus here:: UP and DOWN to choose, ENTER to select +.. _expo_format: + Expo Format ----------- @@ -211,7 +211,8 @@ makes it easier and faster to create and edit the description. An expo builder is provided to convert this format into an expo structure. Layout of the expo scenes is handled automatically, based on a set of simple -rules. +rules. The :doc:`../usage/cmd/cedit` can be used to load a configuration +and create an expo from it. Top-level node ~~~~~~~~~~~~~~ @@ -464,6 +465,7 @@ Some ideas for future work: - Support unicode - Support curses for proper serial-terminal menus - Add support for large menus which need to scroll +- Add support for reading and writing configuration settings with cedit .. Simon Glass .. 7-Oct-22 diff --git a/doc/usage/cmd/cedit.rst b/doc/usage/cmd/cedit.rst new file mode 100644 index 00000000000..8e1110c7c77 --- /dev/null +++ b/doc/usage/cmd/cedit.rst @@ -0,0 +1,31 @@ +.. SPDX-License-Identifier: GPL-2.0+: + +cedit command +============= + +Synopis +------- + +:: + + cedit load + cedit run + +Description +----------- + +The *cedit* command is used to load a configuration-editor description and allow +the user to interact with it. + +It makes use of the expo subsystem. + +The description is in the form of a devicetree file, as documented at +:ref:`expo_format`. + +Example +------- + +:: + + => cedit load hostfs - fred.dtb + => cedit run diff --git a/doc/usage/index.rst b/doc/usage/index.rst index 388e59f1733..f2ffd2787aa 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -39,6 +39,7 @@ Shell commands cmd/bootz cmd/cat cmd/cbsysinfo + cmd/cedit cmd/cls cmd/cmp cmd/coninfo diff --git a/include/expo.h b/include/expo.h index 9fec4d0cd84..0b1d944a169 100644 --- a/include/expo.h +++ b/include/expo.h @@ -11,6 +11,7 @@ #include struct udevice; +struct video_priv; /** * enum expoact_type - types of actions reported by the expo @@ -378,6 +379,14 @@ int expo_calc_dims(struct expo *exp); */ int expo_set_scene_id(struct expo *exp, uint scene_id); +/** + * expo_first_scene_id() - Get the ID of the first scene + * + * @exp: Expo to check + * Returns: Scene ID of first scene, or -ENOENT if there are no scenes + */ +int expo_first_scene_id(struct expo *exp); + /** * expo_render() - render the expo on the display / console * @@ -667,4 +676,24 @@ int expo_apply_theme(struct expo *exp, ofnode node); */ int expo_build(ofnode root, struct expo **expp); +/** + * cedit_arange() - Arrange objects in a configuration-editor scene + * + * @exp: Expo to update + * @vid_priv: Private info of the video device + * @scene_id: scene ID to arrange + * Returns: 0 if OK, -ve on error + */ +int cedit_arange(struct expo *exp, struct video_priv *vid_priv, uint scene_id); + +/** + * cedit_run() - Run a configuration editor + * + * This accepts input until the user quits with Escape + * + * @exp: Expo to use + * Returns: 0 if OK, -ve on error + */ +int cedit_run(struct expo *exp); + #endif /*__SCENE_H */ -- cgit v1.3.1