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 <sjg@chromium.org>
This commit is contained in:
Simon Glass 2025-05-02 08:46:46 -06:00
parent 09f6f915fe
commit e005f18b93
8 changed files with 164 additions and 4 deletions

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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 */

61
boot/scene_textedit.c Normal file
View File

@ -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 <sjg@chromium.org>
*/
#define LOG_CATEGORY LOGC_EXPO
#include <expo.h>
#include <log.h>
#include <linux/err.h>
#include <linux/sizes.h>
#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;
}

View File

@ -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

View File

@ -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
*

View File

@ -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));