From 97b586695cd80821455ae06ee178c6c8cf759ce6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 May 2025 07:37:01 -0600 Subject: abuf: Add a helper for initing and allocating a buffer This construct appears in various places. Reduce code size by adding a function for it. It inits the abuf, then allocates it to the requested size. Signed-off-by: Simon Glass --- include/abuf.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/abuf.h b/include/abuf.h index 62ff6499a0c..749bb188b0c 100644 --- a/include/abuf.h +++ b/include/abuf.h @@ -170,6 +170,17 @@ void abuf_init_set(struct abuf *abuf, void *data, size_t size); */ void abuf_init_const(struct abuf *abuf, const void *data, size_t size); +/** + * abuf_init_size() - Set up an allocated abuf + * + * Init a new abuf and allocate its size. + * + * @abuf: abuf to set up + * @data: New contents of abuf + * @size: New size of abuf + */ +bool abuf_init_size(struct abuf *buf, size_t size); + /** * abuf_uninit() - Free any memory used by an abuf * -- cgit v1.3.1 From d58cebbbc7617fbc45e604c883e8501a58d25e62 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:03 -0600 Subject: abuf: Add a function to copy a buffer It is useful to be able to copy an abuf, to allow changes while preserving the original. Add a function for this. Signed-off-by: Simon Glass --- include/abuf.h | 11 +++++++++++ lib/abuf.c | 14 ++++++++++++++ test/lib/abuf.c | 23 +++++++++++++++++++++++ 3 files changed, 48 insertions(+) (limited to 'include') diff --git a/include/abuf.h b/include/abuf.h index 749bb188b0c..bbb3c51f334 100644 --- a/include/abuf.h +++ b/include/abuf.h @@ -111,6 +111,17 @@ bool abuf_realloc(struct abuf *abuf, size_t new_size); */ bool abuf_realloc_inc(struct abuf *abuf, size_t inc); +/** + * abuf_copy() - Make a copy of an abuf + * + * Creates an allocated copy of @old in @new + * + * @old: abuf to copy + * @new: new abuf to hold the copy (inited by this function) + * Return: true if OK, false if out of memory + */ +bool abuf_copy(const struct abuf *old, struct abuf *new); + /** * abuf_uninit_move() - Return the allocated contents and uninit the abuf * diff --git a/lib/abuf.c b/lib/abuf.c index 3cbe320fb08..28c748acb9f 100644 --- a/lib/abuf.c +++ b/lib/abuf.c @@ -128,6 +128,20 @@ bool abuf_init_size(struct abuf *buf, size_t size) return true; } +bool abuf_copy(const struct abuf *old, struct abuf *copy) +{ + char *data; + + data = malloc(old->size); + if (!data) + return false; + memcpy(data, old->data, old->size); + abuf_init_set(copy, data, old->size); + copy->alloced = true; + + return true; +} + void abuf_init_const(struct abuf *abuf, const void *data, size_t size) { /* for now there is no flag indicating that the abuf data is constant */ diff --git a/test/lib/abuf.c b/test/lib/abuf.c index cdc86aad988..96c77ed2379 100644 --- a/test/lib/abuf.c +++ b/test/lib/abuf.c @@ -420,6 +420,29 @@ static int lib_test_abuf_init(struct unit_test_state *uts) } LIB_TEST(lib_test_abuf_init, 0); +/* Test abuf_copy() */ +static int lib_test_abuf_copy(struct unit_test_state *uts) +{ + struct abuf buf, copy; + ulong start; + + start = ut_check_free(); + + abuf_init_set(&buf, test_data, TEST_DATA_LEN); + ut_assert(abuf_copy(&buf, ©)); + ut_asserteq(buf.size, copy.size); + ut_assert(buf.data != copy.data); + ut_assert(copy.alloced); + abuf_uninit(©); + abuf_uninit(&buf); + + /* Check for memory leaks */ + ut_assertok(ut_check_delta(start)); + + return 0; +} +LIB_TEST(lib_test_abuf_copy, 0); + /* Test abuf_init_size() */ static int lib_test_abuf_init_size(struct unit_test_state *uts) { -- cgit v1.3.1 From 4f4b9477f4476cd86ffd4219111065d610c5237a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:04 -0600 Subject: abuf: Add a way to printf() into a buffer It is useful to format a string into a buffer, with the sizing handled automatically. Add a function for this. Signed-off-by: Simon Glass --- include/abuf.h | 21 ++++++++++++++++++++ lib/abuf.c | 35 +++++++++++++++++++++++++++++++++ test/lib/abuf.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) (limited to 'include') diff --git a/include/abuf.h b/include/abuf.h index bbb3c51f334..7872e9c9b27 100644 --- a/include/abuf.h +++ b/include/abuf.h @@ -122,6 +122,27 @@ bool abuf_realloc_inc(struct abuf *abuf, size_t inc); */ bool abuf_copy(const struct abuf *old, struct abuf *new); +/** + * abuf_printf() - Format a string and place it in an abuf + * + * @buf: The buffer to place the result into + * @fmt: The format string to use + * @...: Arguments for the format string + * Return: the number of characters writtenwhich would be + * generated for the given input, excluding the trailing null, + * as per ISO C99. + * + * The abuf is expanded as necessary to fit the formated string + * + * See the vsprintf() documentation for format string extensions over C99. + * + * Returns: number of characters written (excluding trailing nul) on success, + * -E2BIG if the size exceeds 4K, -ENOMEM if out of memory, -EFAULT if there is + * an internal bug in the vsnprintf() implementation + */ +int abuf_printf(struct abuf *buf, const char *fmt, ...) + __attribute__ ((format (__printf__, 2, 3))); + /** * abuf_uninit_move() - Return the allocated contents and uninit the abuf * diff --git a/lib/abuf.c b/lib/abuf.c index 28c748acb9f..3a2fd1782e9 100644 --- a/lib/abuf.c +++ b/lib/abuf.c @@ -10,8 +10,11 @@ #include #include #include +#include #endif +#include +#include #include void abuf_set(struct abuf *abuf, void *data, size_t size) @@ -142,6 +145,38 @@ bool abuf_copy(const struct abuf *old, struct abuf *copy) return true; } +int abuf_printf(struct abuf *buf, const char *fmt, ...) +{ + int maxlen = buf->size; + va_list args; + int len; + + va_start(args, fmt); + len = vsnprintf(buf->data, buf->size, fmt, args); + va_end(args); + + /* add the terminator */ + len++; + + if (len > 4096) + return -E2BIG; + if (len > maxlen) { + /* make more space and try again */ + maxlen = len; + if (!abuf_realloc(buf, maxlen)) + return -ENOMEM; + va_start(args, fmt); + len = vsnprintf(buf->data, maxlen, fmt, args); + va_end(args); + + /* check there isn't anything strange going on */ + if (len > maxlen) + return -EFAULT; + } + + return len; +} + void abuf_init_const(struct abuf *abuf, const void *data, size_t size) { /* for now there is no flag indicating that the abuf data is constant */ diff --git a/test/lib/abuf.c b/test/lib/abuf.c index 96c77ed2379..97b128c01c0 100644 --- a/test/lib/abuf.c +++ b/test/lib/abuf.c @@ -463,3 +463,64 @@ static int lib_test_abuf_init_size(struct unit_test_state *uts) return 0; } LIB_TEST(lib_test_abuf_init_size, 0); + +/* Test abuf_printf() */ +static int lib_test_abuf_printf(struct unit_test_state *uts) +{ + struct abuf buf, fmt; + ulong start; + char *ptr; + + start = ut_check_free(); + + /* start with a fresh buffer */ + abuf_init(&buf); + + /* check handling of out-of-memory condition */ + malloc_enable_testing(0); + ut_asserteq(-ENOMEM, abuf_printf(&buf, "%s", "")); + malloc_enable_testing(1); + + ut_asserteq(0, abuf_printf(&buf, "%s", "")); + ut_asserteq(1, buf.size); + ut_asserteq(true, buf.alloced); + ut_asserteq_str("", buf.data); + + /* check expanding it, initially failing */ + ut_asserteq(-ENOMEM, abuf_printf(&buf, "%s", "testing")); + malloc_disable_testing(); + + ut_asserteq(7, abuf_printf(&buf, "%s", "testing")); + ut_asserteq(8, buf.size); + ut_asserteq_str("testing", buf.data); + + ut_asserteq(11, abuf_printf(&buf, "testing %d", 123)); + ut_asserteq(12, buf.size); + ut_asserteq_str("testing 123", buf.data); + + /* make it smaller; buffer should not shrink */ + ut_asserteq(9, abuf_printf(&buf, "test %d", 456)); + ut_asserteq(12, buf.size); + ut_asserteq_str("test 456", buf.data); + + /* test the maximum size */ + abuf_init(&fmt); + ut_assert(abuf_realloc(&fmt, 4100)); + memset(fmt.data, 'x', 4100); + ptr = fmt.data; + ptr[4096] = '\0'; + + /* we are allowed up to 4K including the terminator */ + ut_asserteq(-E2BIG, abuf_printf(&buf, "%s", ptr)); + ptr[4095] = '\0'; + ut_asserteq(4095, abuf_printf(&buf, "%s", ptr)); + + abuf_uninit(&fmt); + abuf_uninit(&buf); + + /* Check for memory leaks */ + ut_assertok(ut_check_delta(start)); + + return 0; +} +LIB_TEST(lib_test_abuf_printf, 0); -- cgit v1.3.1 From 5c365ecabcac6d3218cf7e560bda01629a46d88e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:15 -0600 Subject: expo: Add CLI context to the expo An expo generally needs to keep track of the keyboard state while it is running, so move the context into struct expo Signed-off-by: Simon Glass --- boot/bootflow_menu.c | 9 +++------ boot/cedit.c | 8 +++----- boot/expo.c | 1 + include/expo.h | 2 ++ 4 files changed, 9 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/boot/bootflow_menu.c b/boot/bootflow_menu.c index 9d0dc352f97..43125e15832 100644 --- a/boot/bootflow_menu.c +++ b/boot/bootflow_menu.c @@ -178,7 +178,6 @@ int bootflow_menu_apply_theme(struct expo *exp, ofnode node) int bootflow_menu_run(struct bootstd_priv *std, bool text_mode, struct bootflow **bflowp) { - struct cli_ch_state s_cch, *cch = &s_cch; struct bootflow *sel_bflow; struct udevice *dev; struct expo *exp; @@ -186,8 +185,6 @@ int bootflow_menu_run(struct bootstd_priv *std, bool text_mode, bool done; int ret; - cli_ch_init(cch); - sel_bflow = NULL; *bflowp = NULL; @@ -225,16 +222,16 @@ int bootflow_menu_run(struct bootstd_priv *std, bool text_mode, if (ret) break; - ichar = cli_ch_process(cch, 0); + ichar = cli_ch_process(&exp->cch, 0); if (!ichar) { while (!ichar && !tstc()) { schedule(); mdelay(2); - ichar = cli_ch_process(cch, -ETIMEDOUT); + ichar = cli_ch_process(&exp->cch, -ETIMEDOUT); } if (!ichar) { ichar = getchar(); - ichar = cli_ch_process(cch, ichar); + ichar = cli_ch_process(&exp->cch, ichar); } } diff --git a/boot/cedit.c b/boot/cedit.c index 4e80875828b..ed499f11140 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -151,14 +151,12 @@ int cedit_prepare(struct expo *exp, struct video_priv **vid_privp, int cedit_run(struct expo *exp) { - struct cli_ch_state s_cch, *cch = &s_cch; struct video_priv *vid_priv; uint scene_id; struct scene *scn; bool done, save; int ret; - cli_ch_init(cch); ret = cedit_prepare(exp, &vid_priv, &scn); if (ret < 0) return log_msg_ret("prep", ret); @@ -174,16 +172,16 @@ int cedit_run(struct expo *exp) if (ret) break; - ichar = cli_ch_process(cch, 0); + ichar = cli_ch_process(&exp->cch, 0); if (!ichar) { while (!ichar && !tstc()) { schedule(); mdelay(2); - ichar = cli_ch_process(cch, -ETIMEDOUT); + ichar = cli_ch_process(&exp->cch, -ETIMEDOUT); } if (!ichar) { ichar = getchar(); - ichar = cli_ch_process(cch, ichar); + ichar = cli_ch_process(&exp->cch, ichar); } } diff --git a/boot/expo.c b/boot/expo.c index 8ce645e5a8f..9c042f16fe7 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -30,6 +30,7 @@ int expo_new(const char *name, void *priv, struct expo **expp) INIT_LIST_HEAD(&exp->scene_head); INIT_LIST_HEAD(&exp->str_head); exp->next_id = EXPOID_BASE_ID; + cli_ch_init(&exp->cch); *expp = exp; diff --git a/include/expo.h b/include/expo.h index 3c383d2e2ee..b3b9c0b8872 100644 --- a/include/expo.h +++ b/include/expo.h @@ -108,6 +108,7 @@ struct expo_theme { * @theme: Information about fonts styles, etc. * @scene_head: List of scenes * @str_head: list of strings + * @cch: Keyboard context for input */ struct expo { char *name; @@ -122,6 +123,7 @@ struct expo { struct expo_theme theme; struct list_head scene_head; struct list_head str_head; + struct cli_ch_state cch; }; /** -- cgit v1.3.1 From bf9860459516d1837e92270340ca307211cd961a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:16 -0600 Subject: expo: Add a function to poll for input Both bootflow_menu and cedit use similar logic to poll an expo. Move this into the expo library so the code can be shared. Update bootflow_menu_run() to return -EPIPE when the user quits without choosing anything, since -EAGAIN is ambiguous and elsewhere means that there is no input yet. Signed-off-by: Simon Glass --- boot/bootflow_menu.c | 41 ++++------------------------------------- boot/cedit.c | 35 +++-------------------------------- boot/expo.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ cmd/bootflow.c | 2 +- include/bootflow.h | 2 +- include/expo.h | 13 +++++++++++++ 6 files changed, 66 insertions(+), 71 deletions(-) (limited to 'include') diff --git a/boot/bootflow_menu.c b/boot/bootflow_menu.c index 43125e15832..268c93ae8e3 100644 --- a/boot/bootflow_menu.c +++ b/boot/bootflow_menu.c @@ -216,39 +216,8 @@ int bootflow_menu_run(struct bootstd_priv *std, bool text_mode, done = false; do { struct expo_action act; - int ichar, key; - ret = expo_render(exp); - if (ret) - break; - - ichar = cli_ch_process(&exp->cch, 0); - if (!ichar) { - while (!ichar && !tstc()) { - schedule(); - mdelay(2); - ichar = cli_ch_process(&exp->cch, -ETIMEDOUT); - } - if (!ichar) { - ichar = getchar(); - ichar = cli_ch_process(&exp->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); + ret = expo_poll(exp, &act); if (!ret) { switch (act.type) { case EXPOACT_SELECT: @@ -256,17 +225,15 @@ int bootflow_menu_run(struct bootstd_priv *std, bool text_mode, done = true; break; case EXPOACT_QUIT: - done = true; - break; + return -EPIPE; default: break; } + } else if (ret != -EPIPE && ret != -EAGAIN) { + return log_msg_ret("bmr", ret); } } while (!done); - if (ret) - return log_msg_ret("end", ret); - if (sel_id) { struct bootflow *bflow; int i; diff --git a/boot/cedit.c b/boot/cedit.c index ed499f11140..b7b9cc510e0 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -166,39 +166,8 @@ int cedit_run(struct expo *exp) save = false; do { struct expo_action act; - int ichar, key; - ret = expo_render(exp); - if (ret) - break; - - ichar = cli_ch_process(&exp->cch, 0); - if (!ichar) { - while (!ichar && !tstc()) { - schedule(); - mdelay(2); - ichar = cli_ch_process(&exp->cch, -ETIMEDOUT); - } - if (!ichar) { - ichar = getchar(); - ichar = cli_ch_process(&exp->cch, ichar); - } - } - - key = 0; - if (ichar) { - key = bootmenu_conv_key(ichar); - if (key == BKEY_NONE || key >= BKEY_FIRST_EXTRA) - key = ichar; - } - if (!key) - continue; - - ret = expo_send_key(exp, key); - if (ret) - break; - - ret = expo_action_get(exp, &act); + ret = expo_poll(exp, &act); if (!ret) { switch (act.type) { case EXPOACT_POINT_OBJ: @@ -233,6 +202,8 @@ int cedit_run(struct expo *exp) default: break; } + } else if (ret != -EAGAIN) { + return log_msg_ret("cep", ret); } } while (!done); diff --git a/boot/expo.c b/boot/expo.c index 9c042f16fe7..301bbfa5f9a 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -10,8 +10,12 @@ #include #include +#include #include +#include #include +#include +#include #include "scene_internal.h" int expo_new(const char *name, void *priv, struct expo **expp) @@ -286,3 +290,43 @@ int expo_iter_scene_objs(struct expo *exp, expo_scene_obj_iterator iter, return 0; } + +int expo_poll(struct expo *exp, struct expo_action *act) +{ + int ichar, key, ret; + + ret = expo_render(exp); + if (ret) + return log_msg_ret("ere", ret); + + ichar = cli_ch_process(&exp->cch, 0); + if (!ichar) { + while (!ichar && !tstc()) { + schedule(); + mdelay(2); + ichar = cli_ch_process(&exp->cch, -ETIMEDOUT); + } + if (!ichar) { + ichar = getchar(); + ichar = cli_ch_process(&exp->cch, ichar); + } + } + + key = 0; + if (ichar) { + key = bootmenu_conv_key(ichar); + if (key == BKEY_NONE || key >= BKEY_FIRST_EXTRA) + key = ichar; + } + if (!key) + return -EAGAIN; + + ret = expo_send_key(exp, key); + if (ret) + return log_msg_ret("epk", ret); + ret = expo_action_get(exp, act); + if (ret) + return log_msg_ret("eag", ret); + + return 0; +} diff --git a/cmd/bootflow.c b/cmd/bootflow.c index d4f7d336150..f2662239714 100644 --- a/cmd/bootflow.c +++ b/cmd/bootflow.c @@ -110,7 +110,7 @@ __maybe_unused static int bootflow_handle_menu(struct bootstd_priv *std, ret = bootflow_menu_run(std, text_mode, &bflow); if (ret) { - if (ret == -EAGAIN) { + if (ret == -EPIPE) { printf("Nothing chosen\n"); std->cur_bootflow = NULL; } else { diff --git a/include/bootflow.h b/include/bootflow.h index d408b8c85bd..fe090d39ffb 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -508,7 +508,7 @@ int bootflow_menu_apply_theme(struct expo *exp, ofnode node); * @std: Bootstd information * @text_mode: Uses a text-based menu suitable for a serial port * @bflowp: Returns chosen bootflow (set to NULL if nothing is chosen) - * @return 0 if an option was chosen, -EAGAIN if nothing was chosen, -ve on + * @return 0 if an option was chosen, -EPIPE if nothing was chosen, -ve on * error */ int bootflow_menu_run(struct bootstd_priv *std, bool text_mode, diff --git a/include/expo.h b/include/expo.h index b3b9c0b8872..63452bbdd6a 100644 --- a/include/expo.h +++ b/include/expo.h @@ -772,4 +772,17 @@ int expo_build(ofnode root, struct expo **expp); */ int cb_expo_build(struct expo **expp); +/** + * expo_poll() - render an expo and see if the user takes an action + * + * Thsi calls expo_render() and then checks for a keypress. If there is one, it + * is processed and the resulting action returned, if any + * + * @exp: Expo to poll + * @act: Returns action on success + * Return: 0 if an action was obtained, -EAGAIN if not, other error if something + * went wrong + */ +int expo_poll(struct expo *exp, struct expo_action *act); + #endif /*__EXPO_H */ -- cgit v1.3.1 From 932ea4a1044455e7bcb48fb1391ea7e06137fad5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:20 -0600 Subject: expo: Move cedit-state fields into expo Move the boolean flags into struct expo so that the state can be maintained over function calls. Signed-off-by: Simon Glass --- boot/cedit.c | 17 ++++++++--------- include/expo.h | 4 ++++ 2 files changed, 12 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/boot/cedit.c b/boot/cedit.c index b7b9cc510e0..8c6948d1d46 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -154,7 +154,6 @@ int cedit_run(struct expo *exp) struct video_priv *vid_priv; uint scene_id; struct scene *scn; - bool done, save; int ret; ret = cedit_prepare(exp, &vid_priv, &scn); @@ -162,8 +161,8 @@ int cedit_run(struct expo *exp) return log_msg_ret("prep", ret); scene_id = ret; - done = false; - save = false; + exp->done = false; + exp->save = false; do { struct expo_action act; @@ -179,11 +178,11 @@ int cedit_run(struct expo *exp) cedit_arange(exp, vid_priv, scene_id); switch (scn->highlight_id) { case EXPOID_SAVE: - done = true; - save = true; + exp->done = true; + exp->save = true; break; case EXPOID_DISCARD: - done = true; + exp->done = true; break; } break; @@ -197,7 +196,7 @@ int cedit_run(struct expo *exp) break; case EXPOACT_QUIT: log_debug("quitting\n"); - done = true; + exp->done = true; break; default: break; @@ -205,11 +204,11 @@ int cedit_run(struct expo *exp) } else if (ret != -EAGAIN) { return log_msg_ret("cep", ret); } - } while (!done); + } while (!exp->done); if (ret) return log_msg_ret("end", ret); - if (!save) + if (!exp->save) return -EACCES; return 0; diff --git a/include/expo.h b/include/expo.h index 63452bbdd6a..f8d44c0ea20 100644 --- a/include/expo.h +++ b/include/expo.h @@ -105,6 +105,8 @@ struct expo_theme { * @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 + * @done: Indicates that a cedit session is complete and the user has quit + * @save: Indicates that cedit data should be saved, rather than discarded * @theme: Information about fonts styles, etc. * @scene_head: List of scenes * @str_head: list of strings @@ -120,6 +122,8 @@ struct expo { bool text_mode; bool popup; void *priv; + bool done; + bool save; struct expo_theme theme; struct list_head scene_head; struct list_head str_head; -- cgit v1.3.1 From 5f993342962de816044f4172234ca7ecfd2bcaf6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:21 -0600 Subject: expo: Test some cedit actions Refactor the action-processing code into a new cedit_do_action() function so we can call it from a test. Check moving to a new field and opening the menu, to ensure that rendering is correct. Signed-off-by: Simon Glass --- boot/cedit.c | 80 ++++++++++++++++++++++++++++++------------------------- include/cedit.h | 13 +++++++++ test/boot/cedit.c | 43 ++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/boot/cedit.c b/boot/cedit.c index 8c6948d1d46..f1a9ee7ce20 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -149,6 +149,47 @@ int cedit_prepare(struct expo *exp, struct video_priv **vid_privp, return scene_id; } +int cedit_do_action(struct expo *exp, struct scene *scn, + struct video_priv *vid_priv, struct expo_action *act) +{ + switch (act->type) { + case EXPOACT_NONE: + case EXPOACT_POINT_ITEM: + return -EAGAIN; + case EXPOACT_POINT_OBJ: + scene_set_highlight_id(scn, act->select.id); + cedit_arange(exp, vid_priv, scn->id); + break; + case EXPOACT_OPEN: + scene_set_open(scn, act->select.id, true); + cedit_arange(exp, vid_priv, scn->id); + switch (scn->highlight_id) { + case EXPOID_SAVE: + exp->done = true; + exp->save = true; + break; + case EXPOID_DISCARD: + exp->done = true; + break; + } + break; + case EXPOACT_CLOSE: + scene_set_open(scn, act->select.id, false); + cedit_arange(exp, vid_priv, scn->id); + break; + case EXPOACT_SELECT: + scene_set_open(scn, scn->highlight_id, false); + cedit_arange(exp, vid_priv, scn->id); + break; + case EXPOACT_QUIT: + log_debug("quitting\n"); + exp->done = true; + break; + } + + return 0; +} + int cedit_run(struct expo *exp) { struct video_priv *vid_priv; @@ -167,43 +208,10 @@ int cedit_run(struct expo *exp) struct expo_action act; ret = expo_poll(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); - switch (scn->highlight_id) { - case EXPOID_SAVE: - exp->done = true; - exp->save = true; - break; - case EXPOID_DISCARD: - exp->done = true; - break; - } - 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"); - exp->done = true; - break; - default: - break; - } - } else if (ret != -EAGAIN) { + if (!ret) + cedit_do_action(exp, scn, vid_priv, &act); + else if (ret != -EAGAIN) return log_msg_ret("cep", ret); - } } while (!exp->done); if (ret) diff --git a/include/cedit.h b/include/cedit.h index 856509f0c7f..a9305ceebcb 100644 --- a/include/cedit.h +++ b/include/cedit.h @@ -13,6 +13,7 @@ struct abuf; struct expo; +struct expo_action; struct scene; struct udevice; struct video_priv; @@ -62,6 +63,18 @@ int cedit_run(struct expo *exp); int cedit_prepare(struct expo *exp, struct video_priv **vid_privp, struct scene **scnp); +/** + * cedit_do_action() - Process an action on a cedit + * + * @exp: Expo to use + * @scn: Current scene + * @vid_priv: Private data for the video device + * @act: Action to process + * Return: 0 on success, -EAGAIN if there was no action taken + */ +int cedit_do_action(struct expo *exp, struct scene *scn, + struct video_priv *vid_priv, struct expo_action *act); + /** * cedit_write_settings() - Write settings in FDT format * diff --git a/test/boot/cedit.c b/test/boot/cedit.c index df191a09f89..5b3e9b586a6 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -226,8 +226,10 @@ BOOTSTD_TEST(cedit_cmos, UTF_CONSOLE); /* Check the cedit displays correctely */ static int cedit_render(struct unit_test_state *uts) { + struct scene_obj_menu *menu; struct video_priv *vid_priv; extern struct expo *cur_exp; + struct expo_action act; struct udevice *dev; struct scene *scn; struct expo *exp; @@ -237,9 +239,50 @@ static int cedit_render(struct unit_test_state *uts) exp = cur_exp; ut_assertok(uclass_first_device_err(UCLASS_VIDEO, &dev)); ut_asserteq(ID_SCENE1, cedit_prepare(exp, &vid_priv, &scn)); + + menu = scene_obj_find(scn, ID_POWER_LOSS, SCENEOBJT_MENU); + ut_assertnonnull(menu); + ut_asserteq(ID_AC_OFF, menu->cur_item_id); + ut_assertok(expo_render(exp)); ut_asserteq(4929, video_compress_fb(uts, dev, false)); ut_assertok(video_check_copy_fb(uts, dev)); + + /* move to the second menu */ + act.type = EXPOACT_POINT_OBJ; + act.select.id = ID_POWER_LOSS; + ut_assertok(cedit_do_action(exp, scn, vid_priv, &act)); + ut_assertok(expo_render(exp)); + ut_asserteq(4986, video_compress_fb(uts, dev, false)); + + /* open the menu */ + act.type = EXPOACT_OPEN; + act.select.id = ID_POWER_LOSS; + ut_assertok(cedit_do_action(exp, scn, vid_priv, &act)); + ut_assertok(expo_render(exp)); + ut_asserteq(5393, video_compress_fb(uts, dev, false)); + + /* close the menu */ + act.type = EXPOACT_CLOSE; + act.select.id = ID_POWER_LOSS; + ut_assertok(cedit_do_action(exp, scn, vid_priv, &act)); + ut_assertok(expo_render(exp)); + ut_asserteq(4986, video_compress_fb(uts, dev, false)); + + /* open the menu again to check it looks the same */ + act.type = EXPOACT_OPEN; + act.select.id = ID_POWER_LOSS; + ut_assertok(cedit_do_action(exp, scn, vid_priv, &act)); + ut_assertok(expo_render(exp)); + ut_asserteq(5393, video_compress_fb(uts, dev, false)); + + /* close the menu */ + act.type = EXPOACT_CLOSE; + act.select.id = ID_POWER_LOSS; + ut_assertok(cedit_do_action(exp, scn, vid_priv, &act)); + ut_assertok(expo_render(exp)); + ut_asserteq(4986, video_compress_fb(uts, dev, false)); + expo_destroy(exp); cur_exp = NULL; -- cgit v1.3.1 From 23d2ddf1b4663c497ae6223174c96138df8b675a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:22 -0600 Subject: expo: Pass in the video device for cedit_prepare() At present this function locates it own video device. Pass it in to provide more flexibility. Signed-off-by: Simon Glass --- boot/cedit.c | 18 ++++++++++-------- include/cedit.h | 4 ++-- test/boot/cedit.c | 20 ++++++++++++++++---- 3 files changed, 28 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/boot/cedit.c b/boot/cedit.c index f1a9ee7ce20..eac851b395a 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -100,19 +100,16 @@ int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) return 0; } -int cedit_prepare(struct expo *exp, struct video_priv **vid_privp, +int cedit_prepare(struct expo *exp, struct udevice *vid_dev, struct scene **scnp) { + struct udevice *dev = vid_dev; struct video_priv *vid_priv; - struct udevice *dev; struct scene *scn; uint scene_id; int ret; /* 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); @@ -143,7 +140,6 @@ int cedit_prepare(struct expo *exp, struct video_priv **vid_privp, if (ret) return log_msg_ret("dim", ret); - *vid_privp = vid_priv; *scnp = scn; return scene_id; @@ -193,11 +189,17 @@ int cedit_do_action(struct expo *exp, struct scene *scn, int cedit_run(struct expo *exp) { struct video_priv *vid_priv; - uint scene_id; + struct udevice *dev; struct scene *scn; + uint scene_id; int ret; - ret = cedit_prepare(exp, &vid_priv, &scn); + ret = uclass_first_device_err(UCLASS_VIDEO, &dev); + if (ret) + return log_msg_ret("vid", ret); + vid_priv = dev_get_uclass_priv(dev); + + ret = cedit_prepare(exp, dev, &scn); if (ret < 0) return log_msg_ret("prep", ret); scene_id = ret; diff --git a/include/cedit.h b/include/cedit.h index a9305ceebcb..319a61aecb8 100644 --- a/include/cedit.h +++ b/include/cedit.h @@ -56,11 +56,11 @@ int cedit_run(struct expo *exp); * This ensures that all menus have a selected item. * * @exp: Expo to use - * @vid_privp: Set to private data for the video device + * @dev: Video device to use * @scnp: Set to the first scene * Return: scene ID of first scene if OK, -ve on error */ -int cedit_prepare(struct expo *exp, struct video_priv **vid_privp, +int cedit_prepare(struct expo *exp, struct udevice *vid_dev, struct scene **scnp); /** diff --git a/test/boot/cedit.c b/test/boot/cedit.c index 5b3e9b586a6..746f60067fd 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -63,6 +63,7 @@ static int cedit_fdt(struct unit_test_state *uts) struct video_priv *vid_priv; extern struct expo *cur_exp; struct scene_obj_menu *menu; + struct udevice *dev; ulong addr = 0x1000; struct ofprop prop; struct scene *scn; @@ -72,9 +73,12 @@ static int cedit_fdt(struct unit_test_state *uts) void *fdt; int i; + ut_assertok(uclass_first_device_err(UCLASS_VIDEO, &dev)); + vid_priv = dev_get_uclass_priv(dev); + ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0)); - ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, &vid_priv, &scn)); + ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, dev, &scn)); /* get a menu to fiddle with */ menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_MENU); @@ -134,12 +138,16 @@ static int cedit_env(struct unit_test_state *uts) struct video_priv *vid_priv; extern struct expo *cur_exp; struct scene_obj_menu *menu; + struct udevice *dev; struct scene *scn; char *str; ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0)); - ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, &vid_priv, &scn)); + ut_assertok(uclass_first_device_err(UCLASS_VIDEO, &dev)); + vid_priv = dev_get_uclass_priv(dev); + + ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, dev, &scn)); /* get a menu to fiddle with */ menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_MENU); @@ -189,11 +197,14 @@ static int cedit_cmos(struct unit_test_state *uts) struct scene_obj_menu *menu, *menu2; struct video_priv *vid_priv; extern struct expo *cur_exp; + struct udevice *dev; struct scene *scn; ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0)); - ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, &vid_priv, &scn)); + ut_assertok(uclass_first_device_err(UCLASS_VIDEO, &dev)); + vid_priv = dev_get_uclass_priv(dev); + ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, dev, &scn)); /* get the menus to fiddle with */ menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_MENU); @@ -238,7 +249,8 @@ static int cedit_render(struct unit_test_state *uts) exp = cur_exp; ut_assertok(uclass_first_device_err(UCLASS_VIDEO, &dev)); - ut_asserteq(ID_SCENE1, cedit_prepare(exp, &vid_priv, &scn)); + vid_priv = dev_get_uclass_priv(dev); + ut_asserteq(ID_SCENE1, cedit_prepare(exp, dev, &scn)); menu = scene_obj_find(scn, ID_POWER_LOSS, SCENEOBJT_MENU); ut_assertnonnull(menu); -- cgit v1.3.1 From 9f44b544e1d8b23dec51567c657e81488a289d6d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:24 -0600 Subject: expo: Provide access to the current menu item Add functions to allow a caller to find out the current menu item and to select a different one. Update the event handling so that an attempt to change the current item (e.g. by pressing the up-arrow key) is reported to the caller, since this may be used to cancel an autoboot timeout. Signed-off-by: Simon Glass --- boot/scene_menu.c | 44 ++++++++++++++++++++++++++++++++++++++++---- include/expo.h | 20 ++++++++++++++++++++ test/boot/expo.c | 4 ++++ 3 files changed, 64 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/boot/scene_menu.c b/boot/scene_menu.c index 17150af145d..06d7e9fc913 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -121,12 +121,21 @@ static int update_pointers(struct scene_obj_menu *menu, uint id, bool point) * * Sets the currently pointed-to / highlighted menu item */ -static void menu_point_to_item(struct scene_obj_menu *menu, uint item_id) +static int menu_point_to_item(struct scene_obj_menu *menu, uint item_id) { - if (menu->cur_item_id) - update_pointers(menu, menu->cur_item_id, false); + int ret; + + if (menu->cur_item_id) { + ret = update_pointers(menu, menu->cur_item_id, false); + if (ret) + return log_msg_ret("mpi", ret); + } menu->cur_item_id = item_id; - update_pointers(menu, item_id, true); + ret = update_pointers(menu, item_id, true); + if (ret) + return log_msg_ret("mpu", ret); + + return 0; } void scene_menu_calc_bbox(struct scene_obj_menu *menu, @@ -483,6 +492,33 @@ int scene_menu_set_pointer(struct scene *scn, uint id, uint pointer_id) return 0; } +int scene_menu_select_item(struct scene *scn, uint id, uint cur_item_id) +{ + struct scene_obj_menu *menu; + int ret; + + menu = scene_obj_find(scn, id, SCENEOBJT_MENU); + if (!menu) + return log_msg_ret("menu", -ENOENT); + + ret = menu_point_to_item(menu, cur_item_id); + if (ret) + return log_msg_ret("msi", ret); + + return 0; +} + +int scene_menu_get_cur_item(struct scene *scn, uint id) +{ + struct scene_obj_menu *menu; + + menu = scene_obj_find(scn, id, SCENEOBJT_MENU); + if (!menu) + return log_msg_ret("menu", -ENOENT); + + return menu->cur_item_id; +} + int scene_menu_display(struct scene_obj_menu *menu) { struct scene *scn = menu->obj.scene; diff --git a/include/expo.h b/include/expo.h index f8d44c0ea20..a2b093c521d 100644 --- a/include/expo.h +++ b/include/expo.h @@ -689,6 +689,26 @@ int scene_menu_set_title(struct scene *scn, uint id, uint title_id); */ int scene_menu_set_pointer(struct scene *scn, uint id, uint cur_item_id); +/** + * scene_menu_select_item() - move the pointer/highlight to an item + * + * @scn: Scene to update + * @id: ID of menu object to update + * @sel_id: ID of the menuitem to select + * Return 0 on success, -ENOENT if there was no such item + */ +int scene_menu_select_item(struct scene *scn, uint id, uint sel_id); + +/** + * scene_menu_get_cur_item() - get the currently pointed-to item + * + * @scn: Scene to update + * @id: ID of menu object to update + * Return ID of the current item the menu is pointing to, -ENOENT if @id is not + * valid, 0 if no item is pointed to + */ +int scene_menu_get_cur_item(struct scene *scn, uint id); + /** * scene_obj_get_hw() - Get width and height of an object in a scene * diff --git a/test/boot/expo.c b/test/boot/expo.c index 1d283a2ac95..616071ead48 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -588,6 +588,8 @@ static int expo_render_image(struct unit_test_state *uts) expo_set_scene_id(exp, SCENE1); ut_assertok(expo_render(exp)); + ut_asserteq(0, scn->highlight_id); + /* move down */ ut_assertok(expo_send_key(exp, BKEY_DOWN)); @@ -595,6 +597,8 @@ static int expo_render_image(struct unit_test_state *uts) ut_asserteq(EXPOACT_POINT_ITEM, act.type); ut_asserteq(ITEM2, act.select.id); + ut_assertok(scene_menu_select_item(scn, OBJ_MENU, act.select.id)); + ut_asserteq(ITEM2, scene_menu_get_cur_item(scn, OBJ_MENU)); ut_assertok(expo_render(exp)); /* make sure only the preview for the second item is shown */ -- cgit v1.3.1 From b991a0c8bf30095837b096acd691e0d2add07b8c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:27 -0600 Subject: expo: Split bootflow_menu_run() into two pieces Split the starting piece of this function into bootflow_menu_start() and the polling part into bootflow_menu_poll() so that it is possible for the caller to be in control of the event loop. Move the expo_destroy() call into the caller. Signed-off-by: Simon Glass --- boot/bootflow_menu.c | 89 ++++++++++++++++++++++++---------------------------- cmd/bootflow.c | 36 +++++++++++++-------- include/bootflow.h | 33 ++++++++++++------- 3 files changed, 85 insertions(+), 73 deletions(-) (limited to 'include') diff --git a/boot/bootflow_menu.c b/boot/bootflow_menu.c index f9ebc526200..ed7c0768c7b 100644 --- a/boot/bootflow_menu.c +++ b/boot/bootflow_menu.c @@ -175,20 +175,13 @@ int bootflow_menu_apply_theme(struct expo *exp, ofnode node) return 0; } -int bootflow_menu_run(struct bootstd_priv *std, bool text_mode, - struct bootflow **bflowp) +int bootflow_menu_start(struct bootstd_priv *std, bool text_mode, + struct expo **expp) { - struct bootflow *sel_bflow; struct udevice *dev; - struct scene *scn; struct expo *exp; - uint sel_id; - bool done; int ret; - sel_bflow = NULL; - *bflowp = NULL; - ret = bootflow_menu_new(&exp); if (ret) return log_msg_ret("exp", ret); @@ -210,58 +203,58 @@ int bootflow_menu_run(struct bootstd_priv *std, bool text_mode, ret = expo_set_scene_id(exp, MAIN); if (ret) return log_msg_ret("scn", ret); - scn = expo_lookup_scene_id(exp, MAIN); - if (!scn) - return log_msg_ret("scn", -ENOENT); if (text_mode) expo_set_text_mode(exp, text_mode); - done = false; - do { - struct expo_action act; - - ret = expo_poll(exp, &act); - if (!ret) { - switch (act.type) { - case EXPOACT_SELECT: - sel_id = act.select.id; - done = true; - break; - case EXPOACT_POINT_ITEM: - ret = scene_menu_select_item(scn, - OBJ_MENU, act.select.id); - if (ret) - return log_msg_ret("bmp", ret); - break; - case EXPOACT_QUIT: - return -EPIPE; - default: - break; - } - } else if (ret != -EPIPE && ret != -EAGAIN) { - return log_msg_ret("bmr", ret); - } - } while (!done); + *expp = exp; + + return 0; +} - if (sel_id) { +int bootflow_menu_poll(struct expo *exp, struct bootflow **bflowp) +{ + struct bootflow *sel_bflow; + struct expo_action act; + int ret; + + sel_bflow = NULL; + *bflowp = NULL; + + ret = expo_poll(exp, &act); + if (ret) + return log_msg_ret("bmp", ret); + + switch (act.type) { + case EXPOACT_SELECT: { struct bootflow *bflow; int i; for (ret = bootflow_first_glob(&bflow), i = 0; !ret && i < 36; ret = bootflow_next_glob(&bflow), i++) { - if (i == sel_id - ITEM) { - sel_bflow = bflow; - break; + if (i == act.select.id - ITEM) { + *bflowp = bflow; + // printf("found %p\n", bflow); + return 0; } } + break; } + case EXPOACT_POINT_ITEM: { + struct scene *scn = expo_lookup_scene_id(exp, MAIN); - expo_destroy(exp); - - if (!sel_bflow) - return -EAGAIN; - *bflowp = sel_bflow; + if (!scn) + return log_msg_ret("bms", -ENOENT); + ret = scene_menu_select_item(scn, OBJ_MENU, act.select.id); + if (ret) + return log_msg_ret("bmp", ret); + break; + } + case EXPOACT_QUIT: + return -EPIPE; + default: + break; + } - return 0; + return -EAGAIN; } diff --git a/cmd/bootflow.c b/cmd/bootflow.c index f2662239714..efac27a5d77 100644 --- a/cmd/bootflow.c +++ b/cmd/bootflow.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include /** @@ -105,24 +107,32 @@ __maybe_unused static int bootflow_handle_menu(struct bootstd_priv *std, bool text_mode, struct bootflow **bflowp) { + struct expo *exp; struct bootflow *bflow; int ret; - ret = bootflow_menu_run(std, text_mode, &bflow); - if (ret) { - if (ret == -EPIPE) { - printf("Nothing chosen\n"); - std->cur_bootflow = NULL; - } else { - printf("Menu failed (err=%d)\n", ret); - } + ret = bootflow_menu_start(std, text_mode, &exp); + if (ret) + return log_msg_ret("bhs", ret); - return ret; - } + do { + ret = bootflow_menu_poll(exp, &bflow); + } while (ret == -EAGAIN); - printf("Selected: %s\n", bflow->os_name ? bflow->os_name : bflow->name); - std->cur_bootflow = bflow; - *bflowp = bflow; + if (ret == -EPIPE) { + printf("Nothing chosen\n"); + std->cur_bootflow = NULL; + } else if (ret) { + printf("Menu failed (err=%d)\n", ret); + } else { + printf("Selected: %s\n", bflow->os_name ? bflow->os_name : + bflow->name); + std->cur_bootflow = bflow; + *bflowp = bflow; + } + expo_destroy(exp); + if (ret) + return ret; return 0; } diff --git a/include/bootflow.h b/include/bootflow.h index fe090d39ffb..8244d4fca18 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -502,18 +502,6 @@ int bootflow_menu_new(struct expo **expp); */ int bootflow_menu_apply_theme(struct expo *exp, ofnode node); -/** - * bootflow_menu_run() - Create and run a menu of available bootflows - * - * @std: Bootstd information - * @text_mode: Uses a text-based menu suitable for a serial port - * @bflowp: Returns chosen bootflow (set to NULL if nothing is chosen) - * @return 0 if an option was chosen, -EPIPE if nothing was chosen, -ve on - * error - */ -int bootflow_menu_run(struct bootstd_priv *std, bool text_mode, - struct bootflow **bflowp); - #define BOOTFLOWCL_EMPTY ((void *)1) /** @@ -638,4 +626,25 @@ struct bootflow_img *bootflow_img_add(struct bootflow *bflow, const char *fname, */ int bootflow_get_seq(const struct bootflow *bflow); +/** + * bootflow_menu_start() - Start up a menu for bootflows + * + * @std: bootstd information + * @text_mode: true to show the menu in text mode, false to use video display + * @expp: Returns the expo created, on success + * Return: 0 if OK, -ve on error + */ +int bootflow_menu_start(struct bootstd_priv *std, bool text_mode, + struct expo **expp); + +/** + * bootflow_menu_poll() - Poll a menu for user action + * + * @exp: Expo to poll + * @bflowp: Returns chosen bootflow (set to NULL if nothing is chosen) + * Return 0 if a bootflow was chosen, -EAGAIN if nothing is chosen yet, -EPIPE + * if the user quit + */ +int bootflow_menu_poll(struct expo *exp, struct bootflow **bflowp); + #endif -- cgit v1.3.1 From da754e20c99a0e611b05c74f9e28daf9cdf1a199 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:28 -0600 Subject: expo: Split bootflow_menu_new() into two pieces Split the iteration piece of this function into bootflow_menu_add_all() so that it is possible for the caller to be in control of adding items to the menu. Move the expo_destroy() call into the caller. Signed-off-by: Simon Glass --- boot/bootflow_menu.c | 31 +++++++++++++++++++++++++------ include/bootflow.h | 13 +++++++++++++ test/boot/bootflow.c | 1 + 3 files changed, 39 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/boot/bootflow_menu.c b/boot/bootflow_menu.c index ed7c0768c7b..f731d7858b2 100644 --- a/boot/bootflow_menu.c +++ b/boot/bootflow_menu.c @@ -32,14 +32,12 @@ struct menu_priv { int bootflow_menu_new(struct expo **expp) { - struct udevice *last_bootdev; struct scene_obj_menu *menu; struct menu_priv *priv; - struct bootflow *bflow; struct scene *scn; struct expo *exp; void *logo; - int ret, i; + int ret; priv = calloc(1, sizeof(*priv)); if (!priv) @@ -74,6 +72,26 @@ int bootflow_menu_new(struct expo **expp) if (ret < 0) return log_msg_ret("new", -EINVAL); + *expp = exp; + + return 0; +} + +int bootflow_menu_add_all(struct expo *exp) +{ + struct menu_priv *priv = exp->priv; + struct udevice *last_bootdev; + struct bootflow *bflow; + struct scene *scn; + uint scene_id; + int ret, i; + + ret = expo_first_scene_id(exp); + if (ret < 0) + return log_msg_ret("scn", ret); + scene_id = ret; + scn = expo_lookup_scene_id(exp, scene_id); + last_bootdev = NULL; for (ret = bootflow_first_glob(&bflow), i = 0; !ret && i < 36; ret = bootflow_next_glob(&bflow), i++) { @@ -133,8 +151,6 @@ int bootflow_menu_new(struct expo **expp) if (ret) return log_msg_ret("arr", ret); - *expp = exp; - return 0; } @@ -184,7 +200,10 @@ int bootflow_menu_start(struct bootstd_priv *std, bool text_mode, ret = bootflow_menu_new(&exp); if (ret) - return log_msg_ret("exp", ret); + return log_msg_ret("bmn", ret); + ret = bootflow_menu_add_all(exp); + if (ret) + return log_msg_ret("bma", ret); if (ofnode_valid(std->theme)) { ret = bootflow_menu_apply_theme(exp, std->theme); diff --git a/include/bootflow.h b/include/bootflow.h index 8244d4fca18..994b38394e8 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -488,11 +488,24 @@ int bootflow_iter_check_system(const struct bootflow_iter *iter); /** * bootflow_menu_new() - Create a new bootflow menu * + * This is initially empty. Call bootflow_menu_add_all() to add all the + * bootflows to it. + * * @expp: Returns the expo created * Returns 0 on success, -ve on error */ int bootflow_menu_new(struct expo **expp); +/** + * bootflow_menu_add_all() - Add all bootflows to a menu + * + * Loops through all bootflows and adds them to the menu + * + * @exp: Menu to update + * Return 0 on success, -ve on error + */ +int bootflow_menu_add_all(struct expo *exp); + /** * bootflow_menu_apply_theme() - Apply a theme to a bootmenu * diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index a930d2cc309..58885b5d8bf 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -878,6 +878,7 @@ static int bootflow_menu_theme(struct unit_test_state *uts) ut_assertok(scan_mmc4_bootdev(uts)); ut_assertok(bootflow_menu_new(&exp)); + ut_assertok(bootflow_menu_add_all(exp)); node = ofnode_path("/bootstd/theme"); ut_assert(ofnode_valid(node)); ut_assertok(bootflow_menu_apply_theme(exp, node)); -- cgit v1.3.1 From a4ede5e0aced904efb17f3a3d5fb2c7f3c622133 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:29 -0600 Subject: expo: Allow adding a single bootflow to a menu Refactor bootflow_menu_add_all() to call a new bootflow_menu_add() to add each of its bootflows. Move the last_bootdev value into struct menu_priv to make this work. Signed-off-by: Simon Glass --- boot/bootflow_menu.c | 103 +++++++++++++++++++++++++++++---------------------- include/bootflow.h | 16 ++++++++ 2 files changed, 75 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/boot/bootflow_menu.c b/boot/bootflow_menu.c index f731d7858b2..6a37fa8fa68 100644 --- a/boot/bootflow_menu.c +++ b/boot/bootflow_menu.c @@ -25,9 +25,11 @@ * struct menu_priv - information about the menu * * @num_bootflows: Number of bootflows in the menu + * @last_bootdev: bootdev of the last bootflow added to the menu, NULL if none */ struct menu_priv { int num_bootflows; + struct udevice *last_bootdev; }; int bootflow_menu_new(struct expo **expp) @@ -77,14 +79,16 @@ int bootflow_menu_new(struct expo **expp) return 0; } -int bootflow_menu_add_all(struct expo *exp) +int bootflow_menu_add(struct expo *exp, struct bootflow *bflow, int seq, + struct scene **scnp) { struct menu_priv *priv = exp->priv; - struct udevice *last_bootdev; - struct bootflow *bflow; + char str[2], *label, *key; struct scene *scn; + uint preview_id; uint scene_id; - int ret, i; + bool add_gap; + int ret; ret = expo_first_scene_id(exp); if (ret < 0) @@ -92,13 +96,57 @@ int bootflow_menu_add_all(struct expo *exp) scene_id = ret; scn = expo_lookup_scene_id(exp, scene_id); - last_bootdev = NULL; + *str = seq < 10 ? '0' + seq : 'A' + seq - 10; + str[1] = '\0'; + key = strdup(str); + if (!key) + return log_msg_ret("key", -ENOMEM); + label = strdup(dev_get_parent(bflow->dev)->name); + if (!label) { + free(key); + return log_msg_ret("nam", -ENOMEM); + } + + add_gap = priv->last_bootdev != bflow->dev; + priv->last_bootdev = bflow->dev; + + ret = expo_str(exp, "prompt", STR_POINTER, ">"); + ret |= scene_txt_str(scn, "label", ITEM_LABEL + seq, + STR_LABEL + seq, label, NULL); + ret |= scene_txt_str(scn, "desc", ITEM_DESC + seq, STR_DESC + seq, + bflow->os_name ? bflow->os_name : + bflow->name, NULL); + ret |= scene_txt_str(scn, "key", ITEM_KEY + seq, STR_KEY + seq, key, + NULL); + preview_id = 0; + if (bflow->logo) { + preview_id = ITEM_PREVIEW + seq; + ret |= scene_img(scn, "preview", preview_id, + bflow->logo, NULL); + } + ret |= scene_menuitem(scn, OBJ_MENU, "item", ITEM + seq, + ITEM_KEY + seq, ITEM_LABEL + seq, + ITEM_DESC + seq, preview_id, + add_gap ? SCENEMIF_GAP_BEFORE : 0, + NULL); + + if (ret < 0) + return log_msg_ret("itm", -EINVAL); + priv->num_bootflows++; + *scnp = scn; + + return 0; +} + +int bootflow_menu_add_all(struct expo *exp) +{ + struct bootflow *bflow; + struct scene *scn; + int ret, i; + for (ret = bootflow_first_glob(&bflow), i = 0; !ret && i < 36; ret = bootflow_next_glob(&bflow), i++) { struct bootmeth_uc_plat *ucp; - char str[2], *label, *key; - uint preview_id; - bool add_gap; if (bflow->state != BOOTFLOWST_READY) continue; @@ -108,43 +156,10 @@ int bootflow_menu_add_all(struct expo *exp) if (ucp->flags & BOOTMETHF_GLOBAL) continue; - *str = i < 10 ? '0' + i : 'A' + i - 10; - str[1] = '\0'; - key = strdup(str); - if (!key) - return log_msg_ret("key", -ENOMEM); - label = strdup(dev_get_parent(bflow->dev)->name); - if (!label) { - free(key); - return log_msg_ret("nam", -ENOMEM); - } + ret = bootflow_menu_add(exp, bflow, i, &scn); + if (ret) + return log_msg_ret("bao", ret); - add_gap = last_bootdev != bflow->dev; - last_bootdev = bflow->dev; - - ret = expo_str(exp, "prompt", STR_POINTER, ">"); - ret |= scene_txt_str(scn, "label", ITEM_LABEL + i, - STR_LABEL + i, label, NULL); - ret |= scene_txt_str(scn, "desc", ITEM_DESC + i, STR_DESC + i, - bflow->os_name ? bflow->os_name : - bflow->name, NULL); - ret |= scene_txt_str(scn, "key", ITEM_KEY + i, STR_KEY + i, key, - NULL); - preview_id = 0; - if (bflow->logo) { - preview_id = ITEM_PREVIEW + i; - ret |= scene_img(scn, "preview", preview_id, - bflow->logo, NULL); - } - ret |= scene_menuitem(scn, OBJ_MENU, "item", ITEM + i, - ITEM_KEY + i, ITEM_LABEL + i, - ITEM_DESC + i, preview_id, - add_gap ? SCENEMIF_GAP_BEFORE : 0, - NULL); - - if (ret < 0) - return log_msg_ret("itm", -EINVAL); - priv->num_bootflows++; } ret = scene_arrange(scn); diff --git a/include/bootflow.h b/include/bootflow.h index 994b38394e8..615fb97f4a4 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -15,6 +15,7 @@ struct bootstd_priv; struct expo; +struct scene; enum { BOOTFLOW_MAX_USED_DEVS = 16, @@ -506,6 +507,21 @@ int bootflow_menu_new(struct expo **expp); */ int bootflow_menu_add_all(struct expo *exp); +/** + * bootflow_menu_add() - Add a bootflow to a menu + * + * Adds a new bootflow to the end of a menu. The caller must be careful to pass + * seq=0 for the first bootflow added, 1 for the second, etc. + * + * @exp: Menu to update + * @bflow: Bootflow to add + * @seq: Sequence number of this bootflow (0 = first) + * @scnp: Returns a pointer to the scene + * Return 0 on success, -ve on error + */ +int bootflow_menu_add(struct expo *exp, struct bootflow *bflow, int seq, + struct scene **scnp); + /** * bootflow_menu_apply_theme() - Apply a theme to a bootmenu * -- cgit v1.3.1 From 85a2954c29cf8631ccd0fb2c2c348711f8b5260e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:30 -0600 Subject: expo: Rename scene_dim to scene_obj_bbox At present we assume that each object is a simple box and that it fills the whole box. This is quite limiting for text objects, which we may want to centre within the box. We need a position within the box where drawing starts. Rename the scene_dim struct to indicate that it is a bounding box. Signed-off-by: Simon Glass --- boot/scene.c | 36 +++++++++++++------------- boot/scene_menu.c | 12 ++++----- boot/scene_textline.c | 22 ++++++++-------- include/expo.h | 8 +++--- test/boot/expo.c | 72 +++++++++++++++++++++++++-------------------------- 5 files changed, 75 insertions(+), 75 deletions(-) (limited to 'include') diff --git a/boot/scene.c b/boot/scene.c index 90b4ccf4766..a0060c6a97d 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -210,8 +210,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->dim.x = x; - obj->dim.y = y; + obj->bbox.x = x; + obj->bbox.y = y; return 0; } @@ -223,8 +223,8 @@ int scene_obj_set_size(struct scene *scn, uint id, int w, int h) obj = scene_obj_find(scn, id, SCENEOBJT_NONE); if (!obj) return log_msg_ret("find", -ENOENT); - obj->dim.w = w; - obj->dim.h = h; + obj->bbox.w = w; + obj->bbox.h = h; return 0; } @@ -368,8 +368,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->dim.x; - y = obj->dim.y; + x = obj->bbox.x; + y = obj->bbox.y; switch (obj->type) { case SCENEOBJT_NONE: @@ -419,8 +419,8 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode) if (obj->flags & SCENEOF_POINT) { vidconsole_push_colour(cons, fore, back, &old); video_fill_part(dev, x - theme->menu_inset, y, - x + obj->dim.w, - y + obj->dim.h, + x + obj->bbox.w, + y + obj->bbox.h, vid_priv->colour_bg); } vidconsole_set_cursor_pos(cons, x, y); @@ -765,8 +765,8 @@ int scene_calc_dims(struct scene *scn, bool 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; + obj->bbox.w = width; + obj->bbox.h = ret; } break; } @@ -915,15 +915,15 @@ int scene_bbox_union(struct scene *scn, uint id, int inset, if (!obj) return log_msg_ret("obj", -ENOENT); if (bbox->valid) { - bbox->x0 = min(bbox->x0, obj->dim.x - inset); - bbox->y0 = min(bbox->y0, obj->dim.y); - bbox->x1 = max(bbox->x1, obj->dim.x + obj->dim.w + inset); - bbox->y1 = max(bbox->y1, obj->dim.y + obj->dim.h); + bbox->x0 = min(bbox->x0, obj->bbox.x - inset); + bbox->y0 = min(bbox->y0, obj->bbox.y); + bbox->x1 = max(bbox->x1, obj->bbox.x + obj->bbox.w + inset); + bbox->y1 = max(bbox->y1, obj->bbox.y + obj->bbox.h); } else { - bbox->x0 = obj->dim.x - inset; - bbox->y0 = obj->dim.y; - bbox->x1 = obj->dim.x + obj->dim.w + inset; - bbox->y1 = obj->dim.y + obj->dim.h; + bbox->x0 = obj->bbox.x - inset; + bbox->y0 = obj->bbox.y; + bbox->x1 = obj->bbox.x + obj->bbox.w + inset; + bbox->y1 = obj->bbox.y + obj->bbox.h; bbox->valid = true; } diff --git a/boot/scene_menu.c b/boot/scene_menu.c index 6a2f5b6b93f..20a6a6b6a01 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -102,7 +102,7 @@ static int update_pointers(struct scene_obj_menu *menu, uint id, bool point) label = scene_obj_find(scn, item->label_id, SCENEOBJT_NONE); ret = scene_obj_set_pos(scn, menu->pointer_id, - menu->obj.dim.x + 200, label->dim.y); + menu->obj.bbox.x + 200, label->bbox.y); if (ret < 0) return log_msg_ret("ptr", ret); } @@ -186,8 +186,8 @@ int scene_menu_calc_dims(struct scene_obj_menu *menu) } if (bbox.valid) { - menu->obj.dim.w = bbox.x1 - bbox.x0; - menu->obj.dim.h = bbox.y1 - bbox.y0; + menu->obj.bbox.w = bbox.x1 - bbox.x0; + menu->obj.bbox.h = bbox.y1 - bbox.y0; } return 0; @@ -205,12 +205,12 @@ int scene_menu_arrange(struct scene *scn, struct expo_arrange_info *arr, int x, y; int ret; - x = menu->obj.dim.x; - y = menu->obj.dim.y; + x = menu->obj.bbox.x; + y = menu->obj.bbox.y; if (menu->title_id) { int width; - ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.dim.x, y); + ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.bbox.x, y); if (ret < 0) return log_msg_ret("tit", ret); diff --git a/boot/scene_textline.c b/boot/scene_textline.c index 90642a3f03d..5b4e21e6360 100644 --- a/boot/scene_textline.c +++ b/boot/scene_textline.c @@ -76,11 +76,11 @@ int scene_textline_calc_dims(struct scene_obj_textline *tline) return log_msg_ret("nom", ret); if (bbox.valid) { - tline->obj.dim.w = bbox.x1 - bbox.x0; - tline->obj.dim.h = bbox.y1 - bbox.y0; + tline->obj.bbox.w = bbox.x1 - bbox.x0; + tline->obj.bbox.h = bbox.y1 - bbox.y0; - scene_obj_set_size(scn, tline->edit_id, tline->obj.dim.w, - tline->obj.dim.h); + scene_obj_set_size(scn, tline->edit_id, tline->obj.bbox.w, + tline->obj.bbox.h); } return 0; @@ -94,16 +94,16 @@ int scene_textline_arrange(struct scene *scn, struct expo_arrange_info *arr, int x, y; int ret; - x = tline->obj.dim.x; - y = tline->obj.dim.y; + x = tline->obj.bbox.x; + y = tline->obj.bbox.y; if (tline->label_id) { - ret = scene_obj_set_pos(scn, tline->label_id, tline->obj.dim.x, + ret = scene_obj_set_pos(scn, tline->label_id, tline->obj.bbox.x, y); if (ret < 0) return log_msg_ret("tit", ret); ret = scene_obj_set_pos(scn, tline->edit_id, - tline->obj.dim.x + 200, y); + tline->obj.bbox.x + 200, y); if (ret < 0) return log_msg_ret("tit", ret); @@ -197,8 +197,8 @@ int scene_textline_render_deps(struct scene *scn, if (ret) return log_msg_ret("sav", ret); - vidconsole_set_cursor_visible(cons, true, txt->obj.dim.x, - txt->obj.dim.y, scn->cls.num); + vidconsole_set_cursor_visible(cons, true, txt->obj.bbox.x, + txt->obj.bbox.y, scn->cls.num); } return 0; @@ -219,7 +219,7 @@ int scene_textline_open(struct scene *scn, struct scene_obj_textline *tline) if (!txt) return log_msg_ret("cur", -ENOENT); - vidconsole_set_cursor_pos(cons, txt->obj.dim.x, txt->obj.dim.y); + vidconsole_set_cursor_pos(cons, txt->obj.bbox.x, txt->obj.bbox.y); vidconsole_entry_start(cons); cli_cread_init(&scn->cls, abuf_data(&tline->buf), tline->max_chars); scn->cls.insert = true; diff --git a/include/expo.h b/include/expo.h index a2b093c521d..42e934e3ff2 100644 --- a/include/expo.h +++ b/include/expo.h @@ -192,14 +192,14 @@ enum scene_obj_t { }; /** - * struct scene_dim - Dimensions of an object + * struct scene_obj_bbox - 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 { +struct scene_obj_bbox { int x; int y; int w; @@ -232,7 +232,7 @@ enum { * @name: Name of the object (allocated) * @id: ID number of the object * @type: Type of this object - * @dim: Dimensions for this object + * @bbox: Dimensions for this object * @flags: Flags for this object * @bit_length: Number of bits used for this object in CMOS RAM * @start_bit: Start bit to use for this object in CMOS RAM @@ -243,7 +243,7 @@ struct scene_obj { char *name; uint id; enum scene_obj_t type; - struct scene_dim dim; + struct scene_obj_bbox bbox; u8 flags; u8 bit_length; u16 start_bit; diff --git a/test/boot/expo.c b/test/boot/expo.c index b32a4596e85..77b956bdf06 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -270,8 +270,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.dim.x); - ut_asserteq(456, img->obj.dim.y); + ut_asserteq(123, img->obj.bbox.x); + ut_asserteq(456, img->obj.bbox.y); ut_asserteq(-ENOENT, scene_obj_set_pos(scn, OBJ_TEXT2, 0, 0)); @@ -360,8 +360,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.dim.x); - ut_asserteq(400, menu->obj.dim.y); + ut_asserteq(50, menu->obj.bbox.x); + ut_asserteq(400, menu->obj.bbox.y); id = scene_txt_str(scn, "title", OBJ_MENU_TITLE, STR_MENU_TITLE, "Main Menu", &tit); @@ -407,24 +407,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.dim.x, tit->obj.dim.x); - ut_asserteq(menu->obj.dim.y, tit->obj.dim.y); + ut_asserteq(menu->obj.bbox.x, tit->obj.bbox.x); + ut_asserteq(menu->obj.bbox.y, tit->obj.bbox.y); /* the first item should be next */ - 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.bbox.x, name1->obj.bbox.x); + ut_asserteq(menu->obj.bbox.y + 32, name1->obj.bbox.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.bbox.x + 230, key1->obj.bbox.x); + ut_asserteq(menu->obj.bbox.y + 32, key1->obj.bbox.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.bbox.x + 200, ptr->obj.bbox.x); + ut_asserteq(menu->obj.bbox.y + 32, ptr->obj.bbox.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(menu->obj.bbox.x + 280, desc1->obj.bbox.x); + ut_asserteq(menu->obj.bbox.y + 32, desc1->obj.bbox.y); - ut_asserteq(-4, prev1->obj.dim.x); - ut_asserteq(menu->obj.dim.y + 32, prev1->obj.dim.y); + ut_asserteq(-4, prev1->obj.bbox.x); + ut_asserteq(menu->obj.bbox.y + 32, prev1->obj.bbox.y); ut_asserteq(true, prev1->obj.flags & SCENEOF_HIDE); /* check iterating through scene items */ @@ -548,41 +548,41 @@ static int expo_render_image(struct unit_test_state *uts) /* 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); + ut_asserteq(400, obj->bbox.x); + ut_asserteq(100, obj->bbox.y); + ut_asserteq(126, obj->bbox.w); + ut_asserteq(40, obj->bbox.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); + ut_asserteq(50, obj->bbox.x); + ut_asserteq(20, obj->bbox.y); + ut_asserteq(160, obj->bbox.w); + ut_asserteq(160, obj->bbox.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); + ut_asserteq(50, obj->bbox.x); + ut_asserteq(436, obj->bbox.y); + ut_asserteq(29, obj->bbox.w); + ut_asserteq(18, obj->bbox.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); + ut_asserteq(50, obj->bbox.x); + ut_asserteq(454, obj->bbox.y); + ut_asserteq(29, obj->bbox.w); + ut_asserteq(18, obj->bbox.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); + ut_asserteq(50, obj->bbox.x); + ut_asserteq(400, obj->bbox.y); + ut_asserteq(160, obj->bbox.w); + ut_asserteq(160, obj->bbox.h); /* render it */ expo_set_scene_id(exp, SCENE1); -- cgit v1.3.1 From 8aa384d01a079ec2d816f7051a7208a337727338 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:31 -0600 Subject: expo: Rename x and y in struct scene_obj_bbox These coordinates are the top left values, so rename them to x0 and y0 in preparation for changing the width and height to x1 and y1 Signed-off-by: Simon Glass --- boot/scene.c | 24 ++++++++++++------------ boot/scene_menu.c | 8 ++++---- boot/scene_textline.c | 16 ++++++++-------- include/expo.h | 8 ++++---- test/boot/expo.c | 52 +++++++++++++++++++++++++-------------------------- 5 files changed, 54 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/boot/scene.c b/boot/scene.c index a0060c6a97d..49dac90ceed 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -210,8 +210,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->bbox.x = x; - obj->bbox.y = y; + obj->bbox.x0 = x; + obj->bbox.y0 = y; return 0; } @@ -368,8 +368,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->bbox.x; - y = obj->bbox.y; + x = obj->bbox.x0; + y = obj->bbox.y0; switch (obj->type) { case SCENEOBJT_NONE: @@ -915,15 +915,15 @@ int scene_bbox_union(struct scene *scn, uint id, int inset, if (!obj) return log_msg_ret("obj", -ENOENT); if (bbox->valid) { - bbox->x0 = min(bbox->x0, obj->bbox.x - inset); - bbox->y0 = min(bbox->y0, obj->bbox.y); - bbox->x1 = max(bbox->x1, obj->bbox.x + obj->bbox.w + inset); - bbox->y1 = max(bbox->y1, obj->bbox.y + obj->bbox.h); + bbox->x0 = min(bbox->x0, obj->bbox.x0 - inset); + bbox->y0 = min(bbox->y0, obj->bbox.y0); + bbox->x1 = max(bbox->x1, obj->bbox.x0 + obj->bbox.w + inset); + bbox->y1 = max(bbox->y1, obj->bbox.y0 + obj->bbox.h); } else { - bbox->x0 = obj->bbox.x - inset; - bbox->y0 = obj->bbox.y; - bbox->x1 = obj->bbox.x + obj->bbox.w + inset; - bbox->y1 = obj->bbox.y + obj->bbox.h; + bbox->x0 = obj->bbox.x0 - inset; + bbox->y0 = obj->bbox.y0; + bbox->x1 = obj->bbox.x0 + obj->bbox.w + inset; + bbox->y1 = obj->bbox.y0 + obj->bbox.h; bbox->valid = true; } diff --git a/boot/scene_menu.c b/boot/scene_menu.c index 20a6a6b6a01..c47c2030527 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -102,7 +102,7 @@ static int update_pointers(struct scene_obj_menu *menu, uint id, bool point) label = scene_obj_find(scn, item->label_id, SCENEOBJT_NONE); ret = scene_obj_set_pos(scn, menu->pointer_id, - menu->obj.bbox.x + 200, label->bbox.y); + menu->obj.bbox.x0 + 200, label->bbox.y0); if (ret < 0) return log_msg_ret("ptr", ret); } @@ -205,12 +205,12 @@ int scene_menu_arrange(struct scene *scn, struct expo_arrange_info *arr, int x, y; int ret; - x = menu->obj.bbox.x; - y = menu->obj.bbox.y; + x = menu->obj.bbox.x0; + y = menu->obj.bbox.y0; if (menu->title_id) { int width; - ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.bbox.x, y); + ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.bbox.x0, y); if (ret < 0) return log_msg_ret("tit", ret); diff --git a/boot/scene_textline.c b/boot/scene_textline.c index 5b4e21e6360..a513d555ded 100644 --- a/boot/scene_textline.c +++ b/boot/scene_textline.c @@ -94,16 +94,16 @@ int scene_textline_arrange(struct scene *scn, struct expo_arrange_info *arr, int x, y; int ret; - x = tline->obj.bbox.x; - y = tline->obj.bbox.y; + x = tline->obj.bbox.x0; + y = tline->obj.bbox.y0; if (tline->label_id) { - ret = scene_obj_set_pos(scn, tline->label_id, tline->obj.bbox.x, - y); + ret = scene_obj_set_pos(scn, tline->label_id, + tline->obj.bbox.x0, y); if (ret < 0) return log_msg_ret("tit", ret); ret = scene_obj_set_pos(scn, tline->edit_id, - tline->obj.bbox.x + 200, y); + tline->obj.bbox.x0 + 200, y); if (ret < 0) return log_msg_ret("tit", ret); @@ -197,8 +197,8 @@ int scene_textline_render_deps(struct scene *scn, if (ret) return log_msg_ret("sav", ret); - vidconsole_set_cursor_visible(cons, true, txt->obj.bbox.x, - txt->obj.bbox.y, scn->cls.num); + vidconsole_set_cursor_visible(cons, true, txt->obj.bbox.x0, + txt->obj.bbox.y0, scn->cls.num); } return 0; @@ -219,7 +219,7 @@ int scene_textline_open(struct scene *scn, struct scene_obj_textline *tline) if (!txt) return log_msg_ret("cur", -ENOENT); - vidconsole_set_cursor_pos(cons, txt->obj.bbox.x, txt->obj.bbox.y); + vidconsole_set_cursor_pos(cons, txt->obj.bbox.x0, txt->obj.bbox.y0); vidconsole_entry_start(cons); cli_cread_init(&scn->cls, abuf_data(&tline->buf), tline->max_chars); scn->cls.insert = true; diff --git a/include/expo.h b/include/expo.h index 42e934e3ff2..84dc77f771e 100644 --- a/include/expo.h +++ b/include/expo.h @@ -194,14 +194,14 @@ enum scene_obj_t { /** * struct scene_obj_bbox - Dimensions of an object * - * @x: x position, in pixels from left side - * @y: y position, in pixels from top + * @x0: x position, in pixels from left side + * @y0: y position, in pixels from top * @w: width, in pixels * @h: height, in pixels */ struct scene_obj_bbox { - int x; - int y; + int x0; + int y0; int w; int h; }; diff --git a/test/boot/expo.c b/test/boot/expo.c index 77b956bdf06..9c940825278 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -270,8 +270,8 @@ static int expo_object_attr(struct unit_test_state *uts) ut_assert(id > 0); ut_assertok(scene_obj_set_pos(scn, OBJ_LOGO, 123, 456)); - ut_asserteq(123, img->obj.bbox.x); - ut_asserteq(456, img->obj.bbox.y); + ut_asserteq(123, img->obj.bbox.x0); + ut_asserteq(456, img->obj.bbox.y0); ut_asserteq(-ENOENT, scene_obj_set_pos(scn, OBJ_TEXT2, 0, 0)); @@ -360,8 +360,8 @@ static int expo_object_menu(struct unit_test_state *uts) ut_asserteq(0, menu->pointer_id); ut_assertok(scene_obj_set_pos(scn, OBJ_MENU, 50, 400)); - ut_asserteq(50, menu->obj.bbox.x); - ut_asserteq(400, menu->obj.bbox.y); + ut_asserteq(50, menu->obj.bbox.x0); + ut_asserteq(400, menu->obj.bbox.y0); id = scene_txt_str(scn, "title", OBJ_MENU_TITLE, STR_MENU_TITLE, "Main Menu", &tit); @@ -407,24 +407,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.bbox.x, tit->obj.bbox.x); - ut_asserteq(menu->obj.bbox.y, tit->obj.bbox.y); + ut_asserteq(menu->obj.bbox.x0, tit->obj.bbox.x0); + ut_asserteq(menu->obj.bbox.y0, tit->obj.bbox.y0); /* the first item should be next */ - ut_asserteq(menu->obj.bbox.x, name1->obj.bbox.x); - ut_asserteq(menu->obj.bbox.y + 32, name1->obj.bbox.y); + ut_asserteq(menu->obj.bbox.x0, name1->obj.bbox.x0); + ut_asserteq(menu->obj.bbox.y0 + 32, name1->obj.bbox.y0); - ut_asserteq(menu->obj.bbox.x + 230, key1->obj.bbox.x); - ut_asserteq(menu->obj.bbox.y + 32, key1->obj.bbox.y); + ut_asserteq(menu->obj.bbox.x0 + 230, key1->obj.bbox.x0); + ut_asserteq(menu->obj.bbox.y0 + 32, key1->obj.bbox.y0); - ut_asserteq(menu->obj.bbox.x + 200, ptr->obj.bbox.x); - ut_asserteq(menu->obj.bbox.y + 32, ptr->obj.bbox.y); + ut_asserteq(menu->obj.bbox.x0 + 200, ptr->obj.bbox.x0); + ut_asserteq(menu->obj.bbox.y0 + 32, ptr->obj.bbox.y0); - ut_asserteq(menu->obj.bbox.x + 280, desc1->obj.bbox.x); - ut_asserteq(menu->obj.bbox.y + 32, desc1->obj.bbox.y); + ut_asserteq(menu->obj.bbox.x0 + 280, desc1->obj.bbox.x0); + ut_asserteq(menu->obj.bbox.y0 + 32, desc1->obj.bbox.y0); - ut_asserteq(-4, prev1->obj.bbox.x); - ut_asserteq(menu->obj.bbox.y + 32, prev1->obj.bbox.y); + ut_asserteq(-4, prev1->obj.bbox.x0); + ut_asserteq(menu->obj.bbox.y0 + 32, prev1->obj.bbox.y0); ut_asserteq(true, prev1->obj.flags & SCENEOF_HIDE); /* check iterating through scene items */ @@ -548,39 +548,39 @@ static int expo_render_image(struct unit_test_state *uts) /* check dimensions of text */ obj = scene_obj_find(scn, OBJ_TEXT, SCENEOBJT_NONE); ut_assertnonnull(obj); - ut_asserteq(400, obj->bbox.x); - ut_asserteq(100, obj->bbox.y); + ut_asserteq(400, obj->bbox.x0); + ut_asserteq(100, obj->bbox.y0); ut_asserteq(126, obj->bbox.w); ut_asserteq(40, obj->bbox.h); /* check dimensions of image */ obj = scene_obj_find(scn, OBJ_LOGO, SCENEOBJT_NONE); ut_assertnonnull(obj); - ut_asserteq(50, obj->bbox.x); - ut_asserteq(20, obj->bbox.y); + ut_asserteq(50, obj->bbox.x0); + ut_asserteq(20, obj->bbox.y0); ut_asserteq(160, obj->bbox.w); ut_asserteq(160, obj->bbox.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->bbox.x); - ut_asserteq(436, obj->bbox.y); + ut_asserteq(50, obj->bbox.x0); + ut_asserteq(436, obj->bbox.y0); ut_asserteq(29, obj->bbox.w); ut_asserteq(18, obj->bbox.h); obj = scene_obj_find(scn, ITEM2_LABEL, SCENEOBJT_NONE); ut_assertnonnull(obj); - ut_asserteq(50, obj->bbox.x); - ut_asserteq(454, obj->bbox.y); + ut_asserteq(50, obj->bbox.x0); + ut_asserteq(454, obj->bbox.y0); ut_asserteq(29, obj->bbox.w); ut_asserteq(18, obj->bbox.h); /* check dimensions of menu */ obj = scene_obj_find(scn, OBJ_MENU, SCENEOBJT_NONE); ut_assertnonnull(obj); - ut_asserteq(50, obj->bbox.x); - ut_asserteq(400, obj->bbox.y); + ut_asserteq(50, obj->bbox.x0); + ut_asserteq(400, obj->bbox.y0); ut_asserteq(160, obj->bbox.w); ut_asserteq(160, obj->bbox.h); -- cgit v1.3.1 From 8636da86a2fb5f5f1a571f6cb3f12c0e8c207698 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:32 -0600 Subject: expo: Use an abuf to hold strings It is more convenient to put strings in an abuf so they can easily be resized. Adjust the struct accordingly. Signed-off-by: Simon Glass --- boot/expo.c | 4 ++-- include/expo.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/boot/expo.c b/boot/expo.c index 301bbfa5f9a..debdb60022b 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -86,7 +86,7 @@ int expo_str(struct expo *exp, const char *name, uint id, const char *str) return log_msg_ret("obj", -ENOMEM); estr->id = resolve_id(exp, id); - estr->str = str; + abuf_init_const(&estr->buf, str, strlen(str) + 1); list_add_tail(&estr->sibling, &exp->str_head); return estr->id; @@ -98,7 +98,7 @@ const char *expo_get_str(struct expo *exp, uint id) list_for_each_entry(estr, &exp->str_head, sibling) { if (estr->id == id) - return estr->str; + return estr->buf.data; } return NULL; diff --git a/include/expo.h b/include/expo.h index 84dc77f771e..b6de0310071 100644 --- a/include/expo.h +++ b/include/expo.h @@ -134,12 +134,12 @@ struct expo { * struct expo_string - a string that can be used in an expo * * @id: ID number of the string - * @str: String + * @buf: String (contains nul terminator) * @sibling: Node to link this object to its siblings */ struct expo_string { uint id; - const char *str; + struct abuf buf; struct list_head sibling; }; -- cgit v1.3.1 From f04026a59f5384f32a452889fc199c06eaf1553e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:33 -0600 Subject: expo: Separate dimensions from the bounding box At present each object has a width and height and the bounding box is implicit in that. This is not flexible enough to handle objects which are larger than their contents might need. For example, when centring a text object we might want to have it stretch across the whole width of the display even if the text itself does not need that much space. Create a new 'dimensions' field and convert the existing width/height into x1/y1 coordinates. Signed-off-by: Simon Glass --- boot/scene.c | 31 +++++++++++++++++++++---------- boot/scene_menu.c | 7 +++++-- boot/scene_textline.c | 18 ++++++++++++------ include/expo.h | 31 ++++++++++++++++++++++++++----- test/boot/expo.c | 20 ++++++++++---------- 5 files changed, 74 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/boot/scene.c b/boot/scene.c index 49dac90ceed..325dd23641f 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -206,12 +206,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) { struct scene_obj *obj; + int w, h; obj = scene_obj_find(scn, id, SCENEOBJT_NONE); if (!obj) return log_msg_ret("find", -ENOENT); + w = obj->bbox.x1 - obj->bbox.x0; + h = obj->bbox.y1 - obj->bbox.y0; obj->bbox.x0 = x; obj->bbox.y0 = y; + obj->bbox.x1 = obj->bbox.x0 + w; + obj->bbox.y1 = obj->bbox.y0 + h; return 0; } @@ -223,8 +228,9 @@ int scene_obj_set_size(struct scene *scn, uint id, int w, int h) obj = scene_obj_find(scn, id, SCENEOBJT_NONE); if (!obj) return log_msg_ret("find", -ENOENT); - obj->bbox.w = w; - obj->bbox.h = h; + obj->bbox.x1 = obj->bbox.x0 + w; + obj->bbox.y1 = obj->bbox.y0 + h; + obj->flags |= SCENEOF_SIZE_VALID; return 0; } @@ -419,8 +425,8 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode) if (obj->flags & SCENEOF_POINT) { vidconsole_push_colour(cons, fore, back, &old); video_fill_part(dev, x - theme->menu_inset, y, - x + obj->bbox.w, - y + obj->bbox.h, + obj->bbox.x1, + obj->bbox.y1, vid_priv->colour_bg); } vidconsole_set_cursor_pos(cons, x, y); @@ -765,8 +771,13 @@ int scene_calc_dims(struct scene *scn, bool do_menus) ret = scene_obj_get_hw(scn, obj->id, &width); if (ret < 0) return log_msg_ret("get", ret); - obj->bbox.w = width; - obj->bbox.h = ret; + obj->dims.x = width; + obj->dims.y = ret; + if (!(obj->flags & SCENEOF_SIZE_VALID)) { + obj->bbox.x1 = obj->bbox.x0 + width; + obj->bbox.y1 = obj->bbox.y0 + ret; + obj->flags |= SCENEOF_SIZE_VALID; + } } break; } @@ -917,13 +928,13 @@ int scene_bbox_union(struct scene *scn, uint id, int inset, if (bbox->valid) { bbox->x0 = min(bbox->x0, obj->bbox.x0 - inset); bbox->y0 = min(bbox->y0, obj->bbox.y0); - bbox->x1 = max(bbox->x1, obj->bbox.x0 + obj->bbox.w + inset); - bbox->y1 = max(bbox->y1, obj->bbox.y0 + obj->bbox.h); + bbox->x1 = max(bbox->x1, obj->bbox.x1 + inset); + bbox->y1 = max(bbox->y1, obj->bbox.y1); } else { bbox->x0 = obj->bbox.x0 - inset; bbox->y0 = obj->bbox.y0; - bbox->x1 = obj->bbox.x0 + obj->bbox.w + inset; - bbox->y1 = obj->bbox.y0 + obj->bbox.h; + bbox->x1 = obj->bbox.x1 + inset; + bbox->y1 = obj->bbox.y1; bbox->valid = true; } diff --git a/boot/scene_menu.c b/boot/scene_menu.c index c47c2030527..dbc6793e302 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -186,8 +186,8 @@ int scene_menu_calc_dims(struct scene_obj_menu *menu) } if (bbox.valid) { - menu->obj.bbox.w = bbox.x1 - bbox.x0; - menu->obj.bbox.h = bbox.y1 - bbox.y0; + menu->obj.dims.x = bbox.x1 - bbox.x0; + menu->obj.dims.y = bbox.y1 - bbox.y0; } return 0; @@ -295,6 +295,9 @@ int scene_menu_arrange(struct scene *scn, struct expo_arrange_info *arr, if (sel_id) menu_point_to_item(menu, sel_id); + menu->obj.bbox.x1 = menu->obj.bbox.x0 + menu->obj.dims.x; + menu->obj.bbox.y1 = menu->obj.bbox.y0 + menu->obj.dims.y; + menu->obj.flags |= SCENEOF_SIZE_VALID; return 0; } diff --git a/boot/scene_textline.c b/boot/scene_textline.c index a513d555ded..f1d6ff7607c 100644 --- a/boot/scene_textline.c +++ b/boot/scene_textline.c @@ -61,7 +61,8 @@ void scene_textline_calc_bbox(struct scene_obj_textline *tline, int scene_textline_calc_dims(struct scene_obj_textline *tline) { - struct scene *scn = tline->obj.scene; + struct scene_obj *obj = &tline->obj; + struct scene *scn = obj->scene; struct vidconsole_bbox bbox; struct scene_obj_txt *txt; int ret; @@ -76,11 +77,16 @@ int scene_textline_calc_dims(struct scene_obj_textline *tline) return log_msg_ret("nom", ret); if (bbox.valid) { - tline->obj.bbox.w = bbox.x1 - bbox.x0; - tline->obj.bbox.h = bbox.y1 - bbox.y0; - - scene_obj_set_size(scn, tline->edit_id, tline->obj.bbox.w, - tline->obj.bbox.h); + obj->dims.x = bbox.x1 - bbox.x0; + obj->dims.y = bbox.y1 - bbox.y0; + if (!(obj->flags & SCENEOF_SIZE_VALID)) { + obj->bbox.x1 = obj->bbox.x0 + obj->dims.x; + obj->bbox.y1 = obj->bbox.y0 + obj->dims.y; + obj->flags |= SCENEOF_SIZE_VALID; + } + scene_obj_set_size(scn, tline->edit_id, + obj->bbox.x1 - obj->bbox.x0, + obj->bbox.y1 - obj->bbox.y0); } return 0; diff --git a/include/expo.h b/include/expo.h index b6de0310071..6f0547a4b38 100644 --- a/include/expo.h +++ b/include/expo.h @@ -9,6 +9,7 @@ #include #include +#include #include struct udevice; @@ -196,14 +197,29 @@ enum scene_obj_t { * * @x0: x position, in pixels from left side * @y0: y position, in pixels from top - * @w: width, in pixels - * @h: height, in pixels + * @x1: x position of right size + * @y1: y position of bottom */ struct scene_obj_bbox { int x0; int y0; - int w; - int h; + int x1; + int y1; +}; + +/** + * struct scene_obj_dims - Dimensions of the object being drawn + * + * Image and text objects have a dimension which can change depending on what + * they contain. For images this stores the size. For text it stores the size as + * rendered on the display + * + * @x: x dimension + * @y: y dimension + */ +struct scene_obj_dims { + int x; + int y; }; /** @@ -213,11 +229,14 @@ struct scene_obj_bbox { * @SCENEOF_POINT: object should be highlighted * @SCENEOF_OPEN: object should be opened (e.g. menu is opened so that an option * can be selected) + * @SCENEOF_SIZE_VALID: object's size (width/height) is valid, so any adjustment + * to x0/y0 should maintain the width/height of the object */ enum scene_obj_flags_t { SCENEOF_HIDE = 1 << 0, SCENEOF_POINT = 1 << 1, SCENEOF_OPEN = 1 << 2, + SCENEOF_SIZE_VALID = BIT(3), }; enum { @@ -232,7 +251,8 @@ enum { * @name: Name of the object (allocated) * @id: ID number of the object * @type: Type of this object - * @bbox: Dimensions for this object + * @bbox: Bounding box for this object + * @dims: Dimensions of the text/image (may be smaller than bbox) * @flags: Flags for this object * @bit_length: Number of bits used for this object in CMOS RAM * @start_bit: Start bit to use for this object in CMOS RAM @@ -244,6 +264,7 @@ struct scene_obj { uint id; enum scene_obj_t type; struct scene_obj_bbox bbox; + struct scene_obj_dims dims; u8 flags; u8 bit_length; u16 start_bit; diff --git a/test/boot/expo.c b/test/boot/expo.c index 9c940825278..94c13e9b71f 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -550,39 +550,39 @@ static int expo_render_image(struct unit_test_state *uts) ut_assertnonnull(obj); ut_asserteq(400, obj->bbox.x0); ut_asserteq(100, obj->bbox.y0); - ut_asserteq(126, obj->bbox.w); - ut_asserteq(40, obj->bbox.h); + ut_asserteq(400 + 126, obj->bbox.x1); + ut_asserteq(100 + 40, obj->bbox.y1); /* check dimensions of image */ obj = scene_obj_find(scn, OBJ_LOGO, SCENEOBJT_NONE); ut_assertnonnull(obj); ut_asserteq(50, obj->bbox.x0); ut_asserteq(20, obj->bbox.y0); - ut_asserteq(160, obj->bbox.w); - ut_asserteq(160, obj->bbox.h); + ut_asserteq(50 + 160, obj->bbox.x1); + ut_asserteq(20 + 160, obj->bbox.y1); /* 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->bbox.x0); ut_asserteq(436, obj->bbox.y0); - ut_asserteq(29, obj->bbox.w); - ut_asserteq(18, obj->bbox.h); + ut_asserteq(50 + 29, obj->bbox.x1); + ut_asserteq(436 + 18, obj->bbox.y1); obj = scene_obj_find(scn, ITEM2_LABEL, SCENEOBJT_NONE); ut_assertnonnull(obj); ut_asserteq(50, obj->bbox.x0); ut_asserteq(454, obj->bbox.y0); - ut_asserteq(29, obj->bbox.w); - ut_asserteq(18, obj->bbox.h); + ut_asserteq(50 + 29, obj->bbox.x1); + ut_asserteq(454 + 18, obj->bbox.y1); /* check dimensions of menu */ obj = scene_obj_find(scn, OBJ_MENU, SCENEOBJT_NONE); ut_assertnonnull(obj); ut_asserteq(50, obj->bbox.x0); ut_asserteq(400, obj->bbox.y0); - ut_asserteq(160, obj->bbox.w); - ut_asserteq(160, obj->bbox.h); + ut_asserteq(50 + 160, obj->bbox.x1); + ut_asserteq(400 + 160, obj->bbox.y1); /* render it */ expo_set_scene_id(exp, SCENE1); -- cgit v1.3.1 From e3c3f83cb6ce85ef541359fa46fd802e1b53494b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:35 -0600 Subject: expo: Support setting the size and bounds of an object Add a function to allow the size of an object to be set independently of its position. Also add a function to permit the object's bounding box to be set independently of its dimensions. Signed-off-by: Simon Glass --- boot/scene.c | 29 +++++++++++++++++++++++++++++ include/expo.h | 24 ++++++++++++++++++++++++ test/boot/expo.c | 11 +++++++++++ 3 files changed, 64 insertions(+) (limited to 'include') diff --git a/boot/scene.c b/boot/scene.c index f1dc2d81922..ea329252d28 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -235,6 +235,35 @@ int scene_obj_set_size(struct scene *scn, uint id, int w, int h) return 0; } +int scene_obj_set_width(struct scene *scn, uint id, int w) +{ + struct scene_obj *obj; + + obj = scene_obj_find(scn, id, SCENEOBJT_NONE); + if (!obj) + return log_msg_ret("find", -ENOENT); + obj->bbox.x1 = obj->bbox.x0 + w; + + return 0; +} + +int scene_obj_set_bbox(struct scene *scn, uint id, int x0, int y0, int x1, + int y1) +{ + struct scene_obj *obj; + + obj = scene_obj_find(scn, id, SCENEOBJT_NONE); + if (!obj) + return log_msg_ret("find", -ENOENT); + obj->bbox.x0 = x0; + obj->bbox.y0 = y0; + obj->bbox.x1 = x1; + obj->bbox.y1 = y1; + obj->flags |= SCENEOF_SIZE_VALID; + + return 0; +} + int scene_obj_set_hide(struct scene *scn, uint id, bool hide) { int ret; diff --git a/include/expo.h b/include/expo.h index 6f0547a4b38..990cb3094ee 100644 --- a/include/expo.h +++ b/include/expo.h @@ -673,6 +673,30 @@ int scene_obj_set_pos(struct scene *scn, uint id, int x, int y); */ int scene_obj_set_size(struct scene *scn, uint id, int w, int h); +/** + * scene_obj_set_width() - Set the width of an object + * + * @scn: Scene to update + * @id: ID of object to update + * @w: width in pixels + * Returns: 0 if OK, -ENOENT if @id is invalid + */ +int scene_obj_set_width(struct scene *scn, uint id, int w); + +/** + * scene_obj_set_bbox() - Set the bounding box of an object + * + * @scn: Scene to update + * @id: ID of object to update + * @x0: x position, in pixels from left side + * @y0: y position, in pixels from top + * @x1: ending x position (right side) + * @y1: ending y position (botton side) + * Returns: 0 if OK, -ENOENT if @id is invalid + */ +int scene_obj_set_bbox(struct scene *scn, uint id, int x0, int y0, int x1, + int y1); + /** * scene_obj_set_hide() - Set whether an object is hidden * diff --git a/test/boot/expo.c b/test/boot/expo.c index 94c13e9b71f..2a430d3d482 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -584,6 +584,17 @@ static int expo_render_image(struct unit_test_state *uts) ut_asserteq(50 + 160, obj->bbox.x1); ut_asserteq(400 + 160, obj->bbox.y1); + scene_obj_set_width(scn, OBJ_MENU, 170); + ut_asserteq(50 + 170, obj->bbox.x1); + scene_obj_set_bbox(scn, OBJ_MENU, 60, 410, 50 + 160, 400 + 160); + ut_asserteq(60, obj->bbox.x0); + ut_asserteq(410, obj->bbox.y0); + ut_asserteq(50 + 160, obj->bbox.x1); + ut_asserteq(400 + 160, obj->bbox.y1); + + /* reset back to normal */ + scene_obj_set_bbox(scn, OBJ_MENU, 50, 400, 50 + 160, 400 + 160); + /* render it */ expo_set_scene_id(exp, SCENE1); ut_assertok(expo_render(exp)); -- cgit v1.3.1 From 5a80996cb6a285d09c03d687dc9ad793f12e7bf1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:37 -0600 Subject: expo: Create a struct for generic text attributes In preparation for adding more text types, refactor the common fields into a new structure. This will allow common code to be used. Signed-off-by: Simon Glass --- boot/cedit.c | 2 +- boot/scene.c | 65 ++++++++++++++++++++++++++++++++++----------------- boot/scene_menu.c | 12 +++++----- boot/scene_textline.c | 10 ++++---- include/expo.h | 21 ++++++++++++----- test/boot/bootflow.c | 2 +- test/boot/cedit.c | 2 +- test/boot/expo.c | 15 ++++++------ 8 files changed, 80 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/boot/cedit.c b/boot/cedit.c index 792ab6d65bd..0f7a8657c58 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -271,7 +271,7 @@ static int get_cur_menuitem_text(const struct scene_obj_menu *menu, if (!txt) return log_msg_ret("txt", -ENOENT); - str = expo_get_str(scn->expo, txt->str_id); + str = expo_get_str(scn->expo, txt->gen.str_id); if (!str) return log_msg_ret("str", -ENOENT); *strp = str; diff --git a/boot/scene.c b/boot/scene.c index ea329252d28..99623d2d3a8 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -142,6 +142,31 @@ int scene_img(struct scene *scn, const char *name, uint id, char *data, return img->obj.id; } +int scene_txt_generic_init(struct expo *exp, struct scene_txt_generic *gen, + const char *name, uint str_id, const char *str) +{ + int ret; + + if (str) { + ret = expo_str(exp, name, str_id, str); + if (ret < 0) + return log_msg_ret("str", ret); + if (str_id && ret != str_id) + return log_msg_ret("id", -EEXIST); + str_id = ret; + } else { + ret = resolve_id(exp, str_id); + if (ret < 0) + return log_msg_ret("nst", ret); + if (str_id && ret != str_id) + return log_msg_ret("nid", -EEXIST); + } + + gen->str_id = str_id; + + return 0; +} + int scene_txt(struct scene *scn, const char *name, uint id, uint str_id, struct scene_obj_txt **txtp) { @@ -154,8 +179,9 @@ int scene_txt(struct scene *scn, const char *name, uint id, uint str_id, if (ret < 0) return log_msg_ret("obj", ret); - txt->str_id = str_id; - + ret = scene_txt_generic_init(scn->expo, &txt->gen, name, str_id, NULL); + if (ret) + return log_msg_ret("stg", ret); if (txtp) *txtp = txt; @@ -168,21 +194,15 @@ int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id, struct scene_obj_txt *txt; int ret; - ret = expo_str(scn->expo, name, str_id, str); - if (ret < 0) - return log_msg_ret("str", ret); - if (str_id && ret != str_id) - return log_msg_ret("id", -EEXIST); - str_id = ret; - ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT, sizeof(struct scene_obj_txt), (struct scene_obj **)&txt); if (ret < 0) return log_msg_ret("obj", ret); - txt->str_id = str_id; - + ret = scene_txt_generic_init(scn->expo, &txt->gen, name, str_id, str); + if (ret) + return log_msg_ret("tsg", ret); if (txtp) *txtp = txt; @@ -197,8 +217,8 @@ int scene_txt_set_font(struct scene *scn, uint id, const char *font_name, txt = scene_obj_find(scn, id, SCENEOBJT_TEXT); if (!txt) return log_msg_ret("find", -ENOENT); - txt->font_name = font_name; - txt->font_size = font_size; + txt->gen.font_name = font_name; + txt->gen.font_size = font_size; return 0; } @@ -313,13 +333,13 @@ int scene_obj_get_hw(struct scene *scn, uint id, int *widthp) return height; } case SCENEOBJT_TEXT: { - struct scene_obj_txt *txt = (struct scene_obj_txt *)obj; + struct scene_txt_generic *gen = &((struct scene_obj_txt *)obj)->gen; struct expo *exp = scn->expo; struct vidconsole_bbox bbox; const char *str; int len, ret; - str = expo_get_str(exp, txt->str_id); + str = expo_get_str(exp, gen->str_id); if (!str) return log_msg_ret("str", -ENOENT); len = strlen(str); @@ -331,8 +351,8 @@ int scene_obj_get_hw(struct scene *scn, uint id, int *widthp) return 16; } - ret = vidconsole_measure(scn->expo->cons, txt->font_name, - txt->font_size, str, -1, &bbox, NULL); + ret = vidconsole_measure(scn->expo->cons, gen->font_name, + gen->font_size, str, -1, &bbox, NULL); if (ret) return log_msg_ret("mea", ret); if (widthp) @@ -424,22 +444,23 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode) break; } case SCENEOBJT_TEXT: { - struct scene_obj_txt *txt = (struct scene_obj_txt *)obj; + struct scene_txt_generic *gen = + &((struct scene_obj_txt *)obj)->gen; const char *str; if (!cons) return -ENOTSUPP; - if (txt->font_name || txt->font_size) { + if (gen->font_name || gen->font_size) { ret = vidconsole_select_font(cons, - txt->font_name, - txt->font_size); + gen->font_name, + gen->font_size); } else { ret = vidconsole_select_font(cons, NULL, 0); } if (ret && ret != -ENOSYS) return log_msg_ret("font", ret); - str = expo_get_str(exp, txt->str_id); + str = expo_get_str(exp, gen->str_id); if (str) { struct video_priv *vid_priv; struct vidconsole_colour old; diff --git a/boot/scene_menu.c b/boot/scene_menu.c index 96ac726ebcd..de433ece6ee 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -359,7 +359,7 @@ static struct scene_menitem *scene_menu_find_key(struct scene *scn, txt = scene_obj_find(scn, item->key_id, SCENEOBJT_TEXT); if (txt) { - str = expo_get_str(scn->expo, txt->str_id); + str = expo_get_str(scn->expo, txt->gen.str_id); if (str && *str == key) return item; } @@ -562,7 +562,7 @@ int scene_menu_display(struct scene_obj_menu *menu) if (!txt) return log_msg_ret("txt", -EINVAL); - str = expo_get_str(exp, txt->str_id); + str = expo_get_str(exp, txt->gen.str_id); printf("%s\n\n", str); } @@ -570,7 +570,7 @@ int scene_menu_display(struct scene_obj_menu *menu) return 0; pointer = scene_obj_find(scn, menu->pointer_id, SCENEOBJT_TEXT); - pstr = expo_get_str(scn->expo, pointer->str_id); + pstr = expo_get_str(scn->expo, pointer->gen.str_id); list_for_each_entry(item, &menu->item_head, sibling) { struct scene_obj_txt *key = NULL, *label = NULL; @@ -579,15 +579,15 @@ int scene_menu_display(struct scene_obj_menu *menu) key = scene_obj_find(scn, item->key_id, SCENEOBJT_TEXT); if (key) - kstr = expo_get_str(exp, key->str_id); + kstr = expo_get_str(exp, key->gen.str_id); label = scene_obj_find(scn, item->label_id, SCENEOBJT_TEXT); if (label) - lstr = expo_get_str(exp, label->str_id); + lstr = expo_get_str(exp, label->gen.str_id); desc = scene_obj_find(scn, item->desc_id, SCENEOBJT_TEXT); if (desc) - dstr = expo_get_str(exp, desc->str_id); + dstr = expo_get_str(exp, desc->gen.str_id); printf("%3s %3s %-10s %s\n", pointer && menu->cur_item_id == item->id ? pstr : "", diff --git a/boot/scene_textline.c b/boot/scene_textline.c index f1d6ff7607c..7bc35a997dc 100644 --- a/boot/scene_textline.c +++ b/boot/scene_textline.c @@ -71,8 +71,8 @@ int scene_textline_calc_dims(struct scene_obj_textline *tline) if (!txt) return log_msg_ret("dim", -ENOENT); - ret = vidconsole_nominal(scn->expo->cons, txt->font_name, - txt->font_size, tline->max_chars, &bbox); + ret = vidconsole_nominal(scn->expo->cons, txt->gen.font_name, + txt->gen.font_size, tline->max_chars, &bbox); if (ret) return log_msg_ret("nom", ret); @@ -191,10 +191,10 @@ int scene_textline_render_deps(struct scene *scn, if (!txt) return log_msg_ret("cur", -ENOENT); - if (txt->font_name || txt->font_size) { + if (txt->gen.font_name || txt->gen.font_size) { ret = vidconsole_select_font(cons, - txt->font_name, - txt->font_size); + txt->gen.font_name, + txt->gen.font_size); } else { ret = vidconsole_select_font(cons, NULL, 0); } diff --git a/include/expo.h b/include/expo.h index 990cb3094ee..32d69f269a7 100644 --- a/include/expo.h +++ b/include/expo.h @@ -291,22 +291,31 @@ struct scene_obj_img { }; /** - * struct scene_obj_txt - information about a text object in a scene - * - * This is a single-line text object + * struct scene_txt_generic - Generic information common to text objects * - * @obj: Basic object information * @str_id: ID of the text string to display * @font_name: Name of font (allocated by caller) * @font_size: Nominal size of font in pixels */ -struct scene_obj_txt { - struct scene_obj obj; +struct scene_txt_generic { uint str_id; const char *font_name; uint font_size; }; +/** + * struct scene_obj_txt - information about a text object in a scene + * + * This is a single-line text object + * + * @obj: Basic object information + * @gen: Generic information common to all objects which show text + */ +struct scene_obj_txt { + struct scene_obj obj; + struct scene_txt_generic gen; +}; + /** * struct scene_obj_menu - information about a menu object in a scene * diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 58885b5d8bf..ea1a994fd3d 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -858,7 +858,7 @@ static int check_font(struct unit_test_state *uts, struct scene *scn, uint id, txt = scene_obj_find(scn, id, SCENEOBJT_TEXT); ut_assertnonnull(txt); - ut_asserteq(font_size, txt->font_size); + ut_asserteq(font_size, txt->gen.font_size); return 0; } diff --git a/test/boot/cedit.c b/test/boot/cedit.c index 0c34d4b3df0..dbf781902fb 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -48,7 +48,7 @@ static int cedit_base(struct unit_test_state *uts) txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_NONE); ut_assertnonnull(txt); - ut_asserteq_str("AC Power", expo_get_str(exp, txt->str_id)); + ut_asserteq_str("AC Power", expo_get_str(exp, txt->gen.str_id)); ut_asserteq(ID_AC_ON, menu->cur_item_id); diff --git a/test/boot/expo.c b/test/boot/expo.c index 70db33d48b1..96c5943f394 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -280,8 +280,8 @@ static int expo_object_attr(struct unit_test_state *uts) strcpy(name, "font2"); ut_assertok(scene_txt_set_font(scn, OBJ_TEXT, name, 42)); - ut_asserteq_ptr(name, txt->font_name); - ut_asserteq(42, txt->font_size); + ut_asserteq_ptr(name, txt->gen.font_name); + ut_asserteq(42, txt->gen.font_size); ut_asserteq(-ENOENT, scene_txt_set_font(scn, OBJ_TEXT2, name, 42)); @@ -296,7 +296,7 @@ static int expo_object_attr(struct unit_test_state *uts) node = ofnode_path("/bootstd/theme"); ut_assert(ofnode_valid(node)); ut_assertok(expo_apply_theme(exp, node)); - ut_asserteq(30, txt->font_size); + ut_asserteq(30, txt->gen.font_size); expo_destroy(exp); @@ -727,7 +727,7 @@ static int expo_test_build(struct unit_test_state *uts) 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(ID_DYNAMIC_START, scn->title_id); ut_asserteq(0, scn->highlight_id); /* check the title */ @@ -739,7 +739,8 @@ static int expo_test_build(struct unit_test_state *uts) 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)); + ut_asserteq_str("Test Configuration", + expo_get_str(exp, txt->gen.str_id)); /* check the menu */ menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_NONE); @@ -751,7 +752,7 @@ static int expo_test_build(struct unit_test_state *uts) 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_str("CPU speed", expo_get_str(exp, txt->gen.str_id)); ut_asserteq(0, menu->cur_item_id); ut_asserteq(0, menu->pointer_id); @@ -768,7 +769,7 @@ static int expo_test_build(struct unit_test_state *uts) ut_asserteq(0, item->value); txt = scene_obj_find(scn, item->label_id, SCENEOBJT_NONE); - ut_asserteq_str("2 GHz", expo_get_str(exp, txt->str_id)); + ut_asserteq_str("2 GHz", expo_get_str(exp, txt->gen.str_id)); count = list_count_nodes(&menu->item_head); ut_asserteq(3, count); -- cgit v1.3.1 From 87750b027691cd67a20ae6c5b95df65c33e8294e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:40 -0600 Subject: expo: Allow strings to be editable In some cases dynamic text is needed, e.g. for a menu countdown. Add a function which handles this, allowing the caller to take control of the text that is shown on each render. Signed-off-by: Simon Glass --- boot/expo.c | 21 +++++++++++++++++++++ include/expo.h | 17 +++++++++++++++++ test/boot/expo.c | 11 +++++++++++ 3 files changed, 49 insertions(+) (limited to 'include') diff --git a/boot/expo.c b/boot/expo.c index debdb60022b..4404fcb67e9 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -104,6 +104,27 @@ const char *expo_get_str(struct expo *exp, uint id) return NULL; } +int expo_edit_str(struct expo *exp, uint id, struct abuf *orig, + struct abuf **copyp) +{ + struct expo_string *estr; + struct abuf old; + + list_for_each_entry(estr, &exp->str_head, sibling) { + if (estr->id == id) { + old = estr->buf; + if (!abuf_copy(&old, &estr->buf)) + return -ENOMEM; + *copyp = &estr->buf; + if (orig) + *orig = old; + return 0; + } + } + + return -ENOENT; +} + int expo_set_display(struct expo *exp, struct udevice *dev) { struct udevice *cons; diff --git a/include/expo.h b/include/expo.h index 32d69f269a7..7c6ab4bf630 100644 --- a/include/expo.h +++ b/include/expo.h @@ -469,6 +469,23 @@ int expo_str(struct expo *exp, const char *name, uint id, const char *str); */ const char *expo_get_str(struct expo *exp, uint id); +/** + * expo_edit_str() - Make a string writeable + * + * This allows a string to be updated under the control of the caller. The + * buffer must remain valid while the expo is active. + * + * @exp: Expo to use + * @id: String ID to look up + * @orig: If non-NULL, returns the original buffer, which can be used by the + * caller. It is no-longer used by expo so must be uninited by the caller. + * It contains a snapshot of the string contents + * @copyp: Returns a pointer to the new, writeable buffer + * Return: 0 if OK, -ENOENT if the id was not found, -ENOMEM if out of memory + */ +int expo_edit_str(struct expo *exp, uint id, struct abuf *orig, + struct abuf **copyp); + /** * expo_set_display() - set the display to use for a expo * diff --git a/test/boot/expo.c b/test/boot/expo.c index 96c5943f394..a50e9f721de 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -707,6 +707,7 @@ 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 abuf orig, *copy; struct scene_obj *obj; struct scene *scn; struct expo *exp; @@ -774,6 +775,16 @@ static int expo_test_build(struct unit_test_state *uts) count = list_count_nodes(&menu->item_head); ut_asserteq(3, count); + /* try editing some text */ + ut_assertok(expo_edit_str(exp, txt->gen.str_id, &orig, ©)); + ut_asserteq_str("2 GHz", orig.data); + ut_asserteq_str("2 GHz", copy->data); + + /* change it and check that things look right */ + abuf_printf(copy, "atlantic %d", 123); + ut_asserteq_str("2 GHz", orig.data); + ut_asserteq_str("atlantic 123", copy->data); + expo_destroy(exp); return 0; -- cgit v1.3.1 From 03f9ce815cbba660bb4c8e7cd28cfc19219631c8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:42 -0600 Subject: expo: Support rendering multiple lines of text Use the measurement info to write each line of text separately, thus respecting word-wrapping and newlines. Fix up the comment for scene_obj_render() while we are here. Since a lineedit does not support alignment, add a special case to just display the text if there is no measurement. This happens assuming the lineedit is initially empty. Signed-off-by: Simon Glass --- boot/bootflow_menu.c | 4 ++++ boot/scene.c | 26 ++++++++++++++++++++++---- include/expo.h | 4 ++++ test/boot/expo.c | 12 +++++++++++- 4 files changed, 41 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/boot/bootflow_menu.c b/boot/bootflow_menu.c index 6a37fa8fa68..ab21ed84ffd 100644 --- a/boot/bootflow_menu.c +++ b/boot/bootflow_menu.c @@ -241,6 +241,10 @@ int bootflow_menu_start(struct bootstd_priv *std, bool text_mode, if (text_mode) expo_set_text_mode(exp, text_mode); + ret = expo_calc_dims(exp); + if (ret) + return log_msg_ret("bmd", ret); + *expp = exp; return 0; diff --git a/boot/scene.c b/boot/scene.c index aacdccdce17..72a8e27a93d 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -8,6 +8,7 @@ #define LOG_CATEGORY LOGC_EXPO +#include #include #include #include @@ -163,6 +164,7 @@ int scene_txt_generic_init(struct expo *exp, struct scene_txt_generic *gen, } gen->str_id = str_id; + alist_init_struct(&gen->lines, struct vidconsole_mline); return 0; } @@ -336,8 +338,8 @@ int scene_obj_get_hw(struct scene *scn, uint id, int *widthp) struct scene_txt_generic *gen = &((struct scene_obj_txt *)obj)->gen; struct expo *exp = scn->expo; struct vidconsole_bbox bbox; + int len, ret, limit; const char *str; - int len, ret; str = expo_get_str(exp, gen->str_id); if (!str) @@ -351,8 +353,12 @@ int scene_obj_get_hw(struct scene *scn, uint id, int *widthp) return 16; } + limit = obj->flags & SCENEOF_SIZE_VALID ? + obj->bbox.x1 - obj->bbox.x0 : -1; + ret = vidconsole_measure(scn->expo->cons, gen->font_name, - gen->font_size, str, -1, &bbox, NULL); + gen->font_size, str, limit, &bbox, + &gen->lines); if (ret) return log_msg_ret("mea", ret); if (widthp) @@ -418,6 +424,7 @@ static int scene_txt_render(struct expo *exp, struct udevice *dev, struct scene_txt_generic *gen, int x, int y, int menu_inset) { + const struct vidconsole_mline *mline; struct video_priv *vid_priv; struct vidconsole_colour old; enum colour_idx fore, back; @@ -453,8 +460,16 @@ static int scene_txt_render(struct expo *exp, struct udevice *dev, video_fill_part(dev, x - menu_inset, y, obj->bbox.x1, obj->bbox.y1, vid_priv->colour_bg); } - vidconsole_set_cursor_pos(cons, x, y); - vidconsole_put_string(cons, str); + + if (!gen->lines.count) { + vidconsole_set_cursor_pos(cons, x, y); + vidconsole_put_string(cons, str); + } + alist_for_each(mline, &gen->lines) { + vidconsole_set_cursor_pos(cons, x + mline->bbox.x0, + y + mline->bbox.y0); + vidconsole_put_stringn(cons, str + mline->start, mline->len); + } if (obj->flags & SCENEOF_POINT) vidconsole_pop_colour(cons, &old); @@ -464,6 +479,9 @@ static int scene_txt_render(struct expo *exp, struct udevice *dev, /** * scene_obj_render() - Render an object * + * @obj: Object to render + * @text_mode: true to use text mode + * Return: 0 if OK, -ve on error */ static int scene_obj_render(struct scene_obj *obj, bool text_mode) { diff --git a/include/expo.h b/include/expo.h index 7c6ab4bf630..a79aa1da74f 100644 --- a/include/expo.h +++ b/include/expo.h @@ -8,6 +8,7 @@ #define __EXPO_H #include +#include #include #include #include @@ -296,11 +297,14 @@ struct scene_obj_img { * @str_id: ID of the text string to display * @font_name: Name of font (allocated by caller) * @font_size: Nominal size of font in pixels + * @lines: alist of struct vidconsole_mline with a separate record for each + * line of text */ struct scene_txt_generic { uint str_id; const char *font_name; uint font_size; + struct alist lines; }; /** diff --git a/test/boot/expo.c b/test/boot/expo.c index 6fb9f810885..c9ff5b8dd24 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -25,6 +25,7 @@ enum { OBJ_LOGO, OBJ_TEXT, OBJ_TEXT2, + OBJ_TEXT3, OBJ_MENU, OBJ_MENU_TITLE, @@ -33,6 +34,7 @@ enum { STR_TEXT, STR_TEXT2, + STR_TEXT3, STR_MENU_TITLE, STR_POINTER_TEXT, @@ -488,6 +490,14 @@ static int expo_render_image(struct unit_test_state *uts) 60)); ut_assertok(scene_obj_set_pos(scn, OBJ_TEXT2, 200, 600)); + id = scene_txt_str(scn, "text", OBJ_TEXT3, STR_TEXT3, + "this is yet\nanother string, with word-wrap", + NULL); + ut_assert(id > 0); + ut_assertok(scene_txt_set_font(scn, OBJ_TEXT3, "nimbus_sans_l_regular", + 60)); + ut_assertok(scene_obj_set_bbox(scn, OBJ_TEXT3, 500, 200, 1000, 700)); + id = scene_menu(scn, "main", OBJ_MENU, &menu); ut_assert(id > 0); @@ -646,7 +656,7 @@ static int expo_render_image(struct unit_test_state *uts) ut_assertok(scene_arrange(scn)); ut_assertok(expo_render(exp)); - ut_asserteq(10314, video_compress_fb(uts, dev, false)); + ut_asserteq(14848, video_compress_fb(uts, dev, false)); ut_assertok(video_check_copy_fb(uts, dev)); /* make sure only the preview for the second item is shown */ -- cgit v1.3.1 From 0635004e2228dea0aab023d7c56b0b74633e8a3c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:44 -0600 Subject: expo: Implement a box It is useful to be able to draw a box around elements in the menu. Add support for an unfilled box with a selectable thickness. Note that there is no support for selecting the colour for any expo objects yet. Signed-off-by: Simon Glass --- boot/cedit.c | 5 +++++ boot/scene.c | 40 +++++++++++++++++++++++++++++++++++++++- doc/develop/expo.rst | 2 ++ include/expo.h | 28 ++++++++++++++++++++++++++++ test/boot/expo.c | 13 +++++++++++-- 5 files changed, 85 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/boot/cedit.c b/boot/cedit.c index 0f7a8657c58..9153fe769c6 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -81,6 +81,7 @@ int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: break; case SCENEOBJT_MENU: scene_obj_set_pos(scn, obj->id, 50, y); @@ -381,6 +382,7 @@ static int h_write_settings(struct scene_obj *obj, void *vpriv) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: break; case SCENEOBJT_TEXTLINE: { const struct scene_obj_textline *tline; @@ -480,6 +482,7 @@ static int h_read_settings(struct scene_obj *obj, void *vpriv) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: break; case SCENEOBJT_TEXTLINE: { const struct scene_obj_textline *tline; @@ -551,6 +554,7 @@ static int h_write_settings_env(struct scene_obj *obj, void *vpriv) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: break; case SCENEOBJT_MENU: menu = (struct scene_obj_menu *)obj; @@ -634,6 +638,7 @@ static int h_read_settings_env(struct scene_obj *obj, void *vpriv) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: break; case SCENEOBJT_MENU: menu = (struct scene_obj_menu *)obj; diff --git a/boot/scene.c b/boot/scene.c index 72a8e27a93d..f971db9aab4 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -211,6 +211,26 @@ int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id, return txt->obj.id; } +int scene_box(struct scene *scn, const char *name, uint id, uint width, + struct scene_obj_box **boxp) +{ + struct scene_obj_box *box; + int ret; + + ret = scene_obj_add(scn, name, id, SCENEOBJT_BOX, + sizeof(struct scene_obj_box), + (struct scene_obj **)&box); + if (ret < 0) + return log_msg_ret("obj", ret); + + box->width = width; + + if (boxp) + *boxp = box; + + return box->obj.id; +} + int scene_txt_set_font(struct scene *scn, uint id, const char *font_name, uint font_size) { @@ -323,6 +343,7 @@ int scene_obj_get_hw(struct scene *scn, uint id, int *widthp) case SCENEOBJT_NONE: case SCENEOBJT_MENU: case SCENEOBJT_TEXTLINE: + case SCENEOBJT_BOX: break; case SCENEOBJT_IMAGE: { struct scene_obj_img *img = (struct scene_obj_img *)obj; @@ -490,10 +511,12 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode) const struct expo_theme *theme = &exp->theme; struct udevice *dev = exp->display; struct udevice *cons = text_mode ? NULL : exp->cons; + struct video_priv *vid_priv; int x, y, ret; - x = obj->bbox.x0; y = obj->bbox.y0; + x = obj->bbox.x0; + vid_priv = dev_get_uclass_priv(dev); switch (obj->type) { case SCENEOBJT_NONE: @@ -544,6 +567,13 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode) if (obj->flags & SCENEOF_OPEN) scene_render_background(obj, true); break; + case SCENEOBJT_BOX: { + struct scene_obj_box *box = (struct scene_obj_box *)obj; + + video_draw_box(dev, obj->bbox.x0, obj->bbox.y0, obj->bbox.x1, + obj->bbox.y1, box->width, vid_priv->colour_fg); + break; + } } return 0; @@ -562,6 +592,7 @@ int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: break; case SCENEOBJT_MENU: { struct scene_obj_menu *menu; @@ -607,6 +638,7 @@ int scene_arrange(struct scene *scn) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: break; case SCENEOBJT_MENU: { struct scene_obj_menu *menu; @@ -652,6 +684,7 @@ int scene_render_deps(struct scene *scn, uint id) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: break; case SCENEOBJT_MENU: scene_menu_render_deps(scn, @@ -771,6 +804,7 @@ int scene_send_key(struct scene *scn, int key, struct expo_action *event) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: break; case SCENEOBJT_MENU: { struct scene_obj_menu *menu; @@ -815,6 +849,7 @@ int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox bbox[]) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: return -ENOSYS; case SCENEOBJT_MENU: { struct scene_obj_menu *menu = (struct scene_obj_menu *)obj; @@ -844,6 +879,7 @@ int scene_calc_dims(struct scene *scn, bool do_menus) switch (obj->type) { case SCENEOBJT_NONE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: case SCENEOBJT_IMAGE: { int width; @@ -902,6 +938,7 @@ int scene_apply_theme(struct scene *scn, struct expo_theme *theme) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_MENU: + case SCENEOBJT_BOX: case SCENEOBJT_TEXTLINE: break; case SCENEOBJT_TEXT: @@ -944,6 +981,7 @@ static int scene_obj_open(struct scene *scn, struct scene_obj *obj) case SCENEOBJT_IMAGE: case SCENEOBJT_MENU: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: break; case SCENEOBJT_TEXTLINE: ret = scene_textline_open(scn, diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst index cc7c36173db..8f63ccbe3ef 100644 --- a/doc/develop/expo.rst +++ b/doc/develop/expo.rst @@ -65,6 +65,8 @@ item is highlighted. A `textline object` contains a label and an editable string. +A `box object` is a rectangle with a given line width. It is not filled. + All components have a name. This is mostly for debugging, so it is easy to see what object is referred to, although the name is also used for saving values. Of course the ID numbers can help as well, but they are less easy to diff --git a/include/expo.h b/include/expo.h index a79aa1da74f..8833dcceb7e 100644 --- a/include/expo.h +++ b/include/expo.h @@ -179,6 +179,7 @@ struct scene { * * @SCENEOBJT_NONE: Used to indicate that the type does not matter * @SCENEOBJT_IMAGE: Image data to render + * @SCENEOBJT_BOX: Rectangular box * @SCENEOBJT_TEXT: Text line to render * @SCENEOBJT_MENU: Menu containing items the user can select * @SCENEOBJT_TEXTLINE: Line of text the user can edit @@ -187,6 +188,7 @@ enum scene_obj_t { SCENEOBJT_NONE = 0, SCENEOBJT_IMAGE, SCENEOBJT_TEXT, + SCENEOBJT_BOX, /* types from here on can be highlighted */ SCENEOBJT_MENU, @@ -406,6 +408,19 @@ struct scene_obj_textline { uint pos; }; +/** + * struct scene_obj_box - information about a box in a scene + * + * A box surrounds a part of the screen with a border + * + * @obj: Basic object information + * @width: Line-width in pixels + */ +struct scene_obj_box { + struct scene_obj obj; + uint width; +}; + /** * struct expo_arrange_info - Information used when arranging a scene * @@ -670,6 +685,19 @@ int scene_menu(struct scene *scn, const char *name, uint id, int scene_textline(struct scene *scn, const char *name, uint id, uint max_chars, struct scene_obj_textline **tlinep); +/** + * scene_box() - create a box + * + * @scn: Scene to update + * @name: Name to use (this is allocated by this call) + * @id: ID to use for the new object (0 to allocate one) + * @width: Line-width in pixels + * @boxp: If non-NULL, returns the new object + * Returns: ID number for the object (typically @id), or -ve on error + */ +int scene_box(struct scene *scn, const char *name, uint id, uint width, + struct scene_obj_box **boxp); + /** * scene_txt_set_font() - Set the font for an object * diff --git a/test/boot/expo.c b/test/boot/expo.c index c9ff5b8dd24..e4f3ffc01fb 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -28,6 +28,8 @@ enum { OBJ_TEXT3, OBJ_MENU, OBJ_MENU_TITLE, + OBJ_BOX, + OBJ_BOX2, /* strings */ STR_SCENE_TITLE, @@ -545,6 +547,14 @@ static int expo_render_image(struct unit_test_state *uts) ut_assertok(scene_obj_set_pos(scn, OBJ_MENU, 50, 400)); + id = scene_box(scn, "box", OBJ_BOX, 3, NULL); + ut_assert(id > 0); + ut_assertok(scene_obj_set_bbox(scn, OBJ_BOX, 40, 390, 1000, 510)); + + id = scene_box(scn, "box2", OBJ_BOX2, 1, NULL); + ut_assert(id > 0); + ut_assertok(scene_obj_set_bbox(scn, OBJ_BOX, 500, 200, 1000, 350)); + scn2 = expo_lookup_scene_id(exp, SCENE1); ut_asserteq_ptr(scn, scn2); scn2 = expo_lookup_scene_id(exp, SCENE2); @@ -655,8 +665,7 @@ static int expo_render_image(struct unit_test_state *uts) ut_asserteq(ITEM2, scene_menu_get_cur_item(scn, OBJ_MENU)); ut_assertok(scene_arrange(scn)); ut_assertok(expo_render(exp)); - - ut_asserteq(14848, video_compress_fb(uts, dev, false)); + ut_asserteq(14883, video_compress_fb(uts, dev, false)); ut_assertok(video_check_copy_fb(uts, dev)); /* make sure only the preview for the second item is shown */ -- cgit v1.3.1 From 09f6f915fea90ea21a1a7b6a0a6907f89034dae1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:45 -0600 Subject: expo: Support object alignment Add support for left, right and centred alignment for text, in the horizontal dimension. Also support top, bottom and centred in the vertical dimension, for the text object as a whole. Alignment is not yet implemented for images. It has no meaning for menus. A textline object uses a text object internally, so alignment is supported there. Provide some documentation to explain how objects are positioned. Signed-off-by: Simon Glass --- boot/scene.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++--- doc/develop/expo.rst | 31 +++++++++++++++ include/expo.h | 63 ++++++++++++++++++++++++++++++ test/boot/expo.c | 23 +++++++++-- 4 files changed, 216 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/boot/scene.c b/boot/scene.c index f971db9aab4..3091a9e0ab1 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -306,6 +306,30 @@ int scene_obj_set_bbox(struct scene *scn, uint id, int x0, int y0, int x1, return 0; } +int scene_obj_set_halign(struct scene *scn, uint id, enum scene_obj_align aln) +{ + struct scene_obj *obj; + + obj = scene_obj_find(scn, id, SCENEOBJT_NONE); + if (!obj) + return log_msg_ret("osh", -ENOENT); + obj->horiz = aln; + + return 0; +} + +int scene_obj_set_valign(struct scene *scn, uint id, enum scene_obj_align aln) +{ + struct scene_obj *obj; + + obj = scene_obj_find(scn, id, SCENEOBJT_NONE); + if (!obj) + return log_msg_ret("osv", -ENOENT); + obj->vert = aln; + + return 0; +} + int scene_obj_set_hide(struct scene *scn, uint id, bool hide) { int ret; @@ -331,6 +355,44 @@ int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set) return 0; } +static void handle_alignment(enum scene_obj_align horiz, + enum scene_obj_align vert, + struct scene_obj_bbox *bbox, + struct scene_obj_dims *dims, + int xsize, int ysize, + struct scene_obj_offset *offset) +{ + int width, height; + + width = bbox->x1 - bbox->x0; + height = bbox->y1 - bbox->y0; + + switch (horiz) { + case SCENEOA_CENTRE: + offset->xofs = (width - dims->x) / 2; + break; + case SCENEOA_RIGHT: + offset->xofs = width - dims->x; + break; + case SCENEOA_LEFT: + offset->xofs = 0; + break; + } + + switch (vert) { + case SCENEOA_CENTRE: + offset->yofs = (height - dims->y) / 2; + break; + case SCENEOA_BOTTOM: + offset->yofs = height - dims->y; + break; + case SCENEOA_TOP: + default: + offset->yofs = 0; + break; + } +} + int scene_obj_get_hw(struct scene *scn, uint id, int *widthp) { struct scene_obj *obj; @@ -445,10 +507,12 @@ static int scene_txt_render(struct expo *exp, struct udevice *dev, struct scene_txt_generic *gen, int x, int y, int menu_inset) { - const struct vidconsole_mline *mline; + const struct vidconsole_mline *mline, *last; struct video_priv *vid_priv; struct vidconsole_colour old; enum colour_idx fore, back; + struct scene_obj_dims dims; + struct scene_obj_bbox bbox; const char *str; int ret; @@ -482,13 +546,33 @@ static int scene_txt_render(struct expo *exp, struct udevice *dev, obj->bbox.y1, vid_priv->colour_bg); } - if (!gen->lines.count) { + mline = alist_get(&gen->lines, 0, typeof(*mline)); + last = alist_get(&gen->lines, gen->lines.count - 1, typeof(*mline)); + if (mline) + dims.y = last->bbox.y1 - mline->bbox.y0; + bbox.y0 = obj->bbox.y0; + bbox.y1 = obj->bbox.y1; + + if (!mline) { vidconsole_set_cursor_pos(cons, x, y); vidconsole_put_string(cons, str); } + alist_for_each(mline, &gen->lines) { - vidconsole_set_cursor_pos(cons, x + mline->bbox.x0, - y + mline->bbox.y0); + struct scene_obj_offset offset; + + bbox.x0 = obj->bbox.x0; + bbox.x1 = obj->bbox.x1; + dims.x = mline->bbox.x1 - mline->bbox.x0; + handle_alignment(obj->horiz, obj->vert, &bbox, &dims, + obj->bbox.x1 - obj->bbox.x0, + obj->bbox.y1 - obj->bbox.y0, &offset); + + x = obj->bbox.x0 + offset.xofs; + y = obj->bbox.y0 + offset.yofs + mline->bbox.y0; + if (y > bbox.y1) + break; /* clip this line and any following */ + vidconsole_set_cursor_pos(cons, x, y); vidconsole_put_stringn(cons, str + mline->start, mline->len); } if (obj->flags & SCENEOF_POINT) @@ -515,7 +599,7 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode) int x, y, ret; y = obj->bbox.y0; - x = obj->bbox.x0; + x = obj->bbox.x0 + obj->ofs.xofs; vid_priv = dev_get_uclass_priv(dev); switch (obj->type) { @@ -626,14 +710,27 @@ int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr) int scene_arrange(struct scene *scn) { struct expo_arrange_info arr; + int xsize = 0, ysize = 0; struct scene_obj *obj; + struct udevice *dev; int ret; + dev = scn->expo->display; + if (dev) { + struct video_priv *priv = dev_get_uclass_priv(dev); + + xsize = priv->xsize; + ysize = priv->ysize; + } + ret = scene_calc_arrange(scn, &arr); if (ret < 0) return log_msg_ret("arr", ret); list_for_each_entry(obj, &scn->obj_head, sibling) { + handle_alignment(obj->horiz, obj->vert, &obj->bbox, &obj->dims, + xsize, ysize, &obj->ofs); + switch (obj->type) { case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst index 8f63ccbe3ef..d6fc487e030 100644 --- a/doc/develop/expo.rst +++ b/doc/develop/expo.rst @@ -107,6 +107,37 @@ refer to objects which have been created. So a menu item is just a collection of IDs of text and image objects. When adding a menu item you must create these objects first, then create the menu item, passing in the relevant IDs. +Position and alignment +~~~~~~~~~~~~~~~~~~~~~~ + +Objects are typically positioned automatically, when scene_arrange() is called. +However it is possible to position objects manually. The scene_obj_set_pos() +sets the coordinates of the top left of the object. + +All objects have a bounding box. Typically this is calculated by looking at the +object contents, in `scene_calc_arrange()`. The calculated dimensions of each +object are stored in the object's `dims` field. + +It is possible to adjust the size of an object with `scene_obj_set_size()` or +even set the bounding box, with `scene_obj_set_bbox()`. The `SCENEOF_SIZE_VALID` +flag tracks whether the width/height should be maintained when the position +changes. + +If the bounding box is larger than the object needs, the object can be aligned +to different edges within the box. Objects can be left- or right-aligned, +or centred. For text objects this applies to each line of text. Normally objects +are drawn starting at the top of their bounding box, but they can be aligned +vertically to the bottom, or centred vertically within the box. + +Where the width of a text object's bounding box is smaller than the space needed +to show the next, the text is word-wrapped onto multiple lines, assuming there +is enough vertical space. Newline characters in the next cause a new line to be +started. The measurement information is created by the Truetype console driver +and stored in an alist in `struct scene_txt_generic`. + +When the object is drawn the `ofs` field indicates the x and y offset to use, +from the top left of the bounding box. These values are affected by alignment. + Creating an expo ---------------- diff --git a/include/expo.h b/include/expo.h index 8833dcceb7e..001f7db2553 100644 --- a/include/expo.h +++ b/include/expo.h @@ -210,6 +210,19 @@ struct scene_obj_bbox { int y1; }; +/** + * struct scene_obj_offset - Offsets for drawing the object + * + * Stores the offset from x0, x1 at which objects are drawn + * + * @xofs: x offset + * @yofs: y offset + */ +struct scene_obj_offset { + int xofs; + int yofs; +}; + /** * struct scene_obj_dims - Dimensions of the object being drawn * @@ -225,6 +238,30 @@ struct scene_obj_dims { int y; }; +/** + * enum scene_obj_halign - Horizontal alignment of objects + * + * Objects are normally drawn on the left size of their bounding box. This + * properly allows aligning on the right or having the object centred. + * + * @SCENEOA_LEFT: Left of object is aligned with its x coordinate + * @SCENEOA_RIGHT: Right of object is aligned with x + w + * @SCENEOA_CENTRE: Centre of object is aligned with centre of bounding box + * @SCENEOA_TOP: Left of object is aligned with its x coordinate + * @SCENEOA_BOTTOM: Right of object is aligned with x + w + * + * Note: It would be nice to make this a char type but Sphinx riddles: + * ./include/expo.h:258: error: Cannot parse enum! + * enum scene_obj_align : char { + */ +enum scene_obj_align { + SCENEOA_LEFT, + SCENEOA_RIGHT, + SCENEOA_CENTRE, + SCENEOA_TOP = SCENEOA_LEFT, + SCENEOA_BOTTOM = SCENEOA_RIGHT, +}; + /** * enum scene_obj_flags_t - flags for objects * @@ -255,7 +292,10 @@ enum { * @id: ID number of the object * @type: Type of this object * @bbox: Bounding box for this object + * @ofs: Offset from x0, y0 where the object is drawn * @dims: Dimensions of the text/image (may be smaller than bbox) + * @horiz: Horizonal alignment + * @vert: Vertical alignment * @flags: Flags for this object * @bit_length: Number of bits used for this object in CMOS RAM * @start_bit: Start bit to use for this object in CMOS RAM @@ -267,7 +307,10 @@ struct scene_obj { uint id; enum scene_obj_t type; struct scene_obj_bbox bbox; + struct scene_obj_offset ofs; struct scene_obj_dims dims; + enum scene_obj_align horiz; + enum scene_obj_align vert; u8 flags; u8 bit_length; u16 start_bit; @@ -755,6 +798,26 @@ int scene_obj_set_width(struct scene *scn, uint id, int w); int scene_obj_set_bbox(struct scene *scn, uint id, int x0, int y0, int x1, int y1); +/** + * scene_obj_set_halign() - Set the horizontal alignment of an object + * + * @scn: Scene to update + * @id: ID of object to update + * @aln: Horizontal alignment to use + * Returns: 0 if OK, -ENOENT if @id is invalid + */ +int scene_obj_set_halign(struct scene *scn, uint id, enum scene_obj_align aln); + +/** + * scene_obj_set_valign() - Set the vertical alignment of an object + * + * @scn: Scene to update + * @id: ID of object to update + * @aln: Vertical alignment to use + * Returns: 0 if OK, -ENOENT if @id is invalid + */ +int scene_obj_set_valign(struct scene *scn, uint id, enum scene_obj_align aln); + /** * scene_obj_set_hide() - Set whether an object is hidden * diff --git a/test/boot/expo.c b/test/boot/expo.c index e4f3ffc01fb..b9093b5780b 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -492,13 +492,14 @@ static int expo_render_image(struct unit_test_state *uts) 60)); ut_assertok(scene_obj_set_pos(scn, OBJ_TEXT2, 200, 600)); + /* this string is clipped as it extends beyond its bottom bound */ id = scene_txt_str(scn, "text", OBJ_TEXT3, STR_TEXT3, - "this is yet\nanother string, with word-wrap", + "this is yet\nanother string, with word-wrap and it goes on for quite a while", NULL); ut_assert(id > 0); ut_assertok(scene_txt_set_font(scn, OBJ_TEXT3, "nimbus_sans_l_regular", 60)); - ut_assertok(scene_obj_set_bbox(scn, OBJ_TEXT3, 500, 200, 1000, 700)); + ut_assertok(scene_obj_set_bbox(scn, OBJ_TEXT3, 500, 200, 1000, 350)); id = scene_menu(scn, "main", OBJ_MENU, &menu); ut_assert(id > 0); @@ -665,9 +666,25 @@ static int expo_render_image(struct unit_test_state *uts) ut_asserteq(ITEM2, scene_menu_get_cur_item(scn, OBJ_MENU)); ut_assertok(scene_arrange(scn)); ut_assertok(expo_render(exp)); - ut_asserteq(14883, video_compress_fb(uts, dev, false)); + ut_asserteq(16304, video_compress_fb(uts, dev, false)); ut_assertok(video_check_copy_fb(uts, dev)); + /* do some alignment checks */ + ut_assertok(scene_obj_set_halign(scn, OBJ_TEXT3, SCENEOA_CENTRE)); + ut_assertok(expo_render(exp)); + ut_asserteq(16368, video_compress_fb(uts, dev, false)); + ut_assertok(scene_obj_set_halign(scn, OBJ_TEXT3, SCENEOA_RIGHT)); + ut_assertok(expo_render(exp)); + ut_asserteq(16321, video_compress_fb(uts, dev, false)); + + ut_assertok(scene_obj_set_halign(scn, OBJ_TEXT3, SCENEOA_LEFT)); + ut_assertok(scene_obj_set_valign(scn, OBJ_TEXT3, SCENEOA_CENTRE)); + ut_assertok(expo_render(exp)); + ut_asserteq(18763, video_compress_fb(uts, dev, false)); + ut_assertok(scene_obj_set_valign(scn, OBJ_TEXT3, SCENEOA_BOTTOM)); + ut_assertok(expo_render(exp)); + ut_asserteq(18714, video_compress_fb(uts, dev, false)); + /* make sure only the preview for the second item is shown */ obj = scene_obj_find(scn, ITEM1_PREVIEW, SCENEOBJT_NONE); ut_asserteq(true, obj->flags & SCENEOF_HIDE); -- cgit v1.3.1 From e005f18b933f3f4555fd08a4d66a1f9aee8f47e1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:46 -0600 Subject: expo: Begin implementation of a text editor It is useful to be able to edit text, e.g. to allow the user to edit the environment or the command-line arguments for the OS. Add the beginnings of an implementation. Future work is needed to finish this: keypress handling and scrolling. For now it just displays the text. Signed-off-by: Simon Glass --- boot/Makefile | 2 +- boot/cedit.c | 5 +++++ boot/scene.c | 30 +++++++++++++++++++++++-- boot/scene_internal.h | 12 ++++++++++ boot/scene_textedit.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++ doc/develop/expo.rst | 1 + include/expo.h | 41 ++++++++++++++++++++++++++++++++++ test/boot/expo.c | 16 +++++++++++++- 8 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 boot/scene_textedit.c (limited to 'include') diff --git a/boot/Makefile b/boot/Makefile index 71dafaefa76..e0d1579827d 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -58,7 +58,7 @@ obj-$(CONFIG_CMD_ADTIMG) += image-android-dt.o obj-$(CONFIG_$(PHASE_)LOAD_FIT) += common_fit.o obj-$(CONFIG_$(PHASE_)EXPO) += expo.o scene.o expo_build.o -obj-$(CONFIG_$(PHASE_)EXPO) += scene_menu.o scene_textline.o +obj-$(CONFIG_$(PHASE_)EXPO) += scene_menu.o scene_textline.o scene_textedit.o ifdef CONFIG_COREBOOT_SYSINFO obj-$(CONFIG_$(PHASE_)EXPO) += expo_build_cb.o endif diff --git a/boot/cedit.c b/boot/cedit.c index 9153fe769c6..54810257fb9 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -82,6 +82,7 @@ int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: break; case SCENEOBJT_MENU: scene_obj_set_pos(scn, obj->id, 50, y); @@ -383,6 +384,7 @@ static int h_write_settings(struct scene_obj *obj, void *vpriv) case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: break; case SCENEOBJT_TEXTLINE: { const struct scene_obj_textline *tline; @@ -483,6 +485,7 @@ static int h_read_settings(struct scene_obj *obj, void *vpriv) case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: break; case SCENEOBJT_TEXTLINE: { const struct scene_obj_textline *tline; @@ -555,6 +558,7 @@ static int h_write_settings_env(struct scene_obj *obj, void *vpriv) case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: break; case SCENEOBJT_MENU: menu = (struct scene_obj_menu *)obj; @@ -639,6 +643,7 @@ static int h_read_settings_env(struct scene_obj *obj, void *vpriv) case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: break; case SCENEOBJT_MENU: menu = (struct scene_obj_menu *)obj; diff --git a/boot/scene.c b/boot/scene.c index 3091a9e0ab1..c8dc1716f1d 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -417,13 +417,19 @@ int scene_obj_get_hw(struct scene *scn, uint id, int *widthp) *widthp = width; return height; } - case SCENEOBJT_TEXT: { - struct scene_txt_generic *gen = &((struct scene_obj_txt *)obj)->gen; + case SCENEOBJT_TEXT: + case SCENEOBJT_TEXTEDIT: { + struct scene_txt_generic *gen; struct expo *exp = scn->expo; struct vidconsole_bbox bbox; int len, ret, limit; const char *str; + if (obj->type == SCENEOBJT_TEXT) + gen = &((struct scene_obj_txt *)obj)->gen; + else + gen = &((struct scene_obj_txtedit *)obj)->gen; + str = expo_get_str(exp, gen->str_id); if (!str) return log_msg_ret("str", -ENOENT); @@ -658,6 +664,13 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode) obj->bbox.y1, box->width, vid_priv->colour_fg); break; } + case SCENEOBJT_TEXTEDIT: { + struct scene_obj_txtedit *ted = (struct scene_obj_txtedit *)obj; + + ret = scene_txt_render(exp, dev, cons, obj, &ted->gen, x, y, + theme->menu_inset); + break; + } } return 0; @@ -677,6 +690,7 @@ int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr) case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: break; case SCENEOBJT_MENU: { struct scene_obj_menu *menu; @@ -736,6 +750,7 @@ int scene_arrange(struct scene *scn) case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: break; case SCENEOBJT_MENU: { struct scene_obj_menu *menu; @@ -782,6 +797,7 @@ int scene_render_deps(struct scene *scn, uint id) case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: break; case SCENEOBJT_MENU: scene_menu_render_deps(scn, @@ -921,6 +937,9 @@ int scene_send_key(struct scene *scn, int key, struct expo_action *event) return log_msg_ret("key", ret); break; } + case SCENEOBJT_TEXTEDIT: + /* TODO(sjg@chromium.org): Implement this */ + break; } return 0; } @@ -947,6 +966,7 @@ int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox bbox[]) case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: return -ENOSYS; case SCENEOBJT_MENU: { struct scene_obj_menu *menu = (struct scene_obj_menu *)obj; @@ -977,6 +997,7 @@ int scene_calc_dims(struct scene *scn, bool do_menus) case SCENEOBJT_NONE: case SCENEOBJT_TEXT: case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: case SCENEOBJT_IMAGE: { int width; @@ -1038,6 +1059,10 @@ int scene_apply_theme(struct scene *scn, struct expo_theme *theme) case SCENEOBJT_BOX: case SCENEOBJT_TEXTLINE: break; + case SCENEOBJT_TEXTEDIT: + scene_txted_set_font(scn, obj->id, NULL, + theme->font_size); + break; case SCENEOBJT_TEXT: scene_txt_set_font(scn, obj->id, NULL, theme->font_size); @@ -1079,6 +1104,7 @@ static int scene_obj_open(struct scene *scn, struct scene_obj *obj) case SCENEOBJT_MENU: case SCENEOBJT_TEXT: case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: break; case SCENEOBJT_TEXTLINE: ret = scene_textline_open(scn, diff --git a/boot/scene_internal.h b/boot/scene_internal.h index ac2a36d6e4d..760cc629b86 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -414,4 +414,16 @@ int scene_textline_close(struct scene *scn, struct scene_obj_textline *tline); */ int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr); +/** + * scene_txt_generic_init() - Set up the generic part of a text object + * + * @exp: Expo containing the object + * @gen: Generic text info + * @name: Object name + * @str_id: String ID for the text + * @str: Initial text string for the object, or NULL to just use str_id + */ +int scene_txt_generic_init(struct expo *exp, struct scene_txt_generic *gen, + const char *name, uint str_id, const char *str); + #endif /* __SCENE_INTERNAL_H */ diff --git a/boot/scene_textedit.c b/boot/scene_textedit.c new file mode 100644 index 00000000000..8242eb39806 --- /dev/null +++ b/boot/scene_textedit.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Implementation of a menu in a scene + * + * Copyright 2025 Google LLC + * Written by Simon Glass + */ + +#define LOG_CATEGORY LOGC_EXPO + +#include +#include +#include +#include +#include "scene_internal.h" + +enum { + INITIAL_SIZE = SZ_4K, +}; + +int scene_texted(struct scene *scn, const char *name, uint id, uint str_id, + struct scene_obj_txtedit **teditp) +{ + struct scene_obj_txtedit *ted; + char *buf; + int ret; + + ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXTEDIT, + sizeof(struct scene_obj_txtedit), + (struct scene_obj **)&ted); + if (ret < 0) + return log_msg_ret("obj", ret); + + abuf_init(&ted->buf); + if (!abuf_realloc(&ted->buf, INITIAL_SIZE)) + return log_msg_ret("buf", -ENOMEM); + buf = abuf_data(&ted->buf); + *buf = '\0'; + + ret = scene_txt_generic_init(scn->expo, &ted->gen, name, str_id, buf); + if (ret) + return log_msg_ret("teg", ret); + if (teditp) + *teditp = ted; + + return ted->obj.id; +} + +int scene_txted_set_font(struct scene *scn, uint id, const char *font_name, + uint font_size) +{ + struct scene_obj_txtedit *ted; + + ted = scene_obj_find(scn, id, SCENEOBJT_TEXTEDIT); + if (!ted) + return log_msg_ret("find", -ENOENT); + ted->gen.font_name = font_name; + ted->gen.font_size = font_size; + + return 0; +} diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst index d6fc487e030..b94340e9a8d 100644 --- a/doc/develop/expo.rst +++ b/doc/develop/expo.rst @@ -560,6 +560,7 @@ Future ideas Some ideas for future work: - Default menu item and a timeout +- Complete the text editor - Image formats other than BMP - Use of ANSI sequences to control a serial terminal - Colour selection diff --git a/include/expo.h b/include/expo.h index 001f7db2553..16f2f18c4fa 100644 --- a/include/expo.h +++ b/include/expo.h @@ -183,12 +183,14 @@ struct scene { * @SCENEOBJT_TEXT: Text line to render * @SCENEOBJT_MENU: Menu containing items the user can select * @SCENEOBJT_TEXTLINE: Line of text the user can edit + * @SCENEOBJT_TEXTEDIT: Simple text editor */ enum scene_obj_t { SCENEOBJT_NONE = 0, SCENEOBJT_IMAGE, SCENEOBJT_TEXT, SCENEOBJT_BOX, + SCENEOBJT_TEXTEDIT, /* types from here on can be highlighted */ SCENEOBJT_MENU, @@ -464,6 +466,21 @@ struct scene_obj_box { uint width; }; +/** + * struct scene_obj_txtedit - information about a box in a scene + * + * A text editor which allows users to edit a small text file + * + * @obj: Basic object information + * @gen: Generic information common to all objects which show text + * @buf: Text buffer containing current text + */ +struct scene_obj_txtedit { + struct scene_obj obj; + struct scene_txt_generic gen; + struct abuf buf; +}; + /** * struct expo_arrange_info - Information used when arranging a scene * @@ -741,6 +758,19 @@ int scene_textline(struct scene *scn, const char *name, uint id, uint max_chars, int scene_box(struct scene *scn, const char *name, uint id, uint width, struct scene_obj_box **boxp); +/** + * scene_texted() - create a text editor + * + * @scn: Scene to update + * @name: Name to use (this is allocated by this call) + * @id: ID to use for the new object (0 to allocate one) + * @strid: ID of the string to edit + * @teditp: If non-NULL, returns the new object + * Returns: ID number for the object (typically @id), or -ve on error + */ +int scene_texted(struct scene *scn, const char *name, uint id, uint strid, + struct scene_obj_txtedit **teditp); + /** * scene_txt_set_font() - Set the font for an object * @@ -752,6 +782,17 @@ int scene_box(struct scene *scn, const char *name, uint id, uint width, int scene_txt_set_font(struct scene *scn, uint id, const char *font_name, uint font_size); +/** + * scene_txted_set_font() - Set the font for an object + * + * @scn: Scene to update + * @id: ID of object to update + * @font_name: Font name to use (allocated by caller) + * @font_size: Font size to use (nominal height in pixels) + */ +int scene_txted_set_font(struct scene *scn, uint id, const char *font_name, + uint font_size); + /** * scene_obj_set_pos() - Set the postion of an object * diff --git a/test/boot/expo.c b/test/boot/expo.c index b9093b5780b..e624a00c2c0 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -30,6 +30,7 @@ enum { OBJ_MENU_TITLE, OBJ_BOX, OBJ_BOX2, + OBJ_TEXTED, /* strings */ STR_SCENE_TITLE, @@ -37,6 +38,7 @@ enum { STR_TEXT, STR_TEXT2, STR_TEXT3, + STR_TEXTED, STR_MENU_TITLE, STR_POINTER_TEXT, @@ -462,6 +464,7 @@ static int expo_render_image(struct unit_test_state *uts) { struct scene_obj_menu *menu; struct scene *scn, *scn2; + struct abuf orig, *text; struct expo_action act; struct scene_obj *obj; struct udevice *dev; @@ -556,6 +559,14 @@ static int expo_render_image(struct unit_test_state *uts) ut_assert(id > 0); ut_assertok(scene_obj_set_bbox(scn, OBJ_BOX, 500, 200, 1000, 350)); + id = scene_texted(scn, "editor", OBJ_TEXTED, STR_TEXTED, NULL); + ut_assert(id > 0); + ut_assertok(scene_obj_set_bbox(scn, OBJ_TEXTED, 100, 200, 400, 650)); + ut_assertok(expo_edit_str(exp, STR_TEXTED, &orig, &text)); + + abuf_printf(text, "This\nis the initial contents of the text editor " + "but it is quite likely that more will be added later"); + scn2 = expo_lookup_scene_id(exp, SCENE1); ut_asserteq_ptr(scn, scn2); scn2 = expo_lookup_scene_id(exp, SCENE2); @@ -666,9 +677,12 @@ static int expo_render_image(struct unit_test_state *uts) ut_asserteq(ITEM2, scene_menu_get_cur_item(scn, OBJ_MENU)); ut_assertok(scene_arrange(scn)); ut_assertok(expo_render(exp)); - ut_asserteq(16304, video_compress_fb(uts, dev, false)); + ut_asserteq(19673, video_compress_fb(uts, dev, false)); ut_assertok(video_check_copy_fb(uts, dev)); + /* hide the text editor since the following tets don't need it */ + scene_obj_set_hide(scn, OBJ_TEXTED, true); + /* do some alignment checks */ ut_assertok(scene_obj_set_halign(scn, OBJ_TEXT3, SCENEOA_CENTRE)); ut_assertok(expo_render(exp)); -- cgit v1.3.1 From 0dc8c7740c8f5a7155259f0e55618f8006c0d7af Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:50 -0600 Subject: expo: Support highlighting menu items Expo normally uses a pointer to show the current item. Add support for highlighting as well, since this makes it easier for the user to see the current item. Signed-off-by: Simon Glass --- boot/cedit.c | 1 + boot/scene.c | 31 +++++++++++++++++++++---------- boot/scene_menu.c | 14 +++++++++++--- include/expo.h | 2 ++ test/boot/expo.c | 13 +++++++++++++ 5 files changed, 48 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/boot/cedit.c b/boot/cedit.c index 54810257fb9..8faf230a9e2 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -126,6 +126,7 @@ int cedit_prepare(struct expo *exp, struct udevice *vid_dev, return log_msg_ret("sid", ret); exp->popup = true; + exp->show_highlight = true; /* This is not supported for now */ if (0) diff --git a/boot/scene.c b/boot/scene.c index c8dc1716f1d..237f2bccc00 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -466,8 +466,10 @@ int scene_obj_get_hw(struct scene *scn, uint id, int *widthp) * @obj: Object to render * @box_only: true to show a box around the object, but keep the normal * background colour inside + * @cur_item: true to render the background only for the current menu item */ -static void scene_render_background(struct scene_obj *obj, bool box_only) +static void scene_render_background(struct scene_obj *obj, bool box_only, + bool cur_item) { struct vidconsole_bbox bbox[SCENEBB_count], *sel; struct expo *exp = obj->scene->expo; @@ -493,7 +495,7 @@ static void scene_render_background(struct scene_obj *obj, bool box_only) if (scene_obj_calc_bbox(obj, bbox)) return; - sel = &bbox[SCENEBB_label]; + sel = cur_item ? &bbox[SCENEBB_curitem] : &bbox[SCENEBB_label]; if (!sel->valid) return; @@ -547,9 +549,13 @@ static int scene_txt_render(struct expo *exp, struct udevice *dev, } if (obj->flags & SCENEOF_POINT) { + int inset; + + inset = exp->popup ? menu_inset : 0; vidconsole_push_colour(cons, fore, back, &old); - video_fill_part(dev, x - menu_inset, y, obj->bbox.x1, - obj->bbox.y1, vid_priv->colour_bg); + video_fill_part(dev, x - inset, y, + obj->bbox.x1, obj->bbox.y1, + vid_priv->colour_bg); } mline = alist_get(&gen->lines, 0, typeof(*mline)); @@ -632,13 +638,18 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode) case SCENEOBJT_MENU: { struct scene_obj_menu *menu = (struct scene_obj_menu *)obj; - if (exp->popup && (obj->flags & SCENEOF_OPEN)) { - if (!cons) - return -ENOTSUPP; + if (exp->popup) { + if (obj->flags & SCENEOF_OPEN) { + if (!cons) + return -ENOTSUPP; - /* draw a background behind the menu items */ - scene_render_background(obj, false); + /* draw a background behind the menu items */ + scene_render_background(obj, false, false); + } + } else if (exp->show_highlight) { + /* do nothing */ } + /* * With a vidconsole, the text and item pointer are rendered as * normal objects so we don't need to do anything here. The menu @@ -655,7 +666,7 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode) } case SCENEOBJT_TEXTLINE: if (obj->flags & SCENEOF_OPEN) - scene_render_background(obj, true); + scene_render_background(obj, true, false); break; case SCENEOBJT_BOX: { struct scene_obj_box *box = (struct scene_obj_box *)obj; diff --git a/boot/scene_menu.c b/boot/scene_menu.c index de433ece6ee..8db6a2b2f4d 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -87,7 +87,7 @@ struct scene_menitem *scene_menuitem_find_val(const 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 bool stack = scn->expo->show_highlight; const struct scene_menitem *item; int ret; @@ -108,9 +108,17 @@ static int update_pointers(struct scene_obj_menu *menu, uint id, bool point) } if (stack) { + uint id; + int val; + point &= scn->highlight_id == menu->obj.id; - scene_obj_flag_clrset(scn, item->label_id, SCENEOF_POINT, - point ? SCENEOF_POINT : 0); + val = point ? SCENEOF_POINT : 0; + id = item->desc_id; + if (!id) + id = item->label_id; + if (!id) + id = item->key_id; + scene_obj_flag_clrset(scn, id, SCENEOF_POINT, val); } return 0; diff --git a/include/expo.h b/include/expo.h index 16f2f18c4fa..dfecd6ed239 100644 --- a/include/expo.h +++ b/include/expo.h @@ -106,6 +106,7 @@ struct expo_theme { * 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 + * @show_highlight: show a highlight bar on the selected menu item * @priv: Private data for the controller * @done: Indicates that a cedit session is complete and the user has quit * @save: Indicates that cedit data should be saved, rather than discarded @@ -123,6 +124,7 @@ struct expo { struct expo_action action; bool text_mode; bool popup; + bool show_highlight; void *priv; bool done; bool save; diff --git a/test/boot/expo.c b/test/boot/expo.c index e624a00c2c0..ddfb739f9cf 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -666,6 +666,13 @@ static int expo_render_image(struct unit_test_state *uts) ut_assertok(scene_arrange(scn)); ut_asserteq(0, scn->highlight_id); + scene_set_highlight_id(scn, OBJ_MENU); + ut_assertok(scene_arrange(scn)); + ut_asserteq(OBJ_MENU, scn->highlight_id); + ut_assertok(expo_render(exp)); + + ut_asserteq(19704, video_compress_fb(uts, dev, false)); + /* move down */ ut_assertok(expo_send_key(exp, BKEY_DOWN)); @@ -719,6 +726,12 @@ static int expo_render_image(struct unit_test_state *uts) /* make sure there was no console output */ ut_assert_console_end(); + /* now try with the highlight */ + exp->show_highlight = true; + ut_assertok(scene_arrange(scn)); + ut_assertok(expo_render(exp)); + ut_asserteq(18844, video_compress_fb(uts, dev, false)); + /* now try in text mode */ expo_set_text_mode(exp, true); ut_assertok(expo_render(exp)); -- cgit v1.3.1 From 8d7ae52d7735ffecf34fa8b2657ee2fcfaac51b6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:52 -0600 Subject: expo: Drop the render from expo_poll() Within tests it is useful to be able to control rendering of the expo. Drop the automatic call to expo_render() within expo_poll() and adjust its callers to handle this instead. Signed-off-by: Simon Glass --- boot/cedit.c | 4 ++++ boot/expo.c | 4 ---- cmd/bootflow.c | 3 +++ include/expo.h | 9 ++++++--- 4 files changed, 13 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/boot/cedit.c b/boot/cedit.c index 8faf230a9e2..56dc7c6af15 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -219,6 +219,10 @@ int cedit_run(struct expo *exp) do { struct expo_action act; + ret = expo_render(exp); + if (ret) + return log_msg_ret("cer", ret); + ret = expo_poll(exp, &act); if (!ret) cedit_do_action(exp, scn, vid_priv, &act); diff --git a/boot/expo.c b/boot/expo.c index ee8ffaf8920..94413acd381 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -320,10 +320,6 @@ int expo_poll(struct expo *exp, struct expo_action *act) { int ichar, key, ret; - ret = expo_render(exp); - if (ret) - return log_msg_ret("ere", ret); - ichar = cli_ch_process(&exp->cch, 0); if (!ichar) { int i; diff --git a/cmd/bootflow.c b/cmd/bootflow.c index efac27a5d77..55643a6876f 100644 --- a/cmd/bootflow.c +++ b/cmd/bootflow.c @@ -116,6 +116,9 @@ __maybe_unused static int bootflow_handle_menu(struct bootstd_priv *std, return log_msg_ret("bhs", ret); do { + ret = expo_render(exp); + if (ret) + return log_msg_ret("bhr", ret); ret = bootflow_menu_poll(exp, &bflow); } while (ret == -EAGAIN); diff --git a/include/expo.h b/include/expo.h index dfecd6ed239..2addddd5012 100644 --- a/include/expo.h +++ b/include/expo.h @@ -1006,10 +1006,13 @@ int expo_build(ofnode root, struct expo **expp); int cb_expo_build(struct expo **expp); /** - * expo_poll() - render an expo and see if the user takes an action + * expo_poll() - see if the user takes an action * - * Thsi calls expo_render() and then checks for a keypress. If there is one, it - * is processed and the resulting action returned, if any + * This checks for a keypress. If there is one, it is processed and the + * resulting action returned, if any. + * + * Note that expo_render() should normally be called immediately before this + * function so that the user can see the latest state. * * @exp: Expo to poll * @act: Returns action on success -- cgit v1.3.1 From ddd1c97653560c6015f2c165de459ebfdc79f5d7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:53 -0600 Subject: expo: Provide a way to position things relative to display It is often necessary to centre objects within the display area. Add a special position value to indicate this. Signed-off-by: Simon Glass --- boot/scene.c | 5 +++++ include/expo.h | 6 ++++++ 2 files changed, 11 insertions(+) (limited to 'include') diff --git a/boot/scene.c b/boot/scene.c index 237f2bccc00..fa8f540bfb0 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -364,6 +364,11 @@ static void handle_alignment(enum scene_obj_align horiz, { int width, height; + if (bbox->x1 == SCENEOB_DISPLAY_MAX) + bbox->x1 = xsize ?: 1280; + if (bbox->y1 == SCENEOB_DISPLAY_MAX) + bbox->y1 = ysize ?: 1024; + width = bbox->x1 - bbox->x0; height = bbox->y1 - bbox->y0; diff --git a/include/expo.h b/include/expo.h index 2addddd5012..4dee479e9a0 100644 --- a/include/expo.h +++ b/include/expo.h @@ -242,6 +242,12 @@ struct scene_obj_dims { int y; }; +/* special values for dimensions */ +enum { + /* width/height of the display */ + SCENEOB_DISPLAY_MAX = 0x7f000000, +}; + /** * enum scene_obj_halign - Horizontal alignment of objects * -- cgit v1.3.1 From 06a9d88e4d887ea4c7395636d2cfb52948ab4112 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:55 -0600 Subject: expo: Update bootflow_menu_poll() to return a sequence ID Rather than returning a bootflow, return the index of the bootflow. This will allow callers to do their own translation to bootflows or some other data structure. Also return a special code when the user tries to move the pointer, so that the caller can cancel the boot-menu timeout, if this is in use. Signed-off-by: Simon Glass --- boot/bootflow_menu.c | 32 +++++++++++++------------------- cmd/bootflow.c | 16 ++++++++++------ include/bootflow.h | 8 ++++---- 3 files changed, 27 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/boot/bootflow_menu.c b/boot/bootflow_menu.c index fb90e51e920..6bca17142ad 100644 --- a/boot/bootflow_menu.c +++ b/boot/bootflow_menu.c @@ -301,34 +301,28 @@ int bootflow_menu_start(struct bootstd_priv *std, bool text_mode, return 0; } -int bootflow_menu_poll(struct expo *exp, struct bootflow **bflowp) +int bootflow_menu_poll(struct expo *exp, int *seqp) { struct bootflow *sel_bflow; struct expo_action act; - int ret; + struct scene *scn; + int item, ret; sel_bflow = NULL; - *bflowp = NULL; + + scn = expo_lookup_scene_id(exp, exp->scene_id); + + item = scene_menu_get_cur_item(scn, OBJ_MENU); + *seqp = item > 0 ? item - ITEM : -1; ret = expo_poll(exp, &act); if (ret) return log_msg_ret("bmp", ret); switch (act.type) { - case EXPOACT_SELECT: { - struct bootflow *bflow; - int i; - - for (ret = bootflow_first_glob(&bflow), i = 0; !ret && i < 36; - ret = bootflow_next_glob(&bflow), i++) { - if (i == act.select.id - ITEM) { - *bflowp = bflow; - // printf("found %p\n", bflow); - return 0; - } - } + case EXPOACT_SELECT: + *seqp = act.select.id - ITEM; break; - } case EXPOACT_POINT_ITEM: { struct scene *scn = expo_lookup_scene_id(exp, MAIN); @@ -337,13 +331,13 @@ int bootflow_menu_poll(struct expo *exp, struct bootflow **bflowp) ret = scene_menu_select_item(scn, OBJ_MENU, act.select.id); if (ret) return log_msg_ret("bmp", ret); - break; + return -ERESTART; } case EXPOACT_QUIT: return -EPIPE; default: - break; + return -EAGAIN; } - return -EAGAIN; + return 0; } diff --git a/cmd/bootflow.c b/cmd/bootflow.c index 55643a6876f..551dffbb8b8 100644 --- a/cmd/bootflow.c +++ b/cmd/bootflow.c @@ -109,18 +109,21 @@ __maybe_unused static int bootflow_handle_menu(struct bootstd_priv *std, { struct expo *exp; struct bootflow *bflow; - int ret; + int ret, seq; ret = bootflow_menu_start(std, text_mode, &exp); if (ret) return log_msg_ret("bhs", ret); + ret = -ERESTART; do { - ret = expo_render(exp); - if (ret) - return log_msg_ret("bhr", ret); - ret = bootflow_menu_poll(exp, &bflow); - } while (ret == -EAGAIN); + if (ret == -ERESTART) { + ret = expo_render(exp); + if (ret) + return log_msg_ret("bhr", ret); + } + ret = bootflow_menu_poll(exp, &seq); + } while (ret == -EAGAIN || ret == -ERESTART); if (ret == -EPIPE) { printf("Nothing chosen\n"); @@ -128,6 +131,7 @@ __maybe_unused static int bootflow_handle_menu(struct bootstd_priv *std, } else if (ret) { printf("Menu failed (err=%d)\n", ret); } else { + bflow = alist_getw(&std->bootflows, seq, struct bootflow); printf("Selected: %s\n", bflow->os_name ? bflow->os_name : bflow->name); std->cur_bootflow = bflow; diff --git a/include/bootflow.h b/include/bootflow.h index 615fb97f4a4..75d88d47884 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -670,10 +670,10 @@ int bootflow_menu_start(struct bootstd_priv *std, bool text_mode, * bootflow_menu_poll() - Poll a menu for user action * * @exp: Expo to poll - * @bflowp: Returns chosen bootflow (set to NULL if nothing is chosen) - * Return 0 if a bootflow was chosen, -EAGAIN if nothing is chosen yet, -EPIPE - * if the user quit + * @seqp: Returns the bootflow chosen or currently pointed to (numbered from 0) + * Return: 0 if a bootflow was chosen, -EAGAIN if nothing is chosen yet, -EPIPE + * if the user quit, -ERESTART if the expo needs refreshing */ -int bootflow_menu_poll(struct expo *exp, struct bootflow **bflowp); +int bootflow_menu_poll(struct expo *exp, int *seqp); #endif -- cgit v1.3.1 From f769735f88fe8d864a895b275b4148616ffe2a29 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 2 May 2025 08:46:56 -0600 Subject: expo: Split setting up the menu from adding items Some callers may wish to add items later as they are discovered. Split the setup code into its own function, to permit this. Signed-off-by: Simon Glass --- boot/bootflow_menu.c | 31 ++++++++++++++++++++++++++----- include/bootflow.h | 15 +++++++++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/boot/bootflow_menu.c b/boot/bootflow_menu.c index 6bca17142ad..56a34ac8ed5 100644 --- a/boot/bootflow_menu.c +++ b/boot/bootflow_menu.c @@ -210,7 +210,6 @@ int bootflow_menu_add_all(struct expo *exp) ret = bootflow_menu_add(exp, bflow, i, &scn); if (ret) return log_msg_ret("bao", ret); - } ret = scene_arrange(scn); @@ -257,7 +256,7 @@ int bootflow_menu_apply_theme(struct expo *exp, ofnode node) return 0; } -int bootflow_menu_start(struct bootstd_priv *std, bool text_mode, +int bootflow_menu_setup(struct bootstd_priv *std, bool text_mode, struct expo **expp) { struct udevice *dev; @@ -267,9 +266,6 @@ int bootflow_menu_start(struct bootstd_priv *std, bool text_mode, ret = bootflow_menu_new(&exp); if (ret) return log_msg_ret("bmn", ret); - ret = bootflow_menu_add_all(exp); - if (ret) - return log_msg_ret("bma", ret); if (ofnode_valid(std->theme)) { ret = bootflow_menu_apply_theme(exp, std->theme); @@ -292,6 +288,31 @@ int bootflow_menu_start(struct bootstd_priv *std, bool text_mode, if (text_mode) expo_set_text_mode(exp, text_mode); + *expp = exp; + + return 0; +} + +int bootflow_menu_start(struct bootstd_priv *std, bool text_mode, + struct expo **expp) +{ + struct expo *exp; + int ret; + + ret = bootflow_menu_setup(std, text_mode, &exp); + if (ret) + return log_msg_ret("bmd", ret); + + ret = bootflow_menu_add_all(exp); + if (ret) + return log_msg_ret("bma", ret); + + if (ofnode_valid(std->theme)) { + ret = expo_apply_theme(exp, std->theme); + if (ret) + return log_msg_ret("thm", ret); + } + ret = expo_calc_dims(exp); if (ret) return log_msg_ret("bmd", ret); diff --git a/include/bootflow.h b/include/bootflow.h index 75d88d47884..8dcc8f96e11 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -655,9 +655,24 @@ struct bootflow_img *bootflow_img_add(struct bootflow *bflow, const char *fname, */ int bootflow_get_seq(const struct bootflow *bflow); +/** + * bootflow_menu_setup() - Set up a menu for bootflows + * + * Set up the expo, initially empty + * + * @std: bootstd information + * @text_mode: true to show the menu in text mode, false to use video display + * @expp: Returns the expo created, on success + * Return: 0 if OK, -ve on error + */ +int bootflow_menu_setup(struct bootstd_priv *std, bool text_mode, + struct expo **expp); + /** * bootflow_menu_start() - Start up a menu for bootflows * + * Set up the expo and add items + * * @std: bootstd information * @text_mode: true to show the menu in text mode, false to use video display * @expp: Returns the expo created, on success -- cgit v1.3.1